MMC core:

- Fix SDIO IRQ bug.
  - MMC regulator improvements.
  - Fix slot-gpio card detect bug.
  - Add support for Driver Stage Register.
  - Convert the common MMC OF parser to use GPIO descriptors.
  - Convert MMC_CAP2_NO_MULTI_READ into a callback, ->multi_io_quirk().
  - Some additional minor fixes.
 
 MMC host:
  - mmci: Support Qualcomm specific DML layer for DMA.
  - dw_mmc: Use common MMC regulators.
  - dw_mmc: Add support for Rock-chips RK3288.
  - tmio: Enable runtime PM support.
  - tmio: Add support for R-Car Gen2 SoCs.
  - tmio: Several fixes and improvements.
  - omap_hsmmc: Removed Balaji from MAINTAINERS.
  - jz4740: add DMA and pre/post support.
  - sdhci: Add support for Intel Braswell.
  - sdhci: Several fixes and improvements.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJUNoFRAAoJEP4mhCVzWIwp+oQP/3a9Rs85+lKwnaDtCotCnvps
 LF2R1qiFbeTgQ4XwJvOctuX0VX3/9/XTRhXq+/txA8phlXzqL5BarbXv8WfLILJJ
 DgXDt/lTeW1NzJ9WYjrmV/rsH7qlbyIq6I+7kXVT15M86Qqx40DF0hSx/idDKDc4
 1ly4trLh0ZeqsM10AR9nu6h/ykVBblHOLSnMZXbBhtmIVshvNg+5KRQkSmwtvTKy
 /DswgxmuM1H1Z0T+qNejh4AZSCvxYPlwN06eqYzpYrGuoPH+SafJVws5o1G9z9SX
 t/A9i1QDxFtvDP0u1twEAYv0R4e3H24OPit3R8p2tgMUw683576DPYkF2A13Yzxj
 c3mYiTAPK8UfRc9kWxCRSkaI38URna1+t7hHRuT/Ha6DBlAvHpRL+wIu+/25XVh+
 vNwOmECtT9DzmL2UP+SHLQtyyy3guAFSsFP5RJzuA5wcYeLpNYobcJJCGuziLNYi
 PZ55O+2HRtd7my4A7NiXAib+CXTPs4VY0XY1tBgaWHl2sxFj/mULILaf+3zxpiWg
 Jc8rWkUMpy1nP1OXUrCRBKbgr/loghUOEM6hozggeisDwpjh7Rm5OXZRj6JdO4QT
 DLCl8NQKL8Ex33XoS45LoF2uuTfLt/E52CT0Sic4JdpwvIDTwlhxQR/Yo5gWuCnQ
 L+J+zbclHjORG5EuIUsw
 =VFRY
 -----END PGP SIGNATURE-----

Merge tag 'mmc-v3.18-1' of git://git.linaro.org/people/ulf.hansson/mmc

Pull MMC updates from Ulf Hansson:
 "MMC core:
   - Fix SDIO IRQ bug
   - MMC regulator improvements
   - Fix slot-gpio card detect bug
   - Add support for Driver Stage Register
   - Convert the common MMC OF parser to use GPIO descriptors
   - Convert MMC_CAP2_NO_MULTI_READ into a callback, ->multi_io_quirk()
   - Some additional minor fixes

  MMC host:
   - mmci: Support Qualcomm specific DML layer for DMA
   - dw_mmc: Use common MMC regulators
   - dw_mmc: Add support for Rock-chips RK3288
   - tmio: Enable runtime PM support
   - tmio: Add support for R-Car Gen2 SoCs
   - tmio: Several fixes and improvements
   - omap_hsmmc: Removed Balaji from MAINTAINERS
   - jz4740: add DMA and pre/post support
   - sdhci: Add support for Intel Braswell
   - sdhci: Several fixes and improvements"

* tag 'mmc-v3.18-1' of git://git.linaro.org/people/ulf.hansson/mmc: (119 commits)
  ARM: dts: fix MMC2 regulators for Exynos5420 Arndale Octa board
  mmc: sdhci-acpi: Fix Braswell eMMC timeout clock frequency
  mmc: sdhci-acpi: Pass HID and UID to probe_slot
  mmc: sdhci-acpi: Get UID directly from acpi_device
  mmc, sdhci, bcm-kona, LLVMLinux: Remove use of __initconst
  mmc: sdhci-pci: Fix Braswell eMMC timeout clock frequency
  mmc: sdhci: Let a driver override timeout clock frequency
  mmc: sdhci-pci: Add Bay Trail and Braswell SD card detect
  mmc: sdhci-pci: Set SDHCI_QUIRK2_STOP_WITH_TC for Intel BYT host controllers
  mmc: sdhci-acpi: Add a HID and UID for a SD Card host controller
  mmc: sdhci-acpi: Set SDHCI_QUIRK2_STOP_WITH_TC for Intel host controllers
  mmc: sdhci: Add quirk for always getting TC with stop cmd
  mmc: core: restore detect line inversion semantics
  mmc: Fix incorrect warning when setting 0 Hz via debugfs
  mmc: Fix use of wrong device in mmc_gpiod_free_cd()
  mmc: atmel-mci: fix mismatched section on atmci_cleanup_slot
  mmc: rtsx_pci: Set power related cap2 macros
  mmc: core: Add new power_mode MMC_POWER_UNDEFINED
  mmc: sdhci: execute tuning when device is not busy
  mmc: atmel-mci: Release mmc resources on failure in probe
  ..
This commit is contained in:
Linus Torvalds 2014-10-11 06:34:22 -04:00
commit f43b179bbd
81 changed files with 2006 additions and 749 deletions

View File

@ -40,6 +40,8 @@ Optional properties:
- mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported - mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported
- mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported - mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported
- mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported - mmc-hs400-1_2v: eMMC HS400 mode(1.2V I/O) is supported
- dsr: Value the card's (optional) Driver Stage Register (DSR) should be
programmed with. Valid range: [0 .. 0xffff].
*NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line
polarity properties, we have to fix the meaning of the "normal" and "inverted" polarity properties, we have to fix the meaning of the "normal" and "inverted"

View File

@ -10,12 +10,14 @@ extensions to the Synopsys Designware Mobile Storage Host Controller.
Required Properties: Required Properties:
* compatible: should be * compatible: should be
- "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following - "rockchip,rk2928-dw-mshc": for Rockchip RK2928 and following,
before RK3288
- "rockchip,rk3288-dw-mshc": for Rockchip RK3288
Example: Example:
rkdwmmc0@12200000 { rkdwmmc0@12200000 {
compatible = "rockchip,rk2928-dw-mshc"; compatible = "rockchip,rk3288-dw-mshc";
reg = <0x12200000 0x1000>; reg = <0x12200000 0x1000>;
interrupts = <0 75 0>; interrupts = <0 75 0>;
#address-cells = <1>; #address-cells = <1>;

View File

@ -19,6 +19,9 @@ Required properties:
"renesas,sdhi-r8a7779" - SDHI IP on R8A7779 SoC "renesas,sdhi-r8a7779" - SDHI IP on R8A7779 SoC
"renesas,sdhi-r8a7790" - SDHI IP on R8A7790 SoC "renesas,sdhi-r8a7790" - SDHI IP on R8A7790 SoC
"renesas,sdhi-r8a7791" - SDHI IP on R8A7791 SoC "renesas,sdhi-r8a7791" - SDHI IP on R8A7791 SoC
"renesas,sdhi-r8a7792" - SDHI IP on R8A7792 SoC
"renesas,sdhi-r8a7793" - SDHI IP on R8A7793 SoC
"renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC
Optional properties: Optional properties:
- toshiba,mmc-wrprotect-disable: write-protect detection is unavailable - toshiba,mmc-wrprotect-disable: write-protect detection is unavailable

View File

@ -6616,10 +6616,9 @@ S: Maintained
F: drivers/mmc/host/omap.c F: drivers/mmc/host/omap.c
OMAP HS MMC SUPPORT OMAP HS MMC SUPPORT
M: Balaji T K <balajitk@ti.com>
L: linux-mmc@vger.kernel.org L: linux-mmc@vger.kernel.org
L: linux-omap@vger.kernel.org L: linux-omap@vger.kernel.org
S: Maintained S: Orphan
F: drivers/mmc/host/omap_hsmmc.c F: drivers/mmc/host/omap_hsmmc.c
OMAP RANDOM NUMBER GENERATOR SUPPORT OMAP RANDOM NUMBER GENERATOR SUPPORT

View File

@ -69,7 +69,8 @@
samsung,dw-mshc-ddr-timing = <1 2>; samsung,dw-mshc-ddr-timing = <1 2>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>; pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_bus4>;
vmmc-supply = <&ldo10_reg>; vmmc-supply = <&ldo19_reg>;
vqmmc-supply = <&ldo13_reg>;
bus-width = <4>; bus-width = <4>;
cap-sd-highspeed; cap-sd-highspeed;
}; };

View File

