MMC highlights for 3.14:

Core:
  - Avoid get_cd() on cards marked nonremovable.
 
 Drivers:
  - arasan: New driver for controllers found in e.g. Xilinx Zynq SoC.
  - dwmmc: Support Hisilicon K3 SoC controllers.
  - esdhc-imx: Support for HS200 mode, DDR modes on MX6, runtime PM.
  - sdhci-pci: Support O2Micro/BayHubTech controllers used in laptops
    like Lenovo ThinkPad W540, Dell Latitude E5440, Dell Latitude E6540.
  - tegra: Support Tegra124 SoCs.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.14 (GNU/Linux)
 
 iQIcBAABAgAGBQJS5G3QAAoJEHNBYZ7TNxYMO2AP/j5JoGfNCga/Ub708yGdJJvm
 /2j2dmfrjdAnohfhwkJI8gdQcaQylZi1QVEnbsyCYCRKo5575miywnHpj4dDyYxn
 bKaYgxFZI3zK7NxOvomCm0GI1C3/bm/10+rS8bqgIGGczRo8owWK0wkipgUgeEIK
 8UHIjSLsITXRCWuFSd9SemDY3kd1VobFRD1ujJmeBaovRjkkKjAzb4cBOjoWWJPW
 YSZvbX4qm6eBrYz/pbpQX5RBUuGE91tcjD3yNtrMw/+tvg9G41vGg9iuinMmwJKh
 3CqYpaNZPhl0QyJlyMjiAmfa/XEtuzhHS9Bly9ge7FzDfg7er5QqUSbmE3FxF6NZ
 gAS49OKObT6mDdz1L7xxJQKFBE3zT+Kvz+l7ZdtFZInhhH0pdHfjva32JvBp8aEr
 oJzJa5xzjtSLq18ZVlUpKkv73ClHGckuJUm41XN/xewuP5TcndKpzVDY/xpsl4lW
 AaI12HME//uCgn9NMZUUfiMXQPTXwGF4NFQBCB+lWnAnLbuFGNPG/2Ioeh2raKEA
 +ACwAvcxSW4PtVVbA8wCTqgL1B3GMllwQChtAqfA0twmf5MucoP0OMHRONU9TpDh
 WnpbmVvI9i+ZGF4wHxcgTicDIipuzlZsBsvHuGvVhNOXe374Iyksdz4AILPmLD3m
 7kZy0K5Ms9B2IUATiUdg
 =wZBx
 -----END PGP SIGNATURE-----

Merge tag 'mmc-updates-for-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc

Pull MMC updates from Chris Ball:
 "MMC highlights for 3.14:

  Core:
   - Avoid get_cd() on cards marked nonremovable

  Drivers:
   - arasan: New driver for controllers found in e.g. Xilinx Zynq SoC
   - dwmmc: Support Hisilicon K3 SoC controllers
   - esdhc-imx: Support for HS200 mode, DDR modes on MX6, runtime PM
   - sdhci-pci: Support O2Micro/BayHubTech controllers used in laptops
     like Lenovo ThinkPad W540, Dell Latitude E5440, Dell Latitude E6540
   - tegra: Support Tegra124 SoCs"

* tag 'mmc-updates-for-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (55 commits)
  mmc: sdhci-pci: Fix possibility of chip->fixes being null
  mmc: sdhci-pci: Fix BYT sd card getting stuck in runtime suspend
  mmc: sdhci: Allow for long command timeouts
  mmc: sdio: add a quirk for broken SDIO_CCCR_INTx polling
  mmc: sdhci: fix lockdep error in tuning routine
  mmc: dw_mmc: k3: remove clk_table
  mmc: dw_mmc: fix dw_mci_get_cd
  mmc: dw_mmc: fix sparse non static symbol warning
  mmc: sdhci-esdhc-imx: fix warning during module remove function
  mmc: sdhci-esdhc-imx: fix access hardirq-unsafe lock in atomic context
  mmc: core: sd: implement proper support for sd3.0 au sizes
  mmc: atmel-mci: add vmmc-supply support
  mmc: sdhci-pci: add broken HS200 quirk for Intel Merrifield
  mmc: sdhci: add quirk for broken HS200 support
  mmc: arasan: Add driver for Arasan SDHCI
  mmc: dw_mmc: add dw_mmc-k3 for k3 platform
  mmc: dw_mmc: use slot-gpio to handle cd pin
  mmc: sdhci-pci: add support of O2Micro/BayHubTech SD hosts
  mmc: sdhci-pci: break out definitions to header file
  mmc: tmio: fixup compile error
  ...

Conflicts:
	MAINTAINERS
This commit is contained in:
Linus Torvalds 2014-01-26 11:00:41 -08:00
commit ccc039d65f
36 changed files with 1347 additions and 345 deletions

View File

@ -0,0 +1,27 @@
Device Tree Bindings for the Arasan SDHCI Controller
The bindings follow the mmc[1], clock[2] and interrupt[3] bindings. Only
deviations are documented here.
[1] Documentation/devicetree/bindings/mmc/mmc.txt
[2] Documentation/devicetree/bindings/clock/clock-bindings.txt
[3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
Required Properties:
- compatible: Compatibility string. Must be 'arasan,sdhci-8.9a'
- reg: From mmc bindings: Register location and length.
- clocks: From clock bindings: Handles to clock inputs.
- clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb"
- interrupts: Interrupt specifier
- interrupt-parent: Phandle for the interrupt controller that services
interrupts for this device.
Example:
sdhci@e0100000 {
compatible = "arasan,sdhci-8.9a";
reg = <0xe0100000 0x1000>;
clock-names = "clk_xin", "clk_ahb";
clocks = <&clkc 21>, <&clkc 32>;
interrupt-parent = <&gic>;
interrupts = <0 24 4>;
} ;

View File

@ -0,0 +1,46 @@
* Hisilicon specific extensions to the Synopsys Designware Mobile
Storage Host Controller
Read synopsys-dw-mshc.txt for more details
The Synopsys designware mobile storage host controller is used to interface
a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
differences between the core Synopsys dw mshc controller properties described
by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific
extensions to the Synopsys Designware Mobile Storage Host Controller.
Required Properties:
* compatible: should be one of the following.
- "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extentions.
Example:
/* for Hi3620 */
/* SoC portion */
dwmmc_0: dwmmc0@fcd03000 {
compatible = "hisilicon,hi4511-dw-mshc";
reg = <0xfcd03000 0x1000>;
interrupts = <0 16 4>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
clock-names = "ciu", "biu";
};
/* Board portion */
dwmmc0@fcd03000 {
num-slots = <1>;
vmmc-supply = <&ldo12>;
fifo-depth = <0x100>;
supports-highspeed;
pinctrl-names = "default";
pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
slot@0 {
reg = <0>;
bus-width = <4>;
disable-wp;
cd-gpios = <&gpio10 3 0>;
};
};

View File

@ -1409,6 +1409,7 @@ F: drivers/cpuidle/cpuidle-zynq.c
N: zynq
N: xilinx
F: drivers/clocksource/cadence_ttc_timer.c
F: drivers/mmc/host/sdhci-of-arasan.c
ARM SMMU DRIVER
M: Will Deacon <will.deacon@arm.com>
@ -5782,7 +5783,7 @@ F: drivers/mfd/
F: include/linux/mfd/
MULTIMEDIA CARD (MMC), SECURE DIGITAL (SD) AND SDIO SUBSYSTEM
M: Chris Ball <cjb@laptop.org>
M: Chris Ball <chris@printf.net>
L: linux-mmc@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
S: Maintained
@ -7609,7 +7610,7 @@ S: Maintained
F: drivers/mmc/host/sdricoh_cs.c
SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER
M: Chris Ball <cjb@laptop.org>
M: Chris Ball <chris@printf.net>
L: linux-mmc@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc.git
S: Maintained
@ -8768,7 +8769,6 @@ F: include/linux/toshiba.h
F: include/uapi/linux/toshiba.h
TMIO MMC DRIVER
M: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
M: Ian Molton <ian@mnementh.co.uk>
L: linux-mmc@vger.kernel.org
S: Maintained

View File

@ -1959,6 +1959,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
struct mmc_card *card = md->queue.card;
struct mmc_host *host = card->host;
unsigned long flags;
unsigned int cmd_flags = req ? req->cmd_flags : 0;
if (req && !mq->mqrq_prev->req)
/* claim host only for the first request */
@ -1974,7 +1975,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
}
mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
if (req && req->cmd_flags & REQ_DISCARD) {
if (cmd_flags & REQ_DISCARD) {
/* complete ongoing async transfer before issuing discard */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
@ -1983,7 +1984,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
ret = mmc_blk_issue_secdiscard_rq(mq, req);
else
ret = mmc_blk_issue_discard_rq(mq, req);
} else if (req && req->cmd_flags & REQ_FLUSH) {
} else if (cmd_flags & REQ_FLUSH) {
/* complete ongoing async transfer before issuing flush */
if (card->host->areq)
mmc_blk_issue_rw_rq(mq, NULL);
@ -1999,7 +2000,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
out:
if ((!req && !(mq->flags & MMC_QUEUE_NEW_REQUEST)) ||
(req && (req->cmd_flags & MMC_REQ_SPECIAL_MASK)))
(cmd_flags & MMC_REQ_SPECIAL_MASK))
/*
* Release host when there are no more requests
* and after special request(discard, flush) is done.

View File

@ -2460,7 +2460,8 @@ void mmc_rescan(struct work_struct *work)
*/
mmc_bus_put(host);
if (host->ops->get_cd && host->ops->get_cd(host) == 0) {
if (!(host->caps & MMC_CAP_NONREMOVABLE) && host->ops->get_cd &&
host->ops->get_cd(host) == 0) {
mmc_claim_host(host);
mmc_power_off(host);
mmc_release_host(host);

View File

@ -1119,14 +1119,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
*/
if (mmc_card_highspeed(card)) {
if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_8V)
&& ((host->caps & (MMC_CAP_1_8V_DDR |
MMC_CAP_UHS_DDR50))
== (MMC_CAP_1_8V_DDR | MMC_CAP_UHS_DDR50)))
&& (host->caps & MMC_CAP_1_8V_DDR))
ddr = MMC_1_8V_DDR_MODE;
else if ((card->ext_csd.card_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
&& ((host->caps & (MMC_CAP_1_2V_DDR |
MMC_CAP_UHS_DDR50))
== (MMC_CAP_1_2V_DDR | MMC_CAP_UHS_DDR50)))
&& (host->caps & MMC_CAP_1_2V_DDR))
ddr = MMC_1_2V_DDR_MODE;
}

