forked from Minki/linux
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:
commit
f43b179bbd
@ -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"
|
||||||
|
@ -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>;
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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 },
|
||||||
{},
|
{},
|
||||||
|
136
drivers/mmc/host/dw_mmc-rockchip.c
Normal file
136
drivers/mmc/host/dw_mmc-rockchip.c
Normal 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");
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
177
drivers/mmc/host/mmci_qcom_dml.c
Normal file
177
drivers/mmc/host/mmci_qcom_dml.c
Normal 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;
|
||||||
|
}
|
31
drivers/mmc/host/mmci_qcom_dml.h
Normal file
31
drivers/mmc/host/mmci_qcom_dml.h
Normal 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__ */
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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),
|
||||||
},
|
},
|
||||||
|
@ -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),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
|
@ -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 = {
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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),
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -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,
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 = {
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user