@ -331,7 +331,6 @@ SDHI_REGULATOR(2, RCAR_GP_PIN(7, 19), RCAR_GP_PIN(2, 26));
static struct sh_mobile_sdhi_info sdhi0_info __initdata = { static struct sh_mobile_sdhi_info sdhi0_info __initdata = {
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_POWER_OFF_CARD, MMC_CAP_POWER_OFF_CARD,
.tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
}; };
@ -344,7 +343,6 @@ static struct resource sdhi0_resources[] __initdata = {
static struct sh_mobile_sdhi_info sdhi1_info __initdata = { static struct sh_mobile_sdhi_info sdhi1_info __initdata = {
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_POWER_OFF_CARD, MMC_CAP_POWER_OFF_CARD,
.tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT, .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
}; };
@ -357,7 +355,6 @@ static struct resource sdhi1_resources[] __initdata = {
static struct sh_mobile_sdhi_info sdhi2_info __initdata = { static struct sh_mobile_sdhi_info sdhi2_info __initdata = {
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_POWER_OFF_CARD, MMC_CAP_POWER_OFF_CARD,
.tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT |
TMIO_MMC_WRPROTECT_DISABLE, TMIO_MMC_WRPROTECT_DISABLE,
}; };

View File

@ -630,7 +630,6 @@ static void __init lager_add_rsnd_device(void)
static struct sh_mobile_sdhi_info sdhi0_info __initdata = { static struct sh_mobile_sdhi_info sdhi0_info __initdata = {
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_POWER_OFF_CARD, MMC_CAP_POWER_OFF_CARD,
.tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT |
TMIO_MMC_WRPROTECT_DISABLE, TMIO_MMC_WRPROTECT_DISABLE,
}; };
@ -644,7 +643,6 @@ static struct resource sdhi0_resources[] __initdata = {
static struct sh_mobile_sdhi_info sdhi2_info __initdata = { static struct sh_mobile_sdhi_info sdhi2_info __initdata = {
.tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | .tmio_caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ |
MMC_CAP_POWER_OFF_CARD, MMC_CAP_POWER_OFF_CARD,
.tmio_caps2 = MMC_CAP2_NO_MULTI_READ,
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT |
TMIO_MMC_WRPROTECT_DISABLE, TMIO_MMC_WRPROTECT_DISABLE,
}; };

View File

@ -977,7 +977,7 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
return ERR_CONTINUE; return ERR_CONTINUE;
/* Now for stop errors. These aren't fatal to the transfer. */ /* Now for stop errors. These aren't fatal to the transfer. */
pr_err("%s: error %d sending stop command, original cmd response %#x, card status %#x\n", pr_info("%s: error %d sending stop command, original cmd response %#x, card status %#x\n",
req->rq_disk->disk_name, brq->stop.error, req->rq_disk->disk_name, brq->stop.error,
brq->cmd.resp[0], status); brq->cmd.resp[0], status);
@ -1398,10 +1398,15 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
if (disable_multi) if (disable_multi)
brq->data.blocks = 1; brq->data.blocks = 1;
/* Some controllers can't do multiblock reads due to hw bugs */ /*
if (card->host->caps2 & MMC_CAP2_NO_MULTI_READ && * Some controllers have HW issues while operating
rq_data_dir(req) == READ) * in multiple I/O mode
brq->data.blocks = 1; */
if (card->host->ops->multi_io_quirk)
brq->data.blocks = card->host->ops->multi_io_quirk(card,
(rq_data_dir(req) == READ) ?
MMC_DATA_READ : MMC_DATA_WRITE,
brq->data.blocks);
} }
if (brq->data.blocks > 1 || do_rel_wr) { if (brq->data.blocks > 1 || do_rel_wr) {
@ -1923,8 +1928,8 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
case MMC_BLK_ECC_ERR: case MMC_BLK_ECC_ERR:
if (brq->data.blocks > 1) { if (brq->data.blocks > 1) {
/* Redo read one sector at a time */ /* Redo read one sector at a time */
pr_warning("%s: retrying using single block read\n", pr_warn("%s: retrying using single block read\n",
req->rq_disk->disk_name); req->rq_disk->disk_name);
disable_multi = 1; disable_multi = 1;
break; break;
} }
@ -2131,7 +2136,7 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
md->disk->queue = md->queue.queue; md->disk->queue = md->queue.queue;
md->disk->driverfs_dev = parent; md->disk->driverfs_dev = parent;
set_disk_ro(md->disk, md->read_only || default_ro); set_disk_ro(md->disk, md->read_only || default_ro);
if (area_type & MMC_BLK_DATA_AREA_RPMB) if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT))
md->disk->flags |= GENHD_FL_NO_PART_SCAN; md->disk->flags |= GENHD_FL_NO_PART_SCAN;
/* /*

View File

@ -229,14 +229,12 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,
if (bouncesz > 512) { if (bouncesz > 512) {
mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); mqrq_cur->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
if (!mqrq_cur->bounce_buf) { if (!mqrq_cur->bounce_buf) {
pr_warning("%s: unable to " pr_warn("%s: unable to allocate bounce cur buffer\n",
"allocate bounce cur buffer\n",
mmc_card_name(card)); mmc_card_name(card));
} }
mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL); mqrq_prev->bounce_buf = kmalloc(bouncesz, GFP_KERNEL);
if (!mqrq_prev->bounce_buf) { if (!mqrq_prev->bounce_buf) {
pr_warning("%s: unable to " pr_warn("%s: unable to allocate bounce prev buffer\n",
"allocate bounce prev buffer\n",
mmc_card_name(card)); mmc_card_name(card));
kfree(mqrq_cur->bounce_buf); kfree(mqrq_cur->bounce_buf);
mqrq_cur->bounce_buf = NULL; mqrq_cur->bounce_buf = NULL;

View File

@ -1063,8 +1063,8 @@ static int sdio_uart_probe(struct sdio_func *func,
return -ENOMEM; return -ENOMEM;
if (func->class == SDIO_CLASS_UART) { if (func->class == SDIO_CLASS_UART) {
pr_warning("%s: need info on UART class basic setup\n", pr_warn("%s: need info on UART class basic setup\n",
sdio_func_id(func)); sdio_func_id(func));
kfree(port); kfree(port);
return -ENOSYS; return -ENOSYS;
} else if (func->class == SDIO_CLASS_GPS) { } else if (func->class == SDIO_CLASS_GPS) {
@ -1082,9 +1082,8 @@ static int sdio_uart_probe(struct sdio_func *func,
break; break;
} }
if (!tpl) { if (!tpl) {
pr_warning( pr_warn("%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n",
"%s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class\n", sdio_func_id(func));
sdio_func_id(func));
kfree(port); kfree(port);
return -EINVAL; return -EINVAL;
} }

View File

@ -433,8 +433,8 @@ static void mmc_wait_for_req_done(struct mmc_host *host,
*/ */
if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) { if (cmd->sanitize_busy && cmd->error == -ETIMEDOUT) {
if (!mmc_interrupt_hpi(host->card)) { if (!mmc_interrupt_hpi(host->card)) {
pr_warning("%s: %s: Interrupted sanitize\n", pr_warn("%s: %s: Interrupted sanitize\n",
mmc_hostname(host), __func__); mmc_hostname(host), __func__);
cmd->error = 0; cmd->error = 0;
break; break;
} else { } else {
@ -995,7 +995,7 @@ void mmc_set_chip_select(struct mmc_host *host, int mode)
*/ */
static void __mmc_set_clock(struct mmc_host *host, unsigned int hz) static void __mmc_set_clock(struct mmc_host *host, unsigned int hz)
{ {
WARN_ON(hz < host->f_min); WARN_ON(hz && hz < host->f_min);
if (hz > host->f_max) if (hz > host->f_max)
hz = host->f_max; hz = host->f_max;
@ -1221,15 +1221,14 @@ int mmc_regulator_get_ocrmask(struct regulator *supply)
int result = 0; int result = 0;
int count; int count;
int i; int i;
int vdd_uV;
int vdd_mV;
count = regulator_count_voltages(supply); count = regulator_count_voltages(supply);
if (count < 0) if (count < 0)
return count; return count;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
int vdd_uV;
int vdd_mV;
vdd_uV = regulator_list_voltage(supply, i); vdd_uV = regulator_list_voltage(supply, i);
if (vdd_uV <= 0) if (vdd_uV <= 0)
continue; continue;
@ -1238,6 +1237,15 @@ int mmc_regulator_get_ocrmask(struct regulator *supply)
result |= mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV); result |= mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV);
} }
if (!result) {
vdd_uV = regulator_get_voltage(supply);
if (vdd_uV <= 0)
return vdd_uV;
vdd_mV = vdd_uV / 1000;
result = mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV);
}
return result; return result;
} }
EXPORT_SYMBOL_GPL(mmc_regulator_get_ocrmask); EXPORT_SYMBOL_GPL(mmc_regulator_get_ocrmask);
@ -1263,7 +1271,6 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
if (vdd_bit) { if (vdd_bit) {
int tmp; int tmp;
int voltage;
/* /*
* REVISIT mmc_vddrange_to_ocrmask() may have set some * REVISIT mmc_vddrange_to_ocrmask() may have set some
@ -1280,22 +1287,7 @@ int mmc_regulator_set_ocr(struct mmc_host *mmc,
max_uV = min_uV + 100 * 1000; max_uV = min_uV + 100 * 1000;
} }
/* result = regulator_set_voltage(supply, min_uV, max_uV);
* If we're using a fixed/static regulator, don't call
* regulator_set_voltage; it would fail.
*/
voltage = regulator_get_voltage(supply);
if (!regulator_can_change_voltage(supply))
min_uV = max_uV = voltage;
if (voltage < 0)
result = voltage;
else if (voltage < min_uV || voltage > max_uV)
result = regulator_set_voltage(supply, min_uV, max_uV);
else
result = 0;
if (result == 0 && !mmc->regulator_enabled) { if (result == 0 && !mmc->regulator_enabled) {
result = regulator_enable(supply); result = regulator_enable(supply);
if (!result) if (!result)
@ -1425,8 +1417,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr)
if (!host->ops->start_signal_voltage_switch) if (!host->ops->start_signal_voltage_switch)
return -EPERM; return -EPERM;
if (!host->ops->card_busy) if (!host->ops->card_busy)
pr_warning("%s: cannot verify signal voltage switch\n", pr_warn("%s: cannot verify signal voltage switch\n",
mmc_hostname(host)); mmc_hostname(host));
cmd.opcode = SD_SWITCH_VOLTAGE; cmd.opcode = SD_SWITCH_VOLTAGE;
cmd.arg = 0; cmd.arg = 0;
@ -1761,7 +1753,7 @@ void mmc_init_erase(struct mmc_card *card)
card->erase_shift = ffs(card->ssr.au) - 1; card->erase_shift = ffs(card->ssr.au) - 1;
} else if (card->ext_csd.hc_erase_size) { } else if (card->ext_csd.hc_erase_size) {
card->pref_erase = card->ext_csd.hc_erase_size; card->pref_erase = card->ext_csd.hc_erase_size;
} else { } else if (card->erase_size) {
sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11; sz = (card->csd.capacity << (card->csd.read_blkbits - 9)) >> 11;
if (sz < 128) if (sz < 128)
card->pref_erase = 512 * 1024 / 512; card->pref_erase = 512 * 1024 / 512;
@ -1778,7 +1770,8 @@ void mmc_init_erase(struct mmc_card *card)
if (sz) if (sz)
card->pref_erase += card->erase_size - sz; card->pref_erase += card->erase_size - sz;
} }
} } else
card->pref_erase = 0;
} }
static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card, static unsigned int mmc_mmc_erase_timeout(struct mmc_card *card,
@ -2489,6 +2482,7 @@ void mmc_start_host(struct mmc_host *host)
{ {
host->f_init = max(freqs[0], host->f_min); host->f_init = max(freqs[0], host->f_min);
host->rescan_disable = 0; host->rescan_disable = 0;
host->ios.power_mode = MMC_POWER_UNDEFINED;
if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
mmc_power_off(host); mmc_power_off(host);
else else

View File

@ -310,9 +310,8 @@ int mmc_of_parse(struct mmc_host *host)
{ {
struct device_node *np; struct device_node *np;
u32 bus_width; u32 bus_width;
bool explicit_inv_wp, gpio_inv_wp = false; int len, ret;
enum of_gpio_flags flags; bool cap_invert, gpio_invert;
int len, ret, gpio;
if (!host->parent || !host->parent->of_node) if (!host->parent || !host->parent->of_node)
return 0; return 0;
@ -360,59 +359,62 @@ int mmc_of_parse(struct mmc_host *host)
if (of_find_property(np, "non-removable", &len)) { if (of_find_property(np, "non-removable", &len)) {
host->caps |= MMC_CAP_NONREMOVABLE; host->caps |= MMC_CAP_NONREMOVABLE;
} else { } else {
bool explicit_inv_cd, gpio_inv_cd = false; if (of_property_read_bool(np, "cd-inverted"))
cap_invert = true;
explicit_inv_cd = of_property_read_bool(np, "cd-inverted"); else
cap_invert = false;
if (of_find_property(np, "broken-cd", &len)) if (of_find_property(np, "broken-cd", &len))
host->caps |= MMC_CAP_NEEDS_POLL; host->caps |= MMC_CAP_NEEDS_POLL;
gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags); ret = mmc_gpiod_request_cd(host, "cd", 0, true,
if (gpio == -EPROBE_DEFER) 0, &gpio_invert);
return gpio; if (ret) {
if (gpio_is_valid(gpio)) { if (ret == -EPROBE_DEFER)
if (!(flags & OF_GPIO_ACTIVE_LOW))
gpio_inv_cd = true;
ret = mmc_gpio_request_cd(host, gpio, 0);
if (ret < 0) {
dev_err(host->parent,
"Failed to request CD GPIO #%d: %d!\n",
gpio, ret);
return ret; return ret;
} else { if (ret != -ENOENT) {
dev_info(host->parent, "Got CD GPIO #%d.\n", dev_err(host->parent,
gpio); "Failed to request CD GPIO: %d\n",
ret);
} }
} } else
dev_info(host->parent, "Got CD GPIO\n");
if (explicit_inv_cd ^ gpio_inv_cd) /*
* There are two ways to flag that the CD line is inverted:
* through the cd-inverted flag and by the GPIO line itself
* being inverted from the GPIO subsystem. This is a leftover
* from the times when the GPIO subsystem did not make it
* possible to flag a line as inverted.
*
* If the capability on the host AND the GPIO line are
* both inverted, the end result is that the CD line is
* not inverted.
*/
if (cap_invert ^ gpio_invert)
host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
} }
/* Parse Write Protection */ /* Parse Write Protection */
explicit_inv_wp = of_property_read_bool(np, "wp-inverted"); if (of_property_read_bool(np, "wp-inverted"))
cap_invert = true;
else
cap_invert = false;
gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); ret = mmc_gpiod_request_ro(host, "wp", 0, false, 0, &gpio_invert);
if (gpio == -EPROBE_DEFER) { if (ret) {
ret = -EPROBE_DEFER; if (ret == -EPROBE_DEFER)
goto out;
}
if (gpio_is_valid(gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
gpio_inv_wp = true;
ret = mmc_gpio_request_ro(host, gpio);
if (ret < 0) {
dev_err(host->parent,
"Failed to request WP GPIO: %d!\n", ret);
goto out; goto out;
} else { if (ret != -ENOENT) {
dev_info(host->parent, "Got WP GPIO #%d.\n", dev_err(host->parent,
gpio); "Failed to request WP GPIO: %d\n",
ret);
} }
} } else
if (explicit_inv_wp ^ gpio_inv_wp) dev_info(host->parent, "Got WP GPIO\n");
/* See the comment on CD inversion above */
if (cap_invert ^ gpio_invert)
host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
if (of_find_property(np, "cap-sd-highspeed", &len)) if (of_find_property(np, "cap-sd-highspeed", &len))
@ -452,6 +454,14 @@ int mmc_of_parse(struct mmc_host *host)
if (of_find_property(np, "mmc-hs400-1_2v", &len)) if (of_find_property(np, "mmc-hs400-1_2v", &len))
host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR; host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
host->dsr_req = !of_property_read_u32(np, "dsr", &host->dsr);
if (host->dsr_req && (host->dsr & ~0xffff)) {
dev_err(host->parent,
"device tree specified broken value for DSR: 0x%x, ignoring\n",
host->dsr);
host->dsr_req = 0;
}
return 0; return 0;
out: out:

View File

@ -162,6 +162,7 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1);
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1); csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
@ -225,9 +226,7 @@ static int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd)
"Card will be ignored.\n", "Card will be ignored.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
} else { } else {
pr_warning("%s: unable to read " pr_warn("%s: unable to read EXT_CSD, performance might suffer\n",
"EXT_CSD, performance might "
"suffer.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
} }
@ -298,6 +297,97 @@ static void mmc_select_card_type(struct mmc_card *card)
card->mmc_avail_type = avail_type; card->mmc_avail_type = avail_type;
} }
static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd)
{
u8 hc_erase_grp_sz, hc_wp_grp_sz;
/*
* Disable these attributes by default
*/
card->ext_csd.enhanced_area_offset = -EINVAL;
card->ext_csd.enhanced_area_size = -EINVAL;
/*
* Enhanced area feature support -- check whether the eMMC
* card has the Enhanced area enabled. If so, export enhanced
* area offset and size to user by adding sysfs interface.
*/
if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) &&
(ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
if (card->ext_csd.partition_setting_completed) {
hc_erase_grp_sz =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
hc_wp_grp_sz =
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
/*
* calculate the enhanced data area offset, in bytes
*/
card->ext_csd.enhanced_area_offset =
(ext_csd[139] << 24) + (ext_csd[138] << 16) +
(ext_csd[137] << 8) + ext_csd[136];
if (mmc_card_blockaddr(card))
card->ext_csd.enhanced_area_offset <<= 9;
/*
* calculate the enhanced data area size, in kilobytes
*/
card->ext_csd.enhanced_area_size =
(ext_csd[142] << 16) + (ext_csd[141] << 8) +
ext_csd[140];
card->ext_csd.enhanced_area_size *=
(size_t)(hc_erase_grp_sz * hc_wp_grp_sz);
card->ext_csd.enhanced_area_size <<= 9;
} else {
pr_warn("%s: defines enhanced area without partition setting complete\n",
mmc_hostname(card->host));
}
}
}
static void mmc_manage_gp_partitions(struct mmc_card *card, u8 *ext_csd)
{
int idx;
u8 hc_erase_grp_sz, hc_wp_grp_sz;
unsigned int part_size;
/*
* General purpose partition feature support --
* If ext_csd has the size of general purpose partitions,
* set size, part_cfg, partition name in mmc_part.
*/
if (ext_csd[EXT_CSD_PARTITION_SUPPORT] &
EXT_CSD_PART_SUPPORT_PART_EN) {
hc_erase_grp_sz =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
hc_wp_grp_sz =
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) {
if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] &&
!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] &&
!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2])
continue;
if (card->ext_csd.partition_setting_completed == 0) {
pr_warn("%s: has partition size defined without partition complete\n",
mmc_hostname(card->host));
break;
}
part_size =
(ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]
<< 16) +
(ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1]
<< 8) +
ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3];
part_size *= (size_t)(hc_erase_grp_sz *
hc_wp_grp_sz);
mmc_part_add(card, part_size << 19,
EXT_CSD_PART_CONFIG_ACC_GP0 + idx,
"gp%d", idx, false,
MMC_BLK_DATA_AREA_GP);
}
}
}
/* /*
* Decode extended CSD. * Decode extended CSD.
*/ */
@ -305,7 +395,6 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
{ {
int err = 0, idx; int err = 0, idx;
unsigned int part_size; unsigned int part_size;
u8 hc_erase_grp_sz = 0, hc_wp_grp_sz = 0;
BUG_ON(!card); BUG_ON(!card);
@ -402,80 +491,16 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd)
ext_csd[EXT_CSD_TRIM_MULT]; ext_csd[EXT_CSD_TRIM_MULT];
card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT]; card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT];
if (card->ext_csd.rev >= 4) { if (card->ext_csd.rev >= 4) {
/* if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] &
* Enhanced area feature support -- check whether the eMMC EXT_CSD_PART_SETTING_COMPLETED)
* card has the Enhanced area enabled. If so, export enhanced card->ext_csd.partition_setting_completed = 1;
* area offset and size to user by adding sysfs interface. else
*/ card->ext_csd.partition_setting_completed = 0;
if ((ext_csd[EXT_CSD_PARTITION_SUPPORT] & 0x2) &&
(ext_csd[EXT_CSD_PARTITION_ATTRIBUTE] & 0x1)) {
hc_erase_grp_sz =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
hc_wp_grp_sz =
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
card->ext_csd.enhanced_area_en = 1; mmc_manage_enhanced_area(card, ext_csd);
/*
* calculate the enhanced data area offset, in bytes
*/
card->ext_csd.enhanced_area_offset =
(ext_csd[139] << 24) + (ext_csd[138] << 16) +
(ext_csd[137] << 8) + ext_csd[136];
if (mmc_card_blockaddr(card))
card->ext_csd.enhanced_area_offset <<= 9;
/*
* calculate the enhanced data area size, in kilobytes
*/
card->ext_csd.enhanced_area_size =
(ext_csd[142] << 16) + (ext_csd[141] << 8) +
ext_csd[140];
card->ext_csd.enhanced_area_size *=
(size_t)(hc_erase_grp_sz * hc_wp_grp_sz);
card->ext_csd.enhanced_area_size <<= 9;
} else {
/*
* If the enhanced area is not enabled, disable these
* device attributes.
*/
card->ext_csd.enhanced_area_offset = -EINVAL;
card->ext_csd.enhanced_area_size = -EINVAL;
}
/* mmc_manage_gp_partitions(card, ext_csd);
* General purpose partition feature support --
* If ext_csd has the size of general purpose partitions,
* set size, part_cfg, partition name in mmc_part.
*/
if (ext_csd[EXT_CSD_PARTITION_SUPPORT] &
EXT_CSD_PART_SUPPORT_PART_EN) {
if (card->ext_csd.enhanced_area_en != 1) {
hc_erase_grp_sz =
ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
hc_wp_grp_sz =
ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
card->ext_csd.enhanced_area_en = 1;
}
for (idx = 0; idx < MMC_NUM_GP_PARTITION; idx++) {
if (!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3] &&
!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1] &&
!ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2])
continue;
part_size =
(ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 2]
<< 16) +
(ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3 + 1]
<< 8) +
ext_csd[EXT_CSD_GP_SIZE_MULT + idx * 3];
part_size *= (size_t)(hc_erase_grp_sz *
hc_wp_grp_sz);
mmc_part_add(card, part_size << 19,
EXT_CSD_PART_CONFIG_ACC_GP0 + idx,
"gp%d", idx, false,
MMC_BLK_DATA_AREA_GP);
}
}
card->ext_csd.sec_trim_mult = card->ext_csd.sec_trim_mult =
ext_csd[EXT_CSD_SEC_TRIM_MULT]; ext_csd[EXT_CSD_SEC_TRIM_MULT];
card->ext_csd.sec_erase_mult = card->ext_csd.sec_erase_mult =
@ -789,8 +814,8 @@ static int __mmc_select_powerclass(struct mmc_card *card,
ext_csd->raw_pwr_cl_200_360; ext_csd->raw_pwr_cl_200_360;
break; break;
default: default:
pr_warning("%s: Voltage range not supported " pr_warn("%s: Voltage range not supported for power class\n",
"for power class.\n", mmc_hostname(host)); mmc_hostname(host));
return -EINVAL; return -EINVAL;
} }
@ -987,19 +1012,35 @@ static int mmc_select_hs_ddr(struct mmc_card *card)
* 1.8V vccq at 3.3V core voltage (vcc) is not required * 1.8V vccq at 3.3V core voltage (vcc) is not required
* in the JEDEC spec for DDR. * in the JEDEC spec for DDR.
* *
* Do not force change in vccq since we are obviously * Even (e)MMC card can support 3.3v to 1.2v vccq, but not all
* working and no change to vccq is needed. * host controller can support this, like some of the SDHCI
* controller which connect to an eMMC device. Some of these
* host controller still needs to use 1.8v vccq for supporting
* DDR mode.
*
* So the sequence will be:
* if (host and device can both support 1.2v IO)
* use 1.2v IO;
* else if (host and device can both support 1.8v IO)
* use 1.8v IO;
* so if host and device can only support 3.3v IO, this is the
* last choice.
* *
* WARNING: eMMC rules are NOT the same as SD DDR * WARNING: eMMC rules are NOT the same as SD DDR
*/ */
if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V) { err = -EINVAL;
err = __mmc_set_signal_voltage(host, if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_2V)
MMC_SIGNAL_VOLTAGE_120); err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120);
if (err)
return err;
}
mmc_set_timing(host, MMC_TIMING_MMC_DDR52); if (err && (card->mmc_avail_type & EXT_CSD_CARD_TYPE_DDR_1_8V))
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180);
/* make sure vccq is 3.3v after switching disaster */
if (err)
err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330);
if (!err)
mmc_set_timing(host, MMC_TIMING_MMC_DDR52);
return err; return err;
} }
@ -1134,6 +1175,38 @@ bus_speed:
return err; return err;
} }
const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE] = {
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
};
EXPORT_SYMBOL(tuning_blk_pattern_4bit);
const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE] = {
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
};
EXPORT_SYMBOL(tuning_blk_pattern_8bit);
/* /*
* Execute tuning sequence to seek the proper bus operating * Execute tuning sequence to seek the proper bus operating
* conditions for HS200 and HS400, which sends CMD21 to the device. * conditions for HS200 and HS400, which sends CMD21 to the device.
@ -1271,6 +1344,13 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
goto free_card; goto free_card;
} }
/*
* handling only for cards supporting DSR and hosts requesting
* DSR configuration
*/
if (card->csd.dsr_imp && host->dsr_req)
mmc_set_dsr(host);
/* /*
* Select card, as all following commands rely on that. * Select card, as all following commands rely on that.
*/ */
@ -1308,7 +1388,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF * If enhanced_area_en is TRUE, host needs to enable ERASE_GRP_DEF
* bit. This bit will be lost every time after a reset or power off. * bit. This bit will be lost every time after a reset or power off.
*/ */
if (card->ext_csd.enhanced_area_en || if (card->ext_csd.partition_setting_completed ||
(card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) { (card->ext_csd.rev >= 3 && (host->caps2 & MMC_CAP2_HC_ERASE_SZ))) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_ERASE_GROUP_DEF, 1, EXT_CSD_ERASE_GROUP_DEF, 1,
@ -1408,8 +1488,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
if (err && err != -EBADMSG) if (err && err != -EBADMSG)
goto free_card; goto free_card;
if (err) { if (err) {
pr_warning("%s: Enabling HPI failed\n", pr_warn("%s: Enabling HPI failed\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
} else } else
card->ext_csd.hpi_en = 1; card->ext_csd.hpi_en = 1;
@ -1430,9 +1510,8 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* Only if no error, cache is turned on successfully. * Only if no error, cache is turned on successfully.
*/ */
if (err) { if (err) {
pr_warning("%s: Cache is supported, " pr_warn("%s: Cache is supported, but failed to turn on (%d)\n",
"but failed to turn on (%d)\n", mmc_hostname(card->host), err);
mmc_hostname(card->host), err);
card->ext_csd.cache_ctrl = 0; card->ext_csd.cache_ctrl = 0;
err = 0; err = 0;
} else { } else {

View File

@ -93,6 +93,26 @@ int mmc_deselect_cards(struct mmc_host *host)
return _mmc_select_card(host, NULL); return _mmc_select_card(host, NULL);
} }
/*
* Write the value specified in the device tree or board code into the optional
* 16 bit Driver Stage Register. This can be used to tune raise/fall times and
* drive strength of the DAT and CMD outputs. The actual meaning of a given
* value is hardware dependant.
* The presence of the DSR register can be determined from the CSD register,
* bit 76.
*/
int mmc_set_dsr(struct mmc_host *host)
{
struct mmc_command cmd = {0};
cmd.opcode = MMC_SET_DSR;
cmd.arg = (host->dsr << 16) | 0xffff;
cmd.flags = MMC_RSP_NONE | MMC_CMD_AC;
return mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
}
int mmc_go_idle(struct mmc_host *host) int mmc_go_idle(struct mmc_host *host)
{ {
int err; int err;
@ -629,8 +649,8 @@ int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status)
int err; int err;
if (!card->ext_csd.hpi) { if (!card->ext_csd.hpi) {
pr_warning("%s: Card didn't support HPI command\n", pr_warn("%s: Card didn't support HPI command\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
return -EINVAL; return -EINVAL;
} }

View File

@ -14,6 +14,7 @@
int mmc_select_card(struct mmc_card *card); int mmc_select_card(struct mmc_card *card);
int mmc_deselect_cards(struct mmc_host *host); int mmc_deselect_cards(struct mmc_host *host);
int mmc_set_dsr(struct mmc_host *host);
int mmc_go_idle(struct mmc_host *host); int mmc_go_idle(struct mmc_host *host);
int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr);
int mmc_all_send_cid(struct mmc_host *host, u32 *cid); int mmc_all_send_cid(struct mmc_host *host, u32 *cid);

View File

@ -127,6 +127,7 @@ static int mmc_decode_csd(struct mmc_card *card)
csd->read_partial = UNSTUFF_BITS(resp, 79, 1); csd->read_partial = UNSTUFF_BITS(resp, 79, 1);
csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); csd->write_misalign = UNSTUFF_BITS(resp, 78, 1);
csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); csd->read_misalign = UNSTUFF_BITS(resp, 77, 1);
csd->dsr_imp = UNSTUFF_BITS(resp, 76, 1);
csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3);
csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4);
csd->write_partial = UNSTUFF_BITS(resp, 21, 1); csd->write_partial = UNSTUFF_BITS(resp, 21, 1);
@ -228,8 +229,8 @@ static int mmc_read_ssr(struct mmc_card *card)
u32 *ssr; u32 *ssr;
if (!(card->csd.cmdclass & CCC_APP_SPEC)) { if (!(card->csd.cmdclass & CCC_APP_SPEC)) {
pr_warning("%s: card lacks mandatory SD Status " pr_warn("%s: card lacks mandatory SD Status function\n",
"function.\n", mmc_hostname(card->host)); mmc_hostname(card->host));
return 0; return 0;
} }
@ -239,8 +240,8 @@ static int mmc_read_ssr(struct mmc_card *card)
err = mmc_app_sd_status(card, ssr); err = mmc_app_sd_status(card, ssr);
if (err) { if (err) {
pr_warning("%s: problem reading SD Status " pr_warn("%s: problem reading SD Status register\n",
"register.\n", mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
goto out; goto out;
} }
@ -264,8 +265,8 @@ static int mmc_read_ssr(struct mmc_card *card)
card->ssr.erase_offset = eo * 1000; card->ssr.erase_offset = eo * 1000;
} }
} else { } else {
pr_warning("%s: SD Status: Invalid Allocation Unit size.\n", pr_warn("%s: SD Status: Invalid Allocation Unit size\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
} }
} }
out: out:
@ -285,8 +286,7 @@ static int mmc_read_switch(struct mmc_card *card)
return 0; return 0;
if (!(card->csd.cmdclass & CCC_SWITCH)) { if (!(card->csd.cmdclass & CCC_SWITCH)) {
pr_warning("%s: card lacks mandatory switch " pr_warn("%s: card lacks mandatory switch function, performance might suffer\n",
"function, performance might suffer.\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
return 0; return 0;
} }
@ -315,7 +315,7 @@ static int mmc_read_switch(struct mmc_card *card)
if (err != -EINVAL && err != -ENOSYS && err != -EFAULT) if (err != -EINVAL && err != -ENOSYS && err != -EFAULT)
goto out; goto out;
pr_warning("%s: problem reading Bus Speed modes.\n", pr_warn("%s: problem reading Bus Speed modes\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
@ -371,8 +371,7 @@ int mmc_sd_switch_hs(struct mmc_card *card)
goto out; goto out;
if ((status[16] & 0xF) != 1) { if ((status[16] & 0xF) != 1) {
pr_warning("%s: Problem switching card " pr_warn("%s: Problem switching card into high-speed mode!\n",
"into high-speed mode!\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
err = 0; err = 0;
} else { } else {
@ -439,7 +438,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status)
return err; return err;
if ((status[15] & 0xF) != drive_strength) { if ((status[15] & 0xF) != drive_strength) {
pr_warning("%s: Problem setting drive strength!\n", pr_warn("%s: Problem setting drive strength!\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
return 0; return 0;
} }
@ -517,7 +516,7 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
return err; return err;
if ((status[16] & 0xF) != card->sd_bus_speed) if ((status[16] & 0xF) != card->sd_bus_speed)
pr_warning("%s: Problem setting bus speed mode!\n", pr_warn("%s: Problem setting bus speed mode!\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
else { else {
mmc_set_timing(card->host, timing); mmc_set_timing(card->host, timing);
@ -597,7 +596,7 @@ static int sd_set_current_limit(struct mmc_card *card, u8 *status)
return err; return err;
if (((status[15] >> 4) & 0x0F) != current_limit) if (((status[15] >> 4) & 0x0F) != current_limit)
pr_warning("%s: Problem setting current limit!\n", pr_warn("%s: Problem setting current limit!\n",
mmc_hostname(card->host)); mmc_hostname(card->host));
} }
@ -726,8 +725,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr)
try_again: try_again:
if (!retries) { if (!retries) {
ocr &= ~SD_OCR_S18R; ocr &= ~SD_OCR_S18R;
pr_warning("%s: Skipping voltage switch\n", pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
mmc_hostname(host));
} }
/* /*
@ -871,9 +869,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card,
} }
if (ro < 0) { if (ro < 0) {
pr_warning("%s: host does not " pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n",
"support reading read-only "
"switch. assuming write-enable.\n",
mmc_hostname(host)); mmc_hostname(host));
} else if (ro > 0) { } else if (ro > 0) {
mmc_card_set_readonly(card); mmc_card_set_readonly(card);
@ -953,6 +949,13 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr,
mmc_decode_cid(card); mmc_decode_cid(card);
} }
/*
* handling only for cards supporting DSR and hosts requesting
* DSR configuration
*/
if (card->csd.dsr_imp && host->dsr_req)
mmc_set_dsr(host);
/* /*
* Select card, as all following commands rely on that. * Select card, as all following commands rely on that.
*/ */

View File

@ -216,8 +216,8 @@ static int sdio_enable_wide(struct mmc_card *card)
return ret; return ret;
if ((ctrl & SDIO_BUS_WIDTH_MASK) == SDIO_BUS_WIDTH_RESERVED) if ((ctrl & SDIO_BUS_WIDTH_MASK) == SDIO_BUS_WIDTH_RESERVED)
pr_warning("%s: SDIO_CCCR_IF is invalid: 0x%02x\n", pr_warn("%s: SDIO_CCCR_IF is invalid: 0x%02x\n",
mmc_hostname(card->host), ctrl); mmc_hostname(card->host), ctrl);
/* set as 4-bit bus width */ /* set as 4-bit bus width */
ctrl &= ~SDIO_BUS_WIDTH_MASK; ctrl &= ~SDIO_BUS_WIDTH_MASK;
@ -605,8 +605,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
try_again: try_again:
if (!retries) { if (!retries) {
pr_warning("%s: Skipping voltage switch\n", pr_warn("%s: Skipping voltage switch\n", mmc_hostname(host));
mmc_hostname(host));
ocr &= ~R4_18V_PRESENT; ocr &= ~R4_18V_PRESENT;
} }
@ -992,8 +991,16 @@ static int mmc_sdio_resume(struct mmc_host *host)
} }
} }
if (!err && host->sdio_irqs) if (!err && host->sdio_irqs) {
wake_up_process(host->sdio_irq_thread); if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
wake_up_process(host->sdio_irq_thread);
} else if (host->caps & MMC_CAP_SDIO_IRQ) {
mmc_host_clk_hold(host);
host->ops->enable_sdio_irq(host, 1);
mmc_host_clk_release(host);
}
}
mmc_release_host(host); mmc_release_host(host);
host->pm_flags &= ~MMC_PM_KEEP_POWER; host->pm_flags &= ~MMC_PM_KEEP_POWER;

View File

@ -178,8 +178,8 @@ static int sdio_bus_remove(struct device *dev)
drv->remove(func); drv->remove(func);
if (func->irq_handler) { if (func->irq_handler) {
pr_warning("WARNING: driver %s did not remove " pr_warn("WARNING: driver %s did not remove its interrupt handler!\n",
"its interrupt handler!\n", drv->name); drv->name);
sdio_claim_host(func); sdio_claim_host(func);
sdio_release_irq(func); sdio_release_irq(func);
sdio_release_host(func); sdio_release_host(func);

View File

@ -69,16 +69,15 @@ static int process_sdio_pending_irqs(struct mmc_host *host)
if (pending & (1 << i)) { if (pending & (1 << i)) {
func = card->sdio_func[i - 1]; func = card->sdio_func[i - 1];
if (!func) { if (!func) {
pr_warning("%s: pending IRQ for " pr_warn("%s: pending IRQ for non-existent function\n",
"non-existent function\n",
mmc_card_id(card)); mmc_card_id(card));
ret = -EINVAL; ret = -EINVAL;
} else if (func->irq_handler) { } else if (func->irq_handler) {
func->irq_handler(func); func->irq_handler(func);
count++; count++;
} else { } else {
pr_warning("%s: pending IRQ with no handler\n", pr_warn("%s: pending IRQ with no handler\n",
sdio_func_id(func)); sdio_func_id(func));
ret = -EINVAL; ret = -EINVAL;
} }
} }
@ -208,7 +207,7 @@ static int sdio_card_irq_get(struct mmc_card *card)
host->sdio_irqs--; host->sdio_irqs--;
return err; return err;
} }
} else { } else if (host->caps & MMC_CAP_SDIO_IRQ) {
mmc_host_clk_hold(host); mmc_host_clk_hold(host);
host->ops->enable_sdio_irq(host, 1); host->ops->enable_sdio_irq(host, 1);
mmc_host_clk_release(host); mmc_host_clk_release(host);
@ -229,7 +228,7 @@ static int sdio_card_irq_put(struct mmc_card *card)
if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) { if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
atomic_set(&host->sdio_irq_thread_abort, 1); atomic_set(&host->sdio_irq_thread_abort, 1);
kthread_stop(host->sdio_irq_thread); kthread_stop(host->sdio_irq_thread);
} else { } else if (host->caps & MMC_CAP_SDIO_IRQ) {
mmc_host_clk_hold(host); mmc_host_clk_hold(host);
host->ops->enable_sdio_irq(host, 0); host->ops->enable_sdio_irq(host, 0);
mmc_host_clk_release(host); mmc_host_clk_release(host);

View File

@ -221,8 +221,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
ctx->override_cd_active_level = true; ctx->override_cd_active_level = true;
ctx->cd_gpio = gpio_to_desc(gpio); ctx->cd_gpio = gpio_to_desc(gpio);
mmc_gpiod_request_cd_irq(host);
return 0; return 0;
} }
EXPORT_SYMBOL(mmc_gpio_request_cd); EXPORT_SYMBOL(mmc_gpio_request_cd);
@ -283,6 +281,8 @@ EXPORT_SYMBOL(mmc_gpio_free_cd);
* @idx: index of the GPIO to obtain in the consumer * @idx: index of the GPIO to obtain in the consumer
* @override_active_level: ignore %GPIO_ACTIVE_LOW flag * @override_active_level: ignore %GPIO_ACTIVE_LOW flag
* @debounce: debounce time in microseconds * @debounce: debounce time in microseconds
* @gpio_invert: will return whether the GPIO line is inverted or not, set
* to NULL to ignore
* *
* Use this function in place of mmc_gpio_request_cd() to use the GPIO * Use this function in place of mmc_gpio_request_cd() to use the GPIO
* descriptor API. Note that it is paired with mmc_gpiod_free_cd() not * descriptor API. Note that it is paired with mmc_gpiod_free_cd() not
@ -293,7 +293,7 @@ EXPORT_SYMBOL(mmc_gpio_free_cd);
*/ */
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
unsigned int idx, bool override_active_level, unsigned int idx, bool override_active_level,
unsigned int debounce) unsigned int debounce, bool *gpio_invert)
{ {
struct mmc_gpio *ctx; struct mmc_gpio *ctx;
struct gpio_desc *desc; struct gpio_desc *desc;
@ -308,20 +308,19 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
if (!con_id) if (!con_id)
con_id = ctx->cd_label; con_id = ctx->cd_label;
desc = devm_gpiod_get_index(host->parent, con_id, idx); desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
if (IS_ERR(desc)) if (IS_ERR(desc))
return PTR_ERR(desc); return PTR_ERR(desc);
ret = gpiod_direction_input(desc);
if (ret < 0)
return ret;
if (debounce) { if (debounce) {
ret = gpiod_set_debounce(desc, debounce); ret = gpiod_set_debounce(desc, debounce);
if (ret < 0) if (ret < 0)
return ret; return ret;
} }
if (gpio_invert)
*gpio_invert = !gpiod_is_active_low(desc);
ctx->override_cd_active_level = override_active_level; ctx->override_cd_active_level = override_active_level;
ctx->cd_gpio = desc; ctx->cd_gpio = desc;
@ -329,6 +328,59 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
} }
EXPORT_SYMBOL(mmc_gpiod_request_cd); EXPORT_SYMBOL(mmc_gpiod_request_cd);
/**
* mmc_gpiod_request_ro - request a gpio descriptor for write protection
* @host: mmc host
* @con_id: function within the GPIO consumer
* @idx: index of the GPIO to obtain in the consumer
* @override_active_level: ignore %GPIO_ACTIVE_LOW flag
* @debounce: debounce time in microseconds
* @gpio_invert: will return whether the GPIO line is inverted or not,
* set to NULL to ignore
*
* Use this function in place of mmc_gpio_request_ro() to use the GPIO
* descriptor API. Note that it is paired with mmc_gpiod_free_ro() not
* mmc_gpio_free_ro().
*
* Returns zero on success, else an error.
*/
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
unsigned int idx, bool override_active_level,
unsigned int debounce, bool *gpio_invert)
{
struct mmc_gpio *ctx;
struct gpio_desc *desc;
int ret;
ret = mmc_gpio_alloc(host);
if (ret < 0)
return ret;
ctx = host->slot.handler_priv;
if (!con_id)
con_id = ctx->ro_label;
desc = devm_gpiod_get_index(host->parent, con_id, idx, GPIOD_IN);
if (IS_ERR(desc))
return PTR_ERR(desc);
if (debounce) {
ret = gpiod_set_debounce(desc, debounce);
if (ret < 0)
return ret;
}
if (gpio_invert)
*gpio_invert = !gpiod_is_active_low(desc);
ctx->override_ro_active_level = override_active_level;
ctx->ro_gpio = desc;
return 0;
}
EXPORT_SYMBOL(mmc_gpiod_request_ro);
/** /**
* mmc_gpiod_free_cd - free the card-detection gpio descriptor * mmc_gpiod_free_cd - free the card-detection gpio descriptor
* @host: mmc host * @host: mmc host
@ -348,7 +400,7 @@ void mmc_gpiod_free_cd(struct mmc_host *host)
host->slot.cd_irq = -EINVAL; host->slot.cd_irq = -EINVAL;
} }
devm_gpiod_put(&host->class_dev, ctx->cd_gpio); devm_gpiod_put(host->parent, ctx->cd_gpio);
ctx->cd_gpio = NULL; ctx->cd_gpio = NULL;
} }

View File

@ -14,6 +14,17 @@ config MMC_ARMMMCI
If unsure, say N. If unsure, say N.
config MMC_QCOM_DML
tristate "Qualcomm Data Mover for SD Card Controller"
depends on MMC_ARMMMCI && QCOM_BAM_DMA
default y
help
This selects the Qualcomm Data Mover lite/local on SD Card controller.
This option will enable the dma to work correctly, if you are using
Qcom SOCs and MMC, you would probably need this option to get DMA working.
if unsure, say N.
config MMC_PXA config MMC_PXA
tristate "Intel PXA25x/26x/27x Multimedia Card Interface support" tristate "Intel PXA25x/26x/27x Multimedia Card Interface support"
depends on ARCH_PXA depends on ARCH_PXA
@ -568,7 +579,8 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND
config MMC_DW config MMC_DW
tristate "Synopsys DesignWare Memory Card Interface" tristate "Synopsys DesignWare Memory Card Interface"
depends on ARC || ARM depends on HAS_DMA
depends on ARC || ARM || MIPS || COMPILE_TEST
help help
This selects support for the Synopsys DesignWare Mobile Storage IP This selects support for the Synopsys DesignWare Mobile Storage IP
block, this provides host support for SD and MMC interfaces, in both block, this provides host support for SD and MMC interfaces, in both
@ -626,6 +638,15 @@ config MMC_DW_PCI
If unsure, say N. If unsure, say N.
config MMC_DW_ROCKCHIP
tristate "Rockchip specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW && ARCH_ROCKCHIP
select MMC_DW_PLTFM
help
This selects support for Rockchip SoC specific extensions to the
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on RK3066, RK3188 and RK3288 SoC's.
config MMC_SH_MMCIF config MMC_SH_MMCIF
tristate "SuperH Internal MMCIF support" tristate "SuperH Internal MMCIF support"
depends on MMC_BLOCK && HAS_DMA depends on MMC_BLOCK && HAS_DMA

View File

@ -3,6 +3,7 @@
# #
obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_ARMMMCI) += mmci.o
obj-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_MXC) += mxcmmc.o
obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o
@ -45,6 +46,7 @@ obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o obj-$(CONFIG_MMC_VUB300) += vub300.o

View File

@ -17,6 +17,7 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
@ -2195,7 +2196,8 @@ static int __init atmci_init_slot(struct atmel_mci *host,
/* Assume card is present initially */ /* Assume card is present initially */
set_bit(ATMCI_CARD_PRESENT, &slot->flags); set_bit(ATMCI_CARD_PRESENT, &slot->flags);
if (gpio_is_valid(slot->detect_pin)) { if (gpio_is_valid(slot->detect_pin)) {
if (gpio_request(slot->detect_pin, "mmc_detect")) { if (devm_gpio_request(&host->pdev->dev, slot->detect_pin,
"mmc_detect")) {
dev_dbg(&mmc->class_dev, "no detect pin available\n"); dev_dbg(&mmc->class_dev, "no detect pin available\n");
slot->detect_pin = -EBUSY; slot->detect_pin = -EBUSY;
} else if (gpio_get_value(slot->detect_pin) ^ } else if (gpio_get_value(slot->detect_pin) ^
@ -2208,7 +2210,8 @@ static int __init atmci_init_slot(struct atmel_mci *host,
mmc->caps |= MMC_CAP_NEEDS_POLL; mmc->caps |= MMC_CAP_NEEDS_POLL;
if (gpio_is_valid(slot->wp_pin)) { if (gpio_is_valid(slot->wp_pin)) {
if (gpio_request(slot->wp_pin, "mmc_wp")) { if (devm_gpio_request(&host->pdev->dev, slot->wp_pin,
"mmc_wp")) {
dev_dbg(&mmc->class_dev, "no WP pin available\n"); dev_dbg(&mmc->class_dev, "no WP pin available\n");
slot->wp_pin = -EBUSY; slot->wp_pin = -EBUSY;
} }
@ -2232,7 +2235,6 @@ static int __init atmci_init_slot(struct atmel_mci *host,
dev_dbg(&mmc->class_dev, dev_dbg(&mmc->class_dev,
"could not request IRQ %d for detect pin\n", "could not request IRQ %d for detect pin\n",
gpio_to_irq(slot->detect_pin)); gpio_to_irq(slot->detect_pin));
gpio_free(slot->detect_pin);
slot->detect_pin = -EBUSY; slot->detect_pin = -EBUSY;
} }
} }
@ -2242,7 +2244,7 @@ static int __init atmci_init_slot(struct atmel_mci *host,
return 0; return 0;
} }
static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot, static void atmci_cleanup_slot(struct atmel_mci_slot *slot,
unsigned int id) unsigned int id)
{ {
/* Debugfs stuff is cleaned up by mmc core */ /* Debugfs stuff is cleaned up by mmc core */
@ -2257,10 +2259,7 @@ static void __exit atmci_cleanup_slot(struct atmel_mci_slot *slot,
free_irq(gpio_to_irq(pin), slot); free_irq(gpio_to_irq(pin), slot);
del_timer_sync(&slot->detect_timer); del_timer_sync(&slot->detect_timer);
gpio_free(pin);
} }
if (gpio_is_valid(slot->wp_pin))
gpio_free(slot->wp_pin);
slot->host->slot[id] = NULL; slot->host->slot[id] = NULL;
mmc_free_host(slot->mmc); mmc_free_host(slot->mmc);
@ -2344,6 +2343,7 @@ static void __init atmci_get_cap(struct atmel_mci *host)
/* keep only major version number */ /* keep only major version number */
switch (version & 0xf00) { switch (version & 0xf00) {
case 0x600:
case 0x500: case 0x500:
host->caps.has_odd_clk_div = 1; host->caps.has_odd_clk_div = 1;
case 0x400: case 0x400:
@ -2377,7 +2377,7 @@ static int __init atmci_probe(struct platform_device *pdev)
struct resource *regs; struct resource *regs;
unsigned int nr_slots; unsigned int nr_slots;
int irq; int irq;
int ret; int ret, i;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) if (!regs)
@ -2395,7 +2395,7 @@ static int __init atmci_probe(struct platform_device *pdev)
if (irq < 0) if (irq < 0)
return irq; return irq;
host = kzalloc(sizeof(struct atmel_mci), GFP_KERNEL); host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
if (!host) if (!host)
return -ENOMEM; return -ENOMEM;
@ -2403,20 +2403,18 @@ static int __init atmci_probe(struct platform_device *pdev)
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
INIT_LIST_HEAD(&host->queue); INIT_LIST_HEAD(&host->queue);
host->mck = clk_get(&pdev->dev, "mci_clk"); host->mck = devm_clk_get(&pdev->dev, "mci_clk");
if (IS_ERR(host->mck)) { if (IS_ERR(host->mck))
ret = PTR_ERR(host->mck); return PTR_ERR(host->mck);
goto err_clk_get;
}
ret = -ENOMEM; host->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
host->regs = ioremap(regs->start, resource_size(regs));
if (!host->regs) if (!host->regs)
goto err_ioremap; return -ENOMEM;
ret = clk_prepare_enable(host->mck); ret = clk_prepare_enable(host->mck);
if (ret) if (ret)
goto err_request_irq; return ret;
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
host->bus_hz = clk_get_rate(host->mck); host->bus_hz = clk_get_rate(host->mck);
clk_disable_unprepare(host->mck); clk_disable_unprepare(host->mck);
@ -2427,7 +2425,7 @@ static int __init atmci_probe(struct platform_device *pdev)
ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host); ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host);
if (ret) if (ret)
goto err_request_irq; return ret;
/* Get MCI capabilities and set operations according to it */ /* Get MCI capabilities and set operations according to it */
atmci_get_cap(host); atmci_get_cap(host);
@ -2485,7 +2483,7 @@ static int __init atmci_probe(struct platform_device *pdev)
if (!host->buffer) { if (!host->buffer) {
ret = -ENOMEM; ret = -ENOMEM;
dev_err(&pdev->dev, "buffer allocation failed\n"); dev_err(&pdev->dev, "buffer allocation failed\n");
goto err_init_slot; goto err_dma_alloc;
} }
} }
@ -2495,16 +2493,16 @@ static int __init atmci_probe(struct platform_device *pdev)
return 0; return 0;
err_dma_alloc:
for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
if (host->slot[i])
atmci_cleanup_slot(host->slot[i], i);
}
err_init_slot: err_init_slot:
del_timer_sync(&host->timer);
if (host->dma.chan) if (host->dma.chan)
dma_release_channel(host->dma.chan); dma_release_channel(host->dma.chan);
free_irq(irq, host); free_irq(irq, host);
err_request_irq:
iounmap(host->regs);
err_ioremap:
clk_put(host->mck);
err_clk_get:
kfree(host);
return ret; return ret;
} }
@ -2528,14 +2526,11 @@ static int __exit atmci_remove(struct platform_device *pdev)
atmci_readl(host, ATMCI_SR); atmci_readl(host, ATMCI_SR);
clk_disable_unprepare(host->mck); clk_disable_unprepare(host->mck);
del_timer_sync(&host->timer);
if (host->dma.chan) if (host->dma.chan)
dma_release_channel(host->dma.chan); dma_release_channel(host->dma.chan);
free_irq(platform_get_irq(pdev, 0), host); free_irq(platform_get_irq(pdev, 0), host);
iounmap(host->regs);
clk_put(host->mck);
kfree(host);
return 0; return 0;
} }