View File

@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/export.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_ids.h>
#ifndef SDIO_VENDOR_ID_TI
#define SDIO_VENDOR_ID_TI 0x0097
@ -30,6 +31,10 @@
#define SDIO_DEVICE_ID_STE_CW1200 0x2280
#endif
#ifndef SDIO_DEVICE_ID_MARVELL_8797_F0
#define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128
#endif
/*
* This hook just adds a quirk for all sdio devices
*/
@ -58,6 +63,9 @@ static const struct mmc_fixup mmc_fixup_methods[] = {
SDIO_FIXUP(SDIO_VENDOR_ID_STE, SDIO_DEVICE_ID_STE_CW1200,
add_quirk, MMC_QUIRK_BROKEN_BYTE_MODE_512),
SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0,
add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING),
END_FIXUP
};

View File

@ -11,6 +11,7 @@
*/
#include <linux/err.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/pm_runtime.h>
@ -45,6 +46,13 @@ static const unsigned int tacc_mant[] = {
35, 40, 45, 50, 55, 60, 70, 80,
};
static const unsigned int sd_au_size[] = {
0, SZ_16K / 512, SZ_32K / 512, SZ_64K / 512,
SZ_128K / 512, SZ_256K / 512, SZ_512K / 512, SZ_1M / 512,
SZ_2M / 512, SZ_4M / 512, SZ_8M / 512, (SZ_8M + SZ_4M) / 512,
SZ_16M / 512, (SZ_16M + SZ_8M) / 512, SZ_32M / 512, SZ_64M / 512,
};
#define UNSTUFF_BITS(resp,start,size) \
({ \
const int __size = size; \
@ -216,7 +224,7 @@ static int mmc_decode_scr(struct mmc_card *card)
static int mmc_read_ssr(struct mmc_card *card)
{
unsigned int au, es, et, eo;
int err, i, max_au;
int err, i;
u32 *ssr;
if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
@ -240,26 +248,25 @@ static int mmc_read_ssr(struct mmc_card *card)
for (i = 0; i < 16; i++)
ssr[i] = be32_to_cpu(ssr[i]);
/* SD3.0 increases max AU size to 64MB (0xF) from 4MB (0x9) */
max_au = card->scr.sda_spec3 ? 0xF : 0x9;
/*
* UNSTUFF_BITS only works with four u32s so we have to offset the
* bitfield positions accordingly.
*/
au = UNSTUFF_BITS(ssr, 428 - 384, 4);
if (au > 0 && au <= max_au) {
card->ssr.au = 1 << (au + 4);
es = UNSTUFF_BITS(ssr, 408 - 384, 16);
et = UNSTUFF_BITS(ssr, 402 - 384, 6);
eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
if (es && et) {
card->ssr.erase_timeout = (et * 1000) / es;
card->ssr.erase_offset = eo * 1000;
if (au) {
if (au <= 9 || card->scr.sda_spec3) {
card->ssr.au = sd_au_size[au];
es = UNSTUFF_BITS(ssr, 408 - 384, 16);
et = UNSTUFF_BITS(ssr, 402 - 384, 6);
if (es && et) {
eo = UNSTUFF_BITS(ssr, 400 - 384, 2);
card->ssr.erase_timeout = (et * 1000) / es;
card->ssr.erase_offset = eo * 1000;
}
} else {
pr_warning("%s: SD Status: Invalid Allocation Unit size.\n",
mmc_hostname(card->host));
}
} else {
pr_warning("%s: SD Status: Invalid Allocation Unit "
"size.\n", mmc_hostname(card->host));
}
out:
kfree(ssr);

View File

@ -53,6 +53,17 @@ static int process_sdio_pending_irqs(struct mmc_host *host)
return ret;
}
if (pending && mmc_card_broken_irq_polling(card) &&
!(host->caps & MMC_CAP_SDIO_IRQ)) {
unsigned char dummy;
/* A fake interrupt could be created when we poll SDIO_CCCR_INTx
* register with a Marvell SD8797 card. A dummy CMD52 read to
* function 0 register 0xff can avoid this.
*/
mmc_io_rw_direct(card, 0, 0, 0xff, 0, &dummy);
}
count = 0;
for (i = 1; i <= 7; i++) {
if (pending & (1 << i)) {

View File

@ -104,6 +104,18 @@ config MMC_SDHCI_PLTFM
If unsure, say N.
config MMC_SDHCI_OF_ARASAN
tristate "SDHCI OF support for the Arasan SDHCI controllers"
depends on MMC_SDHCI_PLTFM
depends on OF
help
This selects the Arasan Secure Digital Host Controller Interface
(SDHCI). This hardware is found e.g. in Xilinx' Zynq SoC.
If you have a controller with this interface, say Y or M here.
If unsure, say N.
config MMC_SDHCI_OF_ESDHC
tristate "SDHCI OF support for the Freescale eSDHC controller"
depends on MMC_SDHCI_PLTFM
@ -479,7 +491,8 @@ config MMC_TMIO
config MMC_SDHI
tristate "SH-Mobile SDHI SD/SDIO controller support"
depends on SUPERH || ARCH_SHMOBILE
depends on SUPERH || ARM
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
select MMC_TMIO_CORE
help
This provides support for the SDHI SD/SDIO controller found in
@ -575,6 +588,16 @@ config MMC_DW_SOCFPGA
This selects support for Altera SoCFPGA specific extensions to the
Synopsys DesignWare Memory Card Interface driver.
config MMC_DW_K3
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
select MMC_DW_PLTFM
select MMC_DW_IDMAC
help
This selects support for Hisilicon K3 SoC specific extensions to the
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Hisilicon K3 SoC's.
config MMC_DW_PCI
tristate "Synopsys Designware MCI support on PCI bus"
depends on MMC_DW && PCI
@ -588,7 +611,8 @@ config MMC_DW_PCI
config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support"
depends on MMC_BLOCK && (SUPERH || ARCH_SHMOBILE)
depends on MMC_BLOCK
depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST
help
This selects the MMC Host Interface controller (MMCIF).

View File

@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o
obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-o2micro.o
obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o
obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o
obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o
@ -43,6 +44,7 @@ obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
@ -57,6 +59,7 @@ obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci-tegra.o
obj-$(CONFIG_MMC_SDHCI_OF_ARASAN) += sdhci-of-arasan.o
obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o

View File

@ -1192,11 +1192,22 @@ static void atmci_start_request(struct atmel_mci *host,
iflags |= ATMCI_CMDRDY;
cmd = mrq->cmd;
cmdflags = atmci_prepare_command(slot->mmc, cmd);
atmci_send_command(host, cmd, cmdflags);
/*
* DMA transfer should be started before sending the command to avoid
* unexpected errors especially for read operations in SDIO mode.
* Unfortunately, in PDC mode, command has to be sent before starting
* the transfer.
*/
if (host->submit_data != &atmci_submit_data_dma)
atmci_send_command(host, cmd, cmdflags);
if (data)
host->submit_data(host, data);
if (host->submit_data == &atmci_submit_data_dma)
atmci_send_command(host, cmd, cmdflags);
if (mrq->stop) {
host->stop_cmdr = atmci_prepare_command(slot->mmc, mrq->stop);
host->stop_cmdr |= ATMCI_CMDR_STOP_XFER;
@ -1391,8 +1402,14 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk_unprepare(host->mck);
switch (ios->power_mode) {
case MMC_POWER_OFF:
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
break;
case MMC_POWER_UP:
set_bit(ATMCI_CARD_NEED_INIT, &slot->flags);
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
break;
default:
/*
@ -2204,6 +2221,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
}
host->slot[id] = slot;
mmc_regulator_get_supply(mmc);
mmc_add_host(mmc);
if (gpio_is_valid(slot->detect_pin)) {

View File

@ -0,0 +1,95 @@
/*
* Copyright (c) 2013 Linaro Ltd.
* Copyright (c) 2013 Hisilicon Limited.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/mmc/host.h>
#include <linux/mmc/dw_mmc.h>
#include <linux/of_address.h>
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
int ret;
ret = clk_set_rate(host->ciu_clk, ios->clock);
if (ret)
dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock);
host->bus_hz = clk_get_rate(host->ciu_clk);
}
static const struct dw_mci_drv_data k3_drv_data = {
.set_ios = dw_mci_k3_set_ios,
};
static const struct of_device_id dw_mci_k3_match[] = {
{ .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_k3_match);
static int dw_mci_k3_probe(struct platform_device *pdev)
{
const struct dw_mci_drv_data *drv_data;
const struct of_device_id *match;
match = of_match_node(dw_mci_k3_match, pdev->dev.of_node);
drv_data = match->data;
return dw_mci_pltfm_register(pdev, drv_data);
}
static int dw_mci_k3_suspend(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
int ret;
ret = dw_mci_suspend(host);
if (!ret)
clk_disable_unprepare(host->ciu_clk);
return ret;
}
static int dw_mci_k3_resume(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(host->ciu_clk);
if (ret) {
dev_err(host->dev, "failed to enable ciu clock\n");
return ret;
}
return dw_mci_resume(host);
}
static SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume);
static struct platform_driver dw_mci_k3_pltfm_driver = {
.probe = dw_mci_k3_probe,
.remove = dw_mci_pltfm_remove,
.driver = {
.name = "dwmmc_k3",
.of_match_table = dw_mci_k3_match,
.pm = &dw_mci_k3_pmops,
},
};
module_platform_driver(dw_mci_k3_pltfm_driver);
MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:dwmmc-k3");

View File

@ -36,6 +36,7 @@
#include <linux/workqueue.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/mmc/slot-gpio.h>
#include "dw_mmc.h"
@ -1032,20 +1033,29 @@ static int dw_mci_get_cd(struct mmc_host *mmc)
int present;
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci_board *brd = slot->host->pdata;
struct dw_mci *host = slot->host;
int gpio_cd = mmc_gpio_get_cd(mmc);
/* Use platform get_cd function, else try onboard card detect */
if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION)
present = 1;
else if (brd->get_cd)
present = !brd->get_cd(slot->id);
else if (!IS_ERR_VALUE(gpio_cd))
present = gpio_cd;
else
present = (mci_readl(slot->host, CDETECT) & (1 << slot->id))
== 0 ? 1 : 0;
if (present)
spin_lock_bh(&host->lock);
if (present) {
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
dev_dbg(&mmc->class_dev, "card is present\n");
else
} else {
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
dev_dbg(&mmc->class_dev, "card is not present\n");
}
spin_unlock_bh(&host->lock);
return present;
}
@ -1926,10 +1936,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
/* Card change detected */
slot->last_detect_state = present;
/* Mark card as present if applicable */
if (present != 0)
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
/* Clean up queue if present */
mrq = slot->mrq;
if (mrq) {
@ -1977,8 +1983,6 @@ static void dw_mci_work_routine_card(struct work_struct *work)
/* Power down slot */
if (present == 0) {
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
/* Clear down the FIFO */
dw_mci_fifo_reset(host);
#ifdef CONFIG_MMC_DW_IDMAC
@ -2079,6 +2083,26 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
return gpio;
}
/* find the cd gpio for a given slot */
static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot,
struct mmc_host *mmc)
{
struct device_node *np = dw_mci_of_find_slot_node(dev, slot);
int gpio;
if (!np)
return;
gpio = of_get_named_gpio(np, "cd-gpios", 0);
/* Having a missing entry is valid; return silently */
if (!gpio_is_valid(gpio))
return;
if (mmc_gpio_request_cd(mmc, gpio, 0))
dev_warn(dev, "gpio [%d] request failed\n", gpio);
}
#else /* CONFIG_OF */
static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
{
@ -2096,6 +2120,11 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot)
{
return -EINVAL;
}
static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot,
struct mmc_host *mmc)
{
return;
}
#endif /* CONFIG_OF */
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
@ -2197,12 +2226,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
#endif /* CONFIG_MMC_DW_IDMAC */
}
if (dw_mci_get_cd(mmc))
set_bit(DW_MMC_CARD_PRESENT, &slot->flags);
else
clear_bit(DW_MMC_CARD_PRESENT, &slot->flags);
slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id);
dw_mci_of_get_cd_gpio(host->dev, slot->id, mmc);
ret = mmc_add_host(mmc);
if (ret)
@ -2389,6 +2414,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host)
if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL))
pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
if (of_get_property(np, "cd-inverted", NULL))
pdata->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
return pdata;
}