View File

@ -1028,9 +1028,12 @@ static int au1xmmc_probe(struct platform_device *pdev)
host->clk = clk_get(&pdev->dev, ALCHEMY_PERIPH_CLK); host->clk = clk_get(&pdev->dev, ALCHEMY_PERIPH_CLK);
if (IS_ERR(host->clk)) { if (IS_ERR(host->clk)) {
dev_err(&pdev->dev, "cannot find clock\n"); dev_err(&pdev->dev, "cannot find clock\n");
ret = PTR_ERR(host->clk);
goto out_irq; goto out_irq;
} }
if (clk_prepare_enable(host->clk)) {
ret = clk_prepare_enable(host->clk);
if (ret) {
dev_err(&pdev->dev, "cannot enable clock\n"); dev_err(&pdev->dev, "cannot enable clock\n");
goto out_clk; goto out_clk;
} }

View File

@ -95,9 +95,6 @@ static int dw_mci_pci_resume(struct device *dev)
return dw_mci_resume(host); return dw_mci_resume(host);
} }
#else
#define dw_mci_pci_suspend NULL
#define dw_mci_pci_resume NULL
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume); static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume);

View File

@ -21,6 +21,7 @@
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
#include <linux/mmc/dw_mmc.h> #include <linux/mmc/dw_mmc.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/clk.h>
#include "dw_mmc.h" #include "dw_mmc.h"
#include "dw_mmc-pltfm.h" #include "dw_mmc-pltfm.h"
@ -30,10 +31,6 @@ static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr)
*cmdr |= SDMMC_CMD_USE_HOLD_REG; *cmdr |= SDMMC_CMD_USE_HOLD_REG;
} }
static const struct dw_mci_drv_data rockchip_drv_data = {
.prepare_command = dw_mci_pltfm_prepare_command,
};
static const struct dw_mci_drv_data socfpga_drv_data = { static const struct dw_mci_drv_data socfpga_drv_data = {
.prepare_command = dw_mci_pltfm_prepare_command, .prepare_command = dw_mci_pltfm_prepare_command,
}; };
@ -84,9 +81,6 @@ static int dw_mci_pltfm_resume(struct device *dev)
return dw_mci_resume(host); return dw_mci_resume(host);
} }
#else
#define dw_mci_pltfm_suspend NULL
#define dw_mci_pltfm_resume NULL
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume); SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
@ -94,8 +88,6 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
static const struct of_device_id dw_mci_pltfm_match[] = { static const struct of_device_id dw_mci_pltfm_match[] = {
{ .compatible = "snps,dw-mshc", }, { .compatible = "snps,dw-mshc", },
{ .compatible = "rockchip,rk2928-dw-mshc",
.data = &rockchip_drv_data },
{ .compatible = "altr,socfpga-dw-mshc", { .compatible = "altr,socfpga-dw-mshc",
.data = &socfpga_drv_data }, .data = &socfpga_drv_data },
{}, {},

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd
*
* 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"
#define RK3288_CLKGEN_DIV 2
static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
{
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
}
static int dw_mci_rk3288_setup_clock(struct dw_mci *host)
{
host->bus_hz /= RK3288_CLKGEN_DIV;
return 0;
}
static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios)
{
int ret;
unsigned int cclkin;
u32 bus_hz;
/*
* cclkin: source clock of mmc controller
* bus_hz: card interface clock generated by CLKGEN
* bus_hz = cclkin / RK3288_CLKGEN_DIV
* ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div))
*
* Note: div can only be 0 or 1
* if DDR50 8bit mode(only emmc work in 8bit mode),
* div must be set 1
*/
if (ios->bus_width == MMC_BUS_WIDTH_8 &&
ios->timing == MMC_TIMING_MMC_DDR52)
cclkin = 2 * ios->clock * RK3288_CLKGEN_DIV;
else
cclkin = ios->clock * RK3288_CLKGEN_DIV;
ret = clk_set_rate(host->ciu_clk, cclkin);
if (ret)
dev_warn(host->dev, "failed to set rate %uHz\n", ios->clock);
bus_hz = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV;
if (bus_hz != host->bus_hz) {
host->bus_hz = bus_hz;
/* force dw_mci_setup_bus() */
host->current_speed = 0;
}
}
static const struct dw_mci_drv_data rk2928_drv_data = {
.prepare_command = dw_mci_rockchip_prepare_command,
};
static const struct dw_mci_drv_data rk3288_drv_data = {
.prepare_command = dw_mci_rockchip_prepare_command,
.set_ios = dw_mci_rk3288_set_ios,
.setup_clock = dw_mci_rk3288_setup_clock,
};
static const struct of_device_id dw_mci_rockchip_match[] = {
{ .compatible = "rockchip,rk2928-dw-mshc",
.data = &rk2928_drv_data },
{ .compatible = "rockchip,rk3288-dw-mshc",
.data = &rk3288_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_rockchip_match);
static int dw_mci_rockchip_probe(struct platform_device *pdev)
{
const struct dw_mci_drv_data *drv_data;
const struct of_device_id *match;
if (!pdev->dev.of_node)
return -ENODEV;
match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
drv_data = match->data;
return dw_mci_pltfm_register(pdev, drv_data);
}
#ifdef CONFIG_PM_SLEEP
static int dw_mci_rockchip_suspend(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
return dw_mci_suspend(host);
}
static int dw_mci_rockchip_resume(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
return dw_mci_resume(host);
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(dw_mci_rockchip_pmops,
dw_mci_rockchip_suspend,
dw_mci_rockchip_resume);
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
.probe = dw_mci_rockchip_probe,
.remove = __exit_p(dw_mci_pltfm_remove),
.driver = {
.name = "dwmmc_rockchip",
.of_match_table = dw_mci_rockchip_match,
.pm = &dw_mci_rockchip_pmops,
},
};
module_platform_driver(dw_mci_rockchip_pltfm_driver);
MODULE_AUTHOR("Addy Ke <addy.ke@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension");
MODULE_ALIAS("platform:dwmmc-rockchip");
MODULE_LICENSE("GPL v2");

View File

@ -29,6 +29,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/mmc/host.h> #include <linux/mmc/host.h>
#include <linux/mmc/mmc.h> #include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <linux/mmc/sdio.h> #include <linux/mmc/sdio.h>
#include <linux/mmc/dw_mmc.h> #include <linux/mmc/dw_mmc.h>
#include <linux/bitops.h> #include <linux/bitops.h>
@ -81,36 +82,6 @@ struct idmac_desc {
}; };
#endif /* CONFIG_MMC_DW_IDMAC */ #endif /* CONFIG_MMC_DW_IDMAC */
static const u8 tuning_blk_pattern_4bit[] = {
0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc,
0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef,
0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb,
0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef,
0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c,
0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee,
0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff,
0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde,
};
static const u8 tuning_blk_pattern_8bit[] = {
0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc,
0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff,
0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff,
0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd,
0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb,
0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff,
0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff,
0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00,
0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc,
0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff,
0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee,
0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd,
0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff,
0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff,
0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee,
};
static bool dw_mci_reset(struct dw_mci *host); static bool dw_mci_reset(struct dw_mci *host);
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
@ -234,10 +205,13 @@ err:
} }
#endif /* defined(CONFIG_DEBUG_FS) */ #endif /* defined(CONFIG_DEBUG_FS) */
static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg);
static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
{ {
struct mmc_data *data; struct mmc_data *data;
struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
const struct dw_mci_drv_data *drv_data = slot->host->drv_data; const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
u32 cmdr; u32 cmdr;
cmd->error = -EINPROGRESS; cmd->error = -EINPROGRESS;
@ -253,6 +227,34 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd)
else if (cmd->opcode != MMC_SEND_STATUS && cmd->data) else if (cmd->opcode != MMC_SEND_STATUS && cmd->data)
cmdr |= SDMMC_CMD_PRV_DAT_WAIT; cmdr |= SDMMC_CMD_PRV_DAT_WAIT;
if (cmd->opcode == SD_SWITCH_VOLTAGE) {
u32 clk_en_a;
/* Special bit makes CMD11 not die */
cmdr |= SDMMC_CMD_VOLT_SWITCH;
/* Change state to continue to handle CMD11 weirdness */
WARN_ON(slot->host->state != STATE_SENDING_CMD);
slot->host->state = STATE_SENDING_CMD11;
/*
* We need to disable low power mode (automatic clock stop)
* while doing voltage switch so we don't confuse the card,
* since stopping the clock is a specific part of the UHS
* voltage change dance.
*
* Note that low power mode (SDMMC_CLKEN_LOW_PWR) will be
* unconditionally turned back on in dw_mci_setup_bus() if it's
* ever called with a non-zero clock. That shouldn't happen
* until the voltage change is all done.
*/
clk_en_a = mci_readl(host, CLKENA);
clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id);
mci_writel(host, CLKENA, clk_en_a);
mci_send_cmd(slot, SDMMC_CMD_UPD_CLK |
SDMMC_CMD_PRV_DAT_WAIT, 0);
}
if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_PRESENT) {
/* We expect a response, so set this bit */ /* We expect a response, so set this bit */
cmdr |= SDMMC_CMD_RESP_EXP; cmdr |= SDMMC_CMD_RESP_EXP;
@ -775,11 +777,15 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
unsigned int clock = slot->clock; unsigned int clock = slot->clock;
u32 div; u32 div;
u32 clk_en_a; u32 clk_en_a;
u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT;
/* We must continue to set bit 28 in CMD until the change is complete */
if (host->state == STATE_WAITING_CMD11_DONE)
sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH;
if (!clock) { if (!clock) {
mci_writel(host, CLKENA, 0); mci_writel(host, CLKENA, 0);
mci_send_cmd(slot, mci_send_cmd(slot, sdmmc_cmd_bits, 0);
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
} else if (clock != host->current_speed || force_clkinit) { } else if (clock != host->current_speed || force_clkinit) {
div = host->bus_hz / clock; div = host->bus_hz / clock;
if (host->bus_hz % clock && host->bus_hz > clock) if (host->bus_hz % clock && host->bus_hz > clock)
@ -803,15 +809,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
mci_writel(host, CLKSRC, 0); mci_writel(host, CLKSRC, 0);
/* inform CIU */ /* inform CIU */
mci_send_cmd(slot, mci_send_cmd(slot, sdmmc_cmd_bits, 0);
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* set clock to desired speed */ /* set clock to desired speed */
mci_writel(host, CLKDIV, div); mci_writel(host, CLKDIV, div);
/* inform CIU */ /* inform CIU */
mci_send_cmd(slot, mci_send_cmd(slot, sdmmc_cmd_bits, 0);
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* enable clock; only low power if no SDIO */ /* enable clock; only low power if no SDIO */
clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; clk_en_a = SDMMC_CLKEN_ENABLE << slot->id;
@ -820,8 +824,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)
mci_writel(host, CLKENA, clk_en_a); mci_writel(host, CLKENA, clk_en_a);
/* inform CIU */ /* inform CIU */
mci_send_cmd(slot, mci_send_cmd(slot, sdmmc_cmd_bits, 0);
SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0);
/* keep the clock with reflecting clock dividor */ /* keep the clock with reflecting clock dividor */
slot->__clk_old = clock << div; slot->__clk_old = clock << div;
@ -897,6 +900,17 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot,
slot->mrq = mrq; slot->mrq = mrq;
if (host->state == STATE_WAITING_CMD11_DONE) {
dev_warn(&slot->mmc->class_dev,
"Voltage change didn't complete\n");
/*
* this case isn't expected to happen, so we can
* either crash here or just try to continue on
* in the closest possible state
*/
host->state = STATE_IDLE;
}
if (host->state == STATE_IDLE) { if (host->state == STATE_IDLE) {
host->state = STATE_SENDING_CMD; host->state = STATE_SENDING_CMD;
dw_mci_start_request(host, slot); dw_mci_start_request(host, slot);
@ -936,6 +950,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci_slot *slot = mmc_priv(mmc);
const struct dw_mci_drv_data *drv_data = slot->host->drv_data; const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
u32 regs; u32 regs;
int ret;
switch (ios->bus_width) { switch (ios->bus_width) {
case MMC_BUS_WIDTH_4: case MMC_BUS_WIDTH_4:
@ -972,14 +987,43 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
/* Slot specific timing and width adjustment */ /* Slot specific timing and width adjustment */
dw_mci_setup_bus(slot, false); dw_mci_setup_bus(slot, false);
if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0)
slot->host->state = STATE_IDLE;
switch (ios->power_mode) { switch (ios->power_mode) {
case MMC_POWER_UP: case MMC_POWER_UP:
if (!IS_ERR(mmc->supply.vmmc)) {
ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
ios->vdd);
if (ret) {
dev_err(slot->host->dev,
"failed to enable vmmc regulator\n");
/*return, if failed turn on vmmc*/
return;
}
}
if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) {
ret = regulator_enable(mmc->supply.vqmmc);
if (ret < 0)
dev_err(slot->host->dev,
"failed to enable vqmmc regulator\n");
else
slot->host->vqmmc_enabled = true;
}
set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags);
regs = mci_readl(slot->host, PWREN); regs = mci_readl(slot->host, PWREN);
regs |= (1 << slot->id); regs |= (1 << slot->id);
mci_writel(slot->host, PWREN, regs); mci_writel(slot->host, PWREN, regs);
break; break;
case MMC_POWER_OFF: case MMC_POWER_OFF:
if (!IS_ERR(mmc->supply.vmmc))
mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) {
regulator_disable(mmc->supply.vqmmc);
slot->host->vqmmc_enabled = false;
}
regs = mci_readl(slot->host, PWREN); regs = mci_readl(slot->host, PWREN);
regs &= ~(1 << slot->id); regs &= ~(1 << slot->id);
mci_writel(slot->host, PWREN, regs); mci_writel(slot->host, PWREN, regs);
@ -989,6 +1033,59 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
} }
} }
static int dw_mci_card_busy(struct mmc_host *mmc)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
u32 status;
/*
* Check the busy bit which is low when DAT[3:0]
* (the data lines) are 0000
*/
status = mci_readl(slot->host, STATUS);
return !!(status & SDMMC_STATUS_BUSY);
}
static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct dw_mci_slot *slot = mmc_priv(mmc);
struct dw_mci *host = slot->host;
u32 uhs;
u32 v18 = SDMMC_UHS_18V << slot->id;
int min_uv, max_uv;
int ret;
/*
* Program the voltage. Note that some instances of dw_mmc may use
* the UHS_REG for this. For other instances (like exynos) the UHS_REG
* does no harm but you need to set the regulator directly. Try both.
*/
uhs = mci_readl(host, UHS_REG);
if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) {
min_uv = 2700000;
max_uv = 3600000;
uhs &= ~v18;
} else {
min_uv = 1700000;
max_uv = 1950000;
uhs |= v18;
}
if (!IS_ERR(mmc->supply.vqmmc)) {
ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv);
if (ret) {
dev_err(&mmc->class_dev,
"Regulator set error %d: %d - %d\n",
ret, min_uv, max_uv);
return ret;
}
}
mci_writel(host, UHS_REG, uhs);
return 0;
}
static int dw_mci_get_ro(struct mmc_host *mmc) static int dw_mci_get_ro(struct mmc_host *mmc)
{ {
int read_only; int read_only;
@ -1131,6 +1228,9 @@ static const struct mmc_host_ops dw_mci_ops = {
.get_cd = dw_mci_get_cd, .get_cd = dw_mci_get_cd,
.enable_sdio_irq = dw_mci_enable_sdio_irq, .enable_sdio_irq = dw_mci_enable_sdio_irq,
.execute_tuning = dw_mci_execute_tuning, .execute_tuning = dw_mci_execute_tuning,
.card_busy = dw_mci_card_busy,
.start_signal_voltage_switch = dw_mci_switch_voltage,
}; };
static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
@ -1154,7 +1254,11 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq)
dw_mci_start_request(host, slot); dw_mci_start_request(host, slot);
} else { } else {
dev_vdbg(host->dev, "list empty\n"); dev_vdbg(host->dev, "list empty\n");
host->state = STATE_IDLE;
if (host->state == STATE_SENDING_CMD11)
host->state = STATE_WAITING_CMD11_DONE;
else
host->state = STATE_IDLE;
} }
spin_unlock(&host->lock); spin_unlock(&host->lock);
@ -1265,8 +1369,10 @@ static void dw_mci_tasklet_func(unsigned long priv)
switch (state) { switch (state) {
case STATE_IDLE: case STATE_IDLE:
case STATE_WAITING_CMD11_DONE:
break; break;
case STATE_SENDING_CMD11:
case STATE_SENDING_CMD: case STATE_SENDING_CMD:
if (!test_and_clear_bit(EVENT_CMD_COMPLETE, if (!test_and_clear_bit(EVENT_CMD_COMPLETE,
&host->pending_events)) &host->pending_events))
@ -1299,6 +1405,14 @@ static void dw_mci_tasklet_func(unsigned long priv)
/* fall through */ /* fall through */
case STATE_SENDING_DATA: case STATE_SENDING_DATA:
/*
* We could get a data error and never a transfer
* complete so we'd better check for it here.
*
* Note that we don't really care if we also got a
* transfer complete; stopping the DMA and sending an
* abort won't hurt.
*/
if (test_and_clear_bit(EVENT_DATA_ERROR, if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) { &host->pending_events)) {
dw_mci_stop_dma(host); dw_mci_stop_dma(host);
@ -1312,7 +1426,29 @@ static void dw_mci_tasklet_func(unsigned long priv)
break; break;
set_bit(EVENT_XFER_COMPLETE, &host->completed_events); set_bit(EVENT_XFER_COMPLETE, &host->completed_events);
/*
* Handle an EVENT_DATA_ERROR that might have shown up
* before the transfer completed. This might not have
* been caught by the check above because the interrupt
* could have gone off between the previous check and
* the check for transfer complete.
*
* Technically this ought not be needed assuming we
* get a DATA_COMPLETE eventually (we'll notice the
* error and end the request), but it shouldn't hurt.
*
* This has the advantage of sending the stop command.
*/
if (test_and_clear_bit(EVENT_DATA_ERROR,
&host->pending_events)) {
dw_mci_stop_dma(host);
send_stop_abort(host, data);
state = STATE_DATA_ERROR;
break;
}
prev_state = state = STATE_DATA_BUSY; prev_state = state = STATE_DATA_BUSY;
/* fall through */ /* fall through */
case STATE_DATA_BUSY: case STATE_DATA_BUSY:
@ -1335,6 +1471,22 @@ static void dw_mci_tasklet_func(unsigned long priv)
/* stop command for open-ended transfer*/ /* stop command for open-ended transfer*/
if (data->stop) if (data->stop)
send_stop_abort(host, data); send_stop_abort(host, data);
} else {
/*
* If we don't have a command complete now we'll
* never get one since we just reset everything;
* better end the request.
*
* If we do have a command complete we'll fall
* through to the SENDING_STOP command and
* everything will be peachy keen.
*/
if (!test_bit(EVENT_CMD_COMPLETE,
&host->pending_events)) {
host->cmd = NULL;
dw_mci_request_end(host, mrq);
goto unlock;
}
} }
/* /*
@ -1821,6 +1973,14 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id)
} }
if (pending) { if (pending) {
/* Check volt switch first, since it can look like an error */
if ((host->state == STATE_SENDING_CMD11) &&
(pending & SDMMC_INT_VOLT_SWITCH)) {
mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH);
pending &= ~SDMMC_INT_VOLT_SWITCH;
dw_mci_cmd_interrupt(host, pending);
}
if (pending & DW_MCI_CMD_ERROR_FLAGS) { if (pending & DW_MCI_CMD_ERROR_FLAGS) {
mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS);
host->cmd_status = pending; host->cmd_status = pending;
@ -1926,7 +2086,9 @@ static void dw_mci_work_routine_card(struct work_struct *work)
switch (host->state) { switch (host->state) {
case STATE_IDLE: case STATE_IDLE:
case STATE_WAITING_CMD11_DONE:
break; break;
case STATE_SENDING_CMD11:
case STATE_SENDING_CMD: case STATE_SENDING_CMD:
mrq->cmd->error = -ENOMEDIUM; mrq->cmd->error = -ENOMEDIUM;
if (!mrq->data) if (!mrq->data)
@ -2028,10 +2190,6 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot)
{ {
return 0; return 0;
} }
static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot)
{
return NULL;
}
#endif /* CONFIG_OF */ #endif /* CONFIG_OF */
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
@ -2064,7 +2222,13 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
mmc->f_max = freq[1]; mmc->f_max = freq[1];
} }
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; /*if there are external regulators, get them*/
ret = mmc_regulator_get_supply(mmc);
if (ret == -EPROBE_DEFER)
goto err_host_allocated;
if (!mmc->ocr_avail)
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
if (host->pdata->caps) if (host->pdata->caps)
mmc->caps = host->pdata->caps; mmc->caps = host->pdata->caps;
@ -2085,7 +2249,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
if (host->pdata->caps2) if (host->pdata->caps2)
mmc->caps2 = host->pdata->caps2; mmc->caps2 = host->pdata->caps2;
mmc_of_parse(mmc); ret = mmc_of_parse(mmc);
if (ret)
goto err_host_allocated;
if (host->pdata->blk_settings) { if (host->pdata->blk_settings) {
mmc->max_segs = host->pdata->blk_settings->max_segs; mmc->max_segs = host->pdata->blk_settings->max_segs;
@ -2117,7 +2283,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
ret = mmc_add_host(mmc); ret = mmc_add_host(mmc);
if (ret) if (ret)
goto err_setup_bus; goto err_host_allocated;
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
dw_mci_init_debugfs(slot); dw_mci_init_debugfs(slot);
@ -2128,9 +2294,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
return 0; return 0;
err_setup_bus: err_host_allocated:
mmc_free_host(mmc); mmc_free_host(mmc);
return -EINVAL; return ret;
} }
static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id)
@ -2423,24 +2589,6 @@ int dw_mci_probe(struct dw_mci *host)
} }
} }
host->vmmc = devm_regulator_get_optional(host->dev, "vmmc");
if (IS_ERR(host->vmmc)) {
ret = PTR_ERR(host->vmmc);
if (ret == -EPROBE_DEFER)
goto err_clk_ciu;
dev_info(host->dev, "no vmmc regulator found: %d\n", ret);
host->vmmc = NULL;
} else {
ret = regulator_enable(host->vmmc);
if (ret) {
if (ret != -EPROBE_DEFER)
dev_err(host->dev,
"regulator_enable fail: %d\n", ret);
goto err_clk_ciu;
}
}
host->quirks = host->pdata->quirks; host->quirks = host->pdata->quirks;
spin_lock_init(&host->lock); spin_lock_init(&host->lock);
@ -2584,8 +2732,6 @@ err_workqueue:
err_dmaunmap: err_dmaunmap:
if (host->use_dma && host->dma_ops->exit) if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host); host->dma_ops->exit(host);
if (host->vmmc)
regulator_disable(host->vmmc);
err_clk_ciu: err_clk_ciu:
if (!IS_ERR(host->ciu_clk)) if (!IS_ERR(host->ciu_clk))
@ -2621,9 +2767,6 @@ void dw_mci_remove(struct dw_mci *host)
if (host->use_dma && host->dma_ops->exit) if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host); host->dma_ops->exit(host);
if (host->vmmc)
regulator_disable(host->vmmc);
if (!IS_ERR(host->ciu_clk)) if (!IS_ERR(host->ciu_clk))
clk_disable_unprepare(host->ciu_clk); clk_disable_unprepare(host->ciu_clk);
@ -2640,9 +2783,6 @@ EXPORT_SYMBOL(dw_mci_remove);
*/ */
int dw_mci_suspend(struct dw_mci *host) int dw_mci_suspend(struct dw_mci *host)
{ {
if (host->vmmc)
regulator_disable(host->vmmc);
return 0; return 0;
} }
EXPORT_SYMBOL(dw_mci_suspend); EXPORT_SYMBOL(dw_mci_suspend);
@ -2651,15 +2791,6 @@ int dw_mci_resume(struct dw_mci *host)
{ {
int i, ret; int i, ret;
if (host->vmmc) {
ret = regulator_enable(host->vmmc);
if (ret) {
dev_err(host->dev,
"failed to enable regulator: %d\n", ret);
return ret;
}
}
if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) { if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) {
ret = -ENODEV; ret = -ENODEV;
return ret; return ret;

View File

@ -99,6 +99,7 @@
#define SDMMC_INT_HLE BIT(12) #define SDMMC_INT_HLE BIT(12)
#define SDMMC_INT_FRUN BIT(11) #define SDMMC_INT_FRUN BIT(11)
#define SDMMC_INT_HTO BIT(10) #define SDMMC_INT_HTO BIT(10)
#define SDMMC_INT_VOLT_SWITCH BIT(10) /* overloads bit 10! */
#define SDMMC_INT_DRTO BIT(9) #define SDMMC_INT_DRTO BIT(9)
#define SDMMC_INT_RTO BIT(8) #define SDMMC_INT_RTO BIT(8)
#define SDMMC_INT_DCRC BIT(7) #define SDMMC_INT_DCRC BIT(7)
@ -113,6 +114,7 @@
/* Command register defines */ /* Command register defines */
#define SDMMC_CMD_START BIT(31) #define SDMMC_CMD_START BIT(31)
#define SDMMC_CMD_USE_HOLD_REG BIT(29) #define SDMMC_CMD_USE_HOLD_REG BIT(29)
#define SDMMC_CMD_VOLT_SWITCH BIT(28)
#define SDMMC_CMD_CCS_EXP BIT(23) #define SDMMC_CMD_CCS_EXP BIT(23)
#define SDMMC_CMD_CEATA_RD BIT(22) #define SDMMC_CMD_CEATA_RD BIT(22)
#define SDMMC_CMD_UPD_CLK BIT(21) #define SDMMC_CMD_UPD_CLK BIT(21)
@ -130,6 +132,7 @@
/* Status register defines */ /* Status register defines */
#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF) #define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF)
#define SDMMC_STATUS_DMA_REQ BIT(31) #define SDMMC_STATUS_DMA_REQ BIT(31)
#define SDMMC_STATUS_BUSY BIT(9)
/* FIFOTH register defines */ /* FIFOTH register defines */
#define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \ #define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \
((r) & 0xFFF) << 16 | \ ((r) & 0xFFF) << 16 | \
@ -150,7 +153,7 @@
#define SDMMC_GET_VERID(x) ((x) & 0xFFFF) #define SDMMC_GET_VERID(x) ((x) & 0xFFFF)
/* Card read threshold */ /* Card read threshold */
#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x)) #define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x))
#define SDMMC_UHS_18V BIT(0)
/* All ctrl reset bits */ /* All ctrl reset bits */
#define SDMMC_CTRL_ALL_RESET_FLAGS \ #define SDMMC_CTRL_ALL_RESET_FLAGS \
(SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET) (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET)

View File

@ -30,7 +30,9 @@
#include <asm/mach-jz4740/gpio.h> #include <asm/mach-jz4740/gpio.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <asm/mach-jz4740/dma.h>
#include <asm/mach-jz4740/jz4740_mmc.h> #include <asm/mach-jz4740/jz4740_mmc.h>
#define JZ_REG_MMC_STRPCL 0x00 #define JZ_REG_MMC_STRPCL 0x00
@ -112,6 +114,11 @@ enum jz4740_mmc_state {
JZ4740_MMC_STATE_DONE, JZ4740_MMC_STATE_DONE,
}; };
struct jz4740_mmc_host_next {
int sg_len;
s32 cookie;
};
struct jz4740_mmc_host { struct jz4740_mmc_host {
struct mmc_host *mmc; struct mmc_host *mmc;
struct platform_device *pdev; struct platform_device *pdev;
@ -122,6 +129,7 @@ struct jz4740_mmc_host {
int card_detect_irq; int card_detect_irq;
void __iomem *base; void __iomem *base;
struct resource *mem_res;
struct mmc_request *req; struct mmc_request *req;
struct mmc_command *cmd; struct mmc_command *cmd;
@ -136,8 +144,220 @@ struct jz4740_mmc_host {
struct timer_list timeout_timer; struct timer_list timeout_timer;
struct sg_mapping_iter miter; struct sg_mapping_iter miter;
enum jz4740_mmc_state state; enum jz4740_mmc_state state;
/* DMA support */
struct dma_chan *dma_rx;
struct dma_chan *dma_tx;
struct jz4740_mmc_host_next next_data;
bool use_dma;
int sg_len;
/* The DMA trigger level is 8 words, that is to say, the DMA read
* trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write
* trigger is when data words in MSC_TXFIFO is < 8.
*/
#define JZ4740_MMC_FIFO_HALF_SIZE 8
}; };
/*----------------------------------------------------------------------------*/
/* DMA infrastructure */
static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host)
{
if (!host->use_dma)
return;
dma_release_channel(host->dma_tx);
dma_release_channel(host->dma_rx);
}
static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
{
dma_cap_mask_t mask;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
host->dma_tx = dma_request_channel(mask, NULL, host);
if (!host->dma_tx) {
dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n");
return -ENODEV;
}
host->dma_rx = dma_request_channel(mask, NULL, host);
if (!host->dma_rx) {
dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n");
goto free_master_write;
}
/* Initialize DMA pre request cookie */
host->next_data.cookie = 1;
return 0;
free_master_write:
dma_release_channel(host->dma_tx);
return -ENODEV;
}
static inline int jz4740_mmc_get_dma_dir(struct mmc_data *data)
{
return (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
}
static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host,
struct mmc_data *data)
{
return (data->flags & MMC_DATA_READ) ? host->dma_rx : host->dma_tx;
}
static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host,
struct mmc_data *data)
{
struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data);
dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
}
/* Prepares DMA data for current/next transfer, returns non-zero on failure */
static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host,
struct mmc_data *data,
struct jz4740_mmc_host_next *next,
struct dma_chan *chan)
{
struct jz4740_mmc_host_next *next_data = &host->next_data;
enum dma_data_direction dir = jz4740_mmc_get_dma_dir(data);
int sg_len;
if (!next && data->host_cookie &&
data->host_cookie != host->next_data.cookie) {
dev_warn(mmc_dev(host->mmc),
"[%s] invalid cookie: data->host_cookie %d host->next_data.cookie %d\n",
__func__,
data->host_cookie,
host->next_data.cookie);
data->host_cookie = 0;
}
/* Check if next job is already prepared */
if (next || data->host_cookie != host->next_data.cookie) {
sg_len = dma_map_sg(chan->device->dev,
data->sg,
data->sg_len,
dir);
} else {
sg_len = next_data->sg_len;
next_data->sg_len = 0;
}
if (sg_len <= 0) {
dev_err(mmc_dev(host->mmc),
"Failed to map scatterlist for DMA operation\n");
return -EINVAL;
}
if (next) {
next->sg_len = sg_len;
data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie;
} else
host->sg_len = sg_len;
return 0;
}
static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
struct mmc_data *data)
{
int ret;
struct dma_chan *chan;
struct dma_async_tx_descriptor *desc;
struct dma_slave_config conf = {
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
.src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
.dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
};
if (data->flags & MMC_DATA_WRITE) {
conf.direction = DMA_MEM_TO_DEV;
conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT;
chan = host->dma_tx;
} else {
conf.direction = DMA_DEV_TO_MEM;
conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE;
chan = host->dma_rx;
}
ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan);
if (ret)
return ret;
dmaengine_slave_config(chan, &conf);
desc = dmaengine_prep_slave_sg(chan,
data->sg,
host->sg_len,
conf.direction,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) {
dev_err(mmc_dev(host->mmc),
"Failed to allocate DMA %s descriptor",
conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX");
goto dma_unmap;
}
dmaengine_submit(desc);
dma_async_issue_pending(chan);
return 0;
dma_unmap:
jz4740_mmc_dma_unmap(host, data);
return -ENOMEM;
}
static void jz4740_mmc_pre_request(struct mmc_host *mmc,
struct mmc_request *mrq,
bool is_first_req)
{
struct jz4740_mmc_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
struct jz4740_mmc_host_next *next_data = &host->next_data;
BUG_ON(data->host_cookie);
if (host->use_dma) {
struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan))
data->host_cookie = 0;
}
}
static void jz4740_mmc_post_request(struct mmc_host *mmc,
struct mmc_request *mrq,
int err)
{
struct jz4740_mmc_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
if (host->use_dma && data->host_cookie) {
jz4740_mmc_dma_unmap(host, data);
data->host_cookie = 0;
}
if (err) {
struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
dmaengine_terminate_all(chan);
}
}
/*----------------------------------------------------------------------------*/
static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host,
unsigned int irq, bool enabled) unsigned int irq, bool enabled)
{ {
@ -442,6 +662,8 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host,
cmdat |= JZ_MMC_CMDAT_WRITE; cmdat |= JZ_MMC_CMDAT_WRITE;
if (cmd->data->flags & MMC_DATA_STREAM) if (cmd->data->flags & MMC_DATA_STREAM)
cmdat |= JZ_MMC_CMDAT_STREAM; cmdat |= JZ_MMC_CMDAT_STREAM;
if (host->use_dma)
cmdat |= JZ_MMC_CMDAT_DMA_EN;
writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
@ -474,6 +696,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid;
struct mmc_command *cmd = host->req->cmd; struct mmc_command *cmd = host->req->cmd;
struct mmc_request *req = host->req; struct mmc_request *req = host->req;
struct mmc_data *data = cmd->data;
bool timeout = false; bool timeout = false;
if (cmd->error) if (cmd->error)
@ -484,23 +707,37 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
if (cmd->flags & MMC_RSP_PRESENT) if (cmd->flags & MMC_RSP_PRESENT)
jz4740_mmc_read_response(host, cmd); jz4740_mmc_read_response(host, cmd);
if (!cmd->data) if (!data)
break; break;
jz_mmc_prepare_data_transfer(host); jz_mmc_prepare_data_transfer(host);
case JZ4740_MMC_STATE_TRANSFER_DATA: case JZ4740_MMC_STATE_TRANSFER_DATA:
if (cmd->data->flags & MMC_DATA_READ) if (host->use_dma) {
timeout = jz4740_mmc_read_data(host, cmd->data); /* Use DMA if enabled.
* Data transfer direction is defined later by
* relying on data flags in
* jz4740_mmc_prepare_dma_data() and
* jz4740_mmc_start_dma_transfer().
*/
timeout = jz4740_mmc_start_dma_transfer(host, data);
data->bytes_xfered = data->blocks * data->blksz;
} else if (data->flags & MMC_DATA_READ)
/* Use PIO if DMA is not enabled.
* Data transfer direction was defined before
* by relying on data flags in
* jz_mmc_prepare_data_transfer().
*/
timeout = jz4740_mmc_read_data(host, data);
else else
timeout = jz4740_mmc_write_data(host, cmd->data); timeout = jz4740_mmc_write_data(host, data);
if (unlikely(timeout)) { if (unlikely(timeout)) {
host->state = JZ4740_MMC_STATE_TRANSFER_DATA; host->state = JZ4740_MMC_STATE_TRANSFER_DATA;
break; break;
} }
jz4740_mmc_transfer_check_state(host, cmd->data); jz4740_mmc_transfer_check_state(host, data);
timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
if (unlikely(timeout)) { if (unlikely(timeout)) {
@ -664,6 +901,8 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
static const struct mmc_host_ops jz4740_mmc_ops = { static const struct mmc_host_ops jz4740_mmc_ops = {
.request = jz4740_mmc_request, .request = jz4740_mmc_request,
.pre_req = jz4740_mmc_pre_request,
.post_req = jz4740_mmc_post_request,
.set_ios = jz4740_mmc_set_ios, .set_ios = jz4740_mmc_set_ios,
.get_ro = mmc_gpio_get_ro, .get_ro = mmc_gpio_get_ro,
.get_cd = mmc_gpio_get_cd, .get_cd = mmc_gpio_get_cd,
@ -757,7 +996,6 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
struct mmc_host *mmc; struct mmc_host *mmc;
struct jz4740_mmc_host *host; struct jz4740_mmc_host *host;
struct jz4740_mmc_platform_data *pdata; struct jz4740_mmc_platform_data *pdata;
struct resource *res;
pdata = pdev->dev.platform_data; pdata = pdev->dev.platform_data;
@ -784,10 +1022,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
goto err_free_host; goto err_free_host;
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->base = devm_ioremap_resource(&pdev->dev, res); host->base = devm_ioremap_resource(&pdev->dev, host->mem_res);
if (IS_ERR(host->base)) { if (IS_ERR(host->base)) {
ret = PTR_ERR(host->base); ret = PTR_ERR(host->base);
dev_err(&pdev->dev, "Failed to ioremap base memory\n");
goto err_free_host; goto err_free_host;
} }
@ -834,6 +1073,10 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
/* It is not important when it times out, it just needs to timeout. */ /* It is not important when it times out, it just needs to timeout. */
set_timer_slack(&host->timeout_timer, HZ); set_timer_slack(&host->timeout_timer, HZ);
host->use_dma = true;
if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0)
host->use_dma = false;
platform_set_drvdata(pdev, host); platform_set_drvdata(pdev, host);
ret = mmc_add_host(mmc); ret = mmc_add_host(mmc);
@ -843,6 +1086,10 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
} }
dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n"); dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n");
dev_info(&pdev->dev, "Using %s, %d-bit mode\n",
host->use_dma ? "DMA" : "PIO",
(mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1);
return 0; return 0;
err_free_irq: err_free_irq:
@ -850,6 +1097,8 @@ err_free_irq:
err_free_gpios: err_free_gpios:
jz4740_mmc_free_gpios(pdev); jz4740_mmc_free_gpios(pdev);
err_gpio_bulk_free: err_gpio_bulk_free:
if (host->use_dma)
jz4740_mmc_release_dma_channels(host);
jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
err_free_host: err_free_host:
mmc_free_host(mmc); mmc_free_host(mmc);
@ -872,6 +1121,9 @@ static int jz4740_mmc_remove(struct platform_device *pdev)
jz4740_mmc_free_gpios(pdev); jz4740_mmc_free_gpios(pdev);
jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host));
if (host->use_dma)
jz4740_mmc_release_dma_channels(host);
mmc_free_host(host->mmc); mmc_free_host(host->mmc);
return 0; return 0;
@ -909,7 +1161,6 @@ static struct platform_driver jz4740_mmc_driver = {
.remove = jz4740_mmc_remove, .remove = jz4740_mmc_remove,
.driver = { .driver = {
.name = "jz4740-mmc", .name = "jz4740-mmc",
.owner = THIS_MODULE,
.pm = JZ4740_MMC_PM_OPS, .pm = JZ4740_MMC_PM_OPS,
}, },
}; };

View File

@ -1436,6 +1436,7 @@ static int mmc_spi_probe(struct spi_device *spi)
host->pdata->cd_debounce); host->pdata->cd_debounce);
if (status != 0) if (status != 0)
goto fail_add_host; goto fail_add_host;
mmc_gpiod_request_cd_irq(mmc);
} }
if (host->pdata && host->pdata->flags & MMC_SPI_USE_RO_GPIO) { if (host->pdata && host->pdata->flags & MMC_SPI_USE_RO_GPIO) {

View File

@ -43,6 +43,7 @@
#include <asm/sizes.h> #include <asm/sizes.h>
#include "mmci.h" #include "mmci.h"
#include "mmci_qcom_dml.h"
#define DRIVER_NAME "mmci-pl18x" #define DRIVER_NAME "mmci-pl18x"
@ -60,12 +61,13 @@ static unsigned int fmax = 515633;
* @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
* is asserted (likewise for RX) * is asserted (likewise for RX)
* @data_cmd_enable: enable value for data commands. * @data_cmd_enable: enable value for data commands.
* @sdio: variant supports SDIO * @st_sdio: enable ST specific SDIO logic
* @st_clkdiv: true if using a ST-specific clock divider algorithm * @st_clkdiv: true if using a ST-specific clock divider algorithm
* @datactrl_mask_ddrmode: ddr mode mask in datactrl register. * @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
* @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
* register * register
* @datactrl_mask_sdio: SDIO enable mask in datactrl register
* @pwrreg_powerup: power up value for MMCIPOWER register * @pwrreg_powerup: power up value for MMCIPOWER register
* @f_max: maximum clk frequency supported by the controller. * @f_max: maximum clk frequency supported by the controller.
* @signal_direction: input/out direction of bus signals can be indicated * @signal_direction: input/out direction of bus signals can be indicated
@ -74,6 +76,7 @@ static unsigned int fmax = 515633;
* @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
* @explicit_mclk_control: enable explicit mclk control in driver. * @explicit_mclk_control: enable explicit mclk control in driver.
* @qcom_fifo: enables qcom specific fifo pio read logic. * @qcom_fifo: enables qcom specific fifo pio read logic.
* @qcom_dml: enables qcom specific dma glue for dma transfers.
* @reversed_irq_handling: handle data irq before cmd irq. * @reversed_irq_handling: handle data irq before cmd irq.
*/ */
struct variant_data { struct variant_data {
@ -86,7 +89,8 @@ struct variant_data {
unsigned int fifohalfsize; unsigned int fifohalfsize;
unsigned int data_cmd_enable; unsigned int data_cmd_enable;
unsigned int datactrl_mask_ddrmode; unsigned int datactrl_mask_ddrmode;
bool sdio; unsigned int datactrl_mask_sdio;
bool st_sdio;
bool st_clkdiv; bool st_clkdiv;
bool blksz_datactrl16; bool blksz_datactrl16;
bool blksz_datactrl4; bool blksz_datactrl4;
@ -98,6 +102,7 @@ struct variant_data {
bool pwrreg_nopower; bool pwrreg_nopower;
bool explicit_mclk_control; bool explicit_mclk_control;
bool qcom_fifo; bool qcom_fifo;
bool qcom_dml;
bool reversed_irq_handling; bool reversed_irq_handling;
}; };
@ -133,7 +138,8 @@ static struct variant_data variant_u300 = {
.clkreg_enable = MCI_ST_U300_HWFCEN, .clkreg_enable = MCI_ST_U300_HWFCEN,
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.datalength_bits = 16, .datalength_bits = 16,
.sdio = true, .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
.st_sdio = true,
.pwrreg_powerup = MCI_PWR_ON, .pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000, .f_max = 100000000,
.signal_direction = true, .signal_direction = true,
@ -146,7 +152,8 @@ static struct variant_data variant_nomadik = {
.fifohalfsize = 8 * 4, .fifohalfsize = 8 * 4,
.clkreg = MCI_CLK_ENABLE, .clkreg = MCI_CLK_ENABLE,
.datalength_bits = 24, .datalength_bits = 24,
.sdio = true, .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
.st_sdio = true,
.st_clkdiv = true, .st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON, .pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000, .f_max = 100000000,
@ -163,7 +170,8 @@ static struct variant_data variant_ux500 = {
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
.datalength_bits = 24, .datalength_bits = 24,
.sdio = true, .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
.st_sdio = true,
.st_clkdiv = true, .st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON, .pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000, .f_max = 100000000,
@ -182,7 +190,8 @@ static struct variant_data variant_ux500v2 = {
.clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
.datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE, .datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE,
.datalength_bits = 24, .datalength_bits = 24,
.sdio = true, .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
.st_sdio = true,
.st_clkdiv = true, .st_clkdiv = true,
.blksz_datactrl16 = true, .blksz_datactrl16 = true,
.pwrreg_powerup = MCI_PWR_ON, .pwrreg_powerup = MCI_PWR_ON,
@ -208,6 +217,7 @@ static struct variant_data variant_qcom = {
.f_max = 208000000, .f_max = 208000000,
.explicit_mclk_control = true, .explicit_mclk_control = true,
.qcom_fifo = true, .qcom_fifo = true,
.qcom_dml = true,
}; };
static int mmci_card_busy(struct mmc_host *mmc) static int mmci_card_busy(struct mmc_host *mmc)
@ -421,6 +431,7 @@ static void mmci_dma_setup(struct mmci_host *host)
{ {
const char *rxname, *txname; const char *rxname, *txname;
dma_cap_mask_t mask; dma_cap_mask_t mask;
struct variant_data *variant = host->variant;
host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx"); host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx"); host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
@ -471,6 +482,10 @@ static void mmci_dma_setup(struct mmci_host *host)
if (max_seg_size < host->mmc->max_seg_size) if (max_seg_size < host->mmc->max_seg_size)
host->mmc->max_seg_size = max_seg_size; host->mmc->max_seg_size = max_seg_size;
} }
if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel)
if (dml_hw_init(host, host->mmc->parent->of_node))
variant->qcom_dml = false;
} }
/* /*
@ -572,6 +587,7 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
struct dma_async_tx_descriptor *desc; struct dma_async_tx_descriptor *desc;
enum dma_data_direction buffer_dirn; enum dma_data_direction buffer_dirn;
int nr_sg; int nr_sg;
unsigned long flags = DMA_CTRL_ACK;
if (data->flags & MMC_DATA_READ) { if (data->flags & MMC_DATA_READ) {
conf.direction = DMA_DEV_TO_MEM; conf.direction = DMA_DEV_TO_MEM;
@ -596,9 +612,12 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
if (nr_sg == 0) if (nr_sg == 0)
return -EINVAL; return -EINVAL;
if (host->variant->qcom_dml)
flags |= DMA_PREP_INTERRUPT;
dmaengine_slave_config(chan, &conf); dmaengine_slave_config(chan, &conf);
desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg, desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
conf.direction, DMA_CTRL_ACK); conf.direction, flags);
if (!desc) if (!desc)
goto unmap_exit; goto unmap_exit;
@ -647,6 +666,9 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
dmaengine_submit(host->dma_desc_current); dmaengine_submit(host->dma_desc_current);
dma_async_issue_pending(host->dma_current); dma_async_issue_pending(host->dma_current);
if (host->variant->qcom_dml)
dml_start_xfer(host, data);
datactrl |= MCI_DPSM_DMAENABLE; datactrl |= MCI_DPSM_DMAENABLE;
/* Trigger the DMA transfer */ /* Trigger the DMA transfer */
@ -792,32 +814,26 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
if (data->flags & MMC_DATA_READ) if (data->flags & MMC_DATA_READ)
datactrl |= MCI_DPSM_DIRECTION; datactrl |= MCI_DPSM_DIRECTION;
/* The ST Micro variants has a special bit to enable SDIO */ if (host->mmc->card && mmc_card_sdio(host->mmc->card)) {
if (variant->sdio && host->mmc->card) u32 clk;
if (mmc_card_sdio(host->mmc->card)) {
/*
* The ST Micro variants has a special bit
* to enable SDIO.
*/
u32 clk;
datactrl |= MCI_ST_DPSM_SDIOEN; datactrl |= variant->datactrl_mask_sdio;
/* /*
* The ST Micro variant for SDIO small write transfers * The ST Micro variant for SDIO small write transfers
* needs to have clock H/W flow control disabled, * needs to have clock H/W flow control disabled,
* otherwise the transfer will not start. The threshold * otherwise the transfer will not start. The threshold
* depends on the rate of MCLK. * depends on the rate of MCLK.
*/ */
if (data->flags & MMC_DATA_WRITE && if (variant->st_sdio && data->flags & MMC_DATA_WRITE &&
(host->size < 8 || (host->size < 8 ||
(host->size <= 8 && host->mclk > 50000000))) (host->size <= 8 && host->mclk > 50000000)))
clk = host->clk_reg & ~variant->clkreg_enable; clk = host->clk_reg & ~variant->clkreg_enable;
else else
clk = host->clk_reg | variant->clkreg_enable; clk = host->clk_reg | variant->clkreg_enable;
mmci_write_clkreg(host, clk); mmci_write_clkreg(host, clk);
} }
if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 ||
host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) host->mmc->ios.timing == MMC_TIMING_MMC_DDR52)
@ -1658,16 +1674,35 @@ static int mmci_probe(struct amba_device *dev,
writel(0, host->base + MMCIMASK1); writel(0, host->base + MMCIMASK1);
writel(0xfff, host->base + MMCICLEAR); writel(0xfff, host->base + MMCICLEAR);
/* If DT, cd/wp gpios must be supplied through it. */ /*
if (!np && gpio_is_valid(plat->gpio_cd)) { * If:
ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0); * - not using DT but using a descriptor table, or
if (ret) * - using a table of descriptors ALONGSIDE DT, or
goto clk_disable; * look up these descriptors named "cd" and "wp" right here, fail
} * silently of these do not exist and proceed to try platform data
if (!np && gpio_is_valid(plat->gpio_wp)) { */
ret = mmc_gpio_request_ro(mmc, plat->gpio_wp); if (!np) {
if (ret) ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
goto clk_disable; if (ret < 0) {
if (ret == -EPROBE_DEFER)
goto clk_disable;
else if (gpio_is_valid(plat->gpio_cd)) {
ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0);
if (ret)
goto clk_disable;
}
}
ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL);
if (ret < 0) {
if (ret == -EPROBE_DEFER)
goto clk_disable;
else if (gpio_is_valid(plat->gpio_wp)) {
ret = mmc_gpio_request_ro(mmc, plat->gpio_wp);
if (ret)
goto clk_disable;
}
}
} }
ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED, ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED,