View File

@ -38,6 +38,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/module.h>
@ -69,37 +70,25 @@ struct mxs_mmc_host {
unsigned char bus_width;
spinlock_t lock;
int sdio_irq_en;
int wp_gpio;
bool wp_inverted;
bool cd_inverted;
bool broken_cd;
bool non_removable;
};
static int mxs_mmc_get_ro(struct mmc_host *mmc)
{
struct mxs_mmc_host *host = mmc_priv(mmc);
int ret;
if (!gpio_is_valid(host->wp_gpio))
return -EINVAL;
ret = gpio_get_value(host->wp_gpio);
if (host->wp_inverted)
ret = !ret;
return ret;
}
static int mxs_mmc_get_cd(struct mmc_host *mmc)
{
struct mxs_mmc_host *host = mmc_priv(mmc);
struct mxs_ssp *ssp = &host->ssp;
int present, ret;
return host->non_removable || host->broken_cd ||
!(readl(ssp->base + HW_SSP_STATUS(ssp)) &
BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted;
ret = mmc_gpio_get_cd(mmc);
if (ret >= 0)
return ret;
present = !(readl(ssp->base + HW_SSP_STATUS(ssp)) &
BM_SSP_STATUS_CARD_DETECT);
if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH)
present = !present;
return present;
}
static int mxs_mmc_reset(struct mxs_mmc_host *host)
@ -549,7 +538,7 @@ static void mxs_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
static const struct mmc_host_ops mxs_mmc_ops = {
.request = mxs_mmc_request,
.get_ro = mxs_mmc_get_ro,
.get_ro = mmc_gpio_get_ro,
.get_cd = mxs_mmc_get_cd,
.set_ios = mxs_mmc_set_ios,
.enable_sdio_irq = mxs_mmc_enable_sdio_irq,
@ -579,15 +568,12 @@ static int mxs_mmc_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
of_match_device(mxs_mmc_dt_ids, &pdev->dev);
struct device_node *np = pdev->dev.of_node;
struct mxs_mmc_host *host;
struct mmc_host *mmc;
struct resource *iores;
int ret = 0, irq_err;
struct regulator *reg_vmmc;
enum of_gpio_flags flags;
struct mxs_ssp *ssp;
u32 bus_width = 0;
iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq_err = platform_get_irq(pdev, 0);
@ -648,23 +634,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL;
of_property_read_u32(np, "bus-width", &bus_width);
if (bus_width == 4)
mmc->caps |= MMC_CAP_4_BIT_DATA;
else if (bus_width == 8)
mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA;
host->broken_cd = of_property_read_bool(np, "broken-cd");
host->non_removable = of_property_read_bool(np, "non-removable");
if (host->non_removable)
mmc->caps |= MMC_CAP_NONREMOVABLE;
host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
if (flags & OF_GPIO_ACTIVE_LOW)
host->wp_inverted = 1;
host->cd_inverted = of_property_read_bool(np, "cd-inverted");
mmc->f_min = 400000;
mmc->f_max = 288000000;
ret = mmc_of_parse(mmc);
if (ret)
goto out_clk_disable;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
mmc->max_segs = 52;

View File

@ -143,6 +143,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd },
{ "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio },
{ "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio },
{ "INT3436" , NULL, &sdhci_acpi_slot_int_sdio },
{ "PNP0D40" },
{ },
};
@ -151,6 +152,7 @@ static const struct acpi_device_id sdhci_acpi_ids[] = {
{ "80860F14" },
{ "INT33BB" },
{ "INT33C6" },
{ "INT3436" },
{ "PNP0D40" },
{ },
};

View File

@ -27,6 +27,7 @@
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_data/mmc-esdhc-imx.h>
#include <linux/pm_runtime.h>
#include "sdhci-pltfm.h"
#include "sdhci-esdhc.h"
@ -45,6 +46,8 @@
#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
/* Bits 3 and 6 are not SDHCI standard definitions */
#define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
/* Tuning bits */
#define ESDHC_MIX_CTRL_TUNING_MASK 0x03c00000
/* dll control register */
#define ESDHC_DLL_CTRL 0x60
@ -385,6 +388,22 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg)
return ret;
}
if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
if (esdhc_is_usdhc(imx_data)) {
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
/* Swap AC23 bit */
if (m & ESDHC_MIX_CTRL_AC23EN) {
ret &= ~ESDHC_MIX_CTRL_AC23EN;
ret |= SDHCI_TRNS_AUTO_CMD23;
}
} else {
ret = readw(host->ioaddr + SDHCI_TRANSFER_MODE);
}
return ret;
}
return readw(host->ioaddr + reg);
}
@ -421,24 +440,20 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
} else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR);
u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL);
new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL);
if (val & SDHCI_CTRL_EXEC_TUNING) {
new_val |= ESDHC_STD_TUNING_EN |
ESDHC_TUNING_START_TAP;
v |= ESDHC_MIX_CTRL_EXE_TUNE;
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
if (val & SDHCI_CTRL_TUNED_CLK) {
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
} else {
new_val &= ~ESDHC_STD_TUNING_EN;
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
}
if (val & SDHCI_CTRL_TUNED_CLK)
v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
else
v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
if (val & SDHCI_CTRL_EXEC_TUNING) {
v |= ESDHC_MIX_CTRL_EXE_TUNE;
m |= ESDHC_MIX_CTRL_FBCLK_SEL;
} else {
v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
}
writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL);
writel(v, host->ioaddr + SDHCI_ACMD12_ERR);
writel(m, host->ioaddr + ESDHC_MIX_CTRL);
}
@ -546,7 +561,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
* Do it manually here.
*/
if (esdhc_is_usdhc(imx_data)) {
writel(0, host->ioaddr + ESDHC_MIX_CTRL);
/* the tuning bits should be kept during reset */
new_val = readl(host->ioaddr + ESDHC_MIX_CTRL);
writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
host->ioaddr + ESDHC_MIX_CTRL);
imx_data->is_ddr = 0;
}
}
@ -558,19 +576,17 @@ static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)
struct pltfm_imx_data *imx_data = pltfm_host->priv;
struct esdhc_platform_data *boarddata = &imx_data->boarddata;
u32 f_host = clk_get_rate(pltfm_host->clk);
if (boarddata->f_max && (boarddata->f_max < f_host))
if (boarddata->f_max && (boarddata->f_max < pltfm_host->clock))
return boarddata->f_max;
else
return f_host;
return pltfm_host->clock;
}
static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
return clk_get_rate(pltfm_host->clk) / 256 / 16;
return pltfm_host->clock / 256 / 16;
}
static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
@ -578,7 +594,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host,
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
unsigned int host_clock = clk_get_rate(pltfm_host->clk);
unsigned int host_clock = pltfm_host->clock;
int pre_div = 2;
int div = 1;
u32 temp, val;
@ -681,6 +697,7 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val)
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
mdelay(1);
pm_runtime_get_sync(host->mmc->parent);
reg = readl(host->ioaddr + ESDHC_MIX_CTRL);
reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
ESDHC_MIX_CTRL_FBCLK_SEL;
@ -699,7 +716,7 @@ static void esdhc_request_done(struct mmc_request *mrq)
static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode)
{
struct mmc_command cmd = {0};
struct mmc_request mrq = {0};
struct mmc_request mrq = {NULL};
struct mmc_data data = {0};
struct scatterlist sg;
char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN];
@ -809,6 +826,7 @@ static int esdhc_change_pinstate(struct sdhci_host *host,
pinctrl = imx_data->pins_100mhz;
break;
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200:
pinctrl = imx_data->pins_200mhz;
break;
default:
@ -836,6 +854,7 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50;
break;
case MMC_TIMING_UHS_SDR104:
case MMC_TIMING_MMC_HS200:
imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104;
break;
case MMC_TIMING_UHS_DDR50:
@ -976,7 +995,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
}
pltfm_host->clk = imx_data->clk_per;
pltfm_host->clock = clk_get_rate(pltfm_host->clk);
clk_prepare_enable(imx_data->clk_per);
clk_prepare_enable(imx_data->clk_ipg);
clk_prepare_enable(imx_data->clk_ahb);
@ -1009,11 +1028,18 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (esdhc_is_usdhc(imx_data)) {
writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL);
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
host->mmc->caps |= MMC_CAP_1_8V_DDR;
}
if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING)
sdhci_esdhc_ops.platform_execute_tuning =
esdhc_executing_tuning;
if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING)
writel(readl(host->ioaddr + ESDHC_TUNING_CTRL) |
ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP,
host->ioaddr + ESDHC_TUNING_CTRL);
boarddata = &imx_data->boarddata;
if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) {
if (!host->mmc->parent->platform_data) {
@ -1053,7 +1079,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
break;
case ESDHC_CD_PERMANENT:
host->mmc->caps = MMC_CAP_NONREMOVABLE;
host->mmc->caps |= MMC_CAP_NONREMOVABLE;
break;
case ESDHC_CD_NONE:
@ -1094,6 +1120,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev)
if (err)
goto disable_clk;
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_use_autosuspend(&pdev->dev);
pm_suspend_ignore_children(&pdev->dev, 1);
return 0;
disable_clk:
@ -1114,21 +1146,63 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev)
sdhci_remove_host(host, dead);
clk_disable_unprepare(imx_data->clk_per);
clk_disable_unprepare(imx_data->clk_ipg);
clk_disable_unprepare(imx_data->clk_ahb);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!IS_ENABLED(CONFIG_PM_RUNTIME)) {
clk_disable_unprepare(imx_data->clk_per);
clk_disable_unprepare(imx_data->clk_ipg);
clk_disable_unprepare(imx_data->clk_ahb);
}
sdhci_pltfm_free(pdev);
return 0;
}
#ifdef CONFIG_PM_RUNTIME
static int sdhci_esdhc_runtime_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
int ret;
ret = sdhci_runtime_suspend_host(host);
clk_disable_unprepare(imx_data->clk_per);
clk_disable_unprepare(imx_data->clk_ipg);
clk_disable_unprepare(imx_data->clk_ahb);
return ret;
}
static int sdhci_esdhc_runtime_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
clk_prepare_enable(imx_data->clk_per);
clk_prepare_enable(imx_data->clk_ipg);
clk_prepare_enable(imx_data->clk_ahb);
return sdhci_runtime_resume_host(host);
}
#endif
static const struct dev_pm_ops sdhci_esdhc_pmops = {
SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_pltfm_resume)
SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend,
sdhci_esdhc_runtime_resume, NULL)
};
static struct platform_driver sdhci_esdhc_imx_driver = {
.driver = {
.name = "sdhci-esdhc-imx",
.owner = THIS_MODULE,
.of_match_table = imx_esdhc_dt_ids,
.pm = SDHCI_PLTFM_PMOPS,
.pm = &sdhci_esdhc_pmops,
},
.id_table = imx_esdhc_devtype,
.probe = sdhci_esdhc_imx_probe,

View File

@ -0,0 +1,224 @@
/*
* Arasan Secure Digital Host Controller Interface.
* Copyright (C) 2011 - 2012 Michal Simek <monstr@monstr.eu>
* Copyright (c) 2012 Wind River Systems, Inc.
* Copyright (C) 2013 Pengutronix e.K.
* Copyright (C) 2013 Xilinx Inc.
*
* Based on sdhci-of-esdhc.c
*
* Copyright (c) 2007 Freescale Semiconductor, Inc.
* Copyright (c) 2009 MontaVista Software, Inc.
*
* Authors: Xiaobo Xie <X.Xie@freescale.com>
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#include <linux/module.h>
#include "sdhci-pltfm.h"
#define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c
#define CLK_CTRL_TIMEOUT_SHIFT 16
#define CLK_CTRL_TIMEOUT_MASK (0xf << CLK_CTRL_TIMEOUT_SHIFT)
#define CLK_CTRL_TIMEOUT_MIN_EXP 13
/**
* struct sdhci_arasan_data
* @clk_ahb: Pointer to the AHB clock
*/
struct sdhci_arasan_data {
struct clk *clk_ahb;
};
static unsigned int sdhci_arasan_get_timeout_clock(struct sdhci_host *host)
{
u32 div;
unsigned long freq;
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
div = readl(host->ioaddr + SDHCI_ARASAN_CLK_CTRL_OFFSET);
div = (div & CLK_CTRL_TIMEOUT_MASK) >> CLK_CTRL_TIMEOUT_SHIFT;
freq = clk_get_rate(pltfm_host->clk);
freq /= 1 << (CLK_CTRL_TIMEOUT_MIN_EXP + div);
return freq;
}
static struct sdhci_ops sdhci_arasan_ops = {
.get_max_clock = sdhci_pltfm_clk_get_max_clock,
.get_timeout_clock = sdhci_arasan_get_timeout_clock,
};
static struct sdhci_pltfm_data sdhci_arasan_pdata = {
.ops = &sdhci_arasan_ops,
};
#ifdef CONFIG_PM_SLEEP
/**
* sdhci_arasan_suspend - Suspend method for the driver
* @dev: Address of the device structure
* Returns 0 on success and error value on error
*
* Put the device in a low power state.
*/
static int sdhci_arasan_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
int ret;
ret = sdhci_suspend_host(host);
if (ret)
return ret;
clk_disable(pltfm_host->clk);
clk_disable(sdhci_arasan->clk_ahb);
return 0;
}
/**
* sdhci_arasan_resume - Resume method for the driver
* @dev: Address of the device structure
* Returns 0 on success and error value on error
*
* Resume operation after suspend
*/
static int sdhci_arasan_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
int ret;
ret = clk_enable(sdhci_arasan->clk_ahb);
if (ret) {
dev_err(dev, "Cannot enable AHB clock.\n");
return ret;
}
ret = clk_enable(pltfm_host->clk);
if (ret) {
dev_err(dev, "Cannot enable SD clock.\n");
clk_disable(sdhci_arasan->clk_ahb);
return ret;
}
return sdhci_resume_host(host);
}
#endif /* ! CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend,
sdhci_arasan_resume);
static int sdhci_arasan_probe(struct platform_device *pdev)
{
int ret;
struct clk *clk_xin;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_arasan_data *sdhci_arasan;
sdhci_arasan = devm_kzalloc(&pdev->dev, sizeof(*sdhci_arasan),
GFP_KERNEL);
if (!sdhci_arasan)
return -ENOMEM;
sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb");
if (IS_ERR(sdhci_arasan->clk_ahb)) {
dev_err(&pdev->dev, "clk_ahb clock not found.\n");
return PTR_ERR(sdhci_arasan->clk_ahb);
}
clk_xin = devm_clk_get(&pdev->dev, "clk_xin");
if (IS_ERR(clk_xin)) {
dev_err(&pdev->dev, "clk_xin clock not found.\n");
return PTR_ERR(clk_xin);
}
ret = clk_prepare_enable(sdhci_arasan->clk_ahb);
if (ret) {
dev_err(&pdev->dev, "Unable to enable AHB clock.\n");
return ret;
}
ret = clk_prepare_enable(clk_xin);
if (ret) {
dev_err(&pdev->dev, "Unable to enable SD clock.\n");
goto clk_dis_ahb;
}
host = sdhci_pltfm_init(pdev, &sdhci_arasan_pdata, 0);
if (IS_ERR(host)) {
ret = PTR_ERR(host);
dev_err(&pdev->dev, "platform init failed (%u)\n", ret);
goto clk_disable_all;
}
sdhci_get_of_property(pdev);
pltfm_host = sdhci_priv(host);
pltfm_host->priv = sdhci_arasan;
pltfm_host->clk = clk_xin;
ret = sdhci_add_host(host);
if (ret) {
dev_err(&pdev->dev, "platform register failed (%u)\n", ret);
goto err_pltfm_free;
}
return 0;
err_pltfm_free:
sdhci_pltfm_free(pdev);
clk_disable_all:
clk_disable_unprepare(clk_xin);
clk_dis_ahb:
clk_disable_unprepare(sdhci_arasan->clk_ahb);
return ret;
}
static int sdhci_arasan_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_arasan_data *sdhci_arasan = pltfm_host->priv;
clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(sdhci_arasan->clk_ahb);
return sdhci_pltfm_unregister(pdev);
}
static const struct of_device_id sdhci_arasan_of_match[] = {
{ .compatible = "arasan,sdhci-8.9a" },
{ }
};
MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
static struct platform_driver sdhci_arasan_driver = {
.driver = {
.name = "sdhci-arasan",
.owner = THIS_MODULE,
.of_match_table = sdhci_arasan_of_match,
.pm = &sdhci_arasan_dev_pm_ops,
},
.probe = sdhci_arasan_probe,
.remove = sdhci_arasan_remove,
};
module_platform_driver(sdhci_arasan_driver);
MODULE_DESCRIPTION("Driver for the Arasan SDHCI Controller");
MODULE_AUTHOR("Soeren Brinkmann <soren.brinkmann@xilinx.com>");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,321 @@
/*
* Copyright (C) 2013 BayHub Technology Ltd.
*
* Authors: Peter Guo <peter.guo@bayhubtech.com>
* Adam Lee <adam.lee@canonical.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/pci.h>
#include "sdhci.h"
#include "sdhci-pci.h"
#include "sdhci-pci-o2micro.h"
void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip)
{
u32 scratch_32;
int ret;
/* Improve write performance for SD3.0 */
ret = pci_read_config_dword(chip->pdev, O2_SD_DEV_CTRL, &scratch_32);
if (ret)
return;
scratch_32 &= ~((1 << 12) | (1 << 13) | (1 << 14));
pci_write_config_dword(chip->pdev, O2_SD_DEV_CTRL, scratch_32);
/* Enable Link abnormal reset generating Reset */
ret = pci_read_config_dword(chip->pdev, O2_SD_MISC_REG5, &scratch_32);
if (ret)
return;
scratch_32 &= ~((1 << 19) | (1 << 11));
scratch_32 |= (1 << 10);
pci_write_config_dword(chip->pdev, O2_SD_MISC_REG5, scratch_32);
/* set card power over current protection */
ret = pci_read_config_dword(chip->pdev, O2_SD_TEST_REG, &scratch_32);
if (ret)
return;
scratch_32 |= (1 << 4);
pci_write_config_dword(chip->pdev, O2_SD_TEST_REG, scratch_32);
/* adjust the output delay for SD mode */
pci_write_config_dword(chip->pdev, O2_SD_DELAY_CTRL, 0x00002492);
/* Set the output voltage setting of Aux 1.2v LDO */
ret = pci_read_config_dword(chip->pdev, O2_SD_LD0_CTRL, &scratch_32);
if (ret)
return;
scratch_32 &= ~(3 << 12);
pci_write_config_dword(chip->pdev, O2_SD_LD0_CTRL, scratch_32);
/* Set Max power supply capability of SD host */
ret = pci_read_config_dword(chip->pdev, O2_SD_CAP_REG0, &scratch_32);
if (ret)
return;
scratch_32 &= ~(0x01FE);
scratch_32 |= 0x00CC;
pci_write_config_dword(chip->pdev, O2_SD_CAP_REG0, scratch_32);
/* Set DLL Tuning Window */
ret = pci_read_config_dword(chip->pdev,
O2_SD_TUNING_CTRL, &scratch_32);
if (ret)
return;
scratch_32 &= ~(0x000000FF);
scratch_32 |= 0x00000066;
pci_write_config_dword(chip->pdev, O2_SD_TUNING_CTRL, scratch_32);
/* Set UHS2 T_EIDLE */
ret = pci_read_config_dword(chip->pdev,
O2_SD_UHS2_L1_CTRL, &scratch_32);
if (ret)
return;
scratch_32 &= ~(0x000000FC);
scratch_32 |= 0x00000084;
pci_write_config_dword(chip->pdev, O2_SD_UHS2_L1_CTRL, scratch_32);
/* Set UHS2 Termination */
ret = pci_read_config_dword(chip->pdev, O2_SD_FUNC_REG3, &scratch_32);
if (ret)
return;
scratch_32 &= ~((1 << 21) | (1 << 30));
/* Set RTD3 function disabled */
scratch_32 |= ((1 << 29) | (1 << 28));
pci_write_config_dword(chip->pdev, O2_SD_FUNC_REG3, scratch_32);
/* Set L1 Entrance Timer */
ret = pci_read_config_dword(chip->pdev, O2_SD_CAPS, &scratch_32);
if (ret)
return;
scratch_32 &= ~(0xf0000000);
scratch_32 |= 0x30000000;
pci_write_config_dword(chip->pdev, O2_SD_CAPS, scratch_32);
ret = pci_read_config_dword(chip->pdev,
O2_SD_MISC_CTRL4, &scratch_32);
if (ret)
return;
scratch_32 &= ~(0x000f0000);
scratch_32 |= 0x00080000;
pci_write_config_dword(chip->pdev, O2_SD_MISC_CTRL4, scratch_32);
}
EXPORT_SYMBOL_GPL(sdhci_pci_o2_fujin2_pci_init);
int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot)
{
struct sdhci_pci_chip *chip;
struct sdhci_host *host;
u32 reg;
chip = slot->chip;
host = slot->host;
switch (chip->pdev->device) {
case PCI_DEVICE_ID_O2_SDS0:
case PCI_DEVICE_ID_O2_SEABIRD0:
case PCI_DEVICE_ID_O2_SEABIRD1:
case PCI_DEVICE_ID_O2_SDS1:
case PCI_DEVICE_ID_O2_FUJIN2:
reg = sdhci_readl(host, O2_SD_VENDOR_SETTING);
if (reg & 0x1)
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
if (chip->pdev->device != PCI_DEVICE_ID_O2_FUJIN2)
break;
/* set dll watch dog timer */
reg = sdhci_readl(host, O2_SD_VENDOR_SETTING2);
reg |= (1 << 12);
sdhci_writel(host, reg, O2_SD_VENDOR_SETTING2);
break;
default:
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe_slot);
int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip)
{
int ret;
u8 scratch;
u32 scratch_32;
switch (chip->pdev->device) {
case PCI_DEVICE_ID_O2_8220:
case PCI_DEVICE_ID_O2_8221:
case PCI_DEVICE_ID_O2_8320:
case PCI_DEVICE_ID_O2_8321:
/* This extra setup is required due to broken ADMA. */
ret = pci_read_config_byte(chip->pdev,
O2_SD_LOCK_WP, &scratch);
if (ret)
return ret;
scratch &= 0x7f;
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
/* Set Multi 3 to VCC3V# */
pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08);
/* Disable CLK_REQ# support after media DET */
ret = pci_read_config_byte(chip->pdev,
O2_SD_CLKREQ, &scratch);
if (ret)
return ret;
scratch |= 0x20;
pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch);
/* Choose capabilities, enable SDMA. We have to write 0x01
* to the capabilities register first to unlock it.
*/
ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch);
if (ret)
return ret;
scratch |= 0x01;
pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch);
pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73);
/* Disable ADMA1/2 */
pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39);
pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08);
/* Disable the infinite transfer mode */
ret = pci_read_config_byte(chip->pdev,
O2_SD_INF_MOD, &scratch);
if (ret)
return ret;
scratch |= 0x08;
pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch);
/* Lock WP */
ret = pci_read_config_byte(chip->pdev,
O2_SD_LOCK_WP, &scratch);
if (ret)
return ret;
scratch |= 0x80;
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
break;
case PCI_DEVICE_ID_O2_SDS0:
case PCI_DEVICE_ID_O2_SDS1:
case PCI_DEVICE_ID_O2_FUJIN2:
/* UnLock WP */
ret = pci_read_config_byte(chip->pdev,
O2_SD_LOCK_WP, &scratch);
if (ret)
return ret;
scratch &= 0x7f;
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
/* Set timeout CLK */
ret = pci_read_config_dword(chip->pdev,
O2_SD_CLK_SETTING, &scratch_32);
if (ret)
return ret;
scratch_32 &= ~(0xFF00);
scratch_32 |= 0x07E0C800;
pci_write_config_dword(chip->pdev,
O2_SD_CLK_SETTING, scratch_32);
ret = pci_read_config_dword(chip->pdev,
O2_SD_CLKREQ, &scratch_32);
if (ret)
return ret;
scratch_32 |= 0x3;
pci_write_config_dword(chip->pdev, O2_SD_CLKREQ, scratch_32);
ret = pci_read_config_dword(chip->pdev,
O2_SD_PLL_SETTING, &scratch_32);
if (ret)
return ret;
scratch_32 &= ~(0x1F3F070E);
scratch_32 |= 0x18270106;
pci_write_config_dword(chip->pdev,
O2_SD_PLL_SETTING, scratch_32);
/* Disable UHS1 funciton */
ret = pci_read_config_dword(chip->pdev,
O2_SD_CAP_REG2, &scratch_32);
if (ret)
return ret;
scratch_32 &= ~(0xE0);
pci_write_config_dword(chip->pdev,
O2_SD_CAP_REG2, scratch_32);
if (chip->pdev->device == PCI_DEVICE_ID_O2_FUJIN2)
sdhci_pci_o2_fujin2_pci_init(chip);
/* Lock WP */
ret = pci_read_config_byte(chip->pdev,
O2_SD_LOCK_WP, &scratch);
if (ret)
return ret;
scratch |= 0x80;
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
break;
case PCI_DEVICE_ID_O2_SEABIRD0:
case PCI_DEVICE_ID_O2_SEABIRD1:
/* UnLock WP */
ret = pci_read_config_byte(chip->pdev,
O2_SD_LOCK_WP, &scratch);
if (ret)
return ret;
scratch &= 0x7f;
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
ret = pci_read_config_dword(chip->pdev,
O2_SD_FUNC_REG0, &scratch_32);
if ((scratch_32 & 0xff000000) == 0x01000000) {
scratch_32 &= 0x0000FFFF;
scratch_32 |= 0x1F340000;
pci_write_config_dword(chip->pdev,
O2_SD_PLL_SETTING, scratch_32);
} else {
scratch_32 &= 0x0000FFFF;
scratch_32 |= 0x2c280000;
pci_write_config_dword(chip->pdev,
O2_SD_PLL_SETTING, scratch_32);
ret = pci_read_config_dword(chip->pdev,
O2_SD_FUNC_REG4,
&scratch_32);
scratch_32 |= (1 << 22);
pci_write_config_dword(chip->pdev,
O2_SD_FUNC_REG4, scratch_32);
}
/* Lock WP */
ret = pci_read_config_byte(chip->pdev,
O2_SD_LOCK_WP, &scratch);
if (ret)
return ret;
scratch |= 0x80;
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
break;
}
return 0;
}
EXPORT_SYMBOL_GPL(sdhci_pci_o2_probe);
int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip)
{
sdhci_pci_o2_probe(chip);
return 0;
}
EXPORT_SYMBOL_GPL(sdhci_pci_o2_resume);

View File

@ -0,0 +1,72 @@
/*
* Copyright (C) 2013 BayHub Technology Ltd.
*
* Authors: Peter Guo <peter.guo@bayhubtech.com>
* Adam Lee <adam.lee@canonical.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef __SDHCI_PCI_O2MICRO_H
#define __SDHCI_PCI_O2MICRO_H
#include "sdhci-pci.h"
/*
* O2Micro device IDs
*/
#define PCI_DEVICE_ID_O2_SDS0 0x8420
#define PCI_DEVICE_ID_O2_SDS1 0x8421
#define PCI_DEVICE_ID_O2_FUJIN2 0x8520
#define PCI_DEVICE_ID_O2_SEABIRD0 0x8620
#define PCI_DEVICE_ID_O2_SEABIRD1 0x8621
/*
* O2Micro device registers
*/
#define O2_SD_MISC_REG5 0x64
#define O2_SD_LD0_CTRL 0x68
#define O2_SD_DEV_CTRL 0x88
#define O2_SD_LOCK_WP 0xD3
#define O2_SD_TEST_REG 0xD4
#define O2_SD_FUNC_REG0 0xDC
#define O2_SD_MULTI_VCC3V 0xEE
#define O2_SD_CLKREQ 0xEC
#define O2_SD_CAPS 0xE0
#define O2_SD_ADMA1 0xE2
#define O2_SD_ADMA2 0xE7
#define O2_SD_INF_MOD 0xF1
#define O2_SD_MISC_CTRL4 0xFC
#define O2_SD_TUNING_CTRL 0x300
#define O2_SD_PLL_SETTING 0x304
#define O2_SD_CLK_SETTING 0x328
#define O2_SD_CAP_REG2 0x330
#define O2_SD_CAP_REG0 0x334
#define O2_SD_UHS1_CAP_SETTING 0x33C
#define O2_SD_DELAY_CTRL 0x350
#define O2_SD_UHS2_L1_CTRL 0x35C
#define O2_SD_FUNC_REG3 0x3E0
#define O2_SD_FUNC_REG4 0x3E4
#define O2_SD_VENDOR_SETTING 0x110
#define O2_SD_VENDOR_SETTING2 0x1C8
extern void sdhci_pci_o2_fujin2_pci_init(struct sdhci_pci_chip *chip);
extern int sdhci_pci_o2_probe_slot(struct sdhci_pci_slot *slot);
extern int sdhci_pci_o2_probe(struct sdhci_pci_chip *chip);
extern int sdhci_pci_o2_resume(struct sdhci_pci_chip *chip);
#endif /* __SDHCI_PCI_O2MICRO_H */