View File

@ -0,0 +1,177 @@
/*
*
* Copyright (c) 2011, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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/of.h>
#include <linux/of_dma.h>
#include <linux/bitops.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include "mmci.h"
/* Registers */
#define DML_CONFIG 0x00
#define PRODUCER_CRCI_MSK GENMASK(1, 0)
#define PRODUCER_CRCI_DISABLE 0
#define PRODUCER_CRCI_X_SEL BIT(0)
#define PRODUCER_CRCI_Y_SEL BIT(1)
#define CONSUMER_CRCI_MSK GENMASK(3, 2)
#define CONSUMER_CRCI_DISABLE 0
#define CONSUMER_CRCI_X_SEL BIT(2)
#define CONSUMER_CRCI_Y_SEL BIT(3)
#define PRODUCER_TRANS_END_EN BIT(4)
#define BYPASS BIT(16)
#define DIRECT_MODE BIT(17)
#define INFINITE_CONS_TRANS BIT(18)
#define DML_SW_RESET 0x08
#define DML_PRODUCER_START 0x0c
#define DML_CONSUMER_START 0x10
#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14
#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18
#define DML_PIPE_ID 0x1c
#define PRODUCER_PIPE_ID_SHFT 0
#define PRODUCER_PIPE_ID_MSK GENMASK(4, 0)
#define CONSUMER_PIPE_ID_SHFT 16
#define CONSUMER_PIPE_ID_MSK GENMASK(20, 16)
#define DML_PRODUCER_BAM_BLOCK_SIZE 0x24
#define DML_PRODUCER_BAM_TRANS_SIZE 0x28
/* other definitions */
#define PRODUCER_PIPE_LOGICAL_SIZE 4096
#define CONSUMER_PIPE_LOGICAL_SIZE 4096
#define DML_OFFSET 0x800
void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
{
u32 config;
void __iomem *base = host->base + DML_OFFSET;
if (data->flags & MMC_DATA_READ) {
/* Read operation: configure DML for producer operation */
/* Set producer CRCI-x and disable consumer CRCI */
config = readl_relaxed(base + DML_CONFIG);
config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE;
writel_relaxed(config, base + DML_CONFIG);
/* Set the Producer BAM block size */
writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE);
/* Set Producer BAM Transaction size */
writel_relaxed(data->blocks * data->blksz,
base + DML_PRODUCER_BAM_TRANS_SIZE);
/* Set Producer Transaction End bit */
config = readl_relaxed(base + DML_CONFIG);
config |= PRODUCER_TRANS_END_EN;
writel_relaxed(config, base + DML_CONFIG);
/* Trigger producer */
writel_relaxed(1, base + DML_PRODUCER_START);
} else {
/* Write operation: configure DML for consumer operation */
/* Set consumer CRCI-x and disable producer CRCI*/
config = readl_relaxed(base + DML_CONFIG);
config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE;
writel_relaxed(config, base + DML_CONFIG);
/* Clear Producer Transaction End bit */
config = readl_relaxed(base + DML_CONFIG);
config &= ~PRODUCER_TRANS_END_EN;
writel_relaxed(config, base + DML_CONFIG);
/* Trigger consumer */
writel_relaxed(1, base + DML_CONSUMER_START);
}
/* make sure the dml is configured before dma is triggered */
wmb();
}
static int of_get_dml_pipe_index(struct device_node *np, const char *name)
{
int index;
struct of_phandle_args dma_spec;
index = of_property_match_string(np, "dma-names", name);
if (index < 0)
return -ENODEV;
if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
&dma_spec))
return -ENODEV;
if (dma_spec.args_count)
return dma_spec.args[0];
return -ENODEV;
}
/* Initialize the dml hardware connected to SD Card controller */
int dml_hw_init(struct mmci_host *host, struct device_node *np)
{
u32 config;
void __iomem *base;
int consumer_id, producer_id;
consumer_id = of_get_dml_pipe_index(np, "tx");
producer_id = of_get_dml_pipe_index(np, "rx");
if (producer_id < 0 || consumer_id < 0)
return -ENODEV;
base = host->base + DML_OFFSET;
/* Reset the DML block */
writel_relaxed(1, base + DML_SW_RESET);
/* Disable the producer and consumer CRCI */
config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE);
/*
* Disable the bypass mode. Bypass mode will only be used
* if data transfer is to happen in PIO mode and don't
* want the BAM interface to connect with SDCC-DML.
*/
config &= ~BYPASS;
/*
* Disable direct mode as we don't DML to MASTER the AHB bus.
* BAM connected with DML should MASTER the AHB bus.
*/
config &= ~DIRECT_MODE;
/*
* Disable infinite mode transfer as we won't be doing any
* infinite size data transfers. All data transfer will be
* of finite data size.
*/
config &= ~INFINITE_CONS_TRANS;
writel_relaxed(config, base + DML_CONFIG);
/*
* Initialize the logical BAM pipe size for producer
* and consumer.
*/
writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
base + DML_PRODUCER_PIPE_LOGICAL_SIZE);
writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
base + DML_CONSUMER_PIPE_LOGICAL_SIZE);
/* Initialize Producer/consumer pipe id */
writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT),
base + DML_PIPE_ID);
/* Make sure dml intialization is finished */
mb();
return 0;
}

View File

@ -0,0 +1,31 @@
/*
*
* Copyright (c) 2011, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* 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 __MMC_QCOM_DML_H__
#define __MMC_QCOM_DML_H__
#ifdef CONFIG_MMC_QCOM_DML
int dml_hw_init(struct mmci_host *host, struct device_node *np);
void dml_start_xfer(struct mmci_host *host, struct mmc_data *data);
#else
static inline int dml_hw_init(struct mmci_host *host, struct device_node *np)
{
return -ENOSYS;
}
static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
{
}
#endif /* CONFIG_MMC_QCOM_DML */
#endif /* __MMC_QCOM_DML_H__ */

View File

@ -717,7 +717,6 @@ static struct platform_driver moxart_mmc_driver = {
.remove = moxart_remove, .remove = moxart_remove,
.driver = { .driver = {
.name = "mmc-moxart", .name = "mmc-moxart",
.owner = THIS_MODULE,
.of_match_table = moxart_mmc_match, .of_match_table = moxart_mmc_match,
}, },
}; };

View File

@ -1238,7 +1238,6 @@ static struct platform_driver mxcmci_driver = {
.id_table = mxcmci_devtype, .id_table = mxcmci_devtype,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE,
.pm = &mxcmci_pm_ops, .pm = &mxcmci_pm_ops,
.of_match_table = mxcmci_of_match, .of_match_table = mxcmci_of_match,
} }

View File

@ -735,7 +735,6 @@ static struct platform_driver mxs_mmc_driver = {
.id_table = mxs_ssp_ids, .id_table = mxs_ssp_ids,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.pm = &mxs_mmc_pm_ops, .pm = &mxs_mmc_pm_ops,
#endif #endif

View File

@ -1494,7 +1494,6 @@ static struct platform_driver mmc_omap_driver = {
.remove = mmc_omap_remove, .remove = mmc_omap_remove,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mmc_omap_match), .of_match_table = of_match_ptr(mmc_omap_match),
}, },
}; };

View File

@ -1829,7 +1829,17 @@ static int omap_hsmmc_disable_fclk(struct mmc_host *mmc)
return 0; return 0;
} }
static const struct mmc_host_ops omap_hsmmc_ops = { static int omap_hsmmc_multi_io_quirk(struct mmc_card *card,
unsigned int direction, int blk_size)
{
/* This controller can't do multiblock reads due to hw bugs */
if (direction == MMC_DATA_READ)
return 1;
return blk_size;
}
static struct mmc_host_ops omap_hsmmc_ops = {
.enable = omap_hsmmc_enable_fclk, .enable = omap_hsmmc_enable_fclk,
.disable = omap_hsmmc_disable_fclk, .disable = omap_hsmmc_disable_fclk,
.post_req = omap_hsmmc_post_req, .post_req = omap_hsmmc_post_req,
@ -2101,7 +2111,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) {
dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n"); dev_info(&pdev->dev, "multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer\n");
mmc->caps2 |= MMC_CAP2_NO_MULTI_READ; omap_hsmmc_ops.multi_io_quirk = omap_hsmmc_multi_io_quirk;
} }
pm_runtime_enable(host->dev); pm_runtime_enable(host->dev);
@ -2489,7 +2499,6 @@ static struct platform_driver omap_hsmmc_driver = {
.remove = omap_hsmmc_remove, .remove = omap_hsmmc_remove,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE,
.pm = &omap_hsmmc_dev_pm_ops, .pm = &omap_hsmmc_dev_pm_ops,
.of_match_table = of_match_ptr(omap_mmc_of_match), .of_match_table = of_match_ptr(omap_mmc_of_match),
}, },

View File

@ -474,7 +474,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
unsigned int clk = rate / ios->clock; unsigned int clk = rate / ios->clock;
if (host->clkrt == CLKRT_OFF) if (host->clkrt == CLKRT_OFF)
clk_enable(host->clk); clk_prepare_enable(host->clk);
if (ios->clock == 26000000) { if (ios->clock == 26000000) {
/* to support 26MHz */ /* to support 26MHz */
@ -501,7 +501,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
pxamci_stop_clock(host); pxamci_stop_clock(host);
if (host->clkrt != CLKRT_OFF) { if (host->clkrt != CLKRT_OFF) {
host->clkrt = CLKRT_OFF; host->clkrt = CLKRT_OFF;
clk_disable(host->clk); clk_disable_unprepare(host->clk);
} }
} }
@ -885,7 +885,6 @@ static struct platform_driver pxamci_driver = {
.remove = pxamci_remove, .remove = pxamci_remove,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(pxa_mmc_dt_ids), .of_match_table = of_match_ptr(pxa_mmc_dt_ids),
}, },
}; };

View File

@ -412,6 +412,13 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
} }
if (rsp_type == SD_RSP_TYPE_R2) { if (rsp_type == SD_RSP_TYPE_R2) {
/*
* The controller offloads the last byte {CRC-7, end bit 1'b1}
* of response type R2. Assign dummy CRC, 0, and end bit to the
* byte(ptr[16], goes into the LSB of resp[3] later).
*/
ptr[16] = 1;
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4); cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4);
dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n", dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n",
@ -1292,6 +1299,7 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host)
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25;
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
mmc->max_current_330 = 400; mmc->max_current_330 = 400;
mmc->max_current_180 = 800; mmc->max_current_180 = 800;
mmc->ops = &realtek_pci_sdmmc_ops; mmc->ops = &realtek_pci_sdmmc_ops;
@ -1416,7 +1424,6 @@ static struct platform_driver rtsx_pci_sdmmc_driver = {
.remove = rtsx_pci_sdmmc_drv_remove, .remove = rtsx_pci_sdmmc_drv_remove,
.id_table = rtsx_pci_sdmmc_ids, .id_table = rtsx_pci_sdmmc_ids,
.driver = { .driver = {
.owner = THIS_MODULE,
.name = DRV_NAME_RTSX_PCI_SDMMC, .name = DRV_NAME_RTSX_PCI_SDMMC,
}, },
}; };

View File

@ -435,6 +435,13 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host,
} }
if (rsp_type == SD_RSP_TYPE_R2) { if (rsp_type == SD_RSP_TYPE_R2) {
/*
* The controller offloads the last byte {CRC-7, end bit 1'b1}
* of response type R2. Assign dummy CRC, 0, and end bit to the
* byte(ptr[16], goes into the LSB of resp[3] later).
*/
ptr[16] = 1;
for (i = 0; i < 4; i++) { for (i = 0; i < 4; i++) {
cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4); cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4);
dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n", dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n",
@ -1329,6 +1336,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host)
MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST |
MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 |
MMC_CAP_NEEDS_POLL; MMC_CAP_NEEDS_POLL;
mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE;
mmc->max_current_330 = 400; mmc->max_current_330 = 400;
mmc->max_current_180 = 800; mmc->max_current_180 = 800;
@ -1445,7 +1453,6 @@ static struct platform_driver rtsx_usb_sdmmc_driver = {
.remove = rtsx_usb_sdmmc_drv_remove, .remove = rtsx_usb_sdmmc_drv_remove,
.id_table = rtsx_usb_sdmmc_ids, .id_table = rtsx_usb_sdmmc_ids,
.driver = { .driver = {
.owner = THIS_MODULE,
.name = "rtsx_usb_sdmmc", .name = "rtsx_usb_sdmmc",
}, },
}; };

View File

@ -985,7 +985,8 @@ static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
* one block being transferred. */ * one block being transferred. */
if (data->blocks > 1) { if (data->blocks > 1) {
pr_warning("%s: can't do non-word sized block transfers (blksz %d)\n", __func__, data->blksz); pr_warn("%s: can't do non-word sized block transfers (blksz %d)\n",
__func__, data->blksz);
return -EINVAL; return -EINVAL;
} }
} }
@ -1874,7 +1875,6 @@ MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
static struct platform_driver s3cmci_driver = { static struct platform_driver s3cmci_driver = {
.driver = { .driver = {
.name = "s3c-sdi", .name = "s3c-sdi",
.owner = THIS_MODULE,
}, },
.id_table = s3cmci_driver_ids, .id_table = s3cmci_driver_ids,
.probe = s3cmci_probe, .probe = s3cmci_probe,

View File

@ -67,6 +67,8 @@ struct sdhci_acpi_slot {
unsigned int caps2; unsigned int caps2;
mmc_pm_flag_t pm_caps; mmc_pm_flag_t pm_caps;
unsigned int flags; unsigned int flags;
int (*probe_slot)(struct platform_device *, const char *, const char *);
int (*remove_slot)(struct platform_device *);
}; };
struct sdhci_acpi_host { struct sdhci_acpi_host {
@ -122,13 +124,67 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = {
.ops = &sdhci_acpi_ops_int, .ops = &sdhci_acpi_ops_int,
}; };
static int sdhci_acpi_emmc_probe_slot(struct platform_device *pdev,
const char *hid, const char *uid)
{
struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
struct sdhci_host *host;
if (!c || !c->host)
return 0;
host = c->host;
/* Platform specific code during emmc proble slot goes here */
if (hid && uid && !strcmp(hid, "80860F14") && !strcmp(uid, "1") &&
sdhci_readl(host, SDHCI_CAPABILITIES) == 0x446cc8b2 &&
sdhci_readl(host, SDHCI_CAPABILITIES_1) == 0x00000807)
host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
return 0;
}
static int sdhci_acpi_sdio_probe_slot(struct platform_device *pdev,
const char *hid, const char *uid)
{
struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
struct sdhci_host *host;
if (!c || !c->host)
return 0;
host = c->host;
/* Platform specific code during emmc proble slot goes here */
return 0;
}
static int sdhci_acpi_sd_probe_slot(struct platform_device *pdev,
const char *hid, const char *uid)
{
struct sdhci_acpi_host *c = platform_get_drvdata(pdev);
struct sdhci_host *host;
if (!c || !c->host || !c->slot)
return 0;
host = c->host;
/* Platform specific code during emmc proble slot goes here */
return 0;
}
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
.chip = &sdhci_acpi_chip_int, .chip = &sdhci_acpi_chip_int,
.caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE |
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR, MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR,
.caps2 = MMC_CAP2_HC_ERASE_SZ, .caps2 = MMC_CAP2_HC_ERASE_SZ,
.flags = SDHCI_ACPI_RUNTIME_PM, .flags = SDHCI_ACPI_RUNTIME_PM,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_STOP_WITH_TC,
.probe_slot = sdhci_acpi_emmc_probe_slot,
}; };
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
@ -137,12 +193,15 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
.caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD, .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
.flags = SDHCI_ACPI_RUNTIME_PM, .flags = SDHCI_ACPI_RUNTIME_PM,
.pm_caps = MMC_PM_KEEP_POWER, .pm_caps = MMC_PM_KEEP_POWER,
.probe_slot = sdhci_acpi_sdio_probe_slot,
}; };
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = { static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
.flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL | .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
SDHCI_ACPI_RUNTIME_PM, SDHCI_ACPI_RUNTIME_PM,
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
SDHCI_QUIRK2_STOP_WITH_TC,
.probe_slot = sdhci_acpi_sd_probe_slot,
}; };
struct sdhci_acpi_uid_slot { struct sdhci_acpi_uid_slot {
@ -156,6 +215,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd }, { "80860F14" , "3" , &sdhci_acpi_slot_int_sd },
{ "80860F16" , NULL, &sdhci_acpi_slot_int_sd }, { "80860F16" , NULL, &sdhci_acpi_slot_int_sd },
{ "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio }, { "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio },
{ "INT33BB" , "3" , &sdhci_acpi_slot_int_sd },
{ "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio }, { "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio },
{ "INT3436" , NULL, &sdhci_acpi_slot_int_sdio }, { "INT3436" , NULL, &sdhci_acpi_slot_int_sdio },
{ "PNP0D40" }, { "PNP0D40" },
@ -173,8 +233,8 @@ static const struct acpi_device_id sdhci_acpi_ids[] = {
}; };
MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids); MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids);
static const struct sdhci_acpi_slot *sdhci_acpi_get_slot_by_ids(const char *hid, static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid,
const char *uid) const char *uid)
{ {
const struct sdhci_acpi_uid_slot *u; const struct sdhci_acpi_uid_slot *u;
@ -189,24 +249,6 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot_by_ids(const char *hid,
return NULL; return NULL;
} }
static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,
const char *hid)
{
const struct sdhci_acpi_slot *slot;
struct acpi_device_info *info;
const char *uid = NULL;
acpi_status status;
status = acpi_get_object_info(handle, &info);
if (!ACPI_FAILURE(status) && (info->valid & ACPI_VALID_UID))
uid = info->unique_id.string;
slot = sdhci_acpi_get_slot_by_ids(hid, uid);
kfree(info);
return slot;
}
static int sdhci_acpi_probe(struct platform_device *pdev) static int sdhci_acpi_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
@ -217,6 +259,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
struct resource *iomem; struct resource *iomem;
resource_size_t len; resource_size_t len;
const char *hid; const char *hid;
const char *uid;
int err; int err;
if (acpi_bus_get_device(handle, &device)) if (acpi_bus_get_device(handle, &device))
@ -226,6 +269,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
hid = acpi_device_hid(device); hid = acpi_device_hid(device);
uid = device->pnp.unique_id;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) if (!iomem)
@ -244,7 +288,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
c = sdhci_priv(host); c = sdhci_priv(host);
c->host = host; c->host = host;
c->slot = sdhci_acpi_get_slot(handle, hid); c->slot = sdhci_acpi_get_slot(hid, uid);
c->pdev = pdev; c->pdev = pdev;
c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM); c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM);
@ -277,6 +321,11 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
} }
if (c->slot) { if (c->slot) {
if (c->slot->probe_slot) {
err = c->slot->probe_slot(pdev, hid, uid);
if (err)
goto err_free;
}
if (c->slot->chip) { if (c->slot->chip) {
host->ops = c->slot->chip->ops; host->ops = c->slot->chip->ops;
host->quirks |= c->slot->chip->quirks; host->quirks |= c->slot->chip->quirks;
@ -297,7 +346,7 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) { if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL); bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0)) { if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0, NULL)) {
dev_warn(dev, "failed to setup card detect gpio\n"); dev_warn(dev, "failed to setup card detect gpio\n");
c->use_runtime_pm = false; c->use_runtime_pm = false;
} }
@ -334,6 +383,9 @@ static int sdhci_acpi_remove(struct platform_device *pdev)
pm_runtime_put_noidle(dev); pm_runtime_put_noidle(dev);
} }
if (c->slot && c->slot->remove_slot)
c->slot->remove_slot(pdev);
dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0); dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0);
sdhci_remove_host(c->host, dead); sdhci_remove_host(c->host, dead);
sdhci_free_host(c->host); sdhci_free_host(c->host);
@ -385,20 +437,13 @@ static int sdhci_acpi_runtime_idle(struct device *dev)
return 0; return 0;
} }
#else
#define sdhci_acpi_runtime_suspend NULL
#define sdhci_acpi_runtime_resume NULL
#define sdhci_acpi_runtime_idle NULL
#endif #endif
static const struct dev_pm_ops sdhci_acpi_pm_ops = { static const struct dev_pm_ops sdhci_acpi_pm_ops = {
.suspend = sdhci_acpi_suspend, .suspend = sdhci_acpi_suspend,
.resume = sdhci_acpi_resume, .resume = sdhci_acpi_resume,
.runtime_suspend = sdhci_acpi_runtime_suspend, SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend,
.runtime_resume = sdhci_acpi_runtime_resume, sdhci_acpi_runtime_resume, sdhci_acpi_runtime_idle)
.runtime_idle = sdhci_acpi_runtime_idle,
}; };
static struct platform_driver sdhci_acpi_driver = { static struct platform_driver sdhci_acpi_driver = {

View File

@ -225,7 +225,7 @@ static struct sdhci_pltfm_data sdhci_pltfm_data_kona = {
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
}; };
static struct __initconst of_device_id sdhci_bcm_kona_of_match[] = { static const struct of_device_id sdhci_bcm_kona_of_match[] = {
{ .compatible = "brcm,kona-sdhci"}, { .compatible = "brcm,kona-sdhci"},
{ .compatible = "bcm,kona-sdhci"}, /* deprecated name */ { .compatible = "bcm,kona-sdhci"}, /* deprecated name */
{} {}
@ -359,7 +359,6 @@ static int sdhci_bcm_kona_remove(struct platform_device *pdev)
static struct platform_driver sdhci_bcm_kona_driver = { static struct platform_driver sdhci_bcm_kona_driver = {
.driver = { .driver = {
.name = "sdhci-kona", .name = "sdhci-kona",
.owner = THIS_MODULE,
.pm = SDHCI_PLTFM_PMOPS, .pm = SDHCI_PLTFM_PMOPS,
.of_match_table = sdhci_bcm_kona_of_match, .of_match_table = sdhci_bcm_kona_of_match,
}, },

View File

@ -194,7 +194,6 @@ MODULE_DEVICE_TABLE(of, bcm2835_sdhci_of_match);
static struct platform_driver bcm2835_sdhci_driver = { static struct platform_driver bcm2835_sdhci_driver = {
.driver = { .driver = {
.name = "sdhci-bcm2835", .name = "sdhci-bcm2835",
.owner = THIS_MODULE,
.of_match_table = bcm2835_sdhci_of_match, .of_match_table = bcm2835_sdhci_of_match,
.pm = SDHCI_PLTFM_PMOPS, .pm = SDHCI_PLTFM_PMOPS,
}, },

View File

@ -106,7 +106,6 @@ static int sdhci_cns3xxx_remove(struct platform_device *pdev)
static struct platform_driver sdhci_cns3xxx_driver = { static struct platform_driver sdhci_cns3xxx_driver = {
.driver = { .driver = {
.name = "sdhci-cns3xxx", .name = "sdhci-cns3xxx",
.owner = THIS_MODULE,
.pm = SDHCI_PLTFM_PMOPS, .pm = SDHCI_PLTFM_PMOPS,
}, },
.probe = sdhci_cns3xxx_probe, .probe = sdhci_cns3xxx_probe,

View File

@ -146,7 +146,6 @@ MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table);
static struct platform_driver sdhci_dove_driver = { static struct platform_driver sdhci_dove_driver = {
.driver = { .driver = {
.name = "sdhci-dove", .name = "sdhci-dove",
.owner = THIS_MODULE,
.pm = SDHCI_PLTFM_PMOPS, .pm = SDHCI_PLTFM_PMOPS,
.of_match_table = sdhci_dove_of_match_table, .of_match_table = sdhci_dove_of_match_table,
}, },

View File

@ -880,6 +880,24 @@ static void esdhc_reset(struct sdhci_host *host, u8 mask)
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
} }
static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
return esdhc_is_usdhc(imx_data) ? 1 << 28 : 1 << 27;
}
static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = pltfm_host->priv;
/* use maximum timeout counter */
sdhci_writeb(host, esdhc_is_usdhc(imx_data) ? 0xF : 0xE,
SDHCI_TIMEOUT_CONTROL);
}
static struct sdhci_ops sdhci_esdhc_ops = { static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = esdhc_readl_le, .read_l = esdhc_readl_le,
.read_w = esdhc_readw_le, .read_w = esdhc_readw_le,
@ -889,7 +907,9 @@ static struct sdhci_ops sdhci_esdhc_ops = {
.set_clock = esdhc_pltfm_set_clock, .set_clock = esdhc_pltfm_set_clock,
.get_max_clock = esdhc_pltfm_get_max_clock, .get_max_clock = esdhc_pltfm_get_max_clock,
.get_min_clock = esdhc_pltfm_get_min_clock, .get_min_clock = esdhc_pltfm_get_min_clock,
.get_max_timeout_count = esdhc_get_max_timeout_count,
.get_ro = esdhc_pltfm_get_ro, .get_ro = esdhc_pltfm_get_ro,
.set_timeout = esdhc_set_timeout,
.set_bus_width = esdhc_pltfm_set_bus_width, .set_bus_width = esdhc_pltfm_set_bus_width,
.set_uhs_signaling = esdhc_set_uhs_signaling, .set_uhs_signaling = esdhc_set_uhs_signaling,
.reset = esdhc_reset, .reset = esdhc_reset,
@ -1207,7 +1227,6 @@ static const struct dev_pm_ops sdhci_esdhc_pmops = {
static struct platform_driver sdhci_esdhc_imx_driver = { static struct platform_driver sdhci_esdhc_imx_driver = {
.driver = { .driver = {
.name = "sdhci-esdhc-imx", .name = "sdhci-esdhc-imx",
.owner = THIS_MODULE,
.of_match_table = imx_esdhc_dt_ids, .of_match_table = imx_esdhc_dt_ids,
.pm = &sdhci_esdhc_pmops, .pm = &sdhci_esdhc_pmops,
}, },

View File

@ -46,24 +46,6 @@
#define CMUX_SHIFT_PHASE_SHIFT 24 #define CMUX_SHIFT_PHASE_SHIFT 24
#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT) #define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
static const u32 tuning_block_64[] = {
0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe,
0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777,
0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff,
0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7
};
static const u32 tuning_block_128[] = {
0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc,
0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff,
0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff,
0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb,
0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc,
0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff,
0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb,
0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77
};
struct sdhci_msm_host { struct sdhci_msm_host {
struct platform_device *pdev; struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */ void __iomem *core_mem; /* MSM SDCC mapped address */
@ -358,8 +340,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
{ {
int tuning_seq_cnt = 3; int tuning_seq_cnt = 3;
u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0; u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
const u32 *tuning_block_pattern = tuning_block_64; const u8 *tuning_block_pattern = tuning_blk_pattern_4bit;
int size = sizeof(tuning_block_64); /* Pattern size in bytes */ int size = sizeof(tuning_blk_pattern_4bit);
int rc; int rc;
struct mmc_host *mmc = host->mmc; struct mmc_host *mmc = host->mmc;
struct mmc_ios ios = host->mmc->ios; struct mmc_ios ios = host->mmc->ios;
@ -375,8 +357,8 @@ static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) && if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
(mmc->ios.bus_width == MMC_BUS_WIDTH_8)) { (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
tuning_block_pattern = tuning_block_128; tuning_block_pattern = tuning_blk_pattern_8bit;
size = sizeof(tuning_block_128); size = sizeof(tuning_blk_pattern_8bit);
} }
data_buf = kmalloc(size, GFP_KERNEL); data_buf = kmalloc(size, GFP_KERNEL);
@ -610,7 +592,6 @@ static struct platform_driver sdhci_msm_driver = {
.remove = sdhci_msm_remove, .remove = sdhci_msm_remove,
.driver = { .driver = {
.name = "sdhci_msm", .name = "sdhci_msm",
.owner = THIS_MODULE,
.of_match_table = sdhci_msm_dt_match, .of_match_table = sdhci_msm_dt_match,
}, },
}; };

View File

@ -213,7 +213,6 @@ MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match);
static struct platform_driver sdhci_arasan_driver = { static struct platform_driver sdhci_arasan_driver = {
.driver = { .driver = {
.name = "sdhci-arasan", .name = "sdhci-arasan",
.owner = THIS_MODULE,
.of_match_table = sdhci_arasan_of_match, .of_match_table = sdhci_arasan_of_match,
.pm = &sdhci_arasan_dev_pm_ops, .pm = &sdhci_arasan_dev_pm_ops,
}, },

View File

@ -388,7 +388,6 @@ MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match);
static struct platform_driver sdhci_esdhc_driver = { static struct platform_driver sdhci_esdhc_driver = {
.driver = { .driver = {
.name = "sdhci-esdhc", .name = "sdhci-esdhc",
.owner = THIS_MODULE,
.of_match_table = sdhci_esdhc_of_match, .of_match_table = sdhci_esdhc_of_match,
.pm = ESDHC_PMOPS, .pm = ESDHC_PMOPS,
}, },

View File

@ -89,7 +89,6 @@ MODULE_DEVICE_TABLE(of, sdhci_hlwd_of_match);
static struct platform_driver sdhci_hlwd_driver = { static struct platform_driver sdhci_hlwd_driver = {
.driver = { .driver = {
.name = "sdhci-hlwd", .name = "sdhci-hlwd",
.owner = THIS_MODULE,
.of_match_table = sdhci_hlwd_of_match, .of_match_table = sdhci_hlwd_of_match,
.pm = SDHCI_PLTFM_PMOPS, .pm = SDHCI_PLTFM_PMOPS,
}, },

View File

@ -24,6 +24,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/mmc/sdhci-pci-data.h> #include <linux/mmc/sdhci-pci-data.h>
#include "sdhci.h" #include "sdhci.h"
@ -271,6 +272,8 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot)
MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR; MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR;
slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ;
slot->hw_reset = sdhci_pci_int_hw_reset; slot->hw_reset = sdhci_pci_int_hw_reset;
if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC)
slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */
return 0; return 0;
} }
@ -280,22 +283,35 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot)
return 0; return 0;
} }
static int byt_sd_probe_slot(struct sdhci_pci_slot *slot)
{
slot->cd_con_id = NULL;
slot->cd_idx = 0;
slot->cd_override_level = true;
return 0;
}
static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = {
.allow_runtime_pm = true, .allow_runtime_pm = true,
.probe_slot = byt_emmc_probe_slot, .probe_slot = byt_emmc_probe_slot,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
}; };
static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = {
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.allow_runtime_pm = true, .allow_runtime_pm = true,
.probe_slot = byt_sdio_probe_slot, .probe_slot = byt_sdio_probe_slot,
}; };
static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { static const struct sdhci_pci_fixes sdhci_intel_byt_sd = {
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON, .quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
.allow_runtime_pm = true, .allow_runtime_pm = true,
.own_cd_for_runtime_pm = true, .own_cd_for_runtime_pm = true,
.probe_slot = byt_sd_probe_slot,
}; };
/* Define Host controllers for Intel Merrifield platform */ /* Define Host controllers for Intel Merrifield platform */
@ -317,7 +333,9 @@ static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot)
static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.quirks2 = SDHCI_QUIRK2_BROKEN_HS200, .quirks2 = SDHCI_QUIRK2_BROKEN_HS200 |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
.allow_runtime_pm = true,
.probe_slot = intel_mrfl_mmc_probe_slot, .probe_slot = intel_mrfl_mmc_probe_slot,
}; };
@ -876,6 +894,29 @@ static const struct pci_device_id pci_ids[] = {
.driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc,
}, },
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_BSW_EMMC,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_BSW_SDIO,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_byt_sdio,
},
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_BSW_SD,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.driver_data = (kernel_ulong_t)&sdhci_intel_byt_sd,
},
{ {
.vendor = PCI_VENDOR_ID_INTEL, .vendor = PCI_VENDOR_ID_INTEL,
@ -1269,20 +1310,13 @@ static int sdhci_pci_runtime_idle(struct device *dev)
return 0; return 0;
} }
#else
#define sdhci_pci_runtime_suspend NULL
#define sdhci_pci_runtime_resume NULL
#define sdhci_pci_runtime_idle NULL
#endif #endif
static const struct dev_pm_ops sdhci_pci_pm_ops = { static const struct dev_pm_ops sdhci_pci_pm_ops = {
.suspend = sdhci_pci_suspend, .suspend = sdhci_pci_suspend,
.resume = sdhci_pci_resume, .resume = sdhci_pci_resume,
.runtime_suspend = sdhci_pci_runtime_suspend, SET_RUNTIME_PM_OPS(sdhci_pci_runtime_suspend,
.runtime_resume = sdhci_pci_runtime_resume, sdhci_pci_runtime_resume, sdhci_pci_runtime_idle)
.runtime_idle = sdhci_pci_runtime_idle,
}; };
/*****************************************************************************\ /*****************************************************************************\
@ -1332,6 +1366,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
slot->pci_bar = bar; slot->pci_bar = bar;
slot->rst_n_gpio = -EINVAL; slot->rst_n_gpio = -EINVAL;
slot->cd_gpio = -EINVAL; slot->cd_gpio = -EINVAL;
slot->cd_idx = -1;
/* Retrieve platform data if there is any */ /* Retrieve platform data if there is any */
if (*sdhci_pci_get_data) if (*sdhci_pci_get_data)
@ -1390,6 +1425,13 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
host->mmc->slotno = slotno; host->mmc->slotno = slotno;
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP; host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
if (slot->cd_idx >= 0 &&
mmc_gpiod_request_cd(host->mmc, slot->cd_con_id, slot->cd_idx,
slot->cd_override_level, 0, NULL)) {
dev_warn(&pdev->dev, "failed to setup card detect gpio\n");
slot->cd_idx = -1;
}
ret = sdhci_add_host(host); ret = sdhci_add_host(host);
if (ret) if (ret)
goto remove; goto remove;
@ -1402,7 +1444,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
* Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure. * Note sdhci_pci_add_own_cd() sets slot->cd_gpio to -EINVAL on failure.
*/ */
if (chip->fixes && chip->fixes->own_cd_for_runtime_pm && if (chip->fixes && chip->fixes->own_cd_for_runtime_pm &&
!gpio_is_valid(slot->cd_gpio)) !gpio_is_valid(slot->cd_gpio) && slot->cd_idx < 0)
chip->allow_runtime_pm = false; chip->allow_runtime_pm = false;
return slot; return slot;

View File

@ -11,6 +11,9 @@
#define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15 #define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15
#define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 #define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16
#define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50 #define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50
#define PCI_DEVICE_ID_INTEL_BSW_EMMC 0x2294
#define PCI_DEVICE_ID_INTEL_BSW_SDIO 0x2295
#define PCI_DEVICE_ID_INTEL_BSW_SD 0x2296
#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190 #define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190
#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9 #define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9
#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa #define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa
@ -61,6 +64,10 @@ struct sdhci_pci_slot {
int cd_gpio; int cd_gpio;
int cd_irq; int cd_irq;
char *cd_con_id;
int cd_idx;
bool cd_override_level;
void (*hw_reset)(struct sdhci_host *host); void (*hw_reset)(struct sdhci_host *host);
}; };