View File

@ -27,79 +27,8 @@
#include <linux/mmc/sdhci-pci-data.h>
#include "sdhci.h"
/*
* PCI device IDs
*/
#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809
#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a
#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50
#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190
#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9
#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5
#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6
/*
* PCI registers
*/
#define PCI_SDHCI_IFPIO 0x00
#define PCI_SDHCI_IFDMA 0x01
#define PCI_SDHCI_IFVENDOR 0x02
#define PCI_SLOT_INFO 0x40 /* 8 bits */
#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7)
#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07
#define MAX_SLOTS 8
struct sdhci_pci_chip;
struct sdhci_pci_slot;
struct sdhci_pci_fixes {
unsigned int quirks;
unsigned int quirks2;
bool allow_runtime_pm;
int (*probe) (struct sdhci_pci_chip *);
int (*probe_slot) (struct sdhci_pci_slot *);
void (*remove_slot) (struct sdhci_pci_slot *, int);
int (*suspend) (struct sdhci_pci_chip *);
int (*resume) (struct sdhci_pci_chip *);
};
struct sdhci_pci_slot {
struct sdhci_pci_chip *chip;
struct sdhci_host *host;
struct sdhci_pci_data *data;
int pci_bar;
int rst_n_gpio;
int cd_gpio;
int cd_irq;
void (*hw_reset)(struct sdhci_host *host);
};
struct sdhci_pci_chip {
struct pci_dev *pdev;
unsigned int quirks;
unsigned int quirks2;
bool allow_runtime_pm;
const struct sdhci_pci_fixes *fixes;
int num_slots; /* Slots on controller */
struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */
};
#include "sdhci-pci.h"
#include "sdhci-pci-o2micro.h"
/*****************************************************************************\
* *
@ -296,6 +225,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = {
static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.allow_runtime_pm = true,
.own_cd_for_runtime_pm = true,
};
static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = {
@ -360,6 +290,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
.allow_runtime_pm = true,
.own_cd_for_runtime_pm = true,
};
/* Define Host controllers for Intel Merrifield platform */
@ -381,6 +312,7 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200,
.probe_slot = intel_mrfl_mmc_probe_slot,
};
@ -393,65 +325,6 @@ static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
#define O2_SD_ADMA2 0xE7
#define O2_SD_INF_MOD 0xF1
static int o2_probe(struct sdhci_pci_chip *chip)
{
int ret;
u8 scratch;
switch (chip->pdev->device) {
case PCI_DEVICE_ID_O2_8220:
case PCI_DEVICE_ID_O2_8221:
case PCI_DEVICE_ID_O2_8320:
case PCI_DEVICE_ID_O2_8321:
/* This extra setup is required due to broken ADMA. */
ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch);
if (ret)
return ret;
scratch &= 0x7f;
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
/* Set Multi 3 to VCC3V# */
pci_write_config_byte(chip->pdev, O2_SD_MULTI_VCC3V, 0x08);
/* Disable CLK_REQ# support after media DET */
ret = pci_read_config_byte(chip->pdev, O2_SD_CLKREQ, &scratch);
if (ret)
return ret;
scratch |= 0x20;
pci_write_config_byte(chip->pdev, O2_SD_CLKREQ, scratch);
/* Choose capabilities, enable SDMA. We have to write 0x01
* to the capabilities register first to unlock it.
*/
ret = pci_read_config_byte(chip->pdev, O2_SD_CAPS, &scratch);
if (ret)
return ret;
scratch |= 0x01;
pci_write_config_byte(chip->pdev, O2_SD_CAPS, scratch);
pci_write_config_byte(chip->pdev, O2_SD_CAPS, 0x73);
/* Disable ADMA1/2 */
pci_write_config_byte(chip->pdev, O2_SD_ADMA1, 0x39);
pci_write_config_byte(chip->pdev, O2_SD_ADMA2, 0x08);
/* Disable the infinite transfer mode */
ret = pci_read_config_byte(chip->pdev, O2_SD_INF_MOD, &scratch);
if (ret)
return ret;
scratch |= 0x08;
pci_write_config_byte(chip->pdev, O2_SD_INF_MOD, scratch);
/* Lock WP */
ret = pci_read_config_byte(chip->pdev, O2_SD_LOCK_WP, &scratch);
if (ret)
return ret;
scratch |= 0x80;
pci_write_config_byte(chip->pdev, O2_SD_LOCK_WP, scratch);
}
return 0;
}
static int jmicron_pmos(struct sdhci_pci_chip *chip, int on)
{
u8 scratch;
@ -642,7 +515,10 @@ static int jmicron_resume(struct sdhci_pci_chip *chip)
}
static const struct sdhci_pci_fixes sdhci_o2 = {
.probe = o2_probe,
.probe = sdhci_pci_o2_probe,
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.probe_slot = sdhci_pci_o2_probe_slot,
.resume = sdhci_pci_o2_resume,
};
static const struct sdhci_pci_fixes sdhci_jmicron = {
@ -1055,6 +931,46 @@ static const struct pci_device_id pci_ids[] = {
.driver_data = (kernel_ulong_t)&sdhci_o2,
},
{
.vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_FUJIN2,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_o2,
},
{
.vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_SDS0,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_o2,
},
{
.vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_SDS1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_o2,
},
{
.vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_SEABIRD0,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_o2,
},
{
.vendor = PCI_VENDOR_ID_O2,
.device = PCI_DEVICE_ID_O2_SEABIRD1,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_o2,
},
{ /* Generic SD host controller */
PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00)
},
@ -1457,6 +1373,15 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
sdhci_pci_add_own_cd(slot);
/*
* Check if the chip needs a separate GPIO for card detect to wake up
* from runtime suspend. If it is not there, don't allow runtime PM.
* Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure.
*/
if (chip->fixes && chip->fixes->own_cd_for_runtime_pm &&
!gpio_is_valid(slot->cd_gpio))
chip->allow_runtime_pm = false;
return slot;
remove:

View File

@ -0,0 +1,78 @@
#ifndef __SDHCI_PCI_H
#define __SDHCI_PCI_H
/*
* PCI device IDs
*/
#define PCI_DEVICE_ID_INTEL_PCH_SDIO0 0x8809
#define PCI_DEVICE_ID_INTEL_PCH_SDIO1 0x880a
#define PCI_DEVICE_ID_INTEL_BYT_EMMC 0x0f14
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50
#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190
#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9
#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa
#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb
#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5
#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6
/*
* PCI registers
*/
#define PCI_SDHCI_IFPIO 0x00
#define PCI_SDHCI_IFDMA 0x01
#define PCI_SDHCI_IFVENDOR 0x02
#define PCI_SLOT_INFO 0x40 /* 8 bits */
#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7)
#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07
#define MAX_SLOTS 8
struct sdhci_pci_chip;
struct sdhci_pci_slot;
struct sdhci_pci_fixes {
unsigned int quirks;
unsigned int quirks2;
bool allow_runtime_pm;
bool own_cd_for_runtime_pm;
int (*probe) (struct sdhci_pci_chip *);
int (*probe_slot) (struct sdhci_pci_slot *);
void (*remove_slot) (struct sdhci_pci_slot *, int);
int (*suspend) (struct sdhci_pci_chip *);
int (*resume) (struct sdhci_pci_chip *);
};
struct sdhci_pci_slot {
struct sdhci_pci_chip *chip;
struct sdhci_host *host;
struct sdhci_pci_data *data;
int pci_bar;
int rst_n_gpio;
int cd_gpio;
int cd_irq;
void (*hw_reset)(struct sdhci_host *host);
};
struct sdhci_pci_chip {
struct pci_dev *pdev;
unsigned int quirks;
unsigned int quirks2;
bool allow_runtime_pm;
const struct sdhci_pci_fixes *fixes;
int num_slots; /* Slots on controller */
struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */
};
#endif /* __SDHCI_PCI_H */

View File

@ -237,19 +237,21 @@ int sdhci_pltfm_unregister(struct platform_device *pdev)
EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);
#ifdef CONFIG_PM
static int sdhci_pltfm_suspend(struct device *dev)
int sdhci_pltfm_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
return sdhci_suspend_host(host);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend);
static int sdhci_pltfm_resume(struct device *dev)
int sdhci_pltfm_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
return sdhci_resume_host(host);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);
const struct dev_pm_ops sdhci_pltfm_pmops = {
.suspend = sdhci_pltfm_suspend,

View File

@ -111,6 +111,8 @@ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
}
#ifdef CONFIG_PM
extern int sdhci_pltfm_suspend(struct device *dev);
extern int sdhci_pltfm_resume(struct device *dev);
extern const struct dev_pm_ops sdhci_pltfm_pmops;
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
#else

View File

@ -198,6 +198,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra114 = {
};
static const struct of_device_id sdhci_tegra_dt_match[] = {
{ .compatible = "nvidia,tegra124-sdhci", .data = &soc_data_tegra114 },
{ .compatible = "nvidia,tegra114-sdhci", .data = &soc_data_tegra114 },
{ .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 },
{ .compatible = "nvidia,tegra20-sdhci", .data = &soc_data_tegra20 },

View File

@ -898,8 +898,13 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
u16 mode;
struct mmc_data *data = cmd->data;
if (data == NULL)
if (data == NULL) {
/* clear Auto CMD settings for no data CMDs */
mode = sdhci_readw(host, SDHCI_TRANSFER_MODE);
sdhci_writew(host, mode & ~(SDHCI_TRNS_AUTO_CMD12 |
SDHCI_TRNS_AUTO_CMD23), SDHCI_TRANSFER_MODE);
return;
}
WARN_ON(!host->data);
@ -1013,7 +1018,12 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
mdelay(1);
}
mod_timer(&host->timer, jiffies + 10 * HZ);
timeout = jiffies;
if (!cmd->data && cmd->cmd_timeout_ms > 9000)
timeout += DIV_ROUND_UP(cmd->cmd_timeout_ms, 1000) * HZ + HZ;
else
timeout += 10 * HZ;
mod_timer(&host->timer, timeout);
host->cmd = cmd;
@ -1391,6 +1401,13 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
mmc->card->type == MMC_TYPE_MMC ?
MMC_SEND_TUNING_BLOCK_HS200 :
MMC_SEND_TUNING_BLOCK;
/* Here we need to set the host->mrq to NULL,
* in case the pending finish_tasklet
* finishes it incorrectly.
*/
host->mrq = NULL;
spin_unlock_irqrestore(&host->lock, flags);
sdhci_execute_tuning(mmc, tuning_opcode);
spin_lock_irqsave(&host->lock, flags);
@ -1845,12 +1862,12 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
unsigned long timeout;
int err = 0;
bool requires_tuning_nonuhs = false;
unsigned long flags;
host = mmc_priv(mmc);
sdhci_runtime_pm_get(host);
disable_irq(host->irq);
spin_lock(&host->lock);
spin_lock_irqsave(&host->lock, flags);
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
@ -1870,15 +1887,13 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
requires_tuning_nonuhs)
ctrl |= SDHCI_CTRL_EXEC_TUNING;
else {
spin_unlock(&host->lock);
enable_irq(host->irq);
spin_unlock_irqrestore(&host->lock, flags);
sdhci_runtime_pm_put(host);
return 0;
}
if (host->ops->platform_execute_tuning) {
spin_unlock(&host->lock);
enable_irq(host->irq);
spin_unlock_irqrestore(&host->lock, flags);
err = host->ops->platform_execute_tuning(host, opcode);
sdhci_runtime_pm_put(host);
return err;
@ -1951,15 +1966,12 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
host->cmd = NULL;
host->mrq = NULL;
spin_unlock(&host->lock);
enable_irq(host->irq);
spin_unlock_irqrestore(&host->lock, flags);
/* Wait for Buffer Read Ready interrupt */
wait_event_interruptible_timeout(host->buf_ready_int,
(host->tuning_done == 1),
msecs_to_jiffies(50));
disable_irq(host->irq);
spin_lock(&host->lock);
spin_lock_irqsave(&host->lock, flags);
if (!host->tuning_done) {
pr_info(DRIVER_NAME ": Timeout waiting for "
@ -2034,8 +2046,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
err = 0;
sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
spin_unlock(&host->lock);
enable_irq(host->irq);
spin_unlock_irqrestore(&host->lock, flags);
sdhci_runtime_pm_put(host);
return err;
@ -3004,7 +3015,8 @@ int sdhci_add_host(struct sdhci_host *host)
/* SD3.0: SDR104 is supported so (for eMMC) the caps2
* field can be promoted to support HS200.
*/
mmc->caps2 |= MMC_CAP2_HS200;
if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200))
mmc->caps2 |= MMC_CAP2_HS200;
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
mmc->caps |= MMC_CAP_UHS_SDR50;

View File

@ -381,14 +381,56 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host)
desc, cookie);
}
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
struct sh_mmcif_plat_data *pdata)
static struct dma_chan *
sh_mmcif_request_dma_one(struct sh_mmcif_host *host,
struct sh_mmcif_plat_data *pdata,
enum dma_transfer_direction direction)
{
struct resource *res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
struct dma_slave_config cfg;
struct dma_chan *chan;
unsigned int slave_id;
struct resource *res;
dma_cap_mask_t mask;
int ret;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
if (pdata)
slave_id = direction == DMA_MEM_TO_DEV
? pdata->slave_id_tx : pdata->slave_id_rx;
else
slave_id = 0;
chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
(void *)(unsigned long)slave_id, &host->pd->dev,
direction == DMA_MEM_TO_DEV ? "tx" : "rx");
dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__,
direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan);
if (!chan)
return NULL;
res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
/* In the OF case the driver will get the slave ID from the DT */
cfg.slave_id = slave_id;
cfg.direction = direction;
cfg.dst_addr = res->start + MMCIF_CE_DATA;
cfg.src_addr = 0;
ret = dmaengine_slave_config(chan, &cfg);
if (ret < 0) {
dma_release_channel(chan);
return NULL;
}
return chan;
}
static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
struct sh_mmcif_plat_data *pdata)
{
host->dma_active = false;
if (pdata) {
@ -399,55 +441,15 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
}
/* We can only either use DMA for both Tx and Rx or not use it at all */
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
host->chan_tx = dma_request_slave_channel_compat(mask, shdma_chan_filter,
pdata ? (void *)pdata->slave_id_tx : NULL,
&host->pd->dev, "tx");
dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
host->chan_tx);
host->chan_tx = sh_mmcif_request_dma_one(host, pdata, DMA_MEM_TO_DEV);
if (!host->chan_tx)
return;
/* In the OF case the driver will get the slave ID from the DT */
if (pdata)
cfg.slave_id = pdata->slave_id_tx;
cfg.direction = DMA_MEM_TO_DEV;
cfg.dst_addr = res->start + MMCIF_CE_DATA;
cfg.src_addr = 0;
ret = dmaengine_slave_config(host->chan_tx, &cfg);
if (ret < 0)
goto ecfgtx;
host->chan_rx = dma_request_slave_channel_compat(mask, shdma_chan_filter,
pdata ? (void *)pdata->slave_id_rx : NULL,
&host->pd->dev, "rx");
dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
host->chan_rx);
if (!host->chan_rx)
goto erqrx;
if (pdata)
cfg.slave_id = pdata->slave_id_rx;
cfg.direction = DMA_DEV_TO_MEM;
cfg.dst_addr = 0;
cfg.src_addr = res->start + MMCIF_CE_DATA;
ret = dmaengine_slave_config(host->chan_rx, &cfg);
if (ret < 0)
goto ecfgrx;
return;
ecfgrx:
dma_release_channel(host->chan_rx);
host->chan_rx = NULL;
erqrx:
ecfgtx:
dma_release_channel(host->chan_tx);
host->chan_tx = NULL;
host->chan_rx = sh_mmcif_request_dma_one(host, pdata, DMA_DEV_TO_MEM);
if (!host->chan_rx) {
dma_release_channel(host->chan_tx);
host->chan_tx = NULL;
}
}
static void sh_mmcif_release_dma(struct sh_mmcif_host *host)

View File