View File

@ -123,7 +123,6 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
size_t priv_size) size_t priv_size)
{ {
struct sdhci_host *host; struct sdhci_host *host;
struct device_node *np = pdev->dev.of_node;
struct resource *iomem; struct resource *iomem;
int ret; int ret;
@ -136,13 +135,8 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
if (resource_size(iomem) < 0x100) if (resource_size(iomem) < 0x100)
dev_err(&pdev->dev, "Invalid iomem size!\n"); dev_err(&pdev->dev, "Invalid iomem size!\n");
/* Some PCI-based MFD need the parent here */ host = sdhci_alloc_host(&pdev->dev,
if (pdev->dev.parent != &platform_bus && !np) sizeof(struct sdhci_pltfm_host) + priv_size);
host = sdhci_alloc_host(pdev->dev.parent,
sizeof(struct sdhci_pltfm_host) + priv_size);
else
host = sdhci_alloc_host(&pdev->dev,
sizeof(struct sdhci_pltfm_host) + priv_size);
if (IS_ERR(host)) { if (IS_ERR(host)) {
ret = PTR_ERR(host); ret = PTR_ERR(host);

View File

@ -261,7 +261,6 @@ static int sdhci_pxav2_remove(struct platform_device *pdev)
static struct platform_driver sdhci_pxav2_driver = { static struct platform_driver sdhci_pxav2_driver = {
.driver = { .driver = {
.name = "sdhci-pxav2", .name = "sdhci-pxav2",
.owner = THIS_MODULE,
#ifdef CONFIG_OF #ifdef CONFIG_OF
.of_match_table = sdhci_pxav2_of_match, .of_match_table = sdhci_pxav2_of_match,
#endif #endif

View File

@ -224,12 +224,11 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs)
static const struct sdhci_ops pxav3_sdhci_ops = { static const struct sdhci_ops pxav3_sdhci_ops = {
.set_clock = sdhci_set_clock, .set_clock = sdhci_set_clock,
.set_uhs_signaling = pxav3_set_uhs_signaling,
.platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks,
.get_max_clock = sdhci_pltfm_clk_get_max_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock,
.set_bus_width = sdhci_set_bus_width, .set_bus_width = sdhci_set_bus_width,
.reset = pxav3_reset, .reset = pxav3_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling, .set_uhs_signaling = pxav3_set_uhs_signaling,
}; };
static struct sdhci_pltfm_data sdhci_pxav3_pdata = { static struct sdhci_pltfm_data sdhci_pxav3_pdata = {
@ -381,11 +380,11 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
return 0; return 0;
err_of_parse:
err_cd_req:
err_add_host: err_add_host:
pm_runtime_put_sync(&pdev->dev); pm_runtime_put_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
err_of_parse:
err_cd_req:
clk_disable_unprepare(clk); clk_disable_unprepare(clk);
err_clk_get: err_clk_get:
err_mbus_win: err_mbus_win:
@ -492,7 +491,6 @@ static struct platform_driver sdhci_pxav3_driver = {
#ifdef CONFIG_OF #ifdef CONFIG_OF
.of_match_table = sdhci_pxav3_of_match, .of_match_table = sdhci_pxav3_of_match,
#endif #endif
.owner = THIS_MODULE,
.pm = SDHCI_PXAV3_PMOPS, .pm = SDHCI_PXAV3_PMOPS,
}, },
.probe = sdhci_pxav3_probe, .probe = sdhci_pxav3_probe,

View File

@ -606,8 +606,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
ret = sdhci_add_host(host); ret = sdhci_add_host(host);
if (ret) { if (ret) {
dev_err(dev, "sdhci_add_host() failed\n"); dev_err(dev, "sdhci_add_host() failed\n");
pm_runtime_forbid(&pdev->dev);
pm_runtime_get_noresume(&pdev->dev);
goto err_req_regs; goto err_req_regs;
} }
@ -618,6 +616,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
return 0; return 0;
err_req_regs: err_req_regs:
pm_runtime_disable(&pdev->dev);
err_no_busclks: err_no_busclks:
clk_disable_unprepare(sc->clk_io); clk_disable_unprepare(sc->clk_io);
@ -747,7 +747,6 @@ static struct platform_driver sdhci_s3c_driver = {
.remove = sdhci_s3c_remove, .remove = sdhci_s3c_remove,
.id_table = sdhci_s3c_driver_ids, .id_table = sdhci_s3c_driver_ids,
.driver = { .driver = {
.owner = THIS_MODULE,
.name = "s3c-sdhci", .name = "s3c-sdhci",
.of_match_table = of_match_ptr(sdhci_s3c_dt_match), .of_match_table = of_match_ptr(sdhci_s3c_dt_match),
.pm = SDHCI_S3C_PMOPS, .pm = SDHCI_S3C_PMOPS,

View File

@ -15,6 +15,8 @@
#include <linux/mmc/slot-gpio.h> #include <linux/mmc/slot-gpio.h>
#include "sdhci-pltfm.h" #include "sdhci-pltfm.h"
#define SDHCI_SIRF_8BITBUS BIT(3)
struct sdhci_sirf_priv { struct sdhci_sirf_priv {
struct clk *clk; struct clk *clk;
int gpio_cd; int gpio_cd;
@ -27,10 +29,30 @@ static unsigned int sdhci_sirf_get_max_clk(struct sdhci_host *host)
return clk_get_rate(priv->clk); return clk_get_rate(priv->clk);
} }
static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width)
{
u8 ctrl;
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
ctrl &= ~(SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS);
/*
* CSR atlas7 and prima2 SD host version is not 3.0
* 8bit-width enable bit of CSR SD hosts is 3,
* while stardard hosts use bit 5
*/
if (width == MMC_BUS_WIDTH_8)
ctrl |= SDHCI_SIRF_8BITBUS;
else if (width == MMC_BUS_WIDTH_4)
ctrl |= SDHCI_CTRL_4BITBUS;
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
static struct sdhci_ops sdhci_sirf_ops = { static struct sdhci_ops sdhci_sirf_ops = {
.set_clock = sdhci_set_clock, .set_clock = sdhci_set_clock,
.get_max_clock = sdhci_sirf_get_max_clk, .get_max_clock = sdhci_sirf_get_max_clk,
.set_bus_width = sdhci_set_bus_width, .set_bus_width = sdhci_sirf_set_bus_width,
.reset = sdhci_reset, .reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling, .set_uhs_signaling = sdhci_set_uhs_signaling,
}; };
@ -94,6 +116,7 @@ static int sdhci_sirf_probe(struct platform_device *pdev)
ret); ret);
goto err_request_cd; goto err_request_cd;
} }
mmc_gpiod_request_cd_irq(host->mmc);
} }
return 0; return 0;
@ -167,7 +190,6 @@ MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match);
static struct platform_driver sdhci_sirf_driver = { static struct platform_driver sdhci_sirf_driver = {
.driver = { .driver = {
.name = "sdhci-sirf", .name = "sdhci-sirf",
.owner = THIS_MODULE,
.of_match_table = sdhci_sirf_of_match, .of_match_table = sdhci_sirf_of_match,
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
.pm = &sdhci_sirf_pm_ops, .pm = &sdhci_sirf_pm_ops,

View File

@ -230,7 +230,6 @@ MODULE_DEVICE_TABLE(of, sdhci_spear_id_table);
static struct platform_driver sdhci_driver = { static struct platform_driver sdhci_driver = {
.driver = { .driver = {
.name = "sdhci", .name = "sdhci",
.owner = THIS_MODULE,
.pm = &sdhci_pm_ops, .pm = &sdhci_pm_ops,
.of_match_table = of_match_ptr(sdhci_spear_id_table), .of_match_table = of_match_ptr(sdhci_spear_id_table),
}, },

View File

@ -318,7 +318,6 @@ static int sdhci_tegra_remove(struct platform_device *pdev)
static struct platform_driver sdhci_tegra_driver = { static struct platform_driver sdhci_tegra_driver = {
.driver = { .driver = {
.name = "sdhci-tegra", .name = "sdhci-tegra",
.owner = THIS_MODULE,
.of_match_table = sdhci_tegra_dt_match, .of_match_table = sdhci_tegra_dt_match,
.pm = SDHCI_PLTFM_PMOPS, .pm = SDHCI_PLTFM_PMOPS,
}, },

View File

@ -707,19 +707,28 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
} }
static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
{ {
u8 count; u8 count;
if (host->ops->set_timeout) {
host->ops->set_timeout(host, cmd);
} else {
count = sdhci_calc_timeout(host, cmd);
sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
}
}
static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
{
u8 ctrl; u8 ctrl;
struct mmc_data *data = cmd->data; struct mmc_data *data = cmd->data;
int ret; int ret;
WARN_ON(host->data); WARN_ON(host->data);
if (data || (cmd->flags & MMC_RSP_BUSY)) { if (data || (cmd->flags & MMC_RSP_BUSY))
count = sdhci_calc_timeout(host, cmd); sdhci_set_timeout(host, cmd);
sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL);
}
if (!data) if (!data)
return; return;
@ -1007,6 +1016,7 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
mod_timer(&host->timer, timeout); mod_timer(&host->timer, timeout);
host->cmd = cmd; host->cmd = cmd;
host->busy_handle = 0;
sdhci_prepare_data(host, cmd); sdhci_prepare_data(host, cmd);
@ -1194,7 +1204,6 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock)
clock_set: clock_set:
if (real_div) if (real_div)
host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div;
clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
<< SDHCI_DIVIDER_HI_SHIFT; << SDHCI_DIVIDER_HI_SHIFT;
@ -1357,11 +1366,12 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
/* /*
* Check if the re-tuning timer has already expired and there * Check if the re-tuning timer has already expired and there
* is no on-going data transfer. If so, we need to execute * is no on-going data transfer and DAT0 is not busy. If so,
* tuning procedure before sending command. * we need to execute tuning procedure before sending command.
*/ */
if ((host->flags & SDHCI_NEEDS_RETUNING) && if ((host->flags & SDHCI_NEEDS_RETUNING) &&
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) { !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ)) &&
(present_state & SDHCI_DATA_0_LVL_MASK)) {
if (mmc->card) { if (mmc->card) {
/* eMMC uses cmd21 but sd and sdio use cmd19 */ /* eMMC uses cmd21 but sd and sdio use cmd19 */
tuning_opcode = tuning_opcode =
@ -1471,6 +1481,18 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios)
if (!ios->clock || ios->clock != host->clock) { if (!ios->clock || ios->clock != host->clock) {
host->ops->set_clock(host, ios->clock); host->ops->set_clock(host, ios->clock);
host->clock = ios->clock; host->clock = ios->clock;
if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK &&
host->clock) {
host->timeout_clk = host->mmc->actual_clock ?
host->mmc->actual_clock / 1000 :
host->clock / 1000;
host->mmc->max_busy_timeout =
host->ops->get_max_timeout_count ?
host->ops->get_max_timeout_count(host) :
1 << 27;
host->mmc->max_busy_timeout /= host->timeout_clk;
}
} }
sdhci_set_power(host, ios->power_mode, ios->vdd); sdhci_set_power(host, ios->power_mode, ios->vdd);
@ -1733,8 +1755,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000, ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000,
3600000); 3600000);
if (ret) { if (ret) {
pr_warning("%s: Switching to 3.3V signalling voltage " pr_warn("%s: Switching to 3.3V signalling voltage failed\n",
" failed\n", mmc_hostname(mmc)); mmc_hostname(mmc));
return -EIO; return -EIO;
} }
} }
@ -1746,8 +1768,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
if (!(ctrl & SDHCI_CTRL_VDD_180)) if (!(ctrl & SDHCI_CTRL_VDD_180))
return 0; return 0;
pr_warning("%s: 3.3V regulator output did not became stable\n", pr_warn("%s: 3.3V regulator output did not became stable\n",
mmc_hostname(mmc)); mmc_hostname(mmc));
return -EAGAIN; return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_180: case MMC_SIGNAL_VOLTAGE_180:
@ -1755,8 +1777,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
ret = regulator_set_voltage(mmc->supply.vqmmc, ret = regulator_set_voltage(mmc->supply.vqmmc,
1700000, 1950000); 1700000, 1950000);
if (ret) { if (ret) {
pr_warning("%s: Switching to 1.8V signalling voltage " pr_warn("%s: Switching to 1.8V signalling voltage failed\n",
" failed\n", mmc_hostname(mmc)); mmc_hostname(mmc));
return -EIO; return -EIO;
} }
} }
@ -1773,8 +1795,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
if (ctrl & SDHCI_CTRL_VDD_180) if (ctrl & SDHCI_CTRL_VDD_180)
return 0; return 0;
pr_warning("%s: 1.8V regulator output did not became stable\n", pr_warn("%s: 1.8V regulator output did not became stable\n",
mmc_hostname(mmc)); mmc_hostname(mmc));
return -EAGAIN; return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_120: case MMC_SIGNAL_VOLTAGE_120:
@ -1782,8 +1804,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host,
ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000, ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000,
1300000); 1300000);
if (ret) { if (ret) {
pr_warning("%s: Switching to 1.2V signalling voltage " pr_warn("%s: Switching to 1.2V signalling voltage failed\n",
" failed\n", mmc_hostname(mmc)); mmc_hostname(mmc));
return -EIO; return -EIO;
} }
} }
@ -2203,7 +2225,7 @@ static void sdhci_tuning_timer(unsigned long data)
* * * *
\*****************************************************************************/ \*****************************************************************************/
static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *mask)
{ {
BUG_ON(intmask == 0); BUG_ON(intmask == 0);
@ -2241,11 +2263,18 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
if (host->cmd->data) if (host->cmd->data)
DBG("Cannot wait for busy signal when also " DBG("Cannot wait for busy signal when also "
"doing a data transfer"); "doing a data transfer");
else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ)) else if (!(host->quirks & SDHCI_QUIRK_NO_BUSY_IRQ)
&& !host->busy_handle) {
/* Mark that command complete before busy is ended */
host->busy_handle = 1;
return; return;
}
/* The controller does not support the end-of-busy IRQ, /* The controller does not support the end-of-busy IRQ,
* fall through and take the SDHCI_INT_RESPONSE */ * fall through and take the SDHCI_INT_RESPONSE */
} else if ((host->quirks2 & SDHCI_QUIRK2_STOP_WITH_TC) &&
host->cmd->opcode == MMC_STOP_TRANSMISSION && !host->data) {
*mask &= ~SDHCI_INT_DATA_END;
} }
if (intmask & SDHCI_INT_RESPONSE) if (intmask & SDHCI_INT_RESPONSE)
@ -2304,8 +2333,21 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
* above in sdhci_cmd_irq(). * above in sdhci_cmd_irq().
*/ */
if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) { if (host->cmd && (host->cmd->flags & MMC_RSP_BUSY)) {
if (intmask & SDHCI_INT_DATA_TIMEOUT) {
host->cmd->error = -ETIMEDOUT;
tasklet_schedule(&host->finish_tasklet);
return;
}
if (intmask & SDHCI_INT_DATA_END) { if (intmask & SDHCI_INT_DATA_END) {
sdhci_finish_command(host); /*
* Some cards handle busy-end interrupt
* before the command completed, so make
* sure we do things in the proper order.
*/
if (host->busy_handle)
sdhci_finish_command(host);
else
host->busy_handle = 1;
return; return;
} }
} }
@ -2442,7 +2484,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
} }
if (intmask & SDHCI_INT_CMD_MASK) if (intmask & SDHCI_INT_CMD_MASK)
sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK,
&intmask);
if (intmask & SDHCI_INT_DATA_MASK) if (intmask & SDHCI_INT_DATA_MASK)
sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
@ -2534,7 +2577,7 @@ void sdhci_enable_irq_wakeups(struct sdhci_host *host)
} }
EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups);
void sdhci_disable_irq_wakeups(struct sdhci_host *host) static void sdhci_disable_irq_wakeups(struct sdhci_host *host)
{ {
u8 val; u8 val;
u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE
@ -2544,7 +2587,6 @@ void sdhci_disable_irq_wakeups(struct sdhci_host *host)
val &= ~mask; val &= ~mask;
sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL); sdhci_writeb(host, val, SDHCI_WAKE_UP_CONTROL);
} }
EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups);
int sdhci_suspend_host(struct sdhci_host *host) int sdhci_suspend_host(struct sdhci_host *host)
{ {
@ -2749,6 +2791,7 @@ int sdhci_add_host(struct sdhci_host *host)
u32 caps[2] = {0, 0}; u32 caps[2] = {0, 0};
u32 max_current_caps; u32 max_current_caps;
unsigned int ocr_avail; unsigned int ocr_avail;
unsigned int override_timeout_clk;
int ret; int ret;
WARN_ON(host == NULL); WARN_ON(host == NULL);
@ -2762,6 +2805,8 @@ int sdhci_add_host(struct sdhci_host *host)
if (debug_quirks2) if (debug_quirks2)
host->quirks2 = debug_quirks2; host->quirks2 = debug_quirks2;
override_timeout_clk = host->timeout_clk;
sdhci_do_reset(host, SDHCI_RESET_ALL); sdhci_do_reset(host, SDHCI_RESET_ALL);
host->version = sdhci_readw(host, SDHCI_HOST_VERSION); host->version = sdhci_readw(host, SDHCI_HOST_VERSION);
@ -2807,8 +2852,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
if (host->ops->enable_dma) { if (host->ops->enable_dma) {
if (host->ops->enable_dma(host)) { if (host->ops->enable_dma(host)) {
pr_warning("%s: No suitable DMA " pr_warn("%s: No suitable DMA available - falling back to PIO\n",
"available. Falling back to PIO.\n",
mmc_hostname(mmc)); mmc_hostname(mmc));
host->flags &= host->flags &=
~(SDHCI_USE_SDMA | SDHCI_USE_ADMA); ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA);
@ -2830,15 +2874,14 @@ int sdhci_add_host(struct sdhci_host *host)
dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
host->adma_desc, host->adma_addr); host->adma_desc, host->adma_addr);
kfree(host->align_buffer); kfree(host->align_buffer);
pr_warning("%s: Unable to allocate ADMA " pr_warn("%s: Unable to allocate ADMA buffers - falling back to standard DMA\n",
"buffers. Falling back to standard DMA.\n",
mmc_hostname(mmc)); mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA; host->flags &= ~SDHCI_USE_ADMA;
host->adma_desc = NULL; host->adma_desc = NULL;
host->align_buffer = NULL; host->align_buffer = NULL;
} else if (host->adma_addr & 3) { } else if (host->adma_addr & 3) {
pr_warning("%s: unable to allocate aligned ADMA descriptor\n", pr_warn("%s: unable to allocate aligned ADMA descriptor\n",
mmc_hostname(mmc)); mmc_hostname(mmc));
host->flags &= ~SDHCI_USE_ADMA; host->flags &= ~SDHCI_USE_ADMA;
dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, dma_free_coherent(mmc_dev(mmc), ADMA_SIZE,
host->adma_desc, host->adma_addr); host->adma_desc, host->adma_addr);
@ -2908,25 +2951,30 @@ int sdhci_add_host(struct sdhci_host *host)
} else } else
mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200; mmc->f_min = host->max_clk / SDHCI_MAX_DIV_SPEC_200;
host->timeout_clk = if (!(host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) {
(caps[0] & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; host->timeout_clk = (caps[0] & SDHCI_TIMEOUT_CLK_MASK) >>
if (host->timeout_clk == 0) { SDHCI_TIMEOUT_CLK_SHIFT;
if (host->ops->get_timeout_clock) { if (host->timeout_clk == 0) {
host->timeout_clk = host->ops->get_timeout_clock(host); if (host->ops->get_timeout_clock) {
} else if (!(host->quirks & host->timeout_clk =
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)) { host->ops->get_timeout_clock(host);
pr_err("%s: Hardware doesn't specify timeout clock " } else {
"frequency.\n", mmc_hostname(mmc)); pr_err("%s: Hardware doesn't specify timeout clock frequency.\n",
return -ENODEV; mmc_hostname(mmc));
return -ENODEV;
}
} }
if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
host->timeout_clk *= 1000;
mmc->max_busy_timeout = host->ops->get_max_timeout_count ?
host->ops->get_max_timeout_count(host) : 1 << 27;
mmc->max_busy_timeout /= host->timeout_clk;
} }
if (caps[0] & SDHCI_TIMEOUT_CLK_UNIT)
host->timeout_clk *= 1000;
if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK) if (override_timeout_clk)
host->timeout_clk = mmc->f_max / 1000; host->timeout_clk = override_timeout_clk;
mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
@ -2998,8 +3046,13 @@ int sdhci_add_host(struct sdhci_host *host)
/* SD3.0: SDR104 is supported so (for eMMC) the caps2 /* SD3.0: SDR104 is supported so (for eMMC) the caps2
* field can be promoted to support HS200. * field can be promoted to support HS200.
*/ */
if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) if (!(host->quirks2 & SDHCI_QUIRK2_BROKEN_HS200)) {
mmc->caps2 |= MMC_CAP2_HS200; mmc->caps2 |= MMC_CAP2_HS200;
if (IS_ERR(mmc->supply.vqmmc) ||
!regulator_is_supported_voltage
(mmc->supply.vqmmc, 1100000, 1300000))
mmc->caps2 &= ~MMC_CAP2_HS200_1_2V_SDR;
}
} else if (caps[1] & SDHCI_SUPPORT_SDR50) } else if (caps[1] & SDHCI_SUPPORT_SDR50)
mmc->caps |= MMC_CAP_UHS_SDR50; mmc->caps |= MMC_CAP_UHS_SDR50;
@ -3049,7 +3102,7 @@ int sdhci_add_host(struct sdhci_host *host)
*/ */
max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT); max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT);
if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) { if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) {
u32 curr = regulator_get_current_limit(mmc->supply.vmmc); int curr = regulator_get_current_limit(mmc->supply.vmmc);
if (curr > 0) { if (curr > 0) {
/* convert to SDHCI_MAX_CURRENT format */ /* convert to SDHCI_MAX_CURRENT format */
@ -3158,8 +3211,8 @@ int sdhci_add_host(struct sdhci_host *host)
mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >> mmc->max_blk_size = (caps[0] & SDHCI_MAX_BLOCK_MASK) >>
SDHCI_MAX_BLOCK_SHIFT; SDHCI_MAX_BLOCK_SHIFT;
if (mmc->max_blk_size >= 3) { if (mmc->max_blk_size >= 3) {
pr_warning("%s: Invalid maximum block size, " pr_warn("%s: Invalid maximum block size, assuming 512 bytes\n",
"assuming 512 bytes\n", mmc_hostname(mmc)); mmc_hostname(mmc));
mmc->max_blk_size = 0; mmc->max_blk_size = 0;
} }
} }

View File

@ -72,6 +72,7 @@
#define SDHCI_WRITE_PROTECT 0x00080000 #define SDHCI_WRITE_PROTECT 0x00080000
#define SDHCI_DATA_LVL_MASK 0x00F00000 #define SDHCI_DATA_LVL_MASK 0x00F00000
#define SDHCI_DATA_LVL_SHIFT 20 #define SDHCI_DATA_LVL_SHIFT 20
#define SDHCI_DATA_0_LVL_MASK 0x00100000
#define SDHCI_HOST_CONTROL 0x28 #define SDHCI_HOST_CONTROL 0x28
#define SDHCI_CTRL_LED 0x01 #define SDHCI_CTRL_LED 0x01
@ -281,6 +282,9 @@ struct sdhci_ops {
unsigned int (*get_max_clock)(struct sdhci_host *host); unsigned int (*get_max_clock)(struct sdhci_host *host);
unsigned int (*get_min_clock)(struct sdhci_host *host); unsigned int (*get_min_clock)(struct sdhci_host *host);
unsigned int (*get_timeout_clock)(struct sdhci_host *host); unsigned int (*get_timeout_clock)(struct sdhci_host *host);
unsigned int (*get_max_timeout_count)(struct sdhci_host *host);
void (*set_timeout)(struct sdhci_host *host,
struct mmc_command *cmd);
void (*set_bus_width)(struct sdhci_host *host, int width); void (*set_bus_width)(struct sdhci_host *host, int width);
void (*platform_send_init_74_clocks)(struct sdhci_host *host, void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode); u8 power_mode);

View File

@ -1553,7 +1553,6 @@ static struct platform_driver sh_mmcif_driver = {
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.pm = &sh_mmcif_dev_pm_ops, .pm = &sh_mmcif_dev_pm_ops,
.owner = THIS_MODULE,
.of_match_table = mmcif_of_match, .of_match_table = mmcif_of_match,
}, },
}; };