@ -33,6 +33,8 @@
#include "tmio_mmc.h"
#define EXT_ACC 0xe4
struct sh_mobile_sdhi_of_data {
unsigned long tmio_flags;
};
@ -54,7 +56,7 @@ static int sh_mobile_sdhi_clk_enable(struct platform_device *pdev, unsigned int
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct tmio_mmc_host *host = mmc_priv(mmc);
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
int ret = clk_enable(priv->clk);
int ret = clk_prepare_enable(priv->clk);
if (ret < 0)
return ret;
@ -67,7 +69,7 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev)
struct mmc_host *mmc = platform_get_drvdata(pdev);
struct tmio_mmc_host *host = mmc_priv(mmc);
struct sh_mobile_sdhi *priv = container_of(host->pdata, struct sh_mobile_sdhi, mmc_data);
clk_disable(priv->clk);
clk_disable_unprepare(priv->clk);
}
static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
@ -133,9 +135,15 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
struct tmio_mmc_data *mmc_data;
struct sh_mobile_sdhi_info *p = pdev->dev.platform_data;
struct tmio_mmc_host *host;
struct resource *res;
int irq, ret, i = 0;
bool multiplexed_isr = true;
struct tmio_mmc_dma *dma_priv;
u16 ver;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
priv = devm_kzalloc(&pdev->dev, sizeof(struct sh_mobile_sdhi), GFP_KERNEL);
if (priv == NULL) {
@ -206,10 +214,21 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->flags |= of_data->tmio_flags;
}
/* SD control register space size is 0x100, 0x200 for bus_shift=1 */
mmc_data->bus_shift = resource_size(res) >> 9;
ret = tmio_mmc_host_probe(&host, pdev, mmc_data);
if (ret < 0)
goto eprobe;
/*
* FIXME:
* this Workaround can be more clever method
*/
ver = sd_ctrl_read16(host, CTL_VERSION);
if (ver == 0xCB0D)
sd_ctrl_write16(host, EXT_ACC, 1);
/*
* Allow one or more specific (named) ISRs or
* one or more multiplexed (un-named) ISRs.

View File

@ -62,6 +62,7 @@ static int tmio_mmc_probe(struct platform_device *pdev)
const struct mfd_cell *cell = mfd_get_cell(pdev);
struct tmio_mmc_data *pdata;
struct tmio_mmc_host *host;
struct resource *res;
int ret = -EINVAL, irq;
if (pdev->num_resources != 2)
@ -84,6 +85,14 @@ static int tmio_mmc_probe(struct platform_device *pdev)
goto out;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -EINVAL;
/* SD control register space size is 0x200, 0x400 for bus_shift=1 */
pdata->bus_shift = resource_size(res) >> 10;
pdata->flags |= TMIO_MMC_HAVE_HIGH_REG;
ret = tmio_mmc_host_probe(&host, pdev, pdata);
if (ret)
goto cell_disable;

View File

@ -58,7 +58,6 @@ enum tmio_mmc_power {
struct tmio_mmc_host {
void __iomem *ctl;
unsigned long bus_shift;
struct mmc_command *cmd;
struct mmc_request *mrq;
struct mmc_data *data;
@ -176,19 +175,19 @@ int tmio_mmc_host_runtime_resume(struct device *dev);
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
{
return readw(host->ctl + (addr << host->bus_shift));
return readw(host->ctl + (addr << host->pdata->bus_shift));
}
static inline void sd_ctrl_read16_rep(struct tmio_mmc_host *host, int addr,
u16 *buf, int count)
{
readsw(host->ctl + (addr << host->bus_shift), buf, count);
readsw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
}
static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr)
{
return readw(host->ctl + (addr << host->bus_shift)) |
readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
return readw(host->ctl + (addr << host->pdata->bus_shift)) |
readw(host->ctl + ((addr + 2) << host->pdata->bus_shift)) << 16;
}
static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
@ -198,19 +197,19 @@ static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val
*/
if (host->pdata->write16_hook && host->pdata->write16_hook(host, addr))
return;
writew(val, host->ctl + (addr << host->bus_shift));
writew(val, host->ctl + (addr << host->pdata->bus_shift));
}
static inline void sd_ctrl_write16_rep(struct tmio_mmc_host *host, int addr,
u16 *buf, int count)
{
writesw(host->ctl + (addr << host->bus_shift), buf, count);
writesw(host->ctl + (addr << host->pdata->bus_shift), buf, count);
}
static inline void sd_ctrl_write32(struct tmio_mmc_host *host, int addr, u32 val)
{
writew(val, host->ctl + (addr << host->bus_shift));
writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
writew(val, host->ctl + (addr << host->pdata->bus_shift));
writew(val >> 16, host->ctl + ((addr + 2) << host->pdata->bus_shift));
}

View File

@ -293,7 +293,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
if (pdata->dma->chan_priv_tx)
cfg.slave_id = pdata->dma->slave_id_tx;
cfg.direction = DMA_MEM_TO_DEV;
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->bus_shift);
cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift);
cfg.src_addr = 0;
ret = dmaengine_slave_config(host->chan_tx, &cfg);
if (ret < 0)

View File

@ -161,10 +161,8 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
{
struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
/* implicit BUG_ON(!res) */
if (resource_size(res) > 0x100) {
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
msleep(10);
}
@ -176,14 +174,12 @@ static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
{
struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, 0x0100 |
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
msleep(10);
/* implicit BUG_ON(!res) */
if (resource_size(res) > 0x100) {
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG) {
sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
msleep(10);
}
@ -191,16 +187,14 @@ static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
static void tmio_mmc_reset(struct tmio_mmc_host *host)
{
struct resource *res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
/* FIXME - should we set stop clock reg here */
sd_ctrl_write16(host, CTL_RESET_SD, 0x0000);
/* implicit BUG_ON(!res) */
if (resource_size(res) > 0x100)
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
msleep(10);
sd_ctrl_write16(host, CTL_RESET_SD, 0x0001);
if (resource_size(res) > 0x100)
if (host->pdata->flags & TMIO_MMC_HAVE_HIGH_REG)
sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
msleep(10);
}
@ -944,17 +938,25 @@ static const struct mmc_host_ops tmio_mmc_ops = {
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
};
static void tmio_mmc_init_ocr(struct tmio_mmc_host *host)
static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
{
struct tmio_mmc_data *pdata = host->pdata;
struct mmc_host *mmc = host->mmc;
mmc_regulator_get_supply(mmc);
/* use ocr_mask if no regulator */
if (!mmc->ocr_avail)
mmc->ocr_avail = pdata->ocr_mask ? : MMC_VDD_32_33 | MMC_VDD_33_34;
else if (pdata->ocr_mask)
dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n");
mmc->ocr_avail = pdata->ocr_mask;
/*
* try again.
* There is possibility that regulator has not been probed
*/
if (!mmc->ocr_avail)
return -EPROBE_DEFER;
return 0;
}
static void tmio_mmc_of_parse(struct platform_device *pdev,
@ -1005,8 +1007,9 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
_host->set_pwr = pdata->set_pwr;
_host->set_clk_div = pdata->set_clk_div;
/* SD control register space size is 0x200, 0x400 for bus_shift=1 */
_host->bus_shift = resource_size(res_ctl) >> 10;
ret = tmio_mmc_init_ocr(_host);
if (ret < 0)
goto host_free;
_host->ctl = ioremap(res_ctl->start, resource_size(res_ctl));
if (!_host->ctl) {
@ -1016,14 +1019,13 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
mmc->ops = &tmio_mmc_ops;
mmc->caps |= MMC_CAP_4_BIT_DATA | pdata->capabilities;
mmc->caps2 = pdata->capabilities2;
mmc->caps2 |= pdata->capabilities2;
mmc->max_segs = 32;
mmc->max_blk_size = 512;
mmc->max_blk_count = (PAGE_CACHE_SIZE / mmc->max_blk_size) *
mmc->max_segs;
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;
tmio_mmc_init_ocr(_host);
_host->native_hotplug = !(pdata->flags & TMIO_MMC_USE_GPIO_CD ||
mmc->caps & MMC_CAP_NEEDS_POLL ||

View File

@ -76,6 +76,13 @@
*/
#define TMIO_MMC_USE_GPIO_CD (1 << 5)
/*
* Some controllers doesn't have over 0x100 register.
* it is used to checking accessibility of
* CTL_SD_CARD_CLK_CTL / CTL_CLK_AND_WAIT_CTL
*/
#define TMIO_MMC_HAVE_HIGH_REG (1 << 6)
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base);
int tmio_core_mmc_resume(void __iomem *cnf, int shift, unsigned long base);
void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
@ -102,6 +109,7 @@ struct tmio_mmc_data {
unsigned long capabilities;
unsigned long capabilities2;
unsigned long flags;
unsigned long bus_shift;
u32 ocr_mask; /* available voltages */
struct tmio_mmc_dma *dma;
struct device *dev;

View File

@ -271,9 +271,10 @@ struct mmc_card {
#define MMC_QUIRK_INAND_CMD38 (1<<6) /* iNAND devices have broken CMD38 */
#define MMC_QUIRK_BLK_NO_CMD23 (1<<7) /* Avoid CMD23 for regular multiblock */
#define MMC_QUIRK_BROKEN_BYTE_MODE_512 (1<<8) /* Avoid sending 512 bytes in */
/* byte mode */
#define MMC_QUIRK_LONG_READ_TIME (1<<9) /* Data read time > CSD says */
#define MMC_QUIRK_SEC_ERASE_TRIM_BROKEN (1<<10) /* Skip secure for erase/trim */
/* byte mode */
#define MMC_QUIRK_BROKEN_IRQ_POLLING (1<<11) /* Polling SDIO_CCCR_INTx could create a fake interrupt */
unsigned int erase_size; /* erase size in sectors */
unsigned int erase_shift; /* if erase unit is power 2 */
@ -505,6 +506,11 @@ static inline int mmc_card_long_read_time(const struct mmc_card *c)
return c->quirks & MMC_QUIRK_LONG_READ_TIME;
}
static inline int mmc_card_broken_irq_polling(const struct mmc_card *c)
{
return c->quirks & MMC_QUIRK_BROKEN_IRQ_POLLING;
}
#define mmc_card_name(c) ((c)->cid.prod_name)
#define mmc_card_id(c) (dev_name(&(c)->dev))

View File

@ -98,6 +98,8 @@ struct sdhci_host {
#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
/* Controller has a non-standard host control register */
#define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5)
/* Controller does not support HS200 */
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */

View File

@ -33,6 +33,7 @@
#define CTL_SDIO_IRQ_MASK 0x38
#define CTL_DMA_ENABLE 0xd8
#define CTL_RESET_SD 0xe0
#define CTL_VERSION 0xe2
#define CTL_SDIO_REGS 0x100
#define CTL_CLK_AND_WAIT_CTL 0x138
#define CTL_RESET_SDIO 0x1e0