View File

@ -39,6 +39,7 @@ struct sh_mobile_sdhi_of_data {
unsigned long tmio_flags; unsigned long tmio_flags;
unsigned long capabilities; unsigned long capabilities;
unsigned long capabilities2; unsigned long capabilities2;
dma_addr_t dma_rx_offset;
}; };
static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = { static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
@ -48,14 +49,16 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
}; };
static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = { static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
TMIO_MMC_CLK_ACTUAL,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
}; };
static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = { static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE, .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
TMIO_MMC_CLK_ACTUAL,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
.capabilities2 = MMC_CAP2_NO_MULTI_READ, .dma_rx_offset = 0x2000,
}; };
static const struct of_device_id sh_mobile_sdhi_of_match[] = { static const struct of_device_id sh_mobile_sdhi_of_match[] = {
@ -68,6 +71,9 @@ static const struct of_device_id sh_mobile_sdhi_of_match[] = {
{ .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, }, { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, },
{ .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, }, { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,sdhi-r8a7792", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,sdhi-r8a7793", .data = &of_rcar_gen2_compatible, },
{ .compatible = "renesas,sdhi-r8a7794", .data = &of_rcar_gen2_compatible, },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
@ -132,6 +138,24 @@ static int sh_mobile_sdhi_write16_hook(struct tmio_mmc_host *host, int addr)
return 0; return 0;
} }
static int sh_mobile_sdhi_multi_io_quirk(struct mmc_card *card,
unsigned int direction, int blk_size)
{
/*
* In Renesas controllers, when performing a
* multiple block read of one or two blocks,
* depending on the timing with which the
* response register is read, the response
* value may not be read properly.
* Use single block read for this HW bug
*/
if ((direction == MMC_DATA_READ) &&
blk_size == 2)
return 1;
return blk_size;
}
static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev) static void sh_mobile_sdhi_cd_wakeup(const struct platform_device *pdev)
{ {
mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100)); mmc_detect_change(platform_get_drvdata(pdev), msecs_to_jiffies(100));
@ -187,6 +211,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->clk_disable = sh_mobile_sdhi_clk_disable; mmc_data->clk_disable = sh_mobile_sdhi_clk_disable;
mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED;
mmc_data->write16_hook = sh_mobile_sdhi_write16_hook; mmc_data->write16_hook = sh_mobile_sdhi_write16_hook;
mmc_data->multi_io_quirk = sh_mobile_sdhi_multi_io_quirk;
if (p) { if (p) {
mmc_data->flags = p->tmio_flags; mmc_data->flags = p->tmio_flags;
mmc_data->ocr_mask = p->tmio_ocr_mask; mmc_data->ocr_mask = p->tmio_ocr_mask;
@ -223,11 +248,27 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
*/ */
mmc_data->flags |= TMIO_MMC_SDIO_IRQ; mmc_data->flags |= TMIO_MMC_SDIO_IRQ;
/*
* All SDHI have CMD12 controll bit
*/
mmc_data->flags |= TMIO_MMC_HAVE_CMD12_CTRL;
/*
* All SDHI need SDIO_INFO1 reserved bit
*/
mmc_data->flags |= TMIO_MMC_SDIO_STATUS_QUIRK;
/*
* All SDHI have DMA control register
*/
mmc_data->flags |= TMIO_MMC_HAVE_CTL_DMA_REG;
if (of_id && of_id->data) { if (of_id && of_id->data) {
const struct sh_mobile_sdhi_of_data *of_data = of_id->data; const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
mmc_data->flags |= of_data->tmio_flags; mmc_data->flags |= of_data->tmio_flags;
mmc_data->capabilities |= of_data->capabilities; mmc_data->capabilities |= of_data->capabilities;
mmc_data->capabilities2 |= of_data->capabilities2; mmc_data->capabilities2 |= of_data->capabilities2;
dma_priv->dma_rx_offset = of_data->dma_rx_offset;
} }
/* SD control register space size is 0x100, 0x200 for bus_shift=1 */ /* SD control register space size is 0x100, 0x200 for bus_shift=1 */
@ -332,8 +373,9 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
} }
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, pm_runtime_force_resume)
SET_PM_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
tmio_mmc_host_runtime_resume, tmio_mmc_host_runtime_resume,
NULL) NULL)
}; };
@ -341,7 +383,6 @@ static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
static struct platform_driver sh_mobile_sdhi_driver = { static struct platform_driver sh_mobile_sdhi_driver = {
.driver = { .driver = {
.name = "sh_mobile_sdhi", .name = "sh_mobile_sdhi",
.owner = THIS_MODULE,
.pm = &tmio_mmc_dev_pm_ops, .pm = &tmio_mmc_dev_pm_ops,
.of_match_table = sh_mobile_sdhi_of_match, .of_match_table = sh_mobile_sdhi_of_match,
}, },

View File

@ -990,7 +990,8 @@ static int sunxi_mmc_probe(struct platform_device *pdev)
/* 400kHz ~ 50MHz */ /* 400kHz ~ 50MHz */
mmc->f_min = 400000; mmc->f_min = 400000;
mmc->f_max = 50000000; mmc->f_max = 50000000;
mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
MMC_CAP_ERASE;
ret = mmc_of_parse(mmc); ret = mmc_of_parse(mmc);
if (ret) if (ret)
@ -1035,7 +1036,6 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
static struct platform_driver sunxi_mmc_driver = { static struct platform_driver sunxi_mmc_driver = {
.driver = { .driver = {
.name = "sunxi-mmc", .name = "sunxi-mmc",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(sunxi_mmc_of_match), .of_match_table = of_match_ptr(sunxi_mmc_of_match),
}, },
.probe = sunxi_mmc_probe, .probe = sunxi_mmc_probe,

View File

@ -952,8 +952,8 @@ static int tifm_sd_probe(struct tifm_dev *sock)
if (!(TIFM_SOCK_STATE_OCCUPIED if (!(TIFM_SOCK_STATE_OCCUPIED
& readl(sock->addr + SOCK_PRESENT_STATE))) { & readl(sock->addr + SOCK_PRESENT_STATE))) {
pr_warning("%s : card gone, unexpectedly\n", pr_warn("%s : card gone, unexpectedly\n",
dev_name(&sock->dev)); dev_name(&sock->dev));
return rc; return rc;
} }

View File

@ -30,7 +30,7 @@ static int tmio_mmc_suspend(struct device *dev)
const struct mfd_cell *cell = mfd_get_cell(pdev); const struct mfd_cell *cell = mfd_get_cell(pdev);
int ret; int ret;
ret = tmio_mmc_host_suspend(dev); ret = pm_runtime_force_suspend(dev);
/* Tell MFD core it can disable us now.*/ /* Tell MFD core it can disable us now.*/
if (!ret && cell->disable) if (!ret && cell->disable)
@ -50,7 +50,7 @@ static int tmio_mmc_resume(struct device *dev)
ret = cell->resume(pdev); ret = cell->resume(pdev);
if (!ret) if (!ret)
ret = tmio_mmc_host_resume(dev); ret = pm_runtime_force_resume(dev);
return ret; return ret;
} }
@ -135,6 +135,9 @@ static int tmio_mmc_remove(struct platform_device *pdev)
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = { static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume) SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume)
SET_PM_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
tmio_mmc_host_runtime_resume,
NULL)
}; };
static struct platform_driver tmio_mmc_driver = { static struct platform_driver tmio_mmc_driver = {

View File

@ -40,22 +40,6 @@
struct tmio_mmc_data; struct tmio_mmc_data;
/*
* We differentiate between the following 3 power states:
* 1. card slot powered off, controller stopped. This is used, when either there
* is no card in the slot, or the card really has to be powered down.
* 2. card slot powered on, controller stopped. This is used, when a card is in
* the slot, but no activity is currently taking place. This is a power-
* saving mode with card-state preserved. This state can be entered, e.g.
* when MMC clock-gating is used.
* 3. card slot powered on, controller running. This is the actual active state.
*/
enum tmio_mmc_power {
TMIO_MMC_OFF_STOP, /* card power off, controller stopped */
TMIO_MMC_ON_STOP, /* card power on, controller stopped */
TMIO_MMC_ON_RUN, /* card power on, controller running */
};
struct tmio_mmc_host { struct tmio_mmc_host {
void __iomem *ctl; void __iomem *ctl;
struct mmc_command *cmd; struct mmc_command *cmd;
@ -63,9 +47,6 @@ struct tmio_mmc_host {
struct mmc_data *data; struct mmc_data *data;
struct mmc_host *mmc; struct mmc_host *mmc;
/* Controller and card power state */
enum tmio_mmc_power power;
/* Callbacks for clock / power control */ /* Callbacks for clock / power control */
void (*set_pwr)(struct platform_device *host, int state); void (*set_pwr)(struct platform_device *host, int state);
void (*set_clk_div)(struct platform_device *host, int state); void (*set_clk_div)(struct platform_device *host, int state);
@ -92,15 +73,16 @@ struct tmio_mmc_host {
struct delayed_work delayed_reset_work; struct delayed_work delayed_reset_work;
struct work_struct done; struct work_struct done;
/* Cache IRQ mask */ /* Cache */
u32 sdcard_irq_mask; u32 sdcard_irq_mask;
u32 sdio_irq_mask; u32 sdio_irq_mask;
unsigned int clk_cache;
spinlock_t lock; /* protect host private data */ spinlock_t lock; /* protect host private data */
unsigned long last_req_ts; unsigned long last_req_ts;
struct mutex ios_lock; /* protect set_ios() context */ struct mutex ios_lock; /* protect set_ios() context */
bool native_hotplug; bool native_hotplug;
bool resuming; bool sdio_irq_enabled;
}; };
int tmio_mmc_host_probe(struct tmio_mmc_host **host, int tmio_mmc_host_probe(struct tmio_mmc_host **host,
@ -162,12 +144,7 @@ static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
} }
#endif #endif
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM
int tmio_mmc_host_suspend(struct device *dev);
int tmio_mmc_host_resume(struct device *dev);
#endif
#ifdef CONFIG_PM_RUNTIME
int tmio_mmc_host_runtime_suspend(struct device *dev); int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev); int tmio_mmc_host_runtime_resume(struct device *dev);
#endif #endif

View File

@ -28,10 +28,8 @@ void tmio_mmc_enable_dma(struct tmio_mmc_host *host, bool enable)
if (!host->chan_tx || !host->chan_rx) if (!host->chan_tx || !host->chan_rx)
return; return;
#if defined(CONFIG_SUPERH) || defined(CONFIG_ARCH_SHMOBILE) if (host->pdata->flags & TMIO_MMC_HAVE_CTL_DMA_REG)
/* Switch DMA mode on or off - SuperH specific? */ sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
sd_ctrl_write16(host, CTL_DMA_ENABLE, enable ? 2 : 0);
#endif
} }
void tmio_mmc_abort_dma(struct tmio_mmc_host *host) void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
@ -312,7 +310,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat
if (pdata->dma->chan_priv_rx) if (pdata->dma->chan_priv_rx)
cfg.slave_id = pdata->dma->slave_id_rx; cfg.slave_id = pdata->dma->slave_id_rx;
cfg.direction = DMA_DEV_TO_MEM; cfg.direction = DMA_DEV_TO_MEM;
cfg.src_addr = cfg.dst_addr; cfg.src_addr = cfg.dst_addr + pdata->dma->dma_rx_offset;
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
cfg.dst_addr = 0; cfg.dst_addr = 0;
ret = dmaengine_slave_config(host->chan_rx, &cfg); ret = dmaengine_slave_config(host->chan_rx, &cfg);

View File

@ -44,6 +44,7 @@
#include <linux/pm_qos.h> #include <linux/pm_qos.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/mmc/sdio.h>
#include <linux/scatterlist.h> #include <linux/scatterlist.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
@ -129,19 +130,28 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
{ {
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
if (enable) { if (enable && !host->sdio_irq_enabled) {
/* Keep device active while SDIO irq is enabled */
pm_runtime_get_sync(mmc_dev(mmc));
host->sdio_irq_enabled = true;
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL & host->sdio_irq_mask = TMIO_SDIO_MASK_ALL &
~TMIO_SDIO_STAT_IOIRQ; ~TMIO_SDIO_STAT_IOIRQ;
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001);
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
} else { } else if (!enable && host->sdio_irq_enabled) {
host->sdio_irq_mask = TMIO_SDIO_MASK_ALL; host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask);
sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000); sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0000);
host->sdio_irq_enabled = false;
pm_runtime_mark_last_busy(mmc_dev(mmc));
pm_runtime_put_autosuspend(mmc_dev(mmc));
} }
} }
static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock) static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
unsigned int new_clock)
{ {
u32 clk = 0, clock; u32 clk = 0, clock;
@ -149,7 +159,11 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, int new_clock)
for (clock = host->mmc->f_min, clk = 0x80000080; for (clock = host->mmc->f_min, clk = 0x80000080;
new_clock >= (clock<<1); clk >>= 1) new_clock >= (clock<<1); clk >>= 1)
clock <<= 1; clock <<= 1;
clk |= 0x100;
/* 1/1 clock is option */
if ((host->pdata->flags & TMIO_MMC_CLK_ACTUAL) &&
((clk >> 22) & 0x1))
clk |= 0xff;
} }
if (host->set_clk_div) if (host->set_clk_div)
@ -245,6 +259,9 @@ static void tmio_mmc_reset_work(struct work_struct *work)
tmio_mmc_abort_dma(host); tmio_mmc_abort_dma(host);
mmc_request_done(host->mmc, mrq); mmc_request_done(host->mmc, mrq);
pm_runtime_mark_last_busy(mmc_dev(host->mmc));
pm_runtime_put_autosuspend(mmc_dev(host->mmc));
} }
/* called with host->lock held, interrupts disabled */ /* called with host->lock held, interrupts disabled */
@ -274,6 +291,9 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
tmio_mmc_abort_dma(host); tmio_mmc_abort_dma(host);
mmc_request_done(host->mmc, mrq); mmc_request_done(host->mmc, mrq);
pm_runtime_mark_last_busy(mmc_dev(host->mmc));
pm_runtime_put_autosuspend(mmc_dev(host->mmc));
} }
static void tmio_mmc_done_work(struct work_struct *work) static void tmio_mmc_done_work(struct work_struct *work)
@ -295,6 +315,7 @@ static void tmio_mmc_done_work(struct work_struct *work)
#define TRANSFER_READ 0x1000 #define TRANSFER_READ 0x1000
#define TRANSFER_MULTI 0x2000 #define TRANSFER_MULTI 0x2000
#define SECURITY_CMD 0x4000 #define SECURITY_CMD 0x4000
#define NO_CMD12_ISSUE 0x4000 /* TMIO_MMC_HAVE_CMD12_CTRL */
static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd) static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd)
{ {
@ -331,6 +352,14 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
if (data->blocks > 1) { if (data->blocks > 1) {
sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x100); sd_ctrl_write16(host, CTL_STOP_INTERNAL_ACTION, 0x100);
c |= TRANSFER_MULTI; c |= TRANSFER_MULTI;
/*
* Disable auto CMD12 at IO_RW_EXTENDED when
* multiple block transfer
*/
if ((host->pdata->flags & TMIO_MMC_HAVE_CMD12_CTRL) &&
(cmd->opcode == SD_IO_RW_EXTENDED))
c |= NO_CMD12_ISSUE;
} }
if (data->flags & MMC_DATA_READ) if (data->flags & MMC_DATA_READ)
c |= TRANSFER_READ; c |= TRANSFER_READ;
@ -347,6 +376,40 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
return 0; return 0;
} }
static void tmio_mmc_transfer_data(struct tmio_mmc_host *host,
unsigned short *buf,
unsigned int count)
{
int is_read = host->data->flags & MMC_DATA_READ;
u8 *buf8;
/*
* Transfer the data
*/
if (is_read)
sd_ctrl_read16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
else
sd_ctrl_write16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
/* if count was even number */
if (!(count & 0x1))
return;
/* if count was odd number */
buf8 = (u8 *)(buf + (count >> 1));
/*
* FIXME
*
* driver and this function are assuming that
* it is used as little endian
*/
if (is_read)
*buf8 = sd_ctrl_read16(host, CTL_SD_DATA_PORT) & 0xff;
else
sd_ctrl_write16(host, CTL_SD_DATA_PORT, *buf8);
}
/* /*
* This chip always returns (at least?) as much data as you ask for. * This chip always returns (at least?) as much data as you ask for.
* I'm unsure what happens if you ask for less than a block. This should be * I'm unsure what happens if you ask for less than a block. This should be
@ -379,10 +442,7 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
count, host->sg_off, data->flags); count, host->sg_off, data->flags);
/* Transfer the data */ /* Transfer the data */
if (data->flags & MMC_DATA_READ) tmio_mmc_transfer_data(host, buf, count);
sd_ctrl_read16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
else
sd_ctrl_write16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
host->sg_off += count; host->sg_off += count;
@ -465,6 +525,9 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
goto out; goto out;
if (host->chan_tx && (data->flags & MMC_DATA_WRITE) && !host->force_pio) { if (host->chan_tx && (data->flags & MMC_DATA_WRITE) && !host->force_pio) {
u32 status = sd_ctrl_read32(host, CTL_STATUS);
bool done = false;
/* /*
* Has all data been written out yet? Testing on SuperH showed, * Has all data been written out yet? Testing on SuperH showed,
* that in most cases the first interrupt comes already with the * that in most cases the first interrupt comes already with the
@ -473,7 +536,15 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
* DATAEND interrupt with the BUSY bit set, in this cases * DATAEND interrupt with the BUSY bit set, in this cases
* waiting for one more interrupt fixes the problem. * waiting for one more interrupt fixes the problem.
*/ */
if (!(sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_CMD_BUSY)) { if (host->pdata->flags & TMIO_MMC_HAS_IDLE_WAIT) {
if (status & TMIO_STAT_ILL_FUNC)
done = true;
} else {
if (!(status & TMIO_STAT_CMD_BUSY))
done = true;
}
if (done) {
tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_DATAEND); tmio_mmc_disable_mmc_irqs(host, TMIO_STAT_DATAEND);
tasklet_schedule(&host->dma_complete); tasklet_schedule(&host->dma_complete);
} }
@ -557,6 +628,9 @@ static void tmio_mmc_card_irq_status(struct tmio_mmc_host *host,
pr_debug_status(*status); pr_debug_status(*status);
pr_debug_status(*ireg); pr_debug_status(*ireg);
/* Clear the status except the interrupt status */
sd_ctrl_write32(host, CTL_STATUS, TMIO_MASK_IRQ);
} }
static bool __tmio_mmc_card_detect_irq(struct tmio_mmc_host *host, static bool __tmio_mmc_card_detect_irq(struct tmio_mmc_host *host,
@ -637,6 +711,7 @@ irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid)
struct mmc_host *mmc = host->mmc; struct mmc_host *mmc = host->mmc;
struct tmio_mmc_data *pdata = host->pdata; struct tmio_mmc_data *pdata = host->pdata;
unsigned int ireg, status; unsigned int ireg, status;
unsigned int sdio_status;
if (!(pdata->flags & TMIO_MMC_SDIO_IRQ)) if (!(pdata->flags & TMIO_MMC_SDIO_IRQ))
return IRQ_HANDLED; return IRQ_HANDLED;
@ -644,7 +719,11 @@ irqreturn_t tmio_mmc_sdio_irq(int irq, void *devid)
status = sd_ctrl_read16(host, CTL_SDIO_STATUS); status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdcard_irq_mask; ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdcard_irq_mask;
sd_ctrl_write16(host, CTL_SDIO_STATUS, status & ~TMIO_SDIO_MASK_ALL); sdio_status = status & ~TMIO_SDIO_MASK_ALL;
if (pdata->flags & TMIO_MMC_SDIO_STATUS_QUIRK)
sdio_status |= 6;
sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status);
if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ) if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ)
mmc_signal_sdio_irq(mmc); mmc_signal_sdio_irq(mmc);
@ -728,6 +807,8 @@ static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
pm_runtime_get_sync(mmc_dev(mmc));
if (mrq->data) { if (mrq->data) {
ret = tmio_mmc_start_data(host, mrq->data); ret = tmio_mmc_start_data(host, mrq->data);
if (ret) if (ret)
@ -746,11 +827,14 @@ fail:
host->mrq = NULL; host->mrq = NULL;
mrq->cmd->error = ret; mrq->cmd->error = ret;
mmc_request_done(mmc, mrq); mmc_request_done(mmc, mrq);
pm_runtime_mark_last_busy(mmc_dev(mmc));
pm_runtime_put_autosuspend(mmc_dev(mmc));
} }
static int tmio_mmc_clk_update(struct mmc_host *mmc) static int tmio_mmc_clk_update(struct tmio_mmc_host *host)
{ {
struct tmio_mmc_host *host = mmc_priv(mmc); struct mmc_host *mmc = host->mmc;
struct tmio_mmc_data *pdata = host->pdata; struct tmio_mmc_data *pdata = host->pdata;
int ret; int ret;
@ -812,6 +896,19 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host)
host->set_pwr(host->pdev, 0); host->set_pwr(host->pdev, 0);
} }
static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host,
unsigned char bus_width)
{
switch (bus_width) {
case MMC_BUS_WIDTH_1:
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
break;
case MMC_BUS_WIDTH_4:
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0);
break;
}
}
/* Set MMC clock / power. /* Set MMC clock / power.
* Note: This controller uses a simple divider scheme therefore it cannot * Note: This controller uses a simple divider scheme therefore it cannot
* run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as
@ -824,6 +921,8 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
struct device *dev = &host->pdev->dev; struct device *dev = &host->pdev->dev;
unsigned long flags; unsigned long flags;
pm_runtime_get_sync(mmc_dev(mmc));
mutex_lock(&host->ios_lock); mutex_lock(&host->ios_lock);
spin_lock_irqsave(&host->lock, flags); spin_lock_irqsave(&host->lock, flags);
@ -850,60 +949,22 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_unlock_irqrestore(&host->lock, flags); spin_unlock_irqrestore(&host->lock, flags);
/* switch (ios->power_mode) {
* host->power toggles between false and true in both cases - either case MMC_POWER_OFF:
* or not the controller can be runtime-suspended during inactivity. tmio_mmc_power_off(host);
* But if the controller has to be kept on, the runtime-pm usage_count tmio_mmc_clk_stop(host);
* is kept positive, so no suspending actually takes place. break;
*/ case MMC_POWER_UP:
if (ios->power_mode == MMC_POWER_ON && ios->clock) {
if (host->power != TMIO_MMC_ON_RUN) {
tmio_mmc_clk_update(mmc);
pm_runtime_get_sync(dev);
if (host->resuming) {
tmio_mmc_reset(host);
host->resuming = false;
}
}
if (host->power == TMIO_MMC_OFF_STOP)
tmio_mmc_reset(host);
tmio_mmc_set_clock(host, ios->clock); tmio_mmc_set_clock(host, ios->clock);
if (host->power == TMIO_MMC_OFF_STOP) tmio_mmc_power_on(host, ios->vdd);
/* power up SD card and the bus */
tmio_mmc_power_on(host, ios->vdd);
host->power = TMIO_MMC_ON_RUN;
/* start bus clock */
tmio_mmc_clk_start(host); tmio_mmc_clk_start(host);
} else if (ios->power_mode != MMC_POWER_UP) { tmio_mmc_set_bus_width(host, ios->bus_width);
struct tmio_mmc_data *pdata = host->pdata;
unsigned int old_power = host->power;
if (old_power != TMIO_MMC_OFF_STOP) {
if (ios->power_mode == MMC_POWER_OFF) {
tmio_mmc_power_off(host);
host->power = TMIO_MMC_OFF_STOP;
} else {
host->power = TMIO_MMC_ON_STOP;
}
}
if (old_power == TMIO_MMC_ON_RUN) {
tmio_mmc_clk_stop(host);
pm_runtime_put(dev);
if (pdata->clk_disable)
pdata->clk_disable(host->pdev);
}
}
if (host->power != TMIO_MMC_OFF_STOP) {
switch (ios->bus_width) {
case MMC_BUS_WIDTH_1:
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
break; break;
case MMC_BUS_WIDTH_4: case MMC_POWER_ON:
sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0); tmio_mmc_set_clock(host, ios->clock);
tmio_mmc_clk_start(host);
tmio_mmc_set_bus_width(host, ios->bus_width);
break; break;
}
} }
/* Let things settle. delay taken from winCE driver */ /* Let things settle. delay taken from winCE driver */
@ -915,7 +976,12 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
ios->clock, ios->power_mode); ios->clock, ios->power_mode);
host->mrq = NULL; host->mrq = NULL;
host->clk_cache = ios->clock;
mutex_unlock(&host->ios_lock); mutex_unlock(&host->ios_lock);
pm_runtime_mark_last_busy(mmc_dev(mmc));
pm_runtime_put_autosuspend(mmc_dev(mmc));
} }
static int tmio_mmc_get_ro(struct mmc_host *mmc) static int tmio_mmc_get_ro(struct mmc_host *mmc)
@ -926,8 +992,25 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc)
if (ret >= 0) if (ret >= 0)
return ret; return ret;
return !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) || pm_runtime_get_sync(mmc_dev(mmc));
(sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)); ret = !((pdata->flags & TMIO_MMC_WRPROTECT_DISABLE) ||
(sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT));
pm_runtime_mark_last_busy(mmc_dev(mmc));
pm_runtime_put_autosuspend(mmc_dev(mmc));
return ret;
}
static int tmio_multi_io_quirk(struct mmc_card *card,
unsigned int direction, int blk_size)
{
struct tmio_mmc_host *host = mmc_priv(card->host);
struct tmio_mmc_data *pdata = host->pdata;
if (pdata->multi_io_quirk)
return pdata->multi_io_quirk(card, direction, blk_size);
return blk_size;
} }
static const struct mmc_host_ops tmio_mmc_ops = { static const struct mmc_host_ops tmio_mmc_ops = {
@ -936,6 +1019,7 @@ static const struct mmc_host_ops tmio_mmc_ops = {
.get_ro = tmio_mmc_get_ro, .get_ro = tmio_mmc_get_ro,
.get_cd = mmc_gpio_get_cd, .get_cd = mmc_gpio_get_cd,
.enable_sdio_irq = tmio_mmc_enable_sdio_irq, .enable_sdio_irq = tmio_mmc_enable_sdio_irq,
.multi_io_quirk = tmio_multi_io_quirk,
}; };
static int tmio_mmc_init_ocr(struct tmio_mmc_host *host) static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
@ -1032,28 +1116,23 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
mmc->caps & MMC_CAP_NONREMOVABLE || mmc->caps & MMC_CAP_NONREMOVABLE ||
mmc->slot.cd_irq >= 0); mmc->slot.cd_irq >= 0);
_host->power = TMIO_MMC_OFF_STOP; if (tmio_mmc_clk_update(_host) < 0) {
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume(&pdev->dev);
if (ret < 0)
goto pm_disable;
if (tmio_mmc_clk_update(mmc) < 0) {
mmc->f_max = pdata->hclk; mmc->f_max = pdata->hclk;
mmc->f_min = mmc->f_max / 512; mmc->f_min = mmc->f_max / 512;
} }
/* /*
* There are 4 different scenarios for the card detection: * Check the sanity of mmc->f_min to prevent tmio_mmc_set_clock() from
* 1) an external gpio irq handles the cd (best for power savings) * looping forever...
* 2) internal sdhi irq handles the cd */
* 3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL if (mmc->f_min == 0) {
* 4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE ret = -EINVAL;
* goto host_free;
* While we increment the runtime PM counter for all scenarios when }
* the mmc core activates us by calling an appropriate set_ios(), we
* must additionally ensure that in case 2) the tmio mmc hardware stays /*
* powered on during runtime for the card detection to work. * While using internal tmio hardware logic for card detection, we need
* to ensure it stays powered for it to work.
*/ */
if (_host->native_hotplug) if (_host->native_hotplug)
pm_runtime_get_noresume(&pdev->dev); pm_runtime_get_noresume(&pdev->dev);
@ -1074,8 +1153,12 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
_host->sdcard_irq_mask &= ~irq_mask; _host->sdcard_irq_mask &= ~irq_mask;
if (pdata->flags & TMIO_MMC_SDIO_IRQ) _host->sdio_irq_enabled = false;
tmio_mmc_enable_sdio_irq(mmc, 0); if (pdata->flags & TMIO_MMC_SDIO_IRQ) {
_host->sdio_irq_mask = TMIO_SDIO_MASK_ALL;
sd_ctrl_write16(_host, CTL_SDIO_IRQ_MASK, _host->sdio_irq_mask);
sd_ctrl_write16(_host, CTL_TRANSACTION_CTL, 0x0000);
}
spin_lock_init(&_host->lock); spin_lock_init(&_host->lock);
mutex_init(&_host->ios_lock); mutex_init(&_host->ios_lock);
@ -1087,9 +1170,12 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
/* See if we also get DMA */ /* See if we also get DMA */
tmio_mmc_request_dma(_host, pdata); tmio_mmc_request_dma(_host, pdata);
pm_runtime_set_active(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_enable(&pdev->dev);
ret = mmc_add_host(mmc); ret = mmc_add_host(mmc);
if (pdata->clk_disable)
pdata->clk_disable(pdev);
if (ret < 0) { if (ret < 0) {
tmio_mmc_host_remove(_host); tmio_mmc_host_remove(_host);
return ret; return ret;
@ -1103,15 +1189,13 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host,
tmio_mmc_host_remove(_host); tmio_mmc_host_remove(_host);
return ret; return ret;
} }
mmc_gpiod_request_cd_irq(mmc);
} }
*host = _host; *host = _host;
return 0; return 0;
pm_disable:
pm_runtime_disable(&pdev->dev);
iounmap(_host->ctl);
host_free: host_free:
mmc_free_host(mmc); mmc_free_host(mmc);
@ -1142,34 +1226,20 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
} }
EXPORT_SYMBOL(tmio_mmc_host_remove); EXPORT_SYMBOL(tmio_mmc_host_remove);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM
int tmio_mmc_host_suspend(struct device *dev) int tmio_mmc_host_runtime_suspend(struct device *dev)
{ {
struct mmc_host *mmc = dev_get_drvdata(dev); struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_suspend);
int tmio_mmc_host_resume(struct device *dev) if (host->clk_cache)
{ tmio_mmc_clk_stop(host);
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
tmio_mmc_enable_dma(host, true); if (host->pdata->clk_disable)
host->pdata->clk_disable(host->pdev);
/* The MMC core will perform the complete set up */
host->resuming = true;
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_resume);
#endif
#ifdef CONFIG_PM_RUNTIME
int tmio_mmc_host_runtime_suspend(struct device *dev)
{
return 0; return 0;
} }
EXPORT_SYMBOL(tmio_mmc_host_runtime_suspend); EXPORT_SYMBOL(tmio_mmc_host_runtime_suspend);
@ -1179,6 +1249,14 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev); struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc); struct tmio_mmc_host *host = mmc_priv(mmc);
tmio_mmc_reset(host);
tmio_mmc_clk_update(host);
if (host->clk_cache) {
tmio_mmc_set_clock(host, host->clk_cache);
tmio_mmc_clk_start(host);
}
tmio_mmc_enable_dma(host, true); tmio_mmc_enable_dma(host, true);
return 0; return 0;

View File

@ -803,8 +803,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
default: default:
#ifdef CONFIG_MMC_DEBUG #ifdef CONFIG_MMC_DEBUG
pr_warning("%s: Data command %d is not " pr_warn("%s: Data command %d is not supported by this controller\n",
"supported by this controller.\n",
mmc_hostname(host->mmc), cmd->opcode); mmc_hostname(host->mmc), cmd->opcode);
#endif #endif
cmd->error = -EINVAL; cmd->error = -EINVAL;
@ -1429,8 +1428,8 @@ free:
free_dma(dma); free_dma(dma);
err: err:
pr_warning(DRIVER_NAME ": Unable to allocate DMA %d. " pr_warn(DRIVER_NAME ": Unable to allocate DMA %d - falling back on FIFO\n",
"Falling back on FIFO.\n", dma); dma);
} }
static void wbsd_release_dma(struct wbsd_host *host) static void wbsd_release_dma(struct wbsd_host *host)
@ -1664,9 +1663,7 @@ static int wbsd_init(struct device *dev, int base, int irq, int dma,
ret = wbsd_scan(host); ret = wbsd_scan(host);
if (ret) { if (ret) {
if (pnp && (ret == -ENODEV)) { if (pnp && (ret == -ENODEV)) {
pr_warning(DRIVER_NAME pr_warn(DRIVER_NAME ": Unable to confirm device presence - you may experience lock-ups\n");
": Unable to confirm device presence. You may "
"experience lock-ups.\n");
} else { } else {
wbsd_free_mmc(dev); wbsd_free_mmc(dev);
return ret; return ret;
@ -1688,10 +1685,7 @@ static int wbsd_init(struct device *dev, int base, int irq, int dma,
*/ */
if (pnp) { if (pnp) {
if ((host->config != 0) && !wbsd_chip_validate(host)) { if ((host->config != 0) && !wbsd_chip_validate(host)) {
pr_warning(DRIVER_NAME pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n");
": PnP active but chip not configured! "
"You probably have a buggy BIOS. "
"Configuring chip manually.\n");
wbsd_chip_config(host); wbsd_chip_config(host);
} }
} else } else
@ -1884,10 +1878,7 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev)
*/ */
if (host->config != 0) { if (host->config != 0) {
if (!wbsd_chip_validate(host)) { if (!wbsd_chip_validate(host)) {
pr_warning(DRIVER_NAME pr_warn(DRIVER_NAME ": PnP active but chip not configured! You probably have a buggy BIOS. Configuring chip manually.\n");
": PnP active but chip not configured! "
"You probably have a buggy BIOS. "
"Configuring chip manually.\n");
wbsd_chip_config(host); wbsd_chip_config(host);
} }
} }

View File

@ -1,6 +1,8 @@
#ifndef __LINUX_ATMEL_MCI_H #ifndef __LINUX_ATMEL_MCI_H
#define __LINUX_ATMEL_MCI_H #define __LINUX_ATMEL_MCI_H
#include <linux/types.h>
#define ATMCI_MAX_NR_SLOTS 2 #define ATMCI_MAX_NR_SLOTS 2
/** /**

View File

@ -5,6 +5,7 @@
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/mmc/card.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
@ -83,6 +84,27 @@
*/ */
#define TMIO_MMC_HAVE_HIGH_REG (1 << 6) #define TMIO_MMC_HAVE_HIGH_REG (1 << 6)
/*
* Some controllers have CMD12 automatically
* issue/non-issue register
*/
#define TMIO_MMC_HAVE_CMD12_CTRL (1 << 7)
/*
* Some controllers needs to set 1 on SDIO status reserved bits
*/
#define TMIO_MMC_SDIO_STATUS_QUIRK (1 << 8)
/*
* Some controllers have DMA enable/disable register
*/
#define TMIO_MMC_HAVE_CTL_DMA_REG (1 << 9)
/*
* Some controllers allows to set SDx actual clock
*/
#define TMIO_MMC_CLK_ACTUAL (1 << 10)
int tmio_core_mmc_enable(void __iomem *cnf, int shift, unsigned long base); 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); 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); void tmio_core_mmc_pwr(void __iomem *cnf, int shift, int state);
@ -96,6 +118,7 @@ struct tmio_mmc_dma {
int slave_id_tx; int slave_id_tx;
int slave_id_rx; int slave_id_rx;
int alignment_shift; int alignment_shift;
dma_addr_t dma_rx_offset;
bool (*filter)(struct dma_chan *chan, void *arg); bool (*filter)(struct dma_chan *chan, void *arg);
}; };
@ -120,6 +143,8 @@ struct tmio_mmc_data {
/* clock management callbacks */ /* clock management callbacks */
int (*clk_enable)(struct platform_device *pdev, unsigned int *f); int (*clk_enable)(struct platform_device *pdev, unsigned int *f);
void (*clk_disable)(struct platform_device *pdev); void (*clk_disable)(struct platform_device *pdev);
int (*multi_io_quirk)(struct mmc_card *card,
unsigned int direction, int blk_size);
}; };
/* /*

View File

@ -42,7 +42,8 @@ struct mmc_csd {
unsigned int read_partial:1, unsigned int read_partial:1,
read_misalign:1, read_misalign:1,
write_partial:1, write_partial:1,
write_misalign:1; write_misalign:1,
dsr_imp:1;
}; };
struct mmc_ext_csd { struct mmc_ext_csd {
@ -74,7 +75,7 @@ struct mmc_ext_csd {
unsigned int sec_trim_mult; /* Secure trim multiplier */ unsigned int sec_trim_mult; /* Secure trim multiplier */
unsigned int sec_erase_mult; /* Secure erase multiplier */ unsigned int sec_erase_mult; /* Secure erase multiplier */
unsigned int trim_timeout; /* In milliseconds */ unsigned int trim_timeout; /* In milliseconds */
bool enhanced_area_en; /* enable bit */ bool partition_setting_completed; /* enable bit */
unsigned long long enhanced_area_offset; /* Units: Byte */ unsigned long long enhanced_area_offset; /* Units: Byte */
unsigned int enhanced_area_size; /* Units: KB */ unsigned int enhanced_area_size; /* Units: KB */
unsigned int cache_size; /* Units: KB */ unsigned int cache_size; /* Units: KB */
@ -214,11 +215,12 @@ enum mmc_blk_status {
}; };
/* The number of MMC physical partitions. These consist of: /* The number of MMC physical partitions. These consist of:
* boot partitions (2), general purpose partitions (4) in MMC v4.4. * boot partitions (2), general purpose partitions (4) and
* RPMB partition (1) in MMC v4.4.
*/ */
#define MMC_NUM_BOOT_PARTITION 2 #define MMC_NUM_BOOT_PARTITION 2
#define MMC_NUM_GP_PARTITION 4 #define MMC_NUM_GP_PARTITION 4
#define MMC_NUM_PHY_PARTITION 6 #define MMC_NUM_PHY_PARTITION 7
#define MAX_MMC_PART_NAME_LEN 20 #define MAX_MMC_PART_NAME_LEN 20
/* /*

View File

@ -26,6 +26,8 @@ enum dw_mci_state {
STATE_DATA_BUSY, STATE_DATA_BUSY,
STATE_SENDING_STOP, STATE_SENDING_STOP,
STATE_DATA_ERROR, STATE_DATA_ERROR,
STATE_SENDING_CMD11,
STATE_WAITING_CMD11_DONE,
}; };
enum { enum {
@ -188,7 +190,7 @@ struct dw_mci {
/* Workaround flags */ /* Workaround flags */
u32 quirks; u32 quirks;
struct regulator *vmmc; /* Power regulator */ bool vqmmc_enabled;
unsigned long irq_flags; /* IRQ flags */ unsigned long irq_flags; /* IRQ flags */
int irq; int irq;
}; };

View File

@ -42,6 +42,7 @@ struct mmc_ios {
#define MMC_POWER_OFF 0 #define MMC_POWER_OFF 0
#define MMC_POWER_UP 1 #define MMC_POWER_UP 1
#define MMC_POWER_ON 2 #define MMC_POWER_ON 2
#define MMC_POWER_UNDEFINED 3
unsigned char bus_width; /* data bus width */ unsigned char bus_width; /* data bus width */
@ -139,6 +140,13 @@ struct mmc_host_ops {
int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv); int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv);
void (*hw_reset)(struct mmc_host *host); void (*hw_reset)(struct mmc_host *host);
void (*card_event)(struct mmc_host *host); void (*card_event)(struct mmc_host *host);
/*
* Optional callback to support controllers with HW issues for multiple
* I/O. Returns the number of supported blocks for the request.
*/
int (*multi_io_quirk)(struct mmc_card *card,
unsigned int direction, int blk_size);
}; };
struct mmc_card; struct mmc_card;
@ -265,7 +273,6 @@ struct mmc_host {
#define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */ #define MMC_CAP2_BOOTPART_NOACC (1 << 0) /* Boot partition no access */
#define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */ #define MMC_CAP2_FULL_PWR_CYCLE (1 << 2) /* Can do full power cycle */
#define MMC_CAP2_NO_MULTI_READ (1 << 3) /* Multiblock reads don't work */
#define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */ #define MMC_CAP2_HS200_1_8V_SDR (1 << 5) /* can support */
#define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */ #define MMC_CAP2_HS200_1_2V_SDR (1 << 6) /* can support */
#define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \ #define MMC_CAP2_HS200 (MMC_CAP2_HS200_1_8V_SDR | \
@ -365,6 +372,9 @@ struct mmc_host {
unsigned int slotno; /* used for sdio acpi binding */ unsigned int slotno; /* used for sdio acpi binding */
int dsr_req; /* DSR value is valid */
u32 dsr; /* optional driver stage (DSR) value */
unsigned long private[0] ____cacheline_aligned; unsigned long private[0] ____cacheline_aligned;
}; };

View File

@ -53,6 +53,11 @@
#define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */ #define MMC_SEND_TUNING_BLOCK 19 /* adtc R1 */
#define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */ #define MMC_SEND_TUNING_BLOCK_HS200 21 /* adtc R1 */
#define MMC_TUNING_BLK_PATTERN_4BIT_SIZE 64
#define MMC_TUNING_BLK_PATTERN_8BIT_SIZE 128
extern const u8 tuning_blk_pattern_4bit[MMC_TUNING_BLK_PATTERN_4BIT_SIZE];
extern const u8 tuning_blk_pattern_8bit[MMC_TUNING_BLK_PATTERN_8BIT_SIZE];
/* class 3 */ /* class 3 */
#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ #define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */
@ -281,6 +286,7 @@ struct _mmc_csd {
#define EXT_CSD_EXP_EVENTS_CTRL 56 /* R/W, 2 bytes */ #define EXT_CSD_EXP_EVENTS_CTRL 56 /* R/W, 2 bytes */
#define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */ #define EXT_CSD_DATA_SECTOR_SIZE 61 /* R */
#define EXT_CSD_GP_SIZE_MULT 143 /* R/W */ #define EXT_CSD_GP_SIZE_MULT 143 /* R/W */
#define EXT_CSD_PARTITION_SETTING_COMPLETED 155 /* R/W */
#define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */ #define EXT_CSD_PARTITION_ATTRIBUTE 156 /* R/W */
#define EXT_CSD_PARTITION_SUPPORT 160 /* RO */ #define EXT_CSD_PARTITION_SUPPORT 160 /* RO */
#define EXT_CSD_HPI_MGMT 161 /* R/W */ #define EXT_CSD_HPI_MGMT 161 /* R/W */
@ -349,6 +355,7 @@ struct _mmc_csd {
#define EXT_CSD_PART_CONFIG_ACC_RPMB (0x3) #define EXT_CSD_PART_CONFIG_ACC_RPMB (0x3)
#define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4) #define EXT_CSD_PART_CONFIG_ACC_GP0 (0x4)
#define EXT_CSD_PART_SETTING_COMPLETED (0x1)
#define EXT_CSD_PART_SUPPORT_PART_EN (0x1) #define EXT_CSD_PART_SUPPORT_PART_EN (0x1)
#define EXT_CSD_CMD_SET_NORMAL (1<<0) #define EXT_CSD_CMD_SET_NORMAL (1<<0)

View File

@ -98,6 +98,8 @@ struct sdhci_host {
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6) #define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
/* Controller does not support DDR50 */ /* Controller does not support DDR50 */
#define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7) #define SDHCI_QUIRK2_BROKEN_DDR50 (1<<7)
/* Stop command (CMD12) can set Transfer Complete when not using MMC_RSP_BUSY */
#define SDHCI_QUIRK2_STOP_WITH_TC (1<<8)
int irq; /* Device IRQ */ int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */ void __iomem *ioaddr; /* Mapped address */
@ -146,6 +148,7 @@ struct sdhci_host {
struct mmc_command *cmd; /* Current command */ struct mmc_command *cmd; /* Current command */
struct mmc_data *data; /* Current data request */ struct mmc_data *data; /* Current data request */
unsigned int data_early:1; /* Data finished before cmd */ unsigned int data_early:1; /* Data finished before cmd */
unsigned int busy_handle:1; /* Handling the order of Busy-end */
struct sg_mapping_iter sg_miter; /* SG state for PIO */ struct sg_mapping_iter sg_miter; /* SG state for PIO */
unsigned int blocks; /* remaining PIO blocks */ unsigned int blocks; /* remaining PIO blocks */

View File

@ -24,7 +24,10 @@ void mmc_gpio_free_cd(struct mmc_host *host);
int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
unsigned int idx, bool override_active_level, unsigned int idx, bool override_active_level,
unsigned int debounce); unsigned int debounce, bool *gpio_invert);
int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
unsigned int idx, bool override_active_level,
unsigned int debounce, bool *gpio_invert);
void mmc_gpiod_free_cd(struct mmc_host *host); void mmc_gpiod_free_cd(struct mmc_host *host);
void mmc_gpiod_request_cd_irq(struct mmc_host *host); void mmc_gpiod_request_cd_irq(struct mmc_host *host);