aboutsummaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/core/block.c13
-rw-r--r--drivers/mmc/core/bus.c4
-rw-r--r--drivers/mmc/core/core.c11
-rw-r--r--drivers/mmc/core/core.h15
-rw-r--r--drivers/mmc/core/host.c6
-rw-r--r--drivers/mmc/core/mmc_test.c5
-rw-r--r--drivers/mmc/core/pwrseq.c2
-rw-r--r--drivers/mmc/core/sd.c11
-rw-r--r--drivers/mmc/core/sdio_bus.c17
-rw-r--r--drivers/mmc/core/sdio_cis.c12
-rw-r--r--drivers/mmc/host/Kconfig81
-rw-r--r--drivers/mmc/host/Makefile4
-rw-r--r--drivers/mmc/host/alcor.c5
-rw-r--r--drivers/mmc/host/atmel-mci.c9
-rw-r--r--drivers/mmc/host/au1xmmc.c8
-rw-r--r--drivers/mmc/host/bcm2835.c12
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c41
-rw-r--r--drivers/mmc/host/dw_mmc.c7
-rw-r--r--drivers/mmc/host/jz4740_mmc.c10
-rw-r--r--drivers/mmc/host/litex_mmc.c1
-rw-r--r--drivers/mmc/host/meson-gx-mmc.c27
-rw-r--r--drivers/mmc/host/mmc_hsq.c40
-rw-r--r--drivers/mmc/host/mmc_hsq.h5
-rw-r--r--drivers/mmc/host/mmc_spi.c8
-rw-r--r--drivers/mmc/host/mmci.c4
-rw-r--r--drivers/mmc/host/moxart-mmc.c4
-rw-r--r--drivers/mmc/host/mtk-sd.c38
-rw-r--r--drivers/mmc/host/mxcmmc.c4
-rw-r--r--drivers/mmc/host/omap_hsmmc.c4
-rw-r--r--drivers/mmc/host/pxamci.c7
-rw-r--r--drivers/mmc/host/renesas_sdhi.h16
-rw-r--r--drivers/mmc/host/renesas_sdhi_core.c32
-rw-r--r--drivers/mmc/host/renesas_sdhi_internal_dmac.c86
-rw-r--r--drivers/mmc/host/renesas_sdhi_sys_dmac.c11
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c9
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c11
-rw-r--r--drivers/mmc/host/s3cmci.c1777
-rw-r--r--drivers/mmc/host/s3cmci.h75
-rw-r--r--drivers/mmc/host/sdhci-acpi.c4
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c2
-rw-r--r--drivers/mmc/host/sdhci-brcmstb.c2
-rw-r--r--drivers/mmc/host/sdhci-cns3xxx.c113
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c109
-rw-r--r--drivers/mmc/host/sdhci-msm.c13
-rw-r--r--drivers/mmc/host/sdhci-of-arasan.c69
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c11
-rw-r--r--drivers/mmc/host/sdhci-omap.c2
-rw-r--r--drivers/mmc/host/sdhci-pci-core.c14
-rw-r--r--drivers/mmc/host/sdhci-pci-gli.c11
-rw-r--r--drivers/mmc/host/sdhci-pci-o2micro.c3
-rw-r--r--drivers/mmc/host/sdhci-sprd.c16
-rw-r--r--drivers/mmc/host/sdhci-tegra.c77
-rw-r--r--drivers/mmc/host/sdhci.c85
-rw-r--r--drivers/mmc/host/sdhci.h4
-rw-r--r--drivers/mmc/host/sdhci_am654.c2
-rw-r--r--drivers/mmc/host/sdhci_f_sdh30.c31
-rw-r--r--drivers/mmc/host/sdhci_f_sdh30.h3
-rw-r--r--drivers/mmc/host/sunplus-mmc.c1000
-rw-r--r--drivers/mmc/host/sunxi-mmc.c8
-rw-r--r--drivers/mmc/host/tifm_sd.c28
-rw-r--r--drivers/mmc/host/tmio_mmc.c227
-rw-r--r--drivers/mmc/host/tmio_mmc.h15
-rw-r--r--drivers/mmc/host/tmio_mmc_core.c18
-rw-r--r--drivers/mmc/host/toshsd.c6
-rw-r--r--drivers/mmc/host/via-sdmmc.c4
-rw-r--r--drivers/mmc/host/vub300.c13
-rw-r--r--drivers/mmc/host/wbsd.c22
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c6
68 files changed, 1677 insertions, 2673 deletions
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
index db6d8a099910..20da7ed43e6d 100644
--- a/drivers/mmc/core/block.c
+++ b/drivers/mmc/core/block.c
@@ -514,19 +514,6 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
if (idata->ic.data_timeout_ns)
data.timeout_ns = idata->ic.data_timeout_ns;
- if ((cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) {
- /*
- * Pretend this is a data transfer and rely on the
- * host driver to compute timeout. When all host
- * drivers support cmd.cmd_timeout for R1B, this
- * can be changed to:
- *
- * mrq.data = NULL;
- * cmd.cmd_timeout = idata->ic.cmd_timeout_ms;
- */
- data.timeout_ns = idata->ic.cmd_timeout_ms * 1000000;
- }
-
mrq.data = &data;
}
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index d8762fa3d5cd..36679f4e9acc 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -359,9 +359,7 @@ int mmc_add_card(struct mmc_card *card)
uhs_bus_speed_mode, type, card->rca);
}
-#ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs(card);
-#endif
card->dev.of_node = mmc_of_find_child_device(card->host, 0);
device_enable_async_suspend(&card->dev);
@@ -383,9 +381,7 @@ void mmc_remove_card(struct mmc_card *card)
{
struct mmc_host *host = card->host;
-#ifdef CONFIG_DEBUG_FS
mmc_remove_card_debugfs(card);
-#endif
if (mmc_card_present(card)) {
if (mmc_host_is_spi(card->host)) {
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index de1cc9e1ae57..426c7f66b349 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -56,7 +56,7 @@ static const unsigned freqs[] = { 400000, 300000, 200000, 100000 };
/*
* Enabling software CRCs on the data blocks can be a significant (30%)
* performance cost, and for other reasons may not always be desired.
- * So we allow it it to be disabled.
+ * So we allow it to be disabled.
*/
bool use_spi_crc = 1;
module_param(use_spi_crc, bool, 0);
@@ -97,8 +97,8 @@ static void mmc_should_fail_request(struct mmc_host *host,
!should_fail(&host->fail_mmc_request, data->blksz * data->blocks))
return;
- data->error = data_errors[prandom_u32_max(ARRAY_SIZE(data_errors))];
- data->bytes_xfered = prandom_u32_max(data->bytes_xfered >> 9) << 9;
+ data->error = data_errors[get_random_u32_below(ARRAY_SIZE(data_errors))];
+ data->bytes_xfered = get_random_u32_below(data->bytes_xfered >> 9) << 9;
}
#else /* CONFIG_FAIL_MMC_REQUEST */
@@ -142,8 +142,7 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
int err = cmd->error;
/* Flag re-tuning needed on CRC errors */
- if (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
- cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 &&
+ if (!mmc_op_tuning(cmd->opcode) &&
!host->retune_crc_disable &&
(err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) ||
(mrq->data && mrq->data->error == -EILSEQ) ||
@@ -527,7 +526,7 @@ EXPORT_SYMBOL(mmc_cqe_post_req);
* mmc_cqe_recovery - Recover from CQE errors.
* @host: MMC host to recover
*
- * Recovery consists of stopping CQE, stopping eMMC, discarding the queue in
+ * Recovery consists of stopping CQE, stopping eMMC, discarding the queue
* in eMMC, and discarding the queue in CQE. CQE must call
* mmc_cqe_request_done() on all requests. An error is returned if the eMMC
* fails to discard its queue.
diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
index f5f3f623ea49..37091a6589ed 100644
--- a/drivers/mmc/core/core.h
+++ b/drivers/mmc/core/core.h
@@ -86,11 +86,26 @@ int mmc_attach_sdio(struct mmc_host *host);
extern bool use_spi_crc;
/* Debugfs information for hosts and cards */
+#ifdef CONFIG_DEBUG_FS
void mmc_add_host_debugfs(struct mmc_host *host);
void mmc_remove_host_debugfs(struct mmc_host *host);
void mmc_add_card_debugfs(struct mmc_card *card);
void mmc_remove_card_debugfs(struct mmc_card *card);
+#else
+static inline void mmc_add_host_debugfs(struct mmc_host *host)
+{
+}
+static inline void mmc_remove_host_debugfs(struct mmc_host *host)
+{
+}
+static inline void mmc_add_card_debugfs(struct mmc_card *card)
+{
+}
+static inline void mmc_remove_card_debugfs(struct mmc_card *card)
+{
+}
+#endif
int mmc_execute_tuning(struct mmc_card *card);
int mmc_hs200_to_hs400(struct mmc_card *card);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index b89dca1f15e9..d17eda753b7e 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -269,7 +269,7 @@ EXPORT_SYMBOL(mmc_of_parse_clk_phase);
* @host: host whose properties should be parsed.
*
* To keep the rest of the MMC subsystem unaware of whether DT has been
- * used to to instantiate and configure this host instance or not, we
+ * used to instantiate and configure this host instance or not, we
* parse the properties and set respective generic mmc-host flags and
* parameters.
*/
@@ -629,9 +629,7 @@ int mmc_add_host(struct mmc_host *host)
led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
-#ifdef CONFIG_DEBUG_FS
mmc_add_host_debugfs(host);
-#endif
mmc_start_host(host);
return 0;
@@ -651,9 +649,7 @@ void mmc_remove_host(struct mmc_host *host)
{
mmc_stop_host(host);
-#ifdef CONFIG_DEBUG_FS
mmc_remove_host_debugfs(host);
-#endif
device_del(&host->class_dev);
diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c
index 155ce2bdfe62..156d34b2ed4d 100644
--- a/drivers/mmc/core/mmc_test.c
+++ b/drivers/mmc/core/mmc_test.c
@@ -932,7 +932,6 @@ static int mmc_test_transfer(struct mmc_test_card *test,
unsigned blocks, unsigned blksz, int write)
{
int ret, i;
- unsigned long flags;
if (write) {
for (i = 0; i < blocks * blksz; i++)
@@ -940,9 +939,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
} else {
memset(test->scratch, 0, BUFFER_SIZE);
}
- local_irq_save(flags);
sg_copy_from_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
- local_irq_restore(flags);
ret = mmc_test_set_blksize(test, blksz);
if (ret)
@@ -987,9 +984,7 @@ static int mmc_test_transfer(struct mmc_test_card *test,
return RESULT_FAIL;
}
} else {
- local_irq_save(flags);
sg_copy_to_buffer(sg, sg_len, test->scratch, BUFFER_SIZE);
- local_irq_restore(flags);
for (i = 0; i < blocks * blksz; i++) {
if (test->scratch[i] != (u8)i)
return RESULT_FAIL;
diff --git a/drivers/mmc/core/pwrseq.c b/drivers/mmc/core/pwrseq.c
index ef675f364bf0..2374669b588a 100644
--- a/drivers/mmc/core/pwrseq.c
+++ b/drivers/mmc/core/pwrseq.c
@@ -29,7 +29,7 @@ int mmc_pwrseq_alloc(struct mmc_host *host)
mutex_lock(&pwrseq_list_mutex);
list_for_each_entry(p, &pwrseq_list, pwrseq_node) {
- if (p->dev->of_node == np) {
+ if (device_match_of_node(p->dev, np)) {
if (!try_module_get(p->owner))
dev_err(host->parent,
"increasing module refcount failed\n");
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 3662bf5320ce..72b664ed90cf 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1259,7 +1259,7 @@ static int sd_read_ext_regs(struct mmc_card *card)
*/
err = sd_read_ext_reg(card, 0, 0, 0, 512, gen_info_buf);
if (err) {
- pr_warn("%s: error %d reading general info of SD ext reg\n",
+ pr_err("%s: error %d reading general info of SD ext reg\n",
mmc_hostname(card->host), err);
goto out;
}
@@ -1273,7 +1273,12 @@ static int sd_read_ext_regs(struct mmc_card *card)
/* Number of extensions to be find. */
num_ext = gen_info_buf[4];
- /* We support revision 0, but limit it to 512 bytes for simplicity. */
+ /*
+ * We only support revision 0 and limit it to 512 bytes for simplicity.
+ * No matter what, let's return zero to allow us to continue using the
+ * card, even if we can't support the features from the SD function
+ * extensions registers.
+ */
if (rev != 0 || len > 512) {
pr_warn("%s: non-supported SD ext reg layout\n",
mmc_hostname(card->host));
@@ -1288,7 +1293,7 @@ static int sd_read_ext_regs(struct mmc_card *card)
for (i = 0; i < num_ext; i++) {
err = sd_parse_ext_reg(card, gen_info_buf, &next_ext_addr);
if (err) {
- pr_warn("%s: error %d parsing SD ext reg\n",
+ pr_err("%s: error %d parsing SD ext reg\n",
mmc_hostname(card->host), err);
goto out;
}
diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
index babf21a0adeb..f191a2a76f3b 100644
--- a/drivers/mmc/core/sdio_bus.c
+++ b/drivers/mmc/core/sdio_bus.c
@@ -294,6 +294,12 @@ static void sdio_release_func(struct device *dev)
if (!(func->card->quirks & MMC_QUIRK_NONSTD_SDIO))
sdio_free_func_cis(func);
+ /*
+ * We have now removed the link to the tuples in the
+ * card structure, so remove the reference.
+ */
+ put_device(&func->card->dev);
+
kfree(func->info);
kfree(func->tmpbuf);
kfree(func);
@@ -324,6 +330,12 @@ struct sdio_func *sdio_alloc_func(struct mmc_card *card)
device_initialize(&func->dev);
+ /*
+ * We may link to tuples in the card structure,
+ * we need make sure we have a reference to it.
+ */
+ get_device(&func->card->dev);
+
func->dev.parent = &card->dev;
func->dev.bus = &sdio_bus_type;
func->dev.release = sdio_release_func;
@@ -377,10 +389,9 @@ int sdio_add_func(struct sdio_func *func)
*/
void sdio_remove_func(struct sdio_func *func)
{
- if (!sdio_func_present(func))
- return;
+ if (sdio_func_present(func))
+ device_del(&func->dev);
- device_del(&func->dev);
of_node_put(func->dev.of_node);
put_device(&func->dev);
}
diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
index a705ba6eff5b..afaa6cab1adc 100644
--- a/drivers/mmc/core/sdio_cis.c
+++ b/drivers/mmc/core/sdio_cis.c
@@ -404,12 +404,6 @@ int sdio_read_func_cis(struct sdio_func *func)
return ret;
/*
- * Since we've linked to tuples in the card structure,
- * we must make sure we have a reference to it.
- */
- get_device(&func->card->dev);
-
- /*
* Vendor/device id is optional for function CIS, so
* copy it from the card structure as needed.
*/
@@ -434,11 +428,5 @@ void sdio_free_func_cis(struct sdio_func *func)
}
func->tuples = NULL;
-
- /*
- * We have now removed the link to the tuples in the
- * card structure, so remove the reference.
- */
- put_device(&func->card->dev);
}
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index fb1062a6394c..e13b0b0b8ebb 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -14,6 +14,15 @@ config MMC_DEBUG
added host drivers please don't invent their private macro for
debugging.
+config MMC_SUNPLUS
+ tristate "Sunplus SP7021 MMC Controller"
+ depends on ARCH_SUNPLUS || COMPILE_TEST
+ help
+ If you say yes here, you will get support for eMMC host interface
+ on Sunplus SoCs.
+
+ If unsure, say N
+
config MMC_ARMMMCI
tristate "ARM AMBA Multimedia Card Interface support"
depends on ARM_AMBA
@@ -253,17 +262,6 @@ config MMC_SDHCI_CADENCE
If unsure, say N.
-config MMC_SDHCI_CNS3XXX
- tristate "SDHCI support on the Cavium Networks CNS3xxx SoC"
- depends on ARCH_CNS3XXX || COMPILE_TEST
- depends on MMC_SDHCI_PLTFM
- help
- This selects the SDHCI support for CNS3xxx System-on-Chip devices.
-
- If you have a controller with this interface, say Y or M here.
-
- If unsure, say N.
-
config MMC_SDHCI_ESDHC_MCF
tristate "SDHCI support for the Freescale eSDHC ColdFire controller"
depends on M5441x
@@ -323,9 +321,8 @@ config MMC_SDHCI_S3C
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
help
This selects the Secure Digital Host Controller Interface (SDHCI)
- often referrered to as the HSMMC block in some of the Samsung S3C
- (S3C2416, S3C2443, S3C6410), S5Pv210 and Exynos (Exynso4210,
- Exynos4412) SoCs.
+ often referrered to as the HSMMC block in some of the Samsung
+ S3C6410, S5Pv210 and Exynos (Exynso4210, Exynos4412) SoCs.
If you have a controller with this interface (thereforeyou build for
such Samsung SoC), say Y or M here.
@@ -491,7 +488,6 @@ config MMC_SDHCI_ST
config MMC_OMAP
tristate "TI OMAP Multimedia Card Interface support"
depends on ARCH_OMAP
- depends on TPS65010 || !MACH_OMAP_H2
help
This selects the TI OMAP Multimedia card Interface.
If you have an OMAP board with a Multimedia Card slot,
@@ -631,49 +627,6 @@ config MMC_SPI
If unsure, or if your system has no SPI master driver, say N.
-config MMC_S3C
- tristate "Samsung S3C SD/MMC Card Interface support"
- depends on ARCH_S3C24XX || COMPILE_TEST
- depends on S3C24XX_DMAC || COMPILE_TEST
- help
- This selects a driver for the MCI interface found in
- Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
- If you have a board based on one of those and a MMC/SD
- slot, say Y or M here.
-
- If unsure, say N.
-
-config MMC_S3C_HW_SDIO_IRQ
- bool "Hardware support for SDIO IRQ"
- depends on MMC_S3C
- help
- Enable the hardware support for SDIO interrupts instead of using
- the generic polling code.
-
-choice
- prompt "Samsung S3C SD/MMC transfer code"
- depends on MMC_S3C
-
-config MMC_S3C_PIO
- bool "Use PIO transfers only"
- help
- Use PIO to transfer data between memory and the hardware.
-
- PIO is slower than DMA as it requires CPU instructions to
- move the data. This has been the traditional default for
- the S3C MCI driver.
-
-config MMC_S3C_DMA
- bool "Use DMA transfers only"
- help
- Use DMA to transfer data between memory and the hardware.
-
- Currently, the DMA support in this driver seems to not be
- working properly and needs to be debugged before this
- option is useful.
-
-endchoice
-
config MMC_SDRICOH_CS
tristate "MMC/SD driver for Ricoh Bay1Controllers"
depends on PCI && PCMCIA
@@ -701,14 +654,6 @@ config MMC_SDHCI_SPRD
config MMC_TMIO_CORE
tristate
-config MMC_TMIO
- tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support"
- depends on MFD_TMIO || MFD_ASIC3 || COMPILE_TEST
- select MMC_TMIO_CORE
- help
- This provides support for the SD/MMC cell found in TC6393XB,
- T7L66XB and also HTC ASIC3
-
config MMC_SDHI
tristate "Renesas SDHI SD/SDIO controller support"
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
@@ -1040,10 +985,10 @@ config MMC_SDHCI_MICROCHIP_PIC32
config MMC_SDHCI_BRCMSTB
tristate "Broadcom SDIO/SD/MMC support"
- depends on ARCH_BRCMSTB || BMIPS_GENERIC
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
depends on MMC_SDHCI_PLTFM
select MMC_CQHCI
- default y
+ default ARCH_BRCMSTB || BMIPS_GENERIC
help
This selects support for the SDIO/SD/MMC Host Controller on
Broadcom STB SoCs.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 4e4ceb32c4b4..b498c17cd124 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -34,9 +34,7 @@ obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o
obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o
obj-$(CONFIG_MMC_SPI) += mmc_spi.o
obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o
-obj-$(CONFIG_MMC_S3C) += s3cmci.o
obj-$(CONFIG_MMC_SDRICOH_CS) += sdricoh_cs.o
-obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o
obj-$(CONFIG_MMC_TMIO_CORE) += tmio_mmc_core.o
obj-$(CONFIG_MMC_SDHI) += renesas_sdhi_core.o
obj-$(CONFIG_MMC_SDHI_SYS_DMAC) += renesas_sdhi_sys_dmac.o
@@ -77,7 +75,6 @@ obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o
obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o
obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o
-obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o
obj-$(CONFIG_MMC_SDHCI_ESDHC_MCF) += sdhci-esdhc-mcf.o
obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o
obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o
@@ -97,6 +94,7 @@ obj-$(CONFIG_MMC_SDHCI_MICROCHIP_PIC32) += sdhci-pic32.o
obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o
obj-$(CONFIG_MMC_SDHCI_SPRD) += sdhci-sprd.o
+obj-$(CONFIG_MMC_SUNPLUS) += sunplus-mmc.o
obj-$(CONFIG_MMC_CQHCI) += cqhci.o
cqhci-y += cqhci-core.o
cqhci-$(CONFIG_MMC_CRYPTO) += cqhci-crypto.o
diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c
index bfb8efeb7eb8..d01df01d4b4d 100644
--- a/drivers/mmc/host/alcor.c
+++ b/drivers/mmc/host/alcor.c
@@ -1114,7 +1114,10 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev)
alcor_hw_init(host);
dev_set_drvdata(&pdev->dev, host);
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto free_host;
+
return 0;
free_host:
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index 91d52ba7a39f..bb9bbf1c927b 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -2222,6 +2222,7 @@ static int atmci_init_slot(struct atmel_mci *host,
{
struct mmc_host *mmc;
struct atmel_mci_slot *slot;
+ int ret;
mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), &host->pdev->dev);
if (!mmc)
@@ -2305,11 +2306,13 @@ static int atmci_init_slot(struct atmel_mci *host,
host->slot[id] = slot;
mmc_regulator_get_supply(mmc);
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret) {
+ mmc_free_host(mmc);
+ return ret;
+ }
if (gpio_is_valid(slot->detect_pin)) {
- int ret;
-
timer_setup(&slot->detect_timer, atmci_detect_change, 0);
ret = request_irq(gpio_to_irq(slot->detect_pin),
diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c
index c88b039dc9fb..82dd0ae40305 100644
--- a/drivers/mmc/host/au1xmmc.c
+++ b/drivers/mmc/host/au1xmmc.c
@@ -388,7 +388,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
/* This is the pointer to the data buffer */
sg = &data->sg[host->pio.index];
- sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset;
+ sg_ptr = kmap_local_page(sg_page(sg)) + sg->offset + host->pio.offset;
/* This is the space left inside the buffer */
sg_len = data->sg[host->pio.index].length - host->pio.offset;
@@ -409,7 +409,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host)
__raw_writel((unsigned long)val, HOST_TXPORT(host));
wmb(); /* drain writebuffer */
}
- kunmap_atomic(sg_ptr);
+ kunmap_local(sg_ptr);
host->pio.len -= count;
host->pio.offset += count;
@@ -446,7 +446,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
if (host->pio.index < host->dma.len) {
sg = &data->sg[host->pio.index];
- sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset;
+ sg_ptr = kmap_local_page(sg_page(sg)) + sg->offset + host->pio.offset;
/* This is the space left inside the buffer */
sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;
@@ -488,7 +488,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
sg_ptr[count] = (unsigned char)(val & 0xFF);
}
if (sg_ptr)
- kunmap_atomic(sg_ptr);
+ kunmap_local(sg_ptr);
host->pio.len -= count;
host->pio.offset += count;
diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c
index 641ab4f42125..8648f7e63ca1 100644
--- a/drivers/mmc/host/bcm2835.c
+++ b/drivers/mmc/host/bcm2835.c
@@ -327,7 +327,6 @@ static void bcm2835_dma_complete(void *param)
static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
{
- unsigned long flags;
size_t blksize;
unsigned long wait_max;
@@ -335,8 +334,6 @@ static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
wait_max = jiffies + msecs_to_jiffies(500);
- local_irq_save(flags);
-
while (blksize) {
int copy_words;
u32 hsts = 0;
@@ -421,8 +418,6 @@ static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read)
}
sg_miter_stop(&host->sg_miter);
-
- local_irq_restore(flags);
}
static void bcm2835_transfer_pio(struct bcm2835_host *host)
@@ -1068,7 +1063,6 @@ static void bcm2835_dma_complete_work(struct work_struct *work)
}
if (host->drain_words) {
- unsigned long flags;
void *page;
u32 *buf;
@@ -1076,8 +1070,7 @@ static void bcm2835_dma_complete_work(struct work_struct *work)
host->drain_page += host->drain_offset >> PAGE_SHIFT;
host->drain_offset &= ~PAGE_MASK;
}
- local_irq_save(flags);
- page = kmap_atomic(host->drain_page);
+ page = kmap_local_page(host->drain_page);
buf = page + host->drain_offset;
while (host->drain_words) {
@@ -1088,8 +1081,7 @@ static void bcm2835_dma_complete_work(struct work_struct *work)
host->drain_words--;
}
- kunmap_atomic(page);
- local_irq_restore(flags);
+ kunmap_local(page);
}
bcm2835_finish_data(host);
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 9901208be797..13e55cff8237 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -17,10 +17,16 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/of.h>
+#include <linux/mfd/altera-sysmgr.h>
+#include <linux/regmap.h>
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
+#define SOCFPGA_DW_MMC_CLK_PHASE_STEP 45
+#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel, reg_shift) \
+ ((((smplsel) & 0x7) << reg_shift) | (((drvsel) & 0x7) << 0))
+
int dw_mci_pltfm_register(struct platform_device *pdev,
const struct dw_mci_drv_data *drv_data)
{
@@ -62,9 +68,42 @@ const struct dev_pm_ops dw_mci_pltfm_pmops = {
};
EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
+static int dw_mci_socfpga_priv_init(struct dw_mci *host)
+{
+ struct device_node *np = host->dev->of_node;
+ struct regmap *sys_mgr_base_addr;
+ u32 clk_phase[2] = {0}, reg_offset, reg_shift;
+ int i, rc, hs_timing;
+
+ rc = of_property_read_variable_u32_array(np, "clk-phase-sd-hs", &clk_phase[0], 2, 0);
+ if (rc < 0)
+ return 0;
+
+ sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon");
+ if (IS_ERR(sys_mgr_base_addr)) {
+ dev_warn(host->dev, "clk-phase-sd-hs was specified, but failed to find altr,sys-mgr regmap!\n");
+ return 0;
+ }
+
+ of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, &reg_offset);
+ of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, &reg_shift);
+
+ for (i = 0; i < ARRAY_SIZE(clk_phase); i++)
+ clk_phase[i] /= SOCFPGA_DW_MMC_CLK_PHASE_STEP;
+
+ hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1], reg_shift);
+ regmap_write(sys_mgr_base_addr, reg_offset, hs_timing);
+
+ return 0;
+}
+
+static const struct dw_mci_drv_data socfpga_drv_data = {
+ .init = dw_mci_socfpga_priv_init,
+};
+
static const struct of_device_id dw_mci_pltfm_match[] = {
{ .compatible = "snps,dw-mshc", },
- { .compatible = "altr,socfpga-dw-mshc", },
+ { .compatible = "altr,socfpga-dw-mshc", .data = &socfpga_drv_data, },
{ .compatible = "img,pistachio-dw-mshc", },
{},
};
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index c78bbc22e0d1..829af2c98a44 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -334,8 +334,7 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd)
cmdr == MMC_READ_MULTIPLE_BLOCK ||
cmdr == MMC_WRITE_BLOCK ||
cmdr == MMC_WRITE_MULTIPLE_BLOCK ||
- cmdr == MMC_SEND_TUNING_BLOCK ||
- cmdr == MMC_SEND_TUNING_BLOCK_HS200 ||
+ mmc_op_tuning(cmdr) ||
cmdr == MMC_GEN_CMD) {
stop->opcode = MMC_STOP_TRANSMISSION;
stop->arg = 0;
@@ -1363,7 +1362,7 @@ static void __dw_mci_start_request(struct dw_mci *host,
* is just about to roll over.
*
* We do this whole thing under spinlock and only if the
- * command hasn't already completed (indicating the the irq
+ * command hasn't already completed (indicating the irq
* already ran so we don't want the timeout).
*/
spin_lock_irqsave(&host->irq_lock, irqflags);
@@ -1858,7 +1857,7 @@ static void dw_mci_start_fault_timer(struct dw_mci *host)
* Try to inject the error at random points during the data transfer.
*/
hrtimer_start(&host->fault_timer,
- ms_to_ktime(prandom_u32_max(25)),
+ ms_to_ktime(get_random_u32_below(25)),
HRTIMER_MODE_REL);
}
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index dc2db9c185ea..eda1e2ddcaca 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -1053,6 +1053,16 @@ static int jz4740_mmc_probe(struct platform_device* pdev)
mmc->ops = &jz4740_mmc_ops;
if (!mmc->f_max)
mmc->f_max = JZ_MMC_CLK_RATE;
+
+ /*
+ * There seems to be a problem with this driver on the JZ4760 and
+ * JZ4760B SoCs. There, when using the maximum rate supported (50 MHz),
+ * the communication fails with many SD cards.
+ * Until this bug is sorted out, limit the maximum rate to 24 MHz.
+ */
+ if (host->version == JZ_MMC_JZ4760 && mmc->f_max > JZ_MMC_CLK_RATE)
+ mmc->f_max = JZ_MMC_CLK_RATE;
+
mmc->f_min = mmc->f_max / 128;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c
index 6ba0d63b8c07..39c6707fdfdb 100644
--- a/drivers/mmc/host/litex_mmc.c
+++ b/drivers/mmc/host/litex_mmc.c
@@ -502,6 +502,7 @@ static int litex_mmc_irq_init(struct platform_device *pdev,
use_polling:
host->mmc->caps |= MMC_CAP_NEEDS_POLL;
+ host->irq = 0;
return 0;
}
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c
index df05e60bed9a..5c94ad4661ce 100644
--- a/drivers/mmc/host/meson-gx-mmc.c
+++ b/drivers/mmc/host/meson-gx-mmc.c
@@ -435,7 +435,8 @@ static int meson_mmc_clk_init(struct meson_host *host)
clk_reg |= FIELD_PREP(CLK_CORE_PHASE_MASK, CLK_PHASE_180);
clk_reg |= FIELD_PREP(CLK_TX_PHASE_MASK, CLK_PHASE_0);
clk_reg |= FIELD_PREP(CLK_RX_PHASE_MASK, CLK_PHASE_0);
- clk_reg |= CLK_IRQ_SDIO_SLEEP(host);
+ if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
+ clk_reg |= CLK_IRQ_SDIO_SLEEP(host);
writel(clk_reg, host->regs + SD_EMMC_CLOCK);
/* get the mux parents */
@@ -948,16 +949,18 @@ static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
{
struct meson_host *host = dev_id;
struct mmc_command *cmd;
- u32 status, raw_status;
+ u32 status, raw_status, irq_mask = IRQ_EN_MASK;
irqreturn_t ret = IRQ_NONE;
+ if (host->mmc->caps & MMC_CAP_SDIO_IRQ)
+ irq_mask |= IRQ_SDIO;
raw_status = readl(host->regs + SD_EMMC_STATUS);
- status = raw_status & (IRQ_EN_MASK | IRQ_SDIO);
+ status = raw_status & irq_mask;
if (!status) {
dev_dbg(host->dev,
- "Unexpected IRQ! irq_en 0x%08lx - status 0x%08x\n",
- IRQ_EN_MASK | IRQ_SDIO, raw_status);
+ "Unexpected IRQ! irq_en 0x%08x - status 0x%08x\n",
+ irq_mask, raw_status);
return IRQ_NONE;
}
@@ -1204,6 +1207,11 @@ static int meson_mmc_probe(struct platform_device *pdev)
goto free_host;
}
+ mmc->caps |= MMC_CAP_CMD23;
+
+ if (mmc->caps & MMC_CAP_SDIO_IRQ)
+ mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
+
host->data = (struct meson_mmc_data *)
of_device_get_match_data(&pdev->dev);
if (!host->data) {
@@ -1277,11 +1285,6 @@ static int meson_mmc_probe(struct platform_device *pdev)
spin_lock_init(&host->lock);
- mmc->caps |= MMC_CAP_CMD23;
-
- if (mmc->caps & MMC_CAP_SDIO_IRQ)
- mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD;
-
if (host->dram_access_quirk) {
/* Limit segments to 1 due to low available sram memory */
mmc->max_segs = 1;
@@ -1335,7 +1338,9 @@ static int meson_mmc_probe(struct platform_device *pdev)
}
mmc->ops = &meson_mmc_ops;
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto err_free_irq;
return 0;
diff --git a/drivers/mmc/host/mmc_hsq.c b/drivers/mmc/host/mmc_hsq.c
index 9d35453e7371..424dc7b07858 100644
--- a/drivers/mmc/host/mmc_hsq.c
+++ b/drivers/mmc/host/mmc_hsq.c
@@ -13,9 +13,6 @@
#include "mmc_hsq.h"
-#define HSQ_NUM_SLOTS 64
-#define HSQ_INVALID_TAG HSQ_NUM_SLOTS
-
static void mmc_hsq_retry_handler(struct work_struct *work)
{
struct mmc_hsq *hsq = container_of(work, struct mmc_hsq, retry_work);
@@ -73,7 +70,6 @@ static void mmc_hsq_pump_requests(struct mmc_hsq *hsq)
static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains)
{
- struct hsq_slot *slot;
int tag;
/*
@@ -82,29 +78,12 @@ static void mmc_hsq_update_next_tag(struct mmc_hsq *hsq, int remains)
*/
if (!remains) {
hsq->next_tag = HSQ_INVALID_TAG;
+ hsq->tail_tag = HSQ_INVALID_TAG;
return;
}
- /*
- * Increasing the next tag and check if the corresponding request is
- * available, if yes, then we found a candidate request.
- */
- if (++hsq->next_tag != HSQ_INVALID_TAG) {
- slot = &hsq->slot[hsq->next_tag];
- if (slot->mrq)
- return;
- }
-
- /* Othersie we should iterate all slots to find a available tag. */
- for (tag = 0; tag < HSQ_NUM_SLOTS; tag++) {
- slot = &hsq->slot[tag];
- if (slot->mrq)
- break;
- }
-
- if (tag == HSQ_NUM_SLOTS)
- tag = HSQ_INVALID_TAG;
-
+ tag = hsq->tag_slot[hsq->next_tag];
+ hsq->tag_slot[hsq->next_tag] = HSQ_INVALID_TAG;
hsq->next_tag = tag;
}
@@ -233,8 +212,14 @@ static int mmc_hsq_request(struct mmc_host *mmc, struct mmc_request *mrq)
* Set the next tag as current request tag if no available
* next tag.
*/
- if (hsq->next_tag == HSQ_INVALID_TAG)
+ if (hsq->next_tag == HSQ_INVALID_TAG) {
hsq->next_tag = tag;
+ hsq->tail_tag = tag;
+ hsq->tag_slot[hsq->tail_tag] = HSQ_INVALID_TAG;
+ } else {
+ hsq->tag_slot[hsq->tail_tag] = tag;
+ hsq->tail_tag = tag;
+ }
hsq->qcnt++;
@@ -339,8 +324,10 @@ static const struct mmc_cqe_ops mmc_hsq_ops = {
int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc)
{
+ int i;
hsq->num_slots = HSQ_NUM_SLOTS;
hsq->next_tag = HSQ_INVALID_TAG;
+ hsq->tail_tag = HSQ_INVALID_TAG;
hsq->slot = devm_kcalloc(mmc_dev(mmc), hsq->num_slots,
sizeof(struct hsq_slot), GFP_KERNEL);
@@ -351,6 +338,9 @@ int mmc_hsq_init(struct mmc_hsq *hsq, struct mmc_host *mmc)
hsq->mmc->cqe_private = hsq;
mmc->cqe_ops = &mmc_hsq_ops;
+ for (i = 0; i < HSQ_NUM_SLOTS; i++)
+ hsq->tag_slot[i] = HSQ_INVALID_TAG;
+
INIT_WORK(&hsq->retry_work, mmc_hsq_retry_handler);
spin_lock_init(&hsq->lock);
init_waitqueue_head(&hsq->wait_queue);
diff --git a/drivers/mmc/host/mmc_hsq.h b/drivers/mmc/host/mmc_hsq.h
index ffdd9cd172c3..1808024fc6c5 100644
--- a/drivers/mmc/host/mmc_hsq.h
+++ b/drivers/mmc/host/mmc_hsq.h
@@ -2,6 +2,9 @@
#ifndef LINUX_MMC_HSQ_H
#define LINUX_MMC_HSQ_H
+#define HSQ_NUM_SLOTS 64
+#define HSQ_INVALID_TAG HSQ_NUM_SLOTS
+
struct hsq_slot {
struct mmc_request *mrq;
};
@@ -17,6 +20,8 @@ struct mmc_hsq {
int next_tag;
int num_slots;
int qcnt;
+ int tail_tag;
+ int tag_slot[HSQ_NUM_SLOTS];
bool enabled;
bool waiting_for_idle;
diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
index 106dd204b1a7..cc333ad67cac 100644
--- a/drivers/mmc/host/mmc_spi.c
+++ b/drivers/mmc/host/mmc_spi.c
@@ -1437,7 +1437,7 @@ static int mmc_spi_probe(struct spi_device *spi)
status = mmc_add_host(mmc);
if (status != 0)
- goto fail_add_host;
+ goto fail_glue_init;
/*
* Index 0 is card detect
@@ -1445,7 +1445,7 @@ static int mmc_spi_probe(struct spi_device *spi)
*/
status = mmc_gpiod_request_cd(mmc, NULL, 0, false, 1000);
if (status == -EPROBE_DEFER)
- goto fail_add_host;
+ goto fail_gpiod_request;
if (!status) {
/*
* The platform has a CD GPIO signal that may support
@@ -1460,7 +1460,7 @@ static int mmc_spi_probe(struct spi_device *spi)
/* Index 1 is write protect/read only */
status = mmc_gpiod_request_ro(mmc, NULL, 1, 0);
if (status == -EPROBE_DEFER)
- goto fail_add_host;
+ goto fail_gpiod_request;
if (!status)
has_ro = true;
@@ -1474,7 +1474,7 @@ static int mmc_spi_probe(struct spi_device *spi)
? ", cd polling" : "");
return 0;
-fail_add_host:
+fail_gpiod_request:
mmc_remove_host(mmc);
fail_glue_init:
mmc_spi_dma_free(host);
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 012aa85489d8..b9e5dfe74e5c 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -2256,7 +2256,9 @@ static int mmci_probe(struct amba_device *dev,
pm_runtime_set_autosuspend_delay(&dev->dev, 50);
pm_runtime_use_autosuspend(&dev->dev);
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto clk_disable;
pm_runtime_put(&dev->dev);
return 0;
diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c
index dfc3ffd5b1f8..52ed30f2d9f4 100644
--- a/drivers/mmc/host/moxart-mmc.c
+++ b/drivers/mmc/host/moxart-mmc.c
@@ -665,7 +665,9 @@ static int moxart_probe(struct platform_device *pdev)
goto out;
dev_set_drvdata(dev, mmc);
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto out;
dev_dbg(dev, "IRQ=%d, FIFO is %d bytes\n", irq, host->fifo_width);
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 26bc59b5a7cc..edade0e54a0c 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -452,6 +452,7 @@ struct msdc_host {
struct clk *bus_clk; /* bus clock which used to access register */
struct clk *src_clk_cg; /* msdc source clock control gate */
struct clk *sys_clk_cg; /* msdc subsys clock control gate */
+ struct clk *crypto_clk; /* msdc crypto clock control gate */
struct clk_bulk_data bulk_clks[MSDC_NR_CLOCKS];
u32 mclk; /* mmc subsystem clock frequency */
u32 src_clk_freq; /* source clock frequency */
@@ -552,6 +553,19 @@ static const struct mtk_mmc_compatible mt7622_compat = {
.support_64g = false,
};
+static const struct mtk_mmc_compatible mt7986_compat = {
+ .clk_div_bits = 12,
+ .recheck_sdio_irq = true,
+ .hs400_tune = false,
+ .pad_tune_reg = MSDC_PAD_TUNE0,
+ .async_fifo = true,
+ .data_tune = true,
+ .busy_check = true,
+ .stop_clk_fix = true,
+ .enhance_rx = true,
+ .support_64g = true,
+};
+
static const struct mtk_mmc_compatible mt8135_compat = {
.clk_div_bits = 8,
.recheck_sdio_irq = true,
@@ -609,6 +623,7 @@ static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt6795-mmc", .data = &mt6795_compat},
{ .compatible = "mediatek,mt7620-mmc", .data = &mt7620_compat},
{ .compatible = "mediatek,mt7622-mmc", .data = &mt7622_compat},
+ { .compatible = "mediatek,mt7986-mmc", .data = &mt7986_compat},
{ .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat},
{ .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat},
{ .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat},
@@ -735,7 +750,7 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma,
else
bd[j].bd_info &= ~BDMA_DESC_EOL;
- /* checksume need to clear first */
+ /* checksum need to clear first */
bd[j].bd_info &= ~BDMA_DESC_CHECKSUM;
bd[j].bd_info |= msdc_dma_calcs((u8 *)(&bd[j]), 16) << 8;
}
@@ -826,6 +841,7 @@ static void msdc_set_busy_timeout(struct msdc_host *host, u64 ns, u64 clks)
static void msdc_gate_clock(struct msdc_host *host)
{
clk_bulk_disable_unprepare(MSDC_NR_CLOCKS, host->bulk_clks);
+ clk_disable_unprepare(host->crypto_clk);
clk_disable_unprepare(host->src_clk_cg);
clk_disable_unprepare(host->src_clk);
clk_disable_unprepare(host->bus_clk);
@@ -841,6 +857,7 @@ static int msdc_ungate_clock(struct msdc_host *host)
clk_prepare_enable(host->bus_clk);
clk_prepare_enable(host->src_clk);
clk_prepare_enable(host->src_clk_cg);
+ clk_prepare_enable(host->crypto_clk);
ret = clk_bulk_prepare_enable(MSDC_NR_CLOCKS, host->bulk_clks);
if (ret) {
dev_err(host->dev, "Cannot enable pclk/axi/ahb clock gates\n");
@@ -1207,12 +1224,10 @@ static bool msdc_cmd_done(struct msdc_host *host, int events,
if (!sbc_error && !(events & MSDC_INT_CMDRDY)) {
if (events & MSDC_INT_CMDTMO ||
- (cmd->opcode != MMC_SEND_TUNING_BLOCK &&
- cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200 &&
- !host->hs400_tuning))
+ (!mmc_op_tuning(cmd->opcode) && !host->hs400_tuning))
/*
* should not clear fifo/interrupt as the tune data
- * may have alreay come when cmd19/cmd21 gets response
+ * may have already come when cmd19/cmd21 gets response
* CRC error.
*/
msdc_reset_hw(host);
@@ -1303,9 +1318,7 @@ static void msdc_cmd_next(struct msdc_host *host,
{
if ((cmd->error &&
!(cmd->error == -EILSEQ &&
- (cmd->opcode == MMC_SEND_TUNING_BLOCK ||
- cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200 ||
- host->hs400_tuning))) ||
+ (mmc_op_tuning(cmd->opcode) || host->hs400_tuning))) ||
(mrq->sbc && mrq->sbc->error))
msdc_request_done(host, mrq);
else if (cmd == mrq->sbc)
@@ -2656,6 +2669,15 @@ static int msdc_drv_probe(struct platform_device *pdev)
goto host_free;
}
+ /* only eMMC has crypto property */
+ if (!(mmc->caps2 & MMC_CAP2_NO_MMC)) {
+ host->crypto_clk = devm_clk_get_optional(&pdev->dev, "crypto");
+ if (IS_ERR(host->crypto_clk))
+ host->crypto_clk = NULL;
+ else
+ mmc->caps2 |= MMC_CAP2_CRYPTO;
+ }
+
host->irq = platform_get_irq(pdev, 0);
if (host->irq < 0) {
ret = -EINVAL;
diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c
index 2cf0413407ea..668f865f3efb 100644
--- a/drivers/mmc/host/mxcmmc.c
+++ b/drivers/mmc/host/mxcmmc.c
@@ -1143,7 +1143,9 @@ static int mxcmci_probe(struct platform_device *pdev)
timer_setup(&host->watchdog, mxcmci_watchdog, 0);
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto out_free_dma;
return 0;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index fca30add563e..4bd744755205 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -1946,7 +1946,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (!ret)
mmc->caps |= MMC_CAP_SDIO_IRQ;
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto err_irq;
if (mmc_pdata(host)->name != NULL) {
ret = device_create_file(&mmc->class_dev, &dev_attr_slot_name);
diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c
index e4003f6058eb..2a988f942b6c 100644
--- a/drivers/mmc/host/pxamci.c
+++ b/drivers/mmc/host/pxamci.c
@@ -763,7 +763,12 @@ static int pxamci_probe(struct platform_device *pdev)
dev_warn(dev, "gpio_ro and get_ro() both defined\n");
}
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret) {
+ if (host->pdata && host->pdata->exit)
+ host->pdata->exit(dev, mmc);
+ goto out;
+ }
return 0;
diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h
index c4abfee1ebae..68da3da9e2e5 100644
--- a/drivers/mmc/host/renesas_sdhi.h
+++ b/drivers/mmc/host/renesas_sdhi.h
@@ -38,12 +38,15 @@ struct renesas_sdhi_of_data {
#define SDHI_CALIB_TABLE_MAX 32
+#define sdhi_has_quirk(p, q) ((p)->quirks && (p)->quirks->q)
+
struct renesas_sdhi_quirks {
bool hs400_disabled;
bool hs400_4taps;
bool fixed_addr_mode;
bool dma_one_rx_only;
bool manual_tap_correction;
+ bool old_info1_layout;
u32 hs400_bad_taps;
const u8 (*hs400_calib_table)[SDHI_CALIB_TABLE_MAX];
};
@@ -53,12 +56,17 @@ struct renesas_sdhi_of_data_with_quirks {
const struct renesas_sdhi_quirks *quirks;
};
-struct tmio_mmc_dma {
+/* We want both end_flags to be set before we mark DMA as finished */
+#define SDHI_DMA_END_FLAG_DMA 0
+#define SDHI_DMA_END_FLAG_ACCESS 1
+
+struct renesas_sdhi_dma {
+ unsigned long end_flags;
enum dma_slave_buswidth dma_buswidth;
bool (*filter)(struct dma_chan *chan, void *arg);
void (*enable)(struct tmio_mmc_host *host, bool enable);
- struct completion dma_dataend;
- struct tasklet_struct dma_complete;
+ struct completion dma_dataend;
+ struct tasklet_struct dma_complete;
};
struct renesas_sdhi {
@@ -66,7 +74,7 @@ struct renesas_sdhi {
struct clk *clkh;
struct clk *clk_cd;
struct tmio_mmc_data mmc_data;
- struct tmio_mmc_dma dma_priv;
+ struct renesas_sdhi_dma dma_priv;
const struct renesas_sdhi_quirks *quirks;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default, *pins_uhs;
diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c
index b970699743e0..345934e4f59e 100644
--- a/drivers/mmc/host/renesas_sdhi_core.c
+++ b/drivers/mmc/host/renesas_sdhi_core.c
@@ -141,7 +141,7 @@ static unsigned int renesas_sdhi_clk_update(struct tmio_mmc_host *host,
if (priv->clkh) {
/* HS400 with 4TAP needs different clock settings */
- bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
+ bool use_4tap = sdhi_has_quirk(priv, hs400_4taps);
bool need_slow_clkh = host->mmc->ios.timing == MMC_TIMING_MMC_HS400;
clkh_shift = use_4tap && need_slow_clkh ? 1 : 2;
ref_clk = priv->clkh;
@@ -383,7 +383,7 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
struct tmio_mmc_host *host = mmc_priv(mmc);
struct renesas_sdhi *priv = host_to_priv(host);
u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
- bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
+ bool use_4tap = sdhi_has_quirk(priv, hs400_4taps);
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
@@ -395,7 +395,7 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc)
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF,
priv->scc_tappos_hs400);
- if (priv->quirks && priv->quirks->manual_tap_correction)
+ if (sdhi_has_quirk(priv, manual_tap_correction))
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
@@ -546,7 +546,7 @@ static void renesas_sdhi_reset_hs400_mode(struct tmio_mmc_host *host,
SH_MOBILE_SDHI_SCC_TMPPORT2_HS400OSEL) &
sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_TMPPORT2));
- if (priv->adjust_hs400_calib_table)
+ if (sdhi_has_quirk(priv, hs400_calib_table) || sdhi_has_quirk(priv, hs400_bad_taps))
renesas_sdhi_adjust_hs400_mode_disable(host);
sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
@@ -732,7 +732,7 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_
sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
/* Change TAP position according to correction status */
- if (priv->quirks && priv->quirks->manual_tap_correction &&
+ if (sdhi_has_quirk(priv, manual_tap_correction) &&
host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0;
/*
@@ -796,7 +796,7 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host,
struct mmc_request *mrq)
{
struct renesas_sdhi *priv = host_to_priv(host);
- bool use_4tap = priv->quirks && priv->quirks->hs400_4taps;
+ bool use_4tap = sdhi_has_quirk(priv, hs400_4taps);
bool ret = false;
/*
@@ -908,7 +908,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
{
struct tmio_mmc_data *mmd = pdev->dev.platform_data;
struct tmio_mmc_data *mmc_data;
- struct tmio_mmc_dma *dma_priv;
+ struct renesas_sdhi_dma *dma_priv;
struct tmio_mmc_host *host;
struct renesas_sdhi *priv;
int num_irqs, irq, ret, i;
@@ -990,7 +990,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
host->multi_io_quirk = renesas_sdhi_multi_io_quirk;
host->dma_ops = dma_ops;
- if (quirks && quirks->hs400_disabled)
+ if (sdhi_has_quirk(priv, hs400_disabled))
host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES);
/* For some SoC, we disable internal WP. GPIO may override this */
@@ -1018,7 +1018,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
dma_priv->filter = shdma_chan_filter;
dma_priv->enable = renesas_sdhi_enable_dma;
- mmc_data->alignment_shift = 1; /* 2-byte alignment */
mmc_data->capabilities |= MMC_CAP_MMC_HIGHSPEED;
/*
@@ -1056,7 +1055,7 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (ver == SDHI_VER_GEN2_SDR50)
mmc_data->flags &= ~TMIO_MMC_HAVE_CBSY;
- if (ver == SDHI_VER_GEN3_SDMMC && quirks && quirks->hs400_calib_table) {
+ if (ver == SDHI_VER_GEN3_SDMMC && sdhi_has_quirk(priv, hs400_calib_table)) {
host->fixup_request = renesas_sdhi_fixup_request;
priv->adjust_hs400_calib_table = *(
res->start == SDHI_GEN3_MMC0_ADDR ?
@@ -1068,13 +1067,15 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (ver >= SDHI_VER_GEN3_SD)
host->get_timeout_cycles = renesas_sdhi_gen3_get_cycles;
+ /* Check for SCC so we can reset it if needed */
+ if (of_data && of_data->scc_offset && ver >= SDHI_VER_GEN2_SDR104)
+ priv->scc_ctl = host->ctl + of_data->scc_offset;
+
/* Enable tuning iff we have an SCC and a supported mode */
- if (of_data && of_data->scc_offset &&
- (host->mmc->caps & MMC_CAP_UHS_SDR104 ||
- host->mmc->caps2 & (MMC_CAP2_HS200_1_8V_SDR |
- MMC_CAP2_HS400_1_8V))) {
+ if (priv->scc_ctl && (host->mmc->caps & MMC_CAP_UHS_SDR104 ||
+ host->mmc->caps2 & MMC_CAP2_HSX00_1_8V)) {
const struct renesas_sdhi_scc *taps = of_data->taps;
- bool use_4tap = quirks && quirks->hs400_4taps;
+ bool use_4tap = sdhi_has_quirk(priv, hs400_4taps);
bool hit = false;
for (i = 0; i < of_data->taps_num; i++) {
@@ -1092,7 +1093,6 @@ int renesas_sdhi_probe(struct platform_device *pdev,
if (!hit)
dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n");
- priv->scc_ctl = host->ctl + of_data->scc_offset;
host->check_retune = renesas_sdhi_check_scc_error;
host->ops.execute_tuning = renesas_sdhi_execute_tuning;
host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning;
diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
index 42937596c4c4..29f562115c66 100644
--- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c
@@ -47,9 +47,9 @@
#define RST_RESERVED_BITS GENMASK_ULL(31, 0)
/* DM_CM_INFO1 and DM_CM_INFO1_MASK */
-#define INFO1_CLEAR 0
#define INFO1_MASK_CLEAR GENMASK_ULL(31, 0)
-#define INFO1_DTRANEND1 BIT(17)
+#define INFO1_DTRANEND1 BIT(20)
+#define INFO1_DTRANEND1_OLD BIT(17)
#define INFO1_DTRANEND0 BIT(16)
/* DM_CM_INFO2 and DM_CM_INFO2_MASK */
@@ -165,6 +165,7 @@ static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400_one_rx = {
.hs400_disabled = true,
.hs400_4taps = true,
.dma_one_rx_only = true,
+ .old_info1_layout = true,
};
static const struct renesas_sdhi_quirks sdhi_quirks_4tap = {
@@ -280,23 +281,17 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = {
MODULE_DEVICE_TABLE(of, renesas_sdhi_internal_dmac_of_match);
static void
-renesas_sdhi_internal_dmac_dm_write(struct tmio_mmc_host *host,
- int addr, u64 val)
-{
- writeq(val, host->ctl + addr);
-}
-
-static void
renesas_sdhi_internal_dmac_enable_dma(struct tmio_mmc_host *host, bool enable)
{
struct renesas_sdhi *priv = host_to_priv(host);
+ u32 dma_irqs = INFO1_DTRANEND0 |
+ (sdhi_has_quirk(priv, old_info1_layout) ?
+ INFO1_DTRANEND1_OLD : INFO1_DTRANEND1);
if (!host->chan_tx || !host->chan_rx)
return;
- if (!enable)
- renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO1,
- INFO1_CLEAR);
+ writel(enable ? ~dma_irqs : INFO1_MASK_CLEAR, host->ctl + DM_CM_INFO1_MASK);
if (priv->dma_priv.enable)
priv->dma_priv.enable(host, enable);
@@ -309,22 +304,44 @@ renesas_sdhi_internal_dmac_abort_dma(struct tmio_mmc_host *host)
renesas_sdhi_internal_dmac_enable_dma(host, false);
- renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST,
- RST_RESERVED_BITS & ~val);
- renesas_sdhi_internal_dmac_dm_write(host, DM_CM_RST,
- RST_RESERVED_BITS | val);
+ writel(RST_RESERVED_BITS & ~val, host->ctl + DM_CM_RST);
+ writel(RST_RESERVED_BITS | val, host->ctl + DM_CM_RST);
clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags);
renesas_sdhi_internal_dmac_enable_dma(host, true);
}
+static bool renesas_sdhi_internal_dmac_dma_irq(struct tmio_mmc_host *host)
+{
+ struct renesas_sdhi *priv = host_to_priv(host);
+ struct renesas_sdhi_dma *dma_priv = &priv->dma_priv;
+
+ u32 dma_irqs = INFO1_DTRANEND0 |
+ (sdhi_has_quirk(priv, old_info1_layout) ?
+ INFO1_DTRANEND1_OLD : INFO1_DTRANEND1);
+ u32 status = readl(host->ctl + DM_CM_INFO1);
+
+ if (status & dma_irqs) {
+ writel(status ^ dma_irqs, host->ctl + DM_CM_INFO1);
+ set_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags);
+ if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags))
+ tasklet_schedule(&dma_priv->dma_complete);
+ }
+
+ return status & dma_irqs;
+}
+
static void
renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host)
{
struct renesas_sdhi *priv = host_to_priv(host);
+ struct renesas_sdhi_dma *dma_priv = &priv->dma_priv;
- tasklet_schedule(&priv->dma_priv.dma_complete);
+ set_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags);
+ if (test_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags) ||
+ host->data->error)
+ tasklet_schedule(&dma_priv->dma_complete);
}
/*
@@ -379,7 +396,7 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
struct scatterlist *sg = host->sg_ptr;
u32 dtran_mode = DTRAN_MODE_BUS_WIDTH;
- if (!(priv->quirks && priv->quirks->fixed_addr_mode))
+ if (!sdhi_has_quirk(priv, fixed_addr_mode))
dtran_mode |= DTRAN_MODE_ADDR_MODE;
if (!renesas_sdhi_internal_dmac_map(host, data, COOKIE_MAPPED))
@@ -387,20 +404,19 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host,
if (data->flags & MMC_DATA_READ) {
dtran_mode |= DTRAN_MODE_CH_NUM_CH1;
- if (priv->quirks && priv->quirks->dma_one_rx_only &&
+ if (sdhi_has_quirk(priv, dma_one_rx_only) &&
test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags))
goto force_pio_with_unmap;
} else {
dtran_mode |= DTRAN_MODE_CH_NUM_CH0;
}
+ priv->dma_priv.end_flags = 0;
renesas_sdhi_internal_dmac_enable_dma(host, true);
/* set dma parameters */
- renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE,
- dtran_mode);
- renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR,
- sg_dma_address(sg));
+ writel(dtran_mode, host->ctl + DM_CM_DTRAN_MODE);
+ writel(sg_dma_address(sg), host->ctl + DM_DTRAN_ADDR);
host->dma_on = true;
@@ -416,12 +432,19 @@ force_pio:
static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg)
{
struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg;
+ struct renesas_sdhi *priv = host_to_priv(host);
tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND);
- /* start the DMAC */
- renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_CTRL,
- DTRAN_CTRL_DM_START);
+ if (!host->cmd->error) {
+ /* start the DMAC */
+ writel(DTRAN_CTRL_DM_START, host->ctl + DM_CM_DTRAN_CTRL);
+ } else {
+ /* on CMD errors, simulate DMA end immediately */
+ set_bit(SDHI_DMA_END_FLAG_DMA, &priv->dma_priv.end_flags);
+ if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &priv->dma_priv.end_flags))
+ tasklet_schedule(&priv->dma_priv.dma_complete);
+ }
}
static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host)
@@ -501,11 +524,11 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host,
{
struct renesas_sdhi *priv = host_to_priv(host);
- /* Disable DMAC interrupts, we don't use them */
- renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO1_MASK,
- INFO1_MASK_CLEAR);
- renesas_sdhi_internal_dmac_dm_write(host, DM_CM_INFO2_MASK,
- INFO2_MASK_CLEAR);
+ /* Disable DMAC interrupts initially */
+ writel(INFO1_MASK_CLEAR, host->ctl + DM_CM_INFO1_MASK);
+ writel(INFO2_MASK_CLEAR, host->ctl + DM_CM_INFO2_MASK);
+ writel(0, host->ctl + DM_CM_INFO1);
+ writel(0, host->ctl + DM_CM_INFO2);
/* Each value is set to non-zero to assume "enabling" each DMA */
host->chan_rx = host->chan_tx = (void *)0xdeadbeaf;
@@ -537,6 +560,7 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = {
.abort = renesas_sdhi_internal_dmac_abort_dma,
.dataend = renesas_sdhi_internal_dmac_dataend_dma,
.end = renesas_sdhi_internal_dmac_end_dma,
+ .dma_irq = renesas_sdhi_internal_dmac_dma_irq,
};
static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev)
diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
index 99e3426df702..b559ad38b667 100644
--- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c
+++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c
@@ -160,7 +160,7 @@ static void renesas_sdhi_sys_dmac_start_dma_rx(struct tmio_mmc_host *host)
dma_cookie_t cookie;
int ret, i;
bool aligned = true, multiple = true;
- unsigned int align = (1 << host->pdata->alignment_shift) - 1;
+ unsigned int align = 1; /* 2-byte alignment */
for_each_sg(sg, sg_tmp, host->sg_len, i) {
if (sg_tmp->offset & align)
@@ -232,7 +232,7 @@ static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host)
dma_cookie_t cookie;
int ret, i;
bool aligned = true, multiple = true;
- unsigned int align = (1 << host->pdata->alignment_shift) - 1;
+ unsigned int align = 1; /* 2-byte alignment */
for_each_sg(sg, sg_tmp, host->sg_len, i) {
if (sg_tmp->offset & align)
@@ -254,12 +254,11 @@ static void renesas_sdhi_sys_dmac_start_dma_tx(struct tmio_mmc_host *host)
/* The only sg element can be unaligned, use our bounce buffer then */
if (!aligned) {
- unsigned long flags;
- void *sg_vaddr = tmio_mmc_kmap_atomic(sg, &flags);
+ void *sg_vaddr = kmap_local_page(sg_page(sg));
sg_init_one(&host->bounce_sg, host->bounce_buf, sg->length);
- memcpy(host->bounce_buf, sg_vaddr, host->bounce_sg.length);
- tmio_mmc_kunmap_atomic(sg, &flags, sg_vaddr);
+ memcpy(host->bounce_buf, sg_vaddr + sg->offset, host->bounce_sg.length);
+ kunmap_local(sg_vaddr);
host->sg_ptr = &host->bounce_sg;
sg = host->sg_ptr;
}
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index e1580f78c6b2..8098726dcc0b 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -1474,6 +1474,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
struct realtek_pci_sdmmc *host;
struct rtsx_pcr *pcr;
struct pcr_handle *handle = pdev->dev.platform_data;
+ int ret;
if (!handle)
return -ENXIO;
@@ -1511,7 +1512,13 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_use_autosuspend(&pdev->dev);
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret) {
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ mmc_free_host(mmc);
+ return ret;
+ }
return 0;
}
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 5798aee06653..2c650cd58693 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -1329,6 +1329,7 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
#ifdef RTSX_USB_USE_LEDS_CLASS
int err;
#endif
+ int ret;
ucr = usb_get_intfdata(to_usb_interface(pdev->dev.parent));
if (!ucr)
@@ -1365,7 +1366,15 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
INIT_WORK(&host->led_work, rtsx_usb_update_led);
#endif
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret) {
+#ifdef RTSX_USB_USE_LEDS_CLASS
+ led_classdev_unregister(&host->led);
+#endif
+ mmc_free_host(mmc);
+ pm_runtime_disable(&pdev->dev);
+ return ret;
+ }
return 0;
}
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
deleted file mode 100644
index 8d5929a32d34..000000000000
--- a/drivers/mmc/host/s3cmci.c
+++ /dev/null
@@ -1,1777 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
- *
- * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <[email protected]>
- *
- * Current driver maintained by Ben Dooks and Simtec Electronics
- * Copyright (C) 2008 Simtec Electronics <[email protected]>
- */
-
-#include <linux/module.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/clk.h>
-#include <linux/mmc/host.h>
-#include <linux/platform_device.h>
-#include <linux/cpufreq.h>
-#include <linux/debugfs.h>
-#include <linux/seq_file.h>
-#include <linux/gpio/consumer.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/mmc/slot-gpio.h>
-#include <linux/platform_data/mmc-s3cmci.h>
-
-#include "s3cmci.h"
-
-#define DRIVER_NAME "s3c-mci"
-
-#define S3C2410_SDICON (0x00)
-#define S3C2410_SDIPRE (0x04)
-#define S3C2410_SDICMDARG (0x08)
-#define S3C2410_SDICMDCON (0x0C)
-#define S3C2410_SDICMDSTAT (0x10)
-#define S3C2410_SDIRSP0 (0x14)
-#define S3C2410_SDIRSP1 (0x18)
-#define S3C2410_SDIRSP2 (0x1C)
-#define S3C2410_SDIRSP3 (0x20)
-#define S3C2410_SDITIMER (0x24)
-#define S3C2410_SDIBSIZE (0x28)
-#define S3C2410_SDIDCON (0x2C)
-#define S3C2410_SDIDCNT (0x30)
-#define S3C2410_SDIDSTA (0x34)
-#define S3C2410_SDIFSTA (0x38)
-
-#define S3C2410_SDIDATA (0x3C)
-#define S3C2410_SDIIMSK (0x40)
-
-#define S3C2440_SDIDATA (0x40)
-#define S3C2440_SDIIMSK (0x3C)
-
-#define S3C2440_SDICON_SDRESET (1 << 8)
-#define S3C2410_SDICON_SDIOIRQ (1 << 3)
-#define S3C2410_SDICON_FIFORESET (1 << 1)
-#define S3C2410_SDICON_CLOCKTYPE (1 << 0)
-
-#define S3C2410_SDICMDCON_LONGRSP (1 << 10)
-#define S3C2410_SDICMDCON_WAITRSP (1 << 9)
-#define S3C2410_SDICMDCON_CMDSTART (1 << 8)
-#define S3C2410_SDICMDCON_SENDERHOST (1 << 6)
-#define S3C2410_SDICMDCON_INDEX (0x3f)
-
-#define S3C2410_SDICMDSTAT_CRCFAIL (1 << 12)
-#define S3C2410_SDICMDSTAT_CMDSENT (1 << 11)
-#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1 << 10)
-#define S3C2410_SDICMDSTAT_RSPFIN (1 << 9)
-
-#define S3C2440_SDIDCON_DS_WORD (2 << 22)
-#define S3C2410_SDIDCON_TXAFTERRESP (1 << 20)
-#define S3C2410_SDIDCON_RXAFTERCMD (1 << 19)
-#define S3C2410_SDIDCON_BLOCKMODE (1 << 17)
-#define S3C2410_SDIDCON_WIDEBUS (1 << 16)
-#define S3C2410_SDIDCON_DMAEN (1 << 15)
-#define S3C2410_SDIDCON_STOP (1 << 14)
-#define S3C2440_SDIDCON_DATSTART (1 << 14)
-
-#define S3C2410_SDIDCON_XFER_RXSTART (2 << 12)
-#define S3C2410_SDIDCON_XFER_TXSTART (3 << 12)
-
-#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF)
-
-#define S3C2410_SDIDSTA_SDIOIRQDETECT (1 << 9)
-#define S3C2410_SDIDSTA_FIFOFAIL (1 << 8)
-#define S3C2410_SDIDSTA_CRCFAIL (1 << 7)
-#define S3C2410_SDIDSTA_RXCRCFAIL (1 << 6)
-#define S3C2410_SDIDSTA_DATATIMEOUT (1 << 5)
-#define S3C2410_SDIDSTA_XFERFINISH (1 << 4)
-#define S3C2410_SDIDSTA_TXDATAON (1 << 1)
-#define S3C2410_SDIDSTA_RXDATAON (1 << 0)
-
-#define S3C2440_SDIFSTA_FIFORESET (1 << 16)
-#define S3C2440_SDIFSTA_FIFOFAIL (3 << 14)
-#define S3C2410_SDIFSTA_TFDET (1 << 13)
-#define S3C2410_SDIFSTA_RFDET (1 << 12)
-#define S3C2410_SDIFSTA_COUNTMASK (0x7f)
-
-#define S3C2410_SDIIMSK_RESPONSECRC (1 << 17)
-#define S3C2410_SDIIMSK_CMDSENT (1 << 16)
-#define S3C2410_SDIIMSK_CMDTIMEOUT (1 << 15)
-#define S3C2410_SDIIMSK_RESPONSEND (1 << 14)
-#define S3C2410_SDIIMSK_SDIOIRQ (1 << 12)
-#define S3C2410_SDIIMSK_FIFOFAIL (1 << 11)
-#define S3C2410_SDIIMSK_CRCSTATUS (1 << 10)
-#define S3C2410_SDIIMSK_DATACRC (1 << 9)
-#define S3C2410_SDIIMSK_DATATIMEOUT (1 << 8)
-#define S3C2410_SDIIMSK_DATAFINISH (1 << 7)
-#define S3C2410_SDIIMSK_TXFIFOHALF (1 << 4)
-#define S3C2410_SDIIMSK_RXFIFOLAST (1 << 2)
-#define S3C2410_SDIIMSK_RXFIFOHALF (1 << 0)
-
-enum dbg_channels {
- dbg_err = (1 << 0),
- dbg_debug = (1 << 1),
- dbg_info = (1 << 2),
- dbg_irq = (1 << 3),
- dbg_sg = (1 << 4),
- dbg_dma = (1 << 5),
- dbg_pio = (1 << 6),
- dbg_fail = (1 << 7),
- dbg_conf = (1 << 8),
-};
-
-static const int dbgmap_err = dbg_fail;
-static const int dbgmap_info = dbg_info | dbg_conf;
-static const int dbgmap_debug = dbg_err | dbg_debug;
-
-#define dbg(host, channels, args...) \
- do { \
- if (dbgmap_err & channels) \
- dev_err(&host->pdev->dev, args); \
- else if (dbgmap_info & channels) \
- dev_info(&host->pdev->dev, args); \
- else if (dbgmap_debug & channels) \
- dev_dbg(&host->pdev->dev, args); \
- } while (0)
-
-static void finalize_request(struct s3cmci_host *host);
-static void s3cmci_send_request(struct mmc_host *mmc);
-static void s3cmci_reset(struct s3cmci_host *host);
-
-#ifdef CONFIG_MMC_DEBUG
-
-static void dbg_dumpregs(struct s3cmci_host *host, char *prefix)
-{
- u32 con, pre, cmdarg, cmdcon, cmdsta, r0, r1, r2, r3, timer;
- u32 datcon, datcnt, datsta, fsta;
-
- con = readl(host->base + S3C2410_SDICON);
- pre = readl(host->base + S3C2410_SDIPRE);
- cmdarg = readl(host->base + S3C2410_SDICMDARG);
- cmdcon = readl(host->base + S3C2410_SDICMDCON);
- cmdsta = readl(host->base + S3C2410_SDICMDSTAT);
- r0 = readl(host->base + S3C2410_SDIRSP0);
- r1 = readl(host->base + S3C2410_SDIRSP1);
- r2 = readl(host->base + S3C2410_SDIRSP2);
- r3 = readl(host->base + S3C2410_SDIRSP3);
- timer = readl(host->base + S3C2410_SDITIMER);
- datcon = readl(host->base + S3C2410_SDIDCON);
- datcnt = readl(host->base + S3C2410_SDIDCNT);
- datsta = readl(host->base + S3C2410_SDIDSTA);
- fsta = readl(host->base + S3C2410_SDIFSTA);
-
- dbg(host, dbg_debug, "%s CON:[%08x] PRE:[%08x] TMR:[%08x]\n",
- prefix, con, pre, timer);
-
- dbg(host, dbg_debug, "%s CCON:[%08x] CARG:[%08x] CSTA:[%08x]\n",
- prefix, cmdcon, cmdarg, cmdsta);
-
- dbg(host, dbg_debug, "%s DCON:[%08x] FSTA:[%08x]"
- " DSTA:[%08x] DCNT:[%08x]\n",
- prefix, datcon, fsta, datsta, datcnt);
-
- dbg(host, dbg_debug, "%s R0:[%08x] R1:[%08x]"
- " R2:[%08x] R3:[%08x]\n",
- prefix, r0, r1, r2, r3);
-}
-
-static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd,
- int stop)
-{
- snprintf(host->dbgmsg_cmd, 300,
- "#%u%s op:%i arg:0x%08x flags:0x08%x retries:%u",
- host->ccnt, (stop ? " (STOP)" : ""),
- cmd->opcode, cmd->arg, cmd->flags, cmd->retries);
-
- if (cmd->data) {
- snprintf(host->dbgmsg_dat, 300,
- "#%u bsize:%u blocks:%u bytes:%u",
- host->dcnt, cmd->data->blksz,
- cmd->data->blocks,
- cmd->data->blocks * cmd->data->blksz);
- } else {
- host->dbgmsg_dat[0] = '\0';
- }
-}
-
-static void dbg_dumpcmd(struct s3cmci_host *host, struct mmc_command *cmd,
- int fail)
-{
- unsigned int dbglvl = fail ? dbg_fail : dbg_debug;
-
- if (!cmd)
- return;
-
- if (cmd->error == 0) {
- dbg(host, dbglvl, "CMD[OK] %s R0:0x%08x\n",
- host->dbgmsg_cmd, cmd->resp[0]);
- } else {
- dbg(host, dbglvl, "CMD[ERR %i] %s Status:%s\n",
- cmd->error, host->dbgmsg_cmd, host->status);
- }
-
- if (!cmd->data)
- return;
-
- if (cmd->data->error == 0) {
- dbg(host, dbglvl, "DAT[OK] %s\n", host->dbgmsg_dat);
- } else {
- dbg(host, dbglvl, "DAT[ERR %i] %s DCNT:0x%08x\n",
- cmd->data->error, host->dbgmsg_dat,
- readl(host->base + S3C2410_SDIDCNT));
- }
-}
-#else
-static void dbg_dumpcmd(struct s3cmci_host *host,
- struct mmc_command *cmd, int fail) { }
-
-static void prepare_dbgmsg(struct s3cmci_host *host, struct mmc_command *cmd,
- int stop) { }
-
-static void dbg_dumpregs(struct s3cmci_host *host, char *prefix) { }
-
-#endif /* CONFIG_MMC_DEBUG */
-
-/**
- * s3cmci_host_usedma - return whether the host is using dma or pio
- * @host: The host state
- *
- * Return true if the host is using DMA to transfer data, else false
- * to use PIO mode. Will return static data depending on the driver
- * configuration.
- */
-static inline bool s3cmci_host_usedma(struct s3cmci_host *host)
-{
-#ifdef CONFIG_MMC_S3C_PIO
- return false;
-#else /* CONFIG_MMC_S3C_DMA */
- return true;
-#endif
-}
-
-static inline u32 enable_imask(struct s3cmci_host *host, u32 imask)
-{
- u32 newmask;
-
- newmask = readl(host->base + host->sdiimsk);
- newmask |= imask;
-
- writel(newmask, host->base + host->sdiimsk);
-
- return newmask;
-}
-
-static inline u32 disable_imask(struct s3cmci_host *host, u32 imask)
-{
- u32 newmask;
-
- newmask = readl(host->base + host->sdiimsk);
- newmask &= ~imask;
-
- writel(newmask, host->base + host->sdiimsk);
-
- return newmask;
-}
-
-static inline void clear_imask(struct s3cmci_host *host)
-{
- u32 mask = readl(host->base + host->sdiimsk);
-
- /* preserve the SDIO IRQ mask state */
- mask &= S3C2410_SDIIMSK_SDIOIRQ;
- writel(mask, host->base + host->sdiimsk);
-}
-
-/**
- * s3cmci_check_sdio_irq - test whether the SDIO IRQ is being signalled
- * @host: The host to check.
- *
- * Test to see if the SDIO interrupt is being signalled in case the
- * controller has failed to re-detect a card interrupt. Read GPE8 and
- * see if it is low and if so, signal a SDIO interrupt.
- *
- * This is currently called if a request is finished (we assume that the
- * bus is now idle) and when the SDIO IRQ is enabled in case the IRQ is
- * already being indicated.
-*/
-static void s3cmci_check_sdio_irq(struct s3cmci_host *host)
-{
- if (host->sdio_irqen) {
- if (host->pdata->bus[3] &&
- gpiod_get_value(host->pdata->bus[3]) == 0) {
- pr_debug("%s: signalling irq\n", __func__);
- mmc_signal_sdio_irq(host->mmc);
- }
- }
-}
-
-static inline int get_data_buffer(struct s3cmci_host *host,
- u32 *bytes, u32 **pointer)
-{
- struct scatterlist *sg;
-
- if (host->pio_active == XFER_NONE)
- return -EINVAL;
-
- if ((!host->mrq) || (!host->mrq->data))
- return -EINVAL;
-
- if (host->pio_sgptr >= host->mrq->data->sg_len) {
- dbg(host, dbg_debug, "no more buffers (%i/%i)\n",
- host->pio_sgptr, host->mrq->data->sg_len);
- return -EBUSY;
- }
- sg = &host->mrq->data->sg[host->pio_sgptr];
-
- *bytes = sg->length;
- *pointer = sg_virt(sg);
-
- host->pio_sgptr++;
-
- dbg(host, dbg_sg, "new buffer (%i/%i)\n",
- host->pio_sgptr, host->mrq->data->sg_len);
-
- return 0;
-}
-
-static inline u32 fifo_count(struct s3cmci_host *host)
-{
- u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
-
- fifostat &= S3C2410_SDIFSTA_COUNTMASK;
- return fifostat;
-}
-
-static inline u32 fifo_free(struct s3cmci_host *host)
-{
- u32 fifostat = readl(host->base + S3C2410_SDIFSTA);
-
- fifostat &= S3C2410_SDIFSTA_COUNTMASK;
- return 63 - fifostat;
-}
-
-/**
- * s3cmci_enable_irq - enable IRQ, after having disabled it.
- * @host: The device state.
- * @more: True if more IRQs are expected from transfer.
- *
- * Enable the main IRQ if needed after it has been disabled.
- *
- * The IRQ can be one of the following states:
- * - disabled during IDLE
- * - disabled whilst processing data
- * - enabled during transfer
- * - enabled whilst awaiting SDIO interrupt detection
- */
-static void s3cmci_enable_irq(struct s3cmci_host *host, bool more)
-{
- unsigned long flags;
- bool enable = false;
-
- local_irq_save(flags);
-
- host->irq_enabled = more;
- host->irq_disabled = false;
-
- enable = more | host->sdio_irqen;
-
- if (host->irq_state != enable) {
- host->irq_state = enable;
-
- if (enable)
- enable_irq(host->irq);
- else
- disable_irq(host->irq);
- }
-
- local_irq_restore(flags);
-}
-
-static void s3cmci_disable_irq(struct s3cmci_host *host, bool transfer)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- /* pr_debug("%s: transfer %d\n", __func__, transfer); */
-
- host->irq_disabled = transfer;
-
- if (transfer && host->irq_state) {
- host->irq_state = false;
- disable_irq(host->irq);
- }
-
- local_irq_restore(flags);
-}
-
-static void do_pio_read(struct s3cmci_host *host)
-{
- int res;
- u32 fifo;
- u32 *ptr;
- u32 fifo_words;
- void __iomem *from_ptr;
-
- /* write real prescaler to host, it might be set slow to fix */
- writel(host->prescaler, host->base + S3C2410_SDIPRE);
-
- from_ptr = host->base + host->sdidata;
-
- while ((fifo = fifo_count(host))) {
- if (!host->pio_bytes) {
- res = get_data_buffer(host, &host->pio_bytes,
- &host->pio_ptr);
- if (res) {
- host->pio_active = XFER_NONE;
- host->complete_what = COMPLETION_FINALIZE;
-
- dbg(host, dbg_pio, "pio_read(): "
- "complete (no more data).\n");
- return;
- }
-
- dbg(host, dbg_pio,
- "pio_read(): new target: [%i]@[%p]\n",
- host->pio_bytes, host->pio_ptr);
- }
-
- dbg(host, dbg_pio,
- "pio_read(): fifo:[%02i] buffer:[%03i] dcnt:[%08X]\n",
- fifo, host->pio_bytes,
- readl(host->base + S3C2410_SDIDCNT));
-
- /* If we have reached the end of the block, we can
- * read a word and get 1 to 3 bytes. If we in the
- * middle of the block, we have to read full words,
- * otherwise we will write garbage, so round down to
- * an even multiple of 4. */
- if (fifo >= host->pio_bytes)
- fifo = host->pio_bytes;
- else
- fifo -= fifo & 3;
-
- host->pio_bytes -= fifo;
- host->pio_count += fifo;
-
- fifo_words = fifo >> 2;
- ptr = host->pio_ptr;
- while (fifo_words--)
- *ptr++ = readl(from_ptr);
- host->pio_ptr = ptr;
-
- if (fifo & 3) {
- u32 n = fifo & 3;
- u32 data = readl(from_ptr);
- u8 *p = (u8 *)host->pio_ptr;
-
- while (n--) {
- *p++ = data;
- data >>= 8;
- }
- }
- }
-
- if (!host->pio_bytes) {
- res = get_data_buffer(host, &host->pio_bytes, &host->pio_ptr);
- if (res) {
- dbg(host, dbg_pio,
- "pio_read(): complete (no more buffers).\n");
- host->pio_active = XFER_NONE;
- host->complete_what = COMPLETION_FINALIZE;
-
- return;
- }
- }
-
- enable_imask(host,
- S3C2410_SDIIMSK_RXFIFOHALF | S3C2410_SDIIMSK_RXFIFOLAST);
-}
-
-static void do_pio_write(struct s3cmci_host *host)
-{
- void __iomem *to_ptr;
- int res;
- u32 fifo;
- u32 *ptr;
-
- to_ptr = host->base + host->sdidata;
-
- while ((fifo = fifo_free(host)) > 3) {
- if (!host->pio_bytes) {
- res = get_data_buffer(host, &host->pio_bytes,
- &host->pio_ptr);
- if (res) {
- dbg(host, dbg_pio,
- "pio_write(): complete (no more data).\n");
- host->pio_active = XFER_NONE;
-
- return;
- }
-
- dbg(host, dbg_pio,
- "pio_write(): new source: [%i]@[%p]\n",
- host->pio_bytes, host->pio_ptr);
-
- }
-
- /* If we have reached the end of the block, we have to
- * write exactly the remaining number of bytes. If we
- * in the middle of the block, we have to write full
- * words, so round down to an even multiple of 4. */
- if (fifo >= host->pio_bytes)
- fifo = host->pio_bytes;
- else
- fifo -= fifo & 3;
-
- host->pio_bytes -= fifo;
- host->pio_count += fifo;
-
- fifo = (fifo + 3) >> 2;
- ptr = host->pio_ptr;
- while (fifo--)
- writel(*ptr++, to_ptr);
- host->pio_ptr = ptr;
- }
-
- enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
-}
-
-static void pio_tasklet(struct tasklet_struct *t)
-{
- struct s3cmci_host *host = from_tasklet(host, t, pio_tasklet);
-
- s3cmci_disable_irq(host, true);
-
- if (host->pio_active == XFER_WRITE)
- do_pio_write(host);
-
- if (host->pio_active == XFER_READ)
- do_pio_read(host);
-
- if (host->complete_what == COMPLETION_FINALIZE) {
- clear_imask(host);
- if (host->pio_active != XFER_NONE) {
- dbg(host, dbg_err, "unfinished %s "
- "- pio_count:[%u] pio_bytes:[%u]\n",
- (host->pio_active == XFER_READ) ? "read" : "write",
- host->pio_count, host->pio_bytes);
-
- if (host->mrq->data)
- host->mrq->data->error = -EINVAL;
- }
-
- s3cmci_enable_irq(host, false);
- finalize_request(host);
- } else
- s3cmci_enable_irq(host, true);
-}
-
-/*
- * ISR for SDI Interface IRQ
- * Communication between driver and ISR works as follows:
- * host->mrq points to current request
- * host->complete_what Indicates when the request is considered done
- * COMPLETION_CMDSENT when the command was sent
- * COMPLETION_RSPFIN when a response was received
- * COMPLETION_XFERFINISH when the data transfer is finished
- * COMPLETION_XFERFINISH_RSPFIN both of the above.
- * host->complete_request is the completion-object the driver waits for
- *
- * 1) Driver sets up host->mrq and host->complete_what
- * 2) Driver prepares the transfer
- * 3) Driver enables interrupts
- * 4) Driver starts transfer
- * 5) Driver waits for host->complete_rquest
- * 6) ISR checks for request status (errors and success)
- * 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
- * 7) ISR completes host->complete_request
- * 8) ISR disables interrupts
- * 9) Driver wakes up and takes care of the request
- *
- * Note: "->error"-fields are expected to be set to 0 before the request
- * was issued by mmc.c - therefore they are only set, when an error
- * contition comes up
- */
-
-static irqreturn_t s3cmci_irq(int irq, void *dev_id)
-{
- struct s3cmci_host *host = dev_id;
- struct mmc_command *cmd;
- u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt, mci_imsk;
- u32 mci_cclear = 0, mci_dclear;
- unsigned long iflags;
-
- mci_dsta = readl(host->base + S3C2410_SDIDSTA);
- mci_imsk = readl(host->base + host->sdiimsk);
-
- if (mci_dsta & S3C2410_SDIDSTA_SDIOIRQDETECT) {
- if (mci_imsk & S3C2410_SDIIMSK_SDIOIRQ) {
- mci_dclear = S3C2410_SDIDSTA_SDIOIRQDETECT;
- writel(mci_dclear, host->base + S3C2410_SDIDSTA);
-
- mmc_signal_sdio_irq(host->mmc);
- return IRQ_HANDLED;
- }
- }
-
- spin_lock_irqsave(&host->complete_lock, iflags);
-
- mci_csta = readl(host->base + S3C2410_SDICMDSTAT);
- mci_dcnt = readl(host->base + S3C2410_SDIDCNT);
- mci_fsta = readl(host->base + S3C2410_SDIFSTA);
- mci_dclear = 0;
-
- if ((host->complete_what == COMPLETION_NONE) ||
- (host->complete_what == COMPLETION_FINALIZE)) {
- host->status = "nothing to complete";
- clear_imask(host);
- goto irq_out;
- }
-
- if (!host->mrq) {
- host->status = "no active mrq";
- clear_imask(host);
- goto irq_out;
- }
-
- cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd;
-
- if (!cmd) {
- host->status = "no active cmd";
- clear_imask(host);
- goto irq_out;
- }
-
- if (!s3cmci_host_usedma(host)) {
- if ((host->pio_active == XFER_WRITE) &&
- (mci_fsta & S3C2410_SDIFSTA_TFDET)) {
-
- disable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
- tasklet_schedule(&host->pio_tasklet);
- host->status = "pio tx";
- }
-
- if ((host->pio_active == XFER_READ) &&
- (mci_fsta & S3C2410_SDIFSTA_RFDET)) {
-
- disable_imask(host,
- S3C2410_SDIIMSK_RXFIFOHALF |
- S3C2410_SDIIMSK_RXFIFOLAST);
-
- tasklet_schedule(&host->pio_tasklet);
- host->status = "pio rx";
- }
- }
-
- if (mci_csta & S3C2410_SDICMDSTAT_CMDTIMEOUT) {
- dbg(host, dbg_err, "CMDSTAT: error CMDTIMEOUT\n");
- cmd->error = -ETIMEDOUT;
- host->status = "error: command timeout";
- goto fail_transfer;
- }
-
- if (mci_csta & S3C2410_SDICMDSTAT_CMDSENT) {
- if (host->complete_what == COMPLETION_CMDSENT) {
- host->status = "ok: command sent";
- goto close_transfer;
- }
-
- mci_cclear |= S3C2410_SDICMDSTAT_CMDSENT;
- }
-
- if (mci_csta & S3C2410_SDICMDSTAT_CRCFAIL) {
- if (cmd->flags & MMC_RSP_CRC) {
- if (host->mrq->cmd->flags & MMC_RSP_136) {
- dbg(host, dbg_irq,
- "fixup: ignore CRC fail with long rsp\n");
- } else {
- /* note, we used to fail the transfer
- * here, but it seems that this is just
- * the hardware getting it wrong.
- *
- * cmd->error = -EILSEQ;
- * host->status = "error: bad command crc";
- * goto fail_transfer;
- */
- }
- }
-
- mci_cclear |= S3C2410_SDICMDSTAT_CRCFAIL;
- }
-
- if (mci_csta & S3C2410_SDICMDSTAT_RSPFIN) {
- if (host->complete_what == COMPLETION_RSPFIN) {
- host->status = "ok: command response received";
- goto close_transfer;
- }
-
- if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
- host->complete_what = COMPLETION_XFERFINISH;
-
- mci_cclear |= S3C2410_SDICMDSTAT_RSPFIN;
- }
-
- /* errors handled after this point are only relevant
- when a data transfer is in progress */
-
- if (!cmd->data)
- goto clear_status_bits;
-
- /* Check for FIFO failure */
- if (host->is2440) {
- if (mci_fsta & S3C2440_SDIFSTA_FIFOFAIL) {
- dbg(host, dbg_err, "FIFO failure\n");
- host->mrq->data->error = -EILSEQ;
- host->status = "error: 2440 fifo failure";
- goto fail_transfer;
- }
- } else {
- if (mci_dsta & S3C2410_SDIDSTA_FIFOFAIL) {
- dbg(host, dbg_err, "FIFO failure\n");
- cmd->data->error = -EILSEQ;
- host->status = "error: fifo failure";
- goto fail_transfer;
- }
- }
-
- if (mci_dsta & S3C2410_SDIDSTA_RXCRCFAIL) {
- dbg(host, dbg_err, "bad data crc (outgoing)\n");
- cmd->data->error = -EILSEQ;
- host->status = "error: bad data crc (outgoing)";
- goto fail_transfer;
- }
-
- if (mci_dsta & S3C2410_SDIDSTA_CRCFAIL) {
- dbg(host, dbg_err, "bad data crc (incoming)\n");
- cmd->data->error = -EILSEQ;
- host->status = "error: bad data crc (incoming)";
- goto fail_transfer;
- }
-
- if (mci_dsta & S3C2410_SDIDSTA_DATATIMEOUT) {
- dbg(host, dbg_err, "data timeout\n");
- cmd->data->error = -ETIMEDOUT;
- host->status = "error: data timeout";
- goto fail_transfer;
- }
-
- if (mci_dsta & S3C2410_SDIDSTA_XFERFINISH) {
- if (host->complete_what == COMPLETION_XFERFINISH) {
- host->status = "ok: data transfer completed";
- goto close_transfer;
- }
-
- if (host->complete_what == COMPLETION_XFERFINISH_RSPFIN)
- host->complete_what = COMPLETION_RSPFIN;
-
- mci_dclear |= S3C2410_SDIDSTA_XFERFINISH;
- }
-
-clear_status_bits:
- writel(mci_cclear, host->base + S3C2410_SDICMDSTAT);
- writel(mci_dclear, host->base + S3C2410_SDIDSTA);
-
- goto irq_out;
-
-fail_transfer:
- host->pio_active = XFER_NONE;
-
-close_transfer:
- host->complete_what = COMPLETION_FINALIZE;
-
- clear_imask(host);
- tasklet_schedule(&host->pio_tasklet);
-
- goto irq_out;
-
-irq_out:
- dbg(host, dbg_irq,
- "csta:0x%08x dsta:0x%08x fsta:0x%08x dcnt:0x%08x status:%s.\n",
- mci_csta, mci_dsta, mci_fsta, mci_dcnt, host->status);
-
- spin_unlock_irqrestore(&host->complete_lock, iflags);
- return IRQ_HANDLED;
-
-}
-
-static void s3cmci_dma_done_callback(void *arg)
-{
- struct s3cmci_host *host = arg;
- unsigned long iflags;
-
- BUG_ON(!host->mrq);
- BUG_ON(!host->mrq->data);
-
- spin_lock_irqsave(&host->complete_lock, iflags);
-
- dbg(host, dbg_dma, "DMA FINISHED\n");
-
- host->dma_complete = 1;
- host->complete_what = COMPLETION_FINALIZE;
-
- tasklet_schedule(&host->pio_tasklet);
- spin_unlock_irqrestore(&host->complete_lock, iflags);
-
-}
-
-static void finalize_request(struct s3cmci_host *host)
-{
- struct mmc_request *mrq = host->mrq;
- struct mmc_command *cmd;
- int debug_as_failure = 0;
-
- if (host->complete_what != COMPLETION_FINALIZE)
- return;
-
- if (!mrq)
- return;
- cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
-
- if (cmd->data && (cmd->error == 0) &&
- (cmd->data->error == 0)) {
- if (s3cmci_host_usedma(host) && (!host->dma_complete)) {
- dbg(host, dbg_dma, "DMA Missing (%d)!\n",
- host->dma_complete);
- return;
- }
- }
-
- /* Read response from controller. */
- cmd->resp[0] = readl(host->base + S3C2410_SDIRSP0);
- cmd->resp[1] = readl(host->base + S3C2410_SDIRSP1);
- cmd->resp[2] = readl(host->base + S3C2410_SDIRSP2);
- cmd->resp[3] = readl(host->base + S3C2410_SDIRSP3);
-
- writel(host->prescaler, host->base + S3C2410_SDIPRE);
-
- if (cmd->error)
- debug_as_failure = 1;
-
- if (cmd->data && cmd->data->error)
- debug_as_failure = 1;
-
- dbg_dumpcmd(host, cmd, debug_as_failure);
-
- /* Cleanup controller */
- writel(0, host->base + S3C2410_SDICMDARG);
- writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
- writel(0, host->base + S3C2410_SDICMDCON);
- clear_imask(host);
-
- if (cmd->data && cmd->error)
- cmd->data->error = cmd->error;
-
- if (cmd->data && cmd->data->stop && (!host->cmd_is_stop)) {
- host->cmd_is_stop = 1;
- s3cmci_send_request(host->mmc);
- return;
- }
-
- /* If we have no data transfer we are finished here */
- if (!mrq->data)
- goto request_done;
-
- /* Calculate the amout of bytes transfer if there was no error */
- if (mrq->data->error == 0) {
- mrq->data->bytes_xfered =
- (mrq->data->blocks * mrq->data->blksz);
- } else {
- mrq->data->bytes_xfered = 0;
- }
-
- /* If we had an error while transferring data we flush the
- * DMA channel and the fifo to clear out any garbage. */
- if (mrq->data->error != 0) {
- if (s3cmci_host_usedma(host))
- dmaengine_terminate_all(host->dma);
-
- if (host->is2440) {
- /* Clear failure register and reset fifo. */
- writel(S3C2440_SDIFSTA_FIFORESET |
- S3C2440_SDIFSTA_FIFOFAIL,
- host->base + S3C2410_SDIFSTA);
- } else {
- u32 mci_con;
-
- /* reset fifo */
- mci_con = readl(host->base + S3C2410_SDICON);
- mci_con |= S3C2410_SDICON_FIFORESET;
-
- writel(mci_con, host->base + S3C2410_SDICON);
- }
- }
-
-request_done:
- host->complete_what = COMPLETION_NONE;
- host->mrq = NULL;
-
- s3cmci_check_sdio_irq(host);
- mmc_request_done(host->mmc, mrq);
-}
-
-static void s3cmci_send_command(struct s3cmci_host *host,
- struct mmc_command *cmd)
-{
- u32 ccon, imsk;
-
- imsk = S3C2410_SDIIMSK_CRCSTATUS | S3C2410_SDIIMSK_CMDTIMEOUT |
- S3C2410_SDIIMSK_RESPONSEND | S3C2410_SDIIMSK_CMDSENT |
- S3C2410_SDIIMSK_RESPONSECRC;
-
- enable_imask(host, imsk);
-
- if (cmd->data)
- host->complete_what = COMPLETION_XFERFINISH_RSPFIN;
- else if (cmd->flags & MMC_RSP_PRESENT)
- host->complete_what = COMPLETION_RSPFIN;
- else
- host->complete_what = COMPLETION_CMDSENT;
-
- writel(cmd->arg, host->base + S3C2410_SDICMDARG);
-
- ccon = cmd->opcode & S3C2410_SDICMDCON_INDEX;
- ccon |= S3C2410_SDICMDCON_SENDERHOST | S3C2410_SDICMDCON_CMDSTART;
-
- if (cmd->flags & MMC_RSP_PRESENT)
- ccon |= S3C2410_SDICMDCON_WAITRSP;
-
- if (cmd->flags & MMC_RSP_136)
- ccon |= S3C2410_SDICMDCON_LONGRSP;
-
- writel(ccon, host->base + S3C2410_SDICMDCON);
-}
-
-static int s3cmci_setup_data(struct s3cmci_host *host, struct mmc_data *data)
-{
- u32 dcon, imsk, stoptries = 3;
-
- if ((data->blksz & 3) != 0) {
- /* We cannot deal with unaligned blocks with more than
- * one block being transferred. */
-
- if (data->blocks > 1) {
- pr_warn("%s: can't do non-word sized block transfers (blksz %d)\n",
- __func__, data->blksz);
- return -EINVAL;
- }
- }
-
- while (readl(host->base + S3C2410_SDIDSTA) &
- (S3C2410_SDIDSTA_TXDATAON | S3C2410_SDIDSTA_RXDATAON)) {
-
- dbg(host, dbg_err,
- "mci_setup_data() transfer stillin progress.\n");
-
- writel(S3C2410_SDIDCON_STOP, host->base + S3C2410_SDIDCON);
- s3cmci_reset(host);
-
- if ((stoptries--) == 0) {
- dbg_dumpregs(host, "DRF");
- return -EINVAL;
- }
- }
-
- dcon = data->blocks & S3C2410_SDIDCON_BLKNUM_MASK;
-
- if (s3cmci_host_usedma(host))
- dcon |= S3C2410_SDIDCON_DMAEN;
-
- if (host->bus_width == MMC_BUS_WIDTH_4)
- dcon |= S3C2410_SDIDCON_WIDEBUS;
-
- dcon |= S3C2410_SDIDCON_BLOCKMODE;
-
- if (data->flags & MMC_DATA_WRITE) {
- dcon |= S3C2410_SDIDCON_TXAFTERRESP;
- dcon |= S3C2410_SDIDCON_XFER_TXSTART;
- }
-
- if (data->flags & MMC_DATA_READ) {
- dcon |= S3C2410_SDIDCON_RXAFTERCMD;
- dcon |= S3C2410_SDIDCON_XFER_RXSTART;
- }
-
- if (host->is2440) {
- dcon |= S3C2440_SDIDCON_DS_WORD;
- dcon |= S3C2440_SDIDCON_DATSTART;
- }
-
- writel(dcon, host->base + S3C2410_SDIDCON);
-
- /* write BSIZE register */
-
- writel(data->blksz, host->base + S3C2410_SDIBSIZE);
-
- /* add to IMASK register */
- imsk = S3C2410_SDIIMSK_FIFOFAIL | S3C2410_SDIIMSK_DATACRC |
- S3C2410_SDIIMSK_DATATIMEOUT | S3C2410_SDIIMSK_DATAFINISH;
-
- enable_imask(host, imsk);
-
- /* write TIMER register */
-
- if (host->is2440) {
- writel(0x007FFFFF, host->base + S3C2410_SDITIMER);
- } else {
- writel(0x0000FFFF, host->base + S3C2410_SDITIMER);
-
- /* FIX: set slow clock to prevent timeouts on read */
- if (data->flags & MMC_DATA_READ)
- writel(0xFF, host->base + S3C2410_SDIPRE);
- }
-
- return 0;
-}
-
-#define BOTH_DIR (MMC_DATA_WRITE | MMC_DATA_READ)
-
-static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data)
-{
- int rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
-
- BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
-
- host->pio_sgptr = 0;
- host->pio_bytes = 0;
- host->pio_count = 0;
- host->pio_active = rw ? XFER_WRITE : XFER_READ;
-
- if (rw) {
- do_pio_write(host);
- enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF);
- } else {
- enable_imask(host, S3C2410_SDIIMSK_RXFIFOHALF
- | S3C2410_SDIIMSK_RXFIFOLAST);
- }
-
- return 0;
-}
-
-static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data)
-{
- int rw = data->flags & MMC_DATA_WRITE;
- struct dma_async_tx_descriptor *desc;
- struct dma_slave_config conf = {
- .src_addr = host->mem->start + host->sdidata,
- .dst_addr = host->mem->start + host->sdidata,
- .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
- .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
- };
-
- BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR);
-
- /* Restore prescaler value */
- writel(host->prescaler, host->base + S3C2410_SDIPRE);
-
- if (!rw)
- conf.direction = DMA_DEV_TO_MEM;
- else
- conf.direction = DMA_MEM_TO_DEV;
-
- dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
- mmc_get_dma_dir(data));
-
- dmaengine_slave_config(host->dma, &conf);
- desc = dmaengine_prep_slave_sg(host->dma, data->sg, data->sg_len,
- conf.direction,
- DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
- if (!desc)
- goto unmap_exit;
- desc->callback = s3cmci_dma_done_callback;
- desc->callback_param = host;
- dmaengine_submit(desc);
- dma_async_issue_pending(host->dma);
-
- return 0;
-
-unmap_exit:
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
- mmc_get_dma_dir(data));
- return -ENOMEM;
-}
-
-static void s3cmci_send_request(struct mmc_host *mmc)
-{
- struct s3cmci_host *host = mmc_priv(mmc);
- struct mmc_request *mrq = host->mrq;
- struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd;
-
- host->ccnt++;
- prepare_dbgmsg(host, cmd, host->cmd_is_stop);
-
- /* Clear command, data and fifo status registers
- Fifo clear only necessary on 2440, but doesn't hurt on 2410
- */
- writel(0xFFFFFFFF, host->base + S3C2410_SDICMDSTAT);
- writel(0xFFFFFFFF, host->base + S3C2410_SDIDSTA);
- writel(0xFFFFFFFF, host->base + S3C2410_SDIFSTA);
-
- if (cmd->data) {
- int res = s3cmci_setup_data(host, cmd->data);
-
- host->dcnt++;
-
- if (res) {
- dbg(host, dbg_err, "setup data error %d\n", res);
- cmd->error = res;
- cmd->data->error = res;
-
- mmc_request_done(mmc, mrq);
- return;
- }
-
- if (s3cmci_host_usedma(host))
- res = s3cmci_prepare_dma(host, cmd->data);
- else
- res = s3cmci_prepare_pio(host, cmd->data);
-
- if (res) {
- dbg(host, dbg_err, "data prepare error %d\n", res);
- cmd->error = res;
- cmd->data->error = res;
-
- mmc_request_done(mmc, mrq);
- return;
- }
- }
-
- /* Send command */
- s3cmci_send_command(host, cmd);
-
- /* Enable Interrupt */
- s3cmci_enable_irq(host, true);
-}
-
-static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
-{
- struct s3cmci_host *host = mmc_priv(mmc);
-
- host->status = "mmc request";
- host->cmd_is_stop = 0;
- host->mrq = mrq;
-
- if (mmc_gpio_get_cd(mmc) == 0) {
- dbg(host, dbg_err, "%s: no medium present\n", __func__);
- host->mrq->cmd->error = -ENOMEDIUM;
- mmc_request_done(mmc, mrq);
- } else
- s3cmci_send_request(mmc);
-}
-
-static void s3cmci_set_clk(struct s3cmci_host *host, struct mmc_ios *ios)
-{
- u32 mci_psc;
-
- /* Set clock */
- for (mci_psc = 0; mci_psc < 255; mci_psc++) {
- host->real_rate = host->clk_rate / (host->clk_div*(mci_psc+1));
-
- if (host->real_rate <= ios->clock)
- break;
- }
-
- if (mci_psc > 255)
- mci_psc = 255;
-
- host->prescaler = mci_psc;
- writel(host->prescaler, host->base + S3C2410_SDIPRE);
-
- /* If requested clock is 0, real_rate will be 0, too */
- if (ios->clock == 0)
- host->real_rate = 0;
-}
-
-static void s3cmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
-{
- struct s3cmci_host *host = mmc_priv(mmc);
- u32 mci_con;
-
- /* Set the power state */
-
- mci_con = readl(host->base + S3C2410_SDICON);
-
- switch (ios->power_mode) {
- case MMC_POWER_ON:
- case MMC_POWER_UP:
- if (!host->is2440)
- mci_con |= S3C2410_SDICON_FIFORESET;
- break;
-
- case MMC_POWER_OFF:
- default:
- if (host->is2440)
- mci_con |= S3C2440_SDICON_SDRESET;
- break;
- }
-
- if (host->pdata->set_power)
- host->pdata->set_power(ios->power_mode, ios->vdd);
-
- s3cmci_set_clk(host, ios);
-
- /* Set CLOCK_ENABLE */
- if (ios->clock)
- mci_con |= S3C2410_SDICON_CLOCKTYPE;
- else
- mci_con &= ~S3C2410_SDICON_CLOCKTYPE;
-
- writel(mci_con, host->base + S3C2410_SDICON);
-
- if ((ios->power_mode == MMC_POWER_ON) ||
- (ios->power_mode == MMC_POWER_UP)) {
- dbg(host, dbg_conf, "running at %lukHz (requested: %ukHz).\n",
- host->real_rate/1000, ios->clock/1000);
- } else {
- dbg(host, dbg_conf, "powered down.\n");
- }
-
- host->bus_width = ios->bus_width;
-}
-
-static void s3cmci_reset(struct s3cmci_host *host)
-{
- u32 con = readl(host->base + S3C2410_SDICON);
-
- con |= S3C2440_SDICON_SDRESET;
- writel(con, host->base + S3C2410_SDICON);
-}
-
-static void s3cmci_enable_sdio_irq(struct mmc_host *mmc, int enable)
-{
- struct s3cmci_host *host = mmc_priv(mmc);
- unsigned long flags;
- u32 con;
-
- local_irq_save(flags);
-
- con = readl(host->base + S3C2410_SDICON);
- host->sdio_irqen = enable;
-
- if (enable == host->sdio_irqen)
- goto same_state;
-
- if (enable) {
- con |= S3C2410_SDICON_SDIOIRQ;
- enable_imask(host, S3C2410_SDIIMSK_SDIOIRQ);
-
- if (!host->irq_state && !host->irq_disabled) {
- host->irq_state = true;
- enable_irq(host->irq);
- }
- } else {
- disable_imask(host, S3C2410_SDIIMSK_SDIOIRQ);
- con &= ~S3C2410_SDICON_SDIOIRQ;
-
- if (!host->irq_enabled && host->irq_state) {
- disable_irq_nosync(host->irq);
- host->irq_state = false;
- }
- }
-
- writel(con, host->base + S3C2410_SDICON);
-
- same_state:
- local_irq_restore(flags);
-
- s3cmci_check_sdio_irq(host);
-}
-
-static const struct mmc_host_ops s3cmci_ops = {
- .request = s3cmci_request,
- .set_ios = s3cmci_set_ios,
- .get_ro = mmc_gpio_get_ro,
- .get_cd = mmc_gpio_get_cd,
- .enable_sdio_irq = s3cmci_enable_sdio_irq,
-};
-
-#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
-
-static int s3cmci_cpufreq_transition(struct notifier_block *nb,
- unsigned long val, void *data)
-{
- struct s3cmci_host *host;
- struct mmc_host *mmc;
- unsigned long newclk;
- unsigned long flags;
-
- host = container_of(nb, struct s3cmci_host, freq_transition);
- newclk = clk_get_rate(host->clk);
- mmc = host->mmc;
-
- if ((val == CPUFREQ_PRECHANGE && newclk > host->clk_rate) ||
- (val == CPUFREQ_POSTCHANGE && newclk < host->clk_rate)) {
- spin_lock_irqsave(&mmc->lock, flags);
-
- host->clk_rate = newclk;
-
- if (mmc->ios.power_mode != MMC_POWER_OFF &&
- mmc->ios.clock != 0)
- s3cmci_set_clk(host, &mmc->ios);
-
- spin_unlock_irqrestore(&mmc->lock, flags);
- }
-
- return 0;
-}
-
-static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
-{
- host->freq_transition.notifier_call = s3cmci_cpufreq_transition;
-
- return cpufreq_register_notifier(&host->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
-{
- cpufreq_unregister_notifier(&host->freq_transition,
- CPUFREQ_TRANSITION_NOTIFIER);
-}
-
-#else
-static inline int s3cmci_cpufreq_register(struct s3cmci_host *host)
-{
- return 0;
-}
-
-static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host)
-{
-}
-#endif
-
-
-#ifdef CONFIG_DEBUG_FS
-
-static int s3cmci_state_show(struct seq_file *seq, void *v)
-{
- struct s3cmci_host *host = seq->private;
-
- seq_printf(seq, "Register base = 0x%p\n", host->base);
- seq_printf(seq, "Clock rate = %ld\n", host->clk_rate);
- seq_printf(seq, "Prescale = %d\n", host->prescaler);
- seq_printf(seq, "is2440 = %d\n", host->is2440);
- seq_printf(seq, "IRQ = %d\n", host->irq);
- seq_printf(seq, "IRQ enabled = %d\n", host->irq_enabled);
- seq_printf(seq, "IRQ disabled = %d\n", host->irq_disabled);
- seq_printf(seq, "IRQ state = %d\n", host->irq_state);
- seq_printf(seq, "CD IRQ = %d\n", host->irq_cd);
- seq_printf(seq, "Do DMA = %d\n", s3cmci_host_usedma(host));
- seq_printf(seq, "SDIIMSK at %d\n", host->sdiimsk);
- seq_printf(seq, "SDIDATA at %d\n", host->sdidata);
-
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(s3cmci_state);
-
-#define DBG_REG(_r) { .addr = S3C2410_SDI##_r, .name = #_r }
-
-struct s3cmci_reg {
- unsigned short addr;
- unsigned char *name;
-};
-
-static const struct s3cmci_reg debug_regs[] = {
- DBG_REG(CON),
- DBG_REG(PRE),
- DBG_REG(CMDARG),
- DBG_REG(CMDCON),
- DBG_REG(CMDSTAT),
- DBG_REG(RSP0),
- DBG_REG(RSP1),
- DBG_REG(RSP2),
- DBG_REG(RSP3),
- DBG_REG(TIMER),
- DBG_REG(BSIZE),
- DBG_REG(DCON),
- DBG_REG(DCNT),
- DBG_REG(DSTA),
- DBG_REG(FSTA),
- {}
-};
-
-static int s3cmci_regs_show(struct seq_file *seq, void *v)
-{
- struct s3cmci_host *host = seq->private;
- const struct s3cmci_reg *rptr = debug_regs;
-
- for (; rptr->name; rptr++)
- seq_printf(seq, "SDI%s\t=0x%08x\n", rptr->name,
- readl(host->base + rptr->addr));
-
- seq_printf(seq, "SDIIMSK\t=0x%08x\n", readl(host->base + host->sdiimsk));
-
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(s3cmci_regs);
-
-static void s3cmci_debugfs_attach(struct s3cmci_host *host)
-{
- struct device *dev = &host->pdev->dev;
- struct dentry *root;
-
- root = debugfs_create_dir(dev_name(dev), NULL);
- host->debug_root = root;
-
- debugfs_create_file("state", 0444, root, host, &s3cmci_state_fops);
- debugfs_create_file("regs", 0444, root, host, &s3cmci_regs_fops);
-}
-
-static void s3cmci_debugfs_remove(struct s3cmci_host *host)
-{
- debugfs_remove_recursive(host->debug_root);
-}
-
-#else
-static inline void s3cmci_debugfs_attach(struct s3cmci_host *host) { }
-static inline void s3cmci_debugfs_remove(struct s3cmci_host *host) { }
-
-#endif /* CONFIG_DEBUG_FS */
-
-static int s3cmci_probe_pdata(struct s3cmci_host *host)
-{
- struct platform_device *pdev = host->pdev;
- struct mmc_host *mmc = host->mmc;
- struct s3c24xx_mci_pdata *pdata;
- int i, ret;
-
- host->is2440 = platform_get_device_id(pdev)->driver_data;
- pdata = pdev->dev.platform_data;
- if (!pdata) {
- dev_err(&pdev->dev, "need platform data");
- return -ENXIO;
- }
-
- for (i = 0; i < 6; i++) {
- pdata->bus[i] = devm_gpiod_get_index(&pdev->dev, "bus", i,
- GPIOD_OUT_LOW);
- if (IS_ERR(pdata->bus[i])) {
- dev_err(&pdev->dev, "failed to get gpio %d\n", i);
- return PTR_ERR(pdata->bus[i]);
- }
- }
-
- if (pdata->no_wprotect)
- mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT;
-
- if (pdata->no_detect)
- mmc->caps |= MMC_CAP_NEEDS_POLL;
-
- if (pdata->wprotect_invert)
- mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
-
- /* If we get -ENOENT we have no card detect GPIO line */
- ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0);
- if (ret != -ENOENT) {
- dev_err(&pdev->dev, "error requesting GPIO for CD %d\n",
- ret);
- return ret;
- }
-
- ret = mmc_gpiod_request_ro(host->mmc, "wp", 0, 0);
- if (ret != -ENOENT) {
- dev_err(&pdev->dev, "error requesting GPIO for WP %d\n",
- ret);
- return ret;
- }
-
- return 0;
-}
-
-static int s3cmci_probe_dt(struct s3cmci_host *host)
-{
- struct platform_device *pdev = host->pdev;
- struct s3c24xx_mci_pdata *pdata;
- struct mmc_host *mmc = host->mmc;
- int ret;
-
- host->is2440 = (long) of_device_get_match_data(&pdev->dev);
-
- ret = mmc_of_parse(mmc);
- if (ret)
- return ret;
-
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- pdev->dev.platform_data = pdata;
-
- return 0;
-}
-
-static int s3cmci_probe(struct platform_device *pdev)
-{
- struct s3cmci_host *host;
- struct mmc_host *mmc;
- int ret;
-
- mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
- if (!mmc) {
- ret = -ENOMEM;
- goto probe_out;
- }
-
- host = mmc_priv(mmc);
- host->mmc = mmc;
- host->pdev = pdev;
-
- if (pdev->dev.of_node)
- ret = s3cmci_probe_dt(host);
- else
- ret = s3cmci_probe_pdata(host);
-
- if (ret)
- goto probe_free_host;
-
- host->pdata = pdev->dev.platform_data;
-
- spin_lock_init(&host->complete_lock);
- tasklet_setup(&host->pio_tasklet, pio_tasklet);
-
- if (host->is2440) {
- host->sdiimsk = S3C2440_SDIIMSK;
- host->sdidata = S3C2440_SDIDATA;
- host->clk_div = 1;
- } else {
- host->sdiimsk = S3C2410_SDIIMSK;
- host->sdidata = S3C2410_SDIDATA;
- host->clk_div = 2;
- }
-
- host->complete_what = COMPLETION_NONE;
- host->pio_active = XFER_NONE;
-
- host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!host->mem) {
- dev_err(&pdev->dev,
- "failed to get io memory region resource.\n");
-
- ret = -ENOENT;
- goto probe_free_host;
- }
-
- host->mem = request_mem_region(host->mem->start,
- resource_size(host->mem), pdev->name);
-
- if (!host->mem) {
- dev_err(&pdev->dev, "failed to request io memory region.\n");
- ret = -ENOENT;
- goto probe_free_host;
- }
-
- host->base = ioremap(host->mem->start, resource_size(host->mem));
- if (!host->base) {
- dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
- ret = -EINVAL;
- goto probe_free_mem_region;
- }
-
- host->irq = platform_get_irq(pdev, 0);
- if (host->irq <= 0) {
- ret = -EINVAL;
- goto probe_iounmap;
- }
-
- if (request_irq(host->irq, s3cmci_irq, IRQF_NO_AUTOEN, DRIVER_NAME, host)) {
- dev_err(&pdev->dev, "failed to request mci interrupt.\n");
- ret = -ENOENT;
- goto probe_iounmap;
- }
-
- host->irq_state = false;
-
- /* Depending on the dma state, get a DMA channel to use. */
-
- if (s3cmci_host_usedma(host)) {
- host->dma = dma_request_chan(&pdev->dev, "rx-tx");
- ret = PTR_ERR_OR_ZERO(host->dma);
- if (ret) {
- dev_err(&pdev->dev, "cannot get DMA channel.\n");
- goto probe_free_irq;
- }
- }
-
- host->clk = clk_get(&pdev->dev, "sdi");
- if (IS_ERR(host->clk)) {
- dev_err(&pdev->dev, "failed to find clock source.\n");
- ret = PTR_ERR(host->clk);
- host->clk = NULL;
- goto probe_free_dma;
- }
-
- ret = clk_prepare_enable(host->clk);
- if (ret) {
- dev_err(&pdev->dev, "failed to enable clock source.\n");
- goto clk_free;
- }
-
- host->clk_rate = clk_get_rate(host->clk);
-
- mmc->ops = &s3cmci_ops;
- mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
-#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ
- mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
-#else
- mmc->caps = MMC_CAP_4_BIT_DATA;
-#endif
- mmc->f_min = host->clk_rate / (host->clk_div * 256);
- mmc->f_max = host->clk_rate / host->clk_div;
-
- if (host->pdata->ocr_avail)
- mmc->ocr_avail = host->pdata->ocr_avail;
-
- mmc->max_blk_count = 4095;
- mmc->max_blk_size = 4095;
- mmc->max_req_size = 4095 * 512;
- mmc->max_seg_size = mmc->max_req_size;
-
- mmc->max_segs = 128;
-
- dbg(host, dbg_debug,
- "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%p.\n",
- (host->is2440?"2440":""),
- host->base, host->irq, host->irq_cd, host->dma);
-
- ret = s3cmci_cpufreq_register(host);
- if (ret) {
- dev_err(&pdev->dev, "failed to register cpufreq\n");
- goto free_dmabuf;
- }
-
- ret = mmc_add_host(mmc);
- if (ret) {
- dev_err(&pdev->dev, "failed to add mmc host.\n");
- goto free_cpufreq;
- }
-
- s3cmci_debugfs_attach(host);
-
- platform_set_drvdata(pdev, mmc);
- dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc),
- s3cmci_host_usedma(host) ? "dma" : "pio",
- mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");
-
- return 0;
-
- free_cpufreq:
- s3cmci_cpufreq_deregister(host);
-
- free_dmabuf:
- clk_disable_unprepare(host->clk);
-
- clk_free:
- clk_put(host->clk);
-
- probe_free_dma:
- if (s3cmci_host_usedma(host))
- dma_release_channel(host->dma);
-
- probe_free_irq:
- free_irq(host->irq, host);
-
- probe_iounmap:
- iounmap(host->base);
-
- probe_free_mem_region:
- release_mem_region(host->mem->start, resource_size(host->mem));
-
- probe_free_host:
- mmc_free_host(mmc);
-
- probe_out:
- return ret;
-}
-
-static void s3cmci_shutdown(struct platform_device *pdev)
-{
- struct mmc_host *mmc = platform_get_drvdata(pdev);
- struct s3cmci_host *host = mmc_priv(mmc);
-
- if (host->irq_cd >= 0)
- free_irq(host->irq_cd, host);
-
- s3cmci_debugfs_remove(host);
- s3cmci_cpufreq_deregister(host);
- mmc_remove_host(mmc);
- clk_disable_unprepare(host->clk);
-}
-
-static int s3cmci_remove(struct platform_device *pdev)
-{
- struct mmc_host *mmc = platform_get_drvdata(pdev);
- struct s3cmci_host *host = mmc_priv(mmc);
-
- s3cmci_shutdown(pdev);
-
- clk_put(host->clk);
-
- tasklet_disable(&host->pio_tasklet);
-
- if (s3cmci_host_usedma(host))
- dma_release_channel(host->dma);
-
- free_irq(host->irq, host);
-
- iounmap(host->base);
- release_mem_region(host->mem->start, resource_size(host->mem));
-
- mmc_free_host(mmc);
- return 0;
-}
-
-static const struct of_device_id s3cmci_dt_match[] = {
- {
- .compatible = "samsung,s3c2410-sdi",
- .data = (void *)0,
- },
- {
- .compatible = "samsung,s3c2412-sdi",
- .data = (void *)1,
- },
- {
- .compatible = "samsung,s3c2440-sdi",
- .data = (void *)1,
- },
- { /* sentinel */ },
-};
-MODULE_DEVICE_TABLE(of, s3cmci_dt_match);
-
-static const struct platform_device_id s3cmci_driver_ids[] = {
- {
- .name = "s3c2410-sdi",
- .driver_data = 0,
- }, {
- .name = "s3c2412-sdi",
- .driver_data = 1,
- }, {
- .name = "s3c2440-sdi",
- .driver_data = 1,
- },
- { }
-};
-
-MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids);
-
-static struct platform_driver s3cmci_driver = {
- .driver = {
- .name = "s3c-sdi",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .of_match_table = s3cmci_dt_match,
- },
- .id_table = s3cmci_driver_ids,
- .probe = s3cmci_probe,
- .remove = s3cmci_remove,
- .shutdown = s3cmci_shutdown,
-};
-
-module_platform_driver(s3cmci_driver);
-
-MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver");
-MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("Thomas Kleffel <[email protected]>, Ben Dooks <[email protected]>");
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h
deleted file mode 100644
index 8b65d7ad9f97..000000000000
--- a/drivers/mmc/host/s3cmci.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * linux/drivers/mmc/s3cmci.h - Samsung S3C MCI driver
- *
- * Copyright (C) 2004-2006 Thomas Kleffel, All Rights Reserved.
- */
-
-enum s3cmci_waitfor {
- COMPLETION_NONE,
- COMPLETION_FINALIZE,
- COMPLETION_CMDSENT,
- COMPLETION_RSPFIN,
- COMPLETION_XFERFINISH,
- COMPLETION_XFERFINISH_RSPFIN,
-};
-
-struct s3cmci_host {
- struct platform_device *pdev;
- struct s3c24xx_mci_pdata *pdata;
- struct mmc_host *mmc;
- struct resource *mem;
- struct clk *clk;
- void __iomem *base;
- int irq;
- int irq_cd;
- struct dma_chan *dma;
-
- unsigned long clk_rate;
- unsigned long clk_div;
- unsigned long real_rate;
- u8 prescaler;
-
- int is2440;
- unsigned sdiimsk;
- unsigned sdidata;
-
- bool irq_disabled;
- bool irq_enabled;
- bool irq_state;
- int sdio_irqen;
-
- struct mmc_request *mrq;
- int cmd_is_stop;
-
- spinlock_t complete_lock;
- enum s3cmci_waitfor complete_what;
-
- int dma_complete;
-
- u32 pio_sgptr;
- u32 pio_bytes;
- u32 pio_count;
- u32 *pio_ptr;
-#define XFER_NONE 0
-#define XFER_READ 1
-#define XFER_WRITE 2
- u32 pio_active;
-
- int bus_width;
-
- char dbgmsg_cmd[301];
- char dbgmsg_dat[301];
- char *status;
-
- unsigned int ccnt, dcnt;
- struct tasklet_struct pio_tasklet;
-
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debug_root;
-#endif
-
-#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
- struct notifier_block freq_transition;
-#endif
-};
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 4cca4c90769b..8f0e639236b1 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -648,10 +648,10 @@ static int sdhci_acpi_emmc_amd_probe_slot(struct platform_device *pdev,
* in reading a garbage value and using the wrong presets.
*
* Since HS400 and HS200 presets must be identical, we could
- * instead use the the SDR104 preset register.
+ * instead use the SDR104 preset register.
*
* If the above issues are resolved we could remove this quirk for
- * firmware that that has valid presets (i.e., SDR12 <= 12 MHz).
+ * firmware that has valid presets (i.e., SDR12 <= 12 MHz).
*/
host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
index 61a12f2f7f03..6a93a54fe067 100644
--- a/drivers/mmc/host/sdhci-bcm-kona.c
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -168,7 +168,7 @@ static void sdhci_bcm_kona_init_74_clocks(struct sdhci_host *host,
/*
* JEDEC and SD spec specify supplying 74 continuous clocks to
* device after power up. With minimum bus (100KHz) that
- * that translates to 740us
+ * translates to 740us
*/
if (power_mode != MMC_POWER_OFF)
udelay(740);
diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
index 55d8bd232695..f2cf3d70db79 100644
--- a/drivers/mmc/host/sdhci-brcmstb.c
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -179,7 +179,7 @@ static const struct brcmstb_match_priv match_priv_7216 = {
.ops = &sdhci_brcmstb_ops_7216,
};
-static const struct of_device_id sdhci_brcm_of_match[] = {
+static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = {
{ .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
{ .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
{ .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c
deleted file mode 100644
index 2a29c7a4f308..000000000000
--- a/drivers/mmc/host/sdhci-cns3xxx.c
+++ /dev/null
@@ -1,113 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * SDHCI support for CNS3xxx SoC
- *
- * Copyright 2008 Cavium Networks
- * Copyright 2010 MontaVista Software, LLC.
- *
- * Authors: Scott Shu
- * Anton Vorontsov <[email protected]>
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/mmc/host.h>
-#include <linux/module.h>
-#include "sdhci-pltfm.h"
-
-static unsigned int sdhci_cns3xxx_get_max_clk(struct sdhci_host *host)
-{
- return 150000000;
-}
-
-static void sdhci_cns3xxx_set_clock(struct sdhci_host *host, unsigned int clock)
-{
- struct device *dev = mmc_dev(host->mmc);
- int div = 1;
- u16 clk;
- unsigned long timeout;
-
- host->mmc->actual_clock = 0;
-
- sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
-
- if (clock == 0)
- return;
-
- while (host->max_clk / div > clock) {
- /*
- * On CNS3xxx divider grows linearly up to 4, and then
- * exponentially up to 256.
- */
- if (div < 4)
- div += 1;
- else if (div < 256)
- div *= 2;
- else
- break;
- }
-
- dev_dbg(dev, "desired SD clock: %d, actual: %d\n",
- clock, host->max_clk / div);
-
- /* Divide by 3 is special. */
- if (div != 3)
- div >>= 1;
-
- clk = div << SDHCI_DIVIDER_SHIFT;
- clk |= SDHCI_CLOCK_INT_EN;
- sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
-
- timeout = 20;
- while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
- & SDHCI_CLOCK_INT_STABLE)) {
- if (timeout == 0) {
- dev_warn(dev, "clock is unstable");
- break;
- }
- timeout--;
- mdelay(1);
- }
-
- clk |= SDHCI_CLOCK_CARD_EN;
- sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
-}
-
-static const struct sdhci_ops sdhci_cns3xxx_ops = {
- .get_max_clock = sdhci_cns3xxx_get_max_clk,
- .set_clock = sdhci_cns3xxx_set_clock,
- .set_bus_width = sdhci_set_bus_width,
- .reset = sdhci_reset,
- .set_uhs_signaling = sdhci_set_uhs_signaling,
-};
-
-static const struct sdhci_pltfm_data sdhci_cns3xxx_pdata = {
- .ops = &sdhci_cns3xxx_ops,
- .quirks = SDHCI_QUIRK_BROKEN_DMA |
- SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
- SDHCI_QUIRK_INVERTED_WRITE_PROTECT |
- SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
- SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
-};
-
-static int sdhci_cns3xxx_probe(struct platform_device *pdev)
-{
- return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata, 0);
-}
-
-static struct platform_driver sdhci_cns3xxx_driver = {
- .driver = {
- .name = "sdhci-cns3xxx",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &sdhci_pltfm_pmops,
- },
- .probe = sdhci_cns3xxx_probe,
- .remove = sdhci_pltfm_unregister,
-};
-
-module_platform_driver(sdhci_cns3xxx_driver);
-
-MODULE_DESCRIPTION("SDHCI driver for CNS3xxx");
-MODULE_AUTHOR("Scott Shu, "
- "Anton Vorontsov <[email protected]>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index ffeb5759830f..9e73c34b6401 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -107,6 +107,7 @@
#define ESDHC_TUNING_START_TAP_DEFAULT 0x1
#define ESDHC_TUNING_START_TAP_MASK 0x7f
#define ESDHC_TUNING_CMD_CRC_CHECK_DISABLE (1 << 7)
+#define ESDHC_TUNING_STEP_DEFAULT 0x1
#define ESDHC_TUNING_STEP_MASK 0x00070000
#define ESDHC_TUNING_STEP_SHIFT 16
@@ -307,7 +308,8 @@ static struct esdhc_soc_data usdhc_imx7ulp_data = {
| ESDHC_FLAG_STATE_LOST_IN_LPMODE,
};
static struct esdhc_soc_data usdhc_imxrt1050_data = {
- .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_HS200 | ESDHC_FLAG_ERR004536,
+ .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
+ | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200,
};
static struct esdhc_soc_data usdhc_imx8qxp_data = {
@@ -1012,6 +1014,44 @@ static void esdhc_pltfm_set_bus_width(struct sdhci_host *host, int width)
SDHCI_HOST_CONTROL);
}
+static void esdhc_reset_tuning(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
+ u32 ctrl;
+ int ret;
+
+ /* Reset the tuning circuit */
+ if (esdhc_is_usdhc(imx_data)) {
+ if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
+ ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
+ ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+ ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
+ writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
+ writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
+ } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
+ ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
+ ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+ ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
+ writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
+ /* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */
+ ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS,
+ ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50);
+ if (ret == -ETIMEDOUT)
+ dev_warn(mmc_dev(host->mmc),
+ "Warning! clear execute tuning bit failed\n");
+ /*
+ * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the
+ * usdhc IP internal logic flag execute_tuning_with_clr_buf, which
+ * will finally make sure the normal data transfer logic correct.
+ */
+ ctrl = readl(host->ioaddr + SDHCI_INT_STATUS);
+ ctrl |= SDHCI_INT_DATA_AVAIL;
+ writel(ctrl, host->ioaddr + SDHCI_INT_STATUS);
+ }
+ }
+}
+
static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
@@ -1023,6 +1063,12 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode)
if (host->timing == MMC_TIMING_UHS_DDR50)
return 0;
+ /*
+ * Reset tuning circuit logic. If not, the previous tuning result
+ * will impact current tuning, make current tuning can't set the
+ * correct delay cell.
+ */
+ esdhc_reset_tuning(host);
return sdhci_execute_tuning(mmc, opcode);
}
@@ -1196,44 +1242,6 @@ static void esdhc_set_strobe_dll(struct sdhci_host *host)
"warning! HS400 strobe DLL status REF/SLV not lock in 50us, STROBE DLL status is %x!\n", v);
}
-static void esdhc_reset_tuning(struct sdhci_host *host)
-{
- struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
- struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
- u32 ctrl;
- int ret;
-
- /* Reset the tuning circuit */
- if (esdhc_is_usdhc(imx_data)) {
- if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) {
- ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL);
- ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
- ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
- writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL);
- writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS);
- } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
- ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS);
- ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
- ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE;
- writel(ctrl, host->ioaddr + SDHCI_AUTO_CMD_STATUS);
- /* Make sure ESDHC_MIX_CTRL_EXE_TUNE cleared */
- ret = readl_poll_timeout(host->ioaddr + SDHCI_AUTO_CMD_STATUS,
- ctrl, !(ctrl & ESDHC_MIX_CTRL_EXE_TUNE), 1, 50);
- if (ret == -ETIMEDOUT)
- dev_warn(mmc_dev(host->mmc),
- "Warning! clear execute tuning bit failed\n");
- /*
- * SDHCI_INT_DATA_AVAIL is W1C bit, set this bit will clear the
- * usdhc IP internal logic flag execute_tuning_with_clr_buf, which
- * will finally make sure the normal data transfer logic correct.
- */
- ctrl = readl(host->ioaddr + SDHCI_INT_STATUS);
- ctrl |= SDHCI_INT_DATA_AVAIL;
- writel(ctrl, host->ioaddr + SDHCI_INT_STATUS);
- }
- }
-}
-
static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
{
u32 m;
@@ -1361,7 +1369,7 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
struct cqhci_host *cq_host = host->mmc->cqe_private;
- int tmp;
+ u32 tmp;
if (esdhc_is_usdhc(imx_data)) {
/*
@@ -1416,17 +1424,24 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) {
tmp = readl(host->ioaddr + ESDHC_TUNING_CTRL);
- tmp |= ESDHC_STD_TUNING_EN |
- ESDHC_TUNING_START_TAP_DEFAULT;
- if (imx_data->boarddata.tuning_start_tap) {
- tmp &= ~ESDHC_TUNING_START_TAP_MASK;
+ tmp |= ESDHC_STD_TUNING_EN;
+
+ /*
+ * ROM code or bootloader may config the start tap
+ * and step, unmask them first.
+ */
+ tmp &= ~(ESDHC_TUNING_START_TAP_MASK | ESDHC_TUNING_STEP_MASK);
+ if (imx_data->boarddata.tuning_start_tap)
tmp |= imx_data->boarddata.tuning_start_tap;
- }
+ else
+ tmp |= ESDHC_TUNING_START_TAP_DEFAULT;
if (imx_data->boarddata.tuning_step) {
- tmp &= ~ESDHC_TUNING_STEP_MASK;
tmp |= imx_data->boarddata.tuning_step
<< ESDHC_TUNING_STEP_SHIFT;
+ } else {
+ tmp |= ESDHC_TUNING_STEP_DEFAULT
+ << ESDHC_TUNING_STEP_SHIFT;
}
/* Disable the CMD CRC check for tuning, if not, need to
@@ -1454,7 +1469,7 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host)
/*
* On i.MX8MM, we are running Dual Linux OS, with 1st Linux using SD Card
- * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let the
+ * as rootfs storage, 2nd Linux using eMMC as rootfs storage. We let
* the 1st linux configure power/clock for the 2nd Linux.
*
* When the 2nd Linux is booting into rootfs stage, we let the 1st Linux
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 3a091a387ecb..4ac8651d0b29 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -19,6 +19,7 @@
#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
+#include "sdhci-cqhci.h"
#include "sdhci-pltfm.h"
#include "cqhci.h"
@@ -2218,8 +2219,7 @@ static int __sdhci_msm_check_write(struct sdhci_host *host, u16 val, int reg)
if (!msm_host->use_cdr)
break;
if ((msm_host->transfer_mode & SDHCI_TRNS_READ) &&
- SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK_HS200 &&
- SDHCI_GET_CMD(val) != MMC_SEND_TUNING_BLOCK)
+ !mmc_op_tuning(SDHCI_GET_CMD(val)))
sdhci_msm_set_cdr(host, true);
else
sdhci_msm_set_cdr(host, false);
@@ -2304,13 +2304,6 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host)
pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps);
}
-static void sdhci_msm_reset(struct sdhci_host *host, u8 mask)
-{
- if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL))
- cqhci_deactivate(host->mmc);
- sdhci_reset(host, mask);
-}
-
static int sdhci_msm_register_vreg(struct sdhci_msm_host *msm_host)
{
int ret;
@@ -2450,7 +2443,7 @@ static const struct of_device_id sdhci_msm_dt_match[] = {
MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
static const struct sdhci_ops sdhci_msm_ops = {
- .reset = sdhci_msm_reset,
+ .reset = sdhci_and_cqhci_reset,
.set_clock = sdhci_msm_set_clock,
.get_min_clock = sdhci_msm_get_min_clock,
.get_max_clock = sdhci_msm_get_max_clock,
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index cfb891430174..89c431a34c43 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -21,6 +21,7 @@
#include <linux/of_device.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
+#include <linux/reset.h>
#include <linux/of.h>
#include <linux/firmware/xlnx-zynqmp.h>
@@ -1522,6 +1523,65 @@ static int sdhci_arasan_register_sdclk(struct sdhci_arasan_data *sdhci_arasan,
return 0;
}
+static int sdhci_zynqmp_set_dynamic_config(struct device *dev,
+ struct sdhci_arasan_data *sdhci_arasan)
+{
+ struct sdhci_host *host = sdhci_arasan->host;
+ struct clk_hw *hw = &sdhci_arasan->clk_data.sdcardclk_hw;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 mhz, node_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : NODE_SD_1;
+ struct reset_control *rstc;
+ int ret;
+
+ /* Obtain SDHC reset control */
+ rstc = devm_reset_control_get_optional_exclusive(dev, NULL);
+ if (IS_ERR(rstc)) {
+ dev_err(dev, "Cannot get SDHC reset.\n");
+ return PTR_ERR(rstc);
+ }
+
+ ret = reset_control_assert(rstc);
+ if (ret)
+ return ret;
+
+ ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_FIXED, 0);
+ if (ret)
+ return ret;
+
+ ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_EMMC_SEL,
+ !!(host->mmc->caps & MMC_CAP_NONREMOVABLE));
+ if (ret)
+ return ret;
+
+ mhz = DIV_ROUND_CLOSEST_ULL(clk_get_rate(pltfm_host->clk), 1000000);
+ if (mhz > 100 && mhz <= 200)
+ mhz = 200;
+ else if (mhz > 50 && mhz <= 100)
+ mhz = 100;
+ else if (mhz > 25 && mhz <= 50)
+ mhz = 50;
+ else
+ mhz = 25;
+
+ ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_BASECLK, mhz);
+ if (ret)
+ return ret;
+
+ ret = zynqmp_pm_set_sd_config(node_id, SD_CONFIG_8BIT,
+ !!(host->mmc->caps & MMC_CAP_8_BIT_DATA));
+ if (ret)
+ return ret;
+
+ ret = reset_control_deassert(rstc);
+ if (ret)
+ return ret;
+
+ usleep_range(1000, 1500);
+
+ return 0;
+}
+
static int sdhci_arasan_add_host(struct sdhci_arasan_data *sdhci_arasan)
{
struct sdhci_host *host = sdhci_arasan->host;
@@ -1686,6 +1746,15 @@ static int sdhci_arasan_probe(struct platform_device *pdev)
goto unreg_clk;
}
+ if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) {
+ ret = zynqmp_pm_is_function_supported(PM_IOCTL, IOCTL_SET_SD_CONFIG);
+ if (!ret) {
+ ret = sdhci_zynqmp_set_dynamic_config(dev, sdhci_arasan);
+ if (ret)
+ goto unreg_clk;
+ }
+ }
+
sdhci_arasan->phy = ERR_PTR(-ENODEV);
if (of_device_is_compatible(np, "arasan,sdhci-5.1")) {
sdhci_arasan->phy = devm_phy_get(dev, "phy_arasan");
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index e0266638381d..4712adac7f7c 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -42,6 +42,12 @@ static const struct esdhc_clk_fixup ls1021a_esdhc_clk = {
.max_clk[MMC_TIMING_SD_HS] = 46500000,
};
+static const struct esdhc_clk_fixup ls1043a_esdhc_clk = {
+ .sd_dflt_max_clk = 25000000,
+ .max_clk[MMC_TIMING_UHS_SDR104] = 116700000,
+ .max_clk[MMC_TIMING_MMC_HS200] = 116700000,
+};
+
static const struct esdhc_clk_fixup ls1046a_esdhc_clk = {
.sd_dflt_max_clk = 25000000,
.max_clk[MMC_TIMING_UHS_SDR104] = 167000000,
@@ -63,6 +69,7 @@ static const struct esdhc_clk_fixup p1010_esdhc_clk = {
static const struct of_device_id sdhci_esdhc_of_match[] = {
{ .compatible = "fsl,ls1021a-esdhc", .data = &ls1021a_esdhc_clk},
+ { .compatible = "fsl,ls1043a-esdhc", .data = &ls1043a_esdhc_clk},
{ .compatible = "fsl,ls1046a-esdhc", .data = &ls1046a_esdhc_clk},
{ .compatible = "fsl,ls1012a-esdhc", .data = &ls1012a_esdhc_clk},
{ .compatible = "fsl,p1010-esdhc", .data = &p1010_esdhc_clk},
@@ -91,7 +98,7 @@ struct sdhci_esdhc {
};
/**
- * esdhc_read*_fixup - Fixup the value read from incompatible eSDHC register
+ * esdhc_readl_fixup - Fixup the value read from incompatible eSDHC register
* to make it compatible with SD spec.
*
* @host: pointer to sdhci_host
@@ -216,7 +223,7 @@ static u8 esdhc_readb_fixup(struct sdhci_host *host,
}
/**
- * esdhc_write*_fixup - Fixup the SD spec register value so that it could be
+ * esdhc_writel_fixup - Fixup the SD spec register value so that it could be
* written into eSDHC register.
*
* @host: pointer to sdhci_host
diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c
index 033be559a730..8ed9256b83da 100644
--- a/drivers/mmc/host/sdhci-omap.c
+++ b/drivers/mmc/host/sdhci-omap.c
@@ -370,7 +370,7 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
/*
* Stage 1: Search for a maximum pass window ignoring any
- * any single point failures. If the tuning value ends up
+ * single point failures. If the tuning value ends up
* near it, move away from it in stage 2 below
*/
while (phase_delay <= MAX_PHASE_DELAY) {
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 28dc65023fa9..c359f867df0a 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -38,6 +38,7 @@
#include "cqhci.h"
#include "sdhci.h"
+#include "sdhci-cqhci.h"
#include "sdhci-pci.h"
static void sdhci_pci_hw_reset(struct sdhci_host *host);
@@ -234,14 +235,6 @@ static void sdhci_pci_dumpregs(struct mmc_host *mmc)
sdhci_dumpregs(mmc_priv(mmc));
}
-static void sdhci_cqhci_reset(struct sdhci_host *host, u8 mask)
-{
- if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL) &&
- host->mmc->cqe_private)
- cqhci_deactivate(host->mmc);
- sdhci_reset(host, mask);
-}
-
/*****************************************************************************\
* *
* Hardware specific quirk handling *
@@ -703,7 +696,7 @@ static const struct sdhci_ops sdhci_intel_glk_ops = {
.set_power = sdhci_intel_set_power,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
- .reset = sdhci_cqhci_reset,
+ .reset = sdhci_and_cqhci_reset,
.set_uhs_signaling = sdhci_intel_set_uhs_signaling,
.hw_reset = sdhci_pci_hw_reset,
.irq = sdhci_cqhci_irq,
@@ -2283,7 +2276,8 @@ static struct pci_driver sdhci_driver = {
.probe = sdhci_pci_probe,
.remove = sdhci_pci_remove,
.driver = {
- .pm = &sdhci_pci_pm_ops
+ .pm = &sdhci_pci_pm_ops,
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
index 4d509f656188..633a8ee8f8c5 100644
--- a/drivers/mmc/host/sdhci-pci-gli.c
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -15,6 +15,7 @@
#include <linux/of.h>
#include <linux/iopoll.h>
#include "sdhci.h"
+#include "sdhci-cqhci.h"
#include "sdhci-pci.h"
#include "cqhci.h"
@@ -922,14 +923,6 @@ cleanup:
return ret;
}
-static void sdhci_gl9763e_reset(struct sdhci_host *host, u8 mask)
-{
- if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL) &&
- host->mmc->cqe_private)
- cqhci_deactivate(host->mmc);
- sdhci_reset(host, mask);
-}
-
static void gli_set_gl9763e(struct sdhci_pci_slot *slot)
{
struct pci_dev *pdev = slot->chip->pdev;
@@ -1136,7 +1129,7 @@ static const struct sdhci_ops sdhci_gl9763e_ops = {
.set_clock = sdhci_set_clock,
.enable_dma = sdhci_pci_enable_dma,
.set_bus_width = sdhci_set_bus_width,
- .reset = sdhci_gl9763e_reset,
+ .reset = sdhci_and_cqhci_reset,
.set_uhs_signaling = sdhci_set_gl9763e_signaling,
.voltage_switch = sdhci_gli_voltage_switch,
.irq = sdhci_gl9763e_cqhci_irq,
diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c
index bca1d095b759..98cadff47b2b 100644
--- a/drivers/mmc/host/sdhci-pci-o2micro.c
+++ b/drivers/mmc/host/sdhci-pci-o2micro.c
@@ -326,8 +326,7 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode)
(host->timing != MMC_TIMING_UHS_SDR50))
return sdhci_execute_tuning(mmc, opcode);
- if (WARN_ON((opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
- (opcode != MMC_SEND_TUNING_BLOCK)))
+ if (WARN_ON(!mmc_op_tuning(opcode)))
return -EINVAL;
/* Force power mode enter L0 */
diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c
index bec3f9e3cd3f..525f979e2a97 100644
--- a/drivers/mmc/host/sdhci-sprd.c
+++ b/drivers/mmc/host/sdhci-sprd.c
@@ -228,13 +228,15 @@ static inline void _sdhci_sprd_set_clock(struct sdhci_host *host,
div = ((div & 0x300) >> 2) | ((div & 0xFF) << 8);
sdhci_enable_clk(host, div);
- /* enable auto gate sdhc_enable_auto_gate */
- val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI);
- mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN |
- SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN;
- if (mask != (val & mask)) {
- val |= mask;
- sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI);
+ /* Enable CLK_AUTO when the clock is greater than 400K. */
+ if (clk > 400000) {
+ val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI);
+ mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN |
+ SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN;
+ if (mask != (val & mask)) {
+ val |= mask;
+ sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI);
+ }
}
}
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index c71000a07656..bff084f178c9 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -3,28 +3,30 @@
* Copyright (C) 2010 Google, Inc.
*/
+#include <linux/bitfield.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
-#include <linux/module.h>
+#include <linux/gpio/consumer.h>
#include <linux/init.h>
-#include <linux/iopoll.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/of.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/ktime.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
-#include <linux/mmc/card.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/mmc.h>
-#include <linux/mmc/slot-gpio.h>
-#include <linux/gpio/consumer.h>
-#include <linux/ktime.h>
#include <soc/tegra/common.h>
@@ -95,6 +97,8 @@
#define SDHCI_TEGRA_AUTO_CAL_STATUS 0x1ec
#define SDHCI_TEGRA_AUTO_CAL_ACTIVE BIT(31)
+#define SDHCI_TEGRA_CIF2AXI_CTRL_0 0x1fc
+
#define NVQUIRK_FORCE_SDHCI_SPEC_200 BIT(0)
#define NVQUIRK_ENABLE_BLOCK_GAP_DET BIT(1)
#define NVQUIRK_ENABLE_SDHCI_SPEC_300 BIT(2)
@@ -122,6 +126,7 @@
#define NVQUIRK_HAS_TMCLK BIT(10)
#define NVQUIRK_HAS_ANDROID_GPT_SECTOR BIT(11)
+#define NVQUIRK_PROGRAM_STREAMID BIT(12)
/* SDMMC CQE Base Address for Tegra Host Ver 4.1 and Higher */
#define SDHCI_TEGRA_CQE_BASE_ADDR 0xF000
@@ -178,6 +183,7 @@ struct sdhci_tegra {
bool enable_hwcq;
unsigned long curr_clk_rate;
u8 tuned_tap_delay;
+ u32 stream_id;
};
static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg)
@@ -268,13 +274,9 @@ static void tegra210_sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
bool is_tuning_cmd = 0;
bool clk_enabled;
- u8 cmd;
- if (reg == SDHCI_COMMAND) {
- cmd = SDHCI_GET_CMD(val);
- is_tuning_cmd = cmd == MMC_SEND_TUNING_BLOCK ||
- cmd == MMC_SEND_TUNING_BLOCK_HS200;
- }
+ if (reg == SDHCI_COMMAND)
+ is_tuning_cmd = mmc_op_tuning(SDHCI_GET_CMD(val));
if (is_tuning_cmd)
clk_enabled = tegra_sdhci_configure_card_clk(host, 0);
@@ -1526,7 +1528,8 @@ static const struct sdhci_pltfm_data sdhci_tegra186_pdata = {
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER,
.ops = &tegra186_sdhci_ops,
};
@@ -1557,7 +1560,22 @@ static const struct sdhci_tegra_soc_data soc_data_tegra194 = {
.max_tap_delay = 139,
};
+static const struct sdhci_tegra_soc_data soc_data_tegra234 = {
+ .pdata = &sdhci_tegra186_pdata,
+ .dma_mask = DMA_BIT_MASK(39),
+ .nvquirks = NVQUIRK_NEEDS_PAD_CONTROL |
+ NVQUIRK_HAS_PADCALIB |
+ NVQUIRK_DIS_CARD_CLK_CONFIG_TAP |
+ NVQUIRK_ENABLE_SDR50 |
+ NVQUIRK_ENABLE_SDR104 |
+ NVQUIRK_PROGRAM_STREAMID |
+ NVQUIRK_HAS_TMCLK,
+ .min_tap_delay = 95,
+ .max_tap_delay = 111,
+};
+
static const struct of_device_id sdhci_tegra_dt_match[] = {
+ { .compatible = "nvidia,tegra234-sdhci", .data = &soc_data_tegra234 },
{ .compatible = "nvidia,tegra194-sdhci", .data = &soc_data_tegra194 },
{ .compatible = "nvidia,tegra186-sdhci", .data = &soc_data_tegra186 },
{ .compatible = "nvidia,tegra210-sdhci", .data = &soc_data_tegra210 },
@@ -1617,6 +1635,19 @@ cleanup:
return ret;
}
+/* Program MC streamID for DMA transfers */
+static void sdhci_tegra_program_stream_id(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_tegra *tegra_host = sdhci_pltfm_priv(pltfm_host);
+
+ if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID) {
+ tegra_sdhci_writel(host, FIELD_PREP(GENMASK(15, 8), tegra_host->stream_id) |
+ FIELD_PREP(GENMASK(7, 0), tegra_host->stream_id),
+ SDHCI_TEGRA_CIF2AXI_CTRL_0);
+ }
+}
+
static int sdhci_tegra_probe(struct platform_device *pdev)
{
const struct sdhci_tegra_soc_data *soc_data;
@@ -1677,6 +1708,12 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
tegra_sdhci_parse_dt(host);
+ if (tegra_host->soc_data->nvquirks & NVQUIRK_PROGRAM_STREAMID &&
+ !tegra_dev_iommu_get_stream_id(&pdev->dev, &tegra_host->stream_id)) {
+ dev_warn(mmc_dev(host->mmc), "missing IOMMU stream ID\n");
+ tegra_host->stream_id = 0x7f;
+ }
+
tegra_host->power_gpio = devm_gpiod_get_optional(&pdev->dev, "power",
GPIOD_OUT_HIGH);
if (IS_ERR(tegra_host->power_gpio)) {
@@ -1762,6 +1799,8 @@ static int sdhci_tegra_probe(struct platform_device *pdev)
if (rc)
goto err_add_host;
+ sdhci_tegra_program_stream_id(host);
+
return 0;
err_add_host:
@@ -1858,6 +1897,8 @@ static int sdhci_tegra_resume(struct device *dev)
if (ret)
return ret;
+ sdhci_tegra_program_stream_id(host);
+
ret = sdhci_resume_host(host);
if (ret)
goto disable_clk;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index c7ad32a75b57..f3af1bd0f7b9 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -270,6 +270,11 @@ enum sdhci_reset_reason {
static void sdhci_reset_for_reason(struct sdhci_host *host, enum sdhci_reset_reason reason)
{
+ if (host->quirks2 & SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER) {
+ sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ return;
+ }
+
switch (reason) {
case SDHCI_RESET_FOR_INIT:
sdhci_do_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
@@ -526,7 +531,6 @@ static inline bool sdhci_has_requests(struct sdhci_host *host)
static void sdhci_read_block_pio(struct sdhci_host *host)
{
- unsigned long flags;
size_t blksize, len, chunk;
u32 scratch;
u8 *buf;
@@ -536,8 +540,6 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
blksize = host->data->blksz;
chunk = 0;
- local_irq_save(flags);
-
while (blksize) {
BUG_ON(!sg_miter_next(&host->sg_miter));
@@ -564,13 +566,10 @@ static void sdhci_read_block_pio(struct sdhci_host *host)
}
sg_miter_stop(&host->sg_miter);
-
- local_irq_restore(flags);
}
static void sdhci_write_block_pio(struct sdhci_host *host)
{
- unsigned long flags;
size_t blksize, len, chunk;
u32 scratch;
u8 *buf;
@@ -581,8 +580,6 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
chunk = 0;
scratch = 0;
- local_irq_save(flags);
-
while (blksize) {
BUG_ON(!sg_miter_next(&host->sg_miter));
@@ -609,8 +606,6 @@ static void sdhci_write_block_pio(struct sdhci_host *host)
}
sg_miter_stop(&host->sg_miter);
-
- local_irq_restore(flags);
}
static void sdhci_transfer_pio(struct sdhci_host *host)
@@ -706,16 +701,14 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
return sg_count;
}
-static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags)
+static char *sdhci_kmap_atomic(struct scatterlist *sg)
{
- local_irq_save(*flags);
- return kmap_atomic(sg_page(sg)) + sg->offset;
+ return kmap_local_page(sg_page(sg)) + sg->offset;
}
-static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags)
+static void sdhci_kunmap_atomic(void *buffer)
{
- kunmap_atomic(buffer);
- local_irq_restore(*flags);
+ kunmap_local(buffer);
}
void sdhci_adma_write_desc(struct sdhci_host *host, void **desc,
@@ -757,7 +750,6 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
struct mmc_data *data, int sg_count)
{
struct scatterlist *sg;
- unsigned long flags;
dma_addr_t addr, align_addr;
void *desc, *align;
char *buffer;
@@ -789,9 +781,9 @@ static void sdhci_adma_table_pre(struct sdhci_host *host,
SDHCI_ADMA2_MASK;
if (offset) {
if (data->flags & MMC_DATA_WRITE) {
- buffer = sdhci_kmap_atomic(sg, &flags);
+ buffer = sdhci_kmap_atomic(sg);
memcpy(align, buffer, offset);
- sdhci_kunmap_atomic(buffer, &flags);
+ sdhci_kunmap_atomic(buffer);
}
/* tran, valid */
@@ -852,7 +844,6 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
int i, size;
void *align;
char *buffer;
- unsigned long flags;
if (data->flags & MMC_DATA_READ) {
bool has_unaligned = false;
@@ -875,9 +866,9 @@ static void sdhci_adma_table_post(struct sdhci_host *host,
size = SDHCI_ADMA2_ALIGN -
(sg_dma_address(sg) & SDHCI_ADMA2_MASK);
- buffer = sdhci_kmap_atomic(sg, &flags);
+ buffer = sdhci_kmap_atomic(sg);
memcpy(buffer, align, size);
- sdhci_kunmap_atomic(buffer, &flags);
+ sdhci_kunmap_atomic(buffer);
align += SDHCI_ADMA2_ALIGN;
}
@@ -1466,7 +1457,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host,
if (host->quirks2 &
SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD) {
/* must not clear SDHCI_TRANSFER_MODE when tuning */
- if (cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200)
+ if (!mmc_op_tuning(cmd->opcode))
sdhci_writew(host, 0x0, SDHCI_TRANSFER_MODE);
} else {
/* clear Auto CMD settings for no data CMDs */
@@ -1707,8 +1698,7 @@ static bool sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
flags |= SDHCI_CMD_INDEX;
/* CMD19 is special in that the Data Present Select should be set */
- if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK ||
- cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)
+ if (cmd->data || mmc_op_tuning(cmd->opcode))
flags |= SDHCI_CMD_DATA;
timeout = jiffies;
@@ -2304,7 +2294,7 @@ static bool sdhci_timing_has_preset(unsigned char timing)
case MMC_TIMING_UHS_DDR50:
case MMC_TIMING_MMC_DDR52:
return true;
- };
+ }
return false;
}
@@ -2418,8 +2408,21 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->version >= SDHCI_SPEC_300) {
u16 clk, ctrl_2;
+ /*
+ * According to SDHCI Spec v3.00, if the Preset Value
+ * Enable in the Host Control 2 register is set, we
+ * need to reset SD Clock Enable before changing High
+ * Speed Enable to avoid generating clock glitches.
+ */
+ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
+ if (clk & SDHCI_CLOCK_CARD_EN) {
+ clk &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
+ }
+
+ sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
+
if (!host->preset_enabled) {
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
/*
* We only need to set Driver Strength if the
* preset value enable is not set.
@@ -2442,30 +2445,8 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
host->drv_type = ios->drv_type;
- } else {
- /*
- * According to SDHC Spec v3.00, if the Preset Value
- * Enable in the Host Control 2 register is set, we
- * need to reset SD Clock Enable before changing High
- * Speed Enable to avoid generating clock gliches.
- */
-
- /* Reset SD Clock Enable */
- clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
- clk &= ~SDHCI_CLOCK_CARD_EN;
- sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
-
- sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
-
- /* Re-enable SD Clock */
- host->ops->set_clock(host, host->clock);
}
- /* Reset SD Clock Enable */
- clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL);
- clk &= ~SDHCI_CLOCK_CARD_EN;
- sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);
-
host->ops->set_uhs_signaling(host, ios->timing);
host->timing = ios->timing;
@@ -3388,8 +3369,6 @@ static void sdhci_adma_show_error(struct sdhci_host *host)
static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
{
- u32 command;
-
/*
* CMD19 generates _only_ Buffer Read Ready interrupt if
* use sdhci_send_tuning.
@@ -3398,9 +3377,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
* SDHCI_INT_DATA_AVAIL always there, stuck in irq storm.
*/
if (intmask & SDHCI_INT_DATA_AVAIL && !host->data) {
- command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND));
- if (command == MMC_SEND_TUNING_BLOCK ||
- command == MMC_SEND_TUNING_BLOCK_HS200) {
+ if (mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)))) {
host->tuning_done = 1;
wake_up(&host->buf_ready_int);
return;
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 87a3aaa07438..605eaee805f7 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -345,7 +345,7 @@ struct sdhci_adma2_64_desc {
*/
#define SDHCI_MAX_SEGS 128
-/* Allow for a a command request and a data request at the same time */
+/* Allow for a command request and a data request at the same time */
#define SDHCI_MAX_MRQS 2
/*
@@ -478,6 +478,8 @@ struct sdhci_host {
* block count.
*/
#define SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1<<18)
+/* Issue CMD and DATA reset together */
+#define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER (1<<19)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
index c2333c7acac9..7ef828942df3 100644
--- a/drivers/mmc/host/sdhci_am654.c
+++ b/drivers/mmc/host/sdhci_am654.c
@@ -836,7 +836,7 @@ static int sdhci_am654_probe(struct platform_device *pdev)
ret = mmc_of_parse(host->mmc);
if (ret) {
- dev_err(dev, "parsing dt failed (%d)\n", ret);
+ dev_err_probe(dev, ret, "parsing dt failed\n");
goto pm_runtime_put;
}
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
index 3f5977979cf2..a202a69a4b08 100644
--- a/drivers/mmc/host/sdhci_f_sdh30.c
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -5,6 +5,7 @@
* Copyright (C) 2013 - 2015 Fujitsu Semiconductor, Ltd
* Vincent Yang <[email protected]>
* Copyright (C) 2015 Linaro Ltd Andy Green <[email protected]>
+ * Copyright (C) 2019 Socionext Inc.
*/
#include <linux/acpi.h>
@@ -14,6 +15,7 @@
#include <linux/of.h>
#include <linux/property.h>
#include <linux/clk.h>
+#include <linux/reset.h>
#include "sdhci-pltfm.h"
#include "sdhci_f_sdh30.h"
@@ -21,6 +23,7 @@
struct f_sdhost_priv {
struct clk *clk_iface;
struct clk *clk;
+ struct reset_control *rst;
u32 vendor_hs200;
struct device *dev;
bool enable_cmd_dat_delay;
@@ -74,6 +77,13 @@ static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
ctl |= F_SDH30_CMD_DAT_DELAY;
sdhci_writel(host, ctl, F_SDH30_ESD_CONTROL);
}
+
+ if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) &&
+ !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) {
+ ctl = sdhci_readl(host, F_SDH30_TEST);
+ ctl |= F_SDH30_FORCE_CARD_INSERT;
+ sdhci_writel(host, ctl, F_SDH30_TEST);
+ }
}
static const struct sdhci_ops sdhci_f_sdh30_ops = {
@@ -150,6 +160,16 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
ret = clk_prepare_enable(priv->clk);
if (ret)
goto err_clk;
+
+ priv->rst = devm_reset_control_get_optional_shared(dev, NULL);
+ if (IS_ERR(priv->rst)) {
+ ret = PTR_ERR(priv->rst);
+ goto err_rst;
+ }
+
+ ret = reset_control_deassert(priv->rst);
+ if (ret)
+ goto err_rst;
}
/* init vendor specific regs */
@@ -168,6 +188,9 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
if (reg & SDHCI_CAN_DO_8BIT)
priv->vendor_hs200 = F_SDH30_EMMC_HS200;
+ if (!(reg & SDHCI_TIMEOUT_CLK_MASK))
+ host->quirks |= SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK;
+
ret = sdhci_add_host(host);
if (ret)
goto err_add_host;
@@ -175,6 +198,8 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
return 0;
err_add_host:
+ reset_control_assert(priv->rst);
+err_rst:
clk_disable_unprepare(priv->clk);
err_clk:
clk_disable_unprepare(priv->clk_iface);
@@ -191,8 +216,9 @@ static int sdhci_f_sdh30_remove(struct platform_device *pdev)
sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) ==
0xffffffff);
- clk_disable_unprepare(priv->clk_iface);
+ reset_control_assert(priv->rst);
clk_disable_unprepare(priv->clk);
+ clk_disable_unprepare(priv->clk_iface);
sdhci_free_host(host);
platform_set_drvdata(pdev, NULL);
@@ -203,6 +229,7 @@ static int sdhci_f_sdh30_remove(struct platform_device *pdev)
#ifdef CONFIG_OF
static const struct of_device_id f_sdh30_dt_ids[] = {
{ .compatible = "fujitsu,mb86s70-sdhci-3.0" },
+ { .compatible = "socionext,f-sdh30-e51-mmc" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, f_sdh30_dt_ids);
@@ -232,5 +259,5 @@ module_platform_driver(sdhci_f_sdh30_driver);
MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver");
MODULE_LICENSE("GPL v2");
-MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD.");
+MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD., Socionext Inc.");
MODULE_ALIAS("platform:f_sdh30");
diff --git a/drivers/mmc/host/sdhci_f_sdh30.h b/drivers/mmc/host/sdhci_f_sdh30.h
index fc1ad28f7ca9..7c3c66291d42 100644
--- a/drivers/mmc/host/sdhci_f_sdh30.h
+++ b/drivers/mmc/host/sdhci_f_sdh30.h
@@ -29,4 +29,7 @@
#define F_SDH30_CMD_DAT_DELAY BIT(9)
#define F_SDH30_EMMC_HS200 BIT(24)
+#define F_SDH30_TEST 0x158
+#define F_SDH30_FORCE_CARD_INSERT BIT(6)
+
#define F_SDH30_MIN_CLOCK 400000
diff --git a/drivers/mmc/host/sunplus-mmc.c b/drivers/mmc/host/sunplus-mmc.c
new file mode 100644
index 000000000000..db5e0dcdfa7f
--- /dev/null
+++ b/drivers/mmc/host/sunplus-mmc.c
@@ -0,0 +1,1000 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Sunplus Inc.
+ * Author: Tony Huang <[email protected]>
+ * Author: Li-hao Kuo <[email protected]>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#define SPMMC_MIN_CLK 400000
+#define SPMMC_MAX_CLK 52000000
+#define SPMMC_MAX_BLK_COUNT 65536
+#define SPMMC_MAX_TUNABLE_DLY 7
+#define SPMMC_TIMEOUT_US 500000
+#define SPMMC_POLL_DELAY_US 10
+
+#define SPMMC_CARD_MEDIATYPE_SRCDST_REG 0x0000
+#define SPMMC_MEDIA_TYPE GENMASK(2, 0)
+#define SPMMC_DMA_SOURCE GENMASK(6, 4)
+#define SPMMC_DMA_DESTINATION GENMASK(10, 8)
+#define SPMMC_MEDIA_NONE 0
+#define SPMMC_MEDIA_SD 6
+#define SPMMC_MEDIA_MS 7
+
+#define SPMMC_SDRAM_SECTOR_0_SIZE_REG 0x0008
+#define SPMMC_DMA_BASE_ADDR_REG 0x000C
+#define SPMMC_HW_DMA_CTRL_REG 0x0010
+#define SPMMC_HW_DMA_RST BIT(9)
+#define SPMMC_DMAIDLE BIT(10)
+
+#define SPMMC_MAX_DMA_MEMORY_SECTORS 8
+
+#define SPMMC_SDRAM_SECTOR_1_ADDR_REG 0x0018
+#define SPMMC_SDRAM_SECTOR_1_LENG_REG 0x001C
+#define SPMMC_SDRAM_SECTOR_2_ADDR_REG 0x0020
+#define SPMMC_SDRAM_SECTOR_2_LENG_REG 0x0024
+#define SPMMC_SDRAM_SECTOR_3_ADDR_REG 0x0028
+#define SPMMC_SDRAM_SECTOR_3_LENG_REG 0x002C
+#define SPMMC_SDRAM_SECTOR_4_ADDR_REG 0x0030
+#define SPMMC_SDRAM_SECTOR_4_LENG_REG 0x0034
+#define SPMMC_SDRAM_SECTOR_5_ADDR_REG 0x0038
+#define SPMMC_SDRAM_SECTOR_5_LENG_REG 0x003C
+#define SPMMC_SDRAM_SECTOR_6_ADDR_REG 0x0040
+#define SPMMC_SDRAM_SECTOR_6_LENG_REG 0x0044
+#define SPMMC_SDRAM_SECTOR_7_ADDR_REG 0x0048
+#define SPMMC_SDRAM_SECTOR_7_LENG_REG 0x004C
+
+#define SPMMC_SD_INT_REG 0x0088
+#define SPMMC_SDINT_SDCMPEN BIT(0)
+#define SPMMC_SDINT_SDCMP BIT(1)
+#define SPMMC_SDINT_SDCMPCLR BIT(2)
+#define SPMMC_SDINT_SDIOEN BIT(3)
+#define SPMMC_SDINT_SDIO BIT(4)
+#define SPMMC_SDINT_SDIOCLR BIT(5)
+
+#define SPMMC_SD_PAGE_NUM_REG 0x008C
+
+#define SPMMC_SD_CONFIG0_REG 0x0090
+#define SPMMC_SD_PIO_MODE BIT(0)
+#define SPMMC_SD_DDR_MODE BIT(1)
+#define SPMMC_SD_LEN_MODE BIT(2)
+#define SPMMC_SD_TRANS_MODE GENMASK(5, 4)
+#define SPMMC_SD_AUTO_RESPONSE BIT(6)
+#define SPMMC_SD_CMD_DUMMY BIT(7)
+#define SPMMC_SD_RSP_CHK_EN BIT(8)
+#define SPMMC_SDIO_MODE BIT(9)
+#define SPMMC_SD_MMC_MODE BIT(10)
+#define SPMMC_SD_DATA_WD BIT(11)
+#define SPMMC_RX4_EN BIT(14)
+#define SPMMC_SD_RSP_TYPE BIT(15)
+#define SPMMC_MMC8_EN BIT(18)
+#define SPMMC_CLOCK_DIVISION GENMASK(31, 20)
+
+#define SPMMC_SDIO_CTRL_REG 0x0094
+#define SPMMC_INT_MULTI_TRIG BIT(6)
+
+#define SPMMC_SD_RST_REG 0x0098
+#define SPMMC_SD_CTRL_REG 0x009C
+#define SPMMC_NEW_COMMAND_TRIGGER BIT(0)
+#define SPMMC_DUMMY_CLOCK_TRIGGER BIT(1)
+
+#define SPMMC_SD_STATUS_REG 0x00A0
+#define SPMMC_SDSTATUS_DUMMY_READY BIT(0)
+#define SPMMC_SDSTATUS_RSP_BUF_FULL BIT(1)
+#define SPMMC_SDSTATUS_TX_DATA_BUF_EMPTY BIT(2)
+#define SPMMC_SDSTATUS_RX_DATA_BUF_FULL BIT(3)
+#define SPMMC_SDSTATUS_CMD_PIN_STATUS BIT(4)
+#define SPMMC_SDSTATUS_DAT0_PIN_STATUS BIT(5)
+#define SPMMC_SDSTATUS_RSP_TIMEOUT BIT(6)
+#define SPMMC_SDSTATUS_CARD_CRC_CHECK_TIMEOUT BIT(7)
+#define SPMMC_SDSTATUS_STB_TIMEOUT BIT(8)
+#define SPMMC_SDSTATUS_RSP_CRC7_ERROR BIT(9)
+#define SPMMC_SDSTATUS_CRC_TOKEN_CHECK_ERROR BIT(10)
+#define SPMMC_SDSTATUS_RDATA_CRC16_ERROR BIT(11)
+#define SPMMC_SDSTATUS_SUSPEND_STATE_READY BIT(12)
+#define SPMMC_SDSTATUS_BUSY_CYCLE BIT(13)
+#define SPMMC_SDSTATUS_DAT1_PIN_STATUS BIT(14)
+#define SPMMC_SDSTATUS_SD_SENSE_STATUS BIT(15)
+#define SPMMC_SDSTATUS_BOOT_ACK_TIMEOUT BIT(16)
+#define SPMMC_SDSTATUS_BOOT_DATA_TIMEOUT BIT(17)
+#define SPMMC_SDSTATUS_BOOT_ACK_ERROR BIT(18)
+
+#define SPMMC_SD_STATE_REG 0x00A4
+#define SPMMC_CRCTOKEN_CHECK_RESULT GENMASK(6, 4)
+#define SPMMC_SDSTATE_ERROR BIT(13)
+#define SPMMC_SDSTATE_FINISH BIT(14)
+
+#define SPMMC_SD_HW_STATE_REG 0x00A8
+#define SPMMC_SD_BLOCKSIZE_REG 0x00AC
+
+#define SPMMC_SD_CONFIG1_REG 0x00B0
+#define SPMMC_TX_DUMMY_NUM GENMASK(8, 0)
+#define SPMMC_SD_HIGH_SPEED_EN BIT(31)
+
+#define SPMMC_SD_TIMING_CONFIG0_REG 0x00B4
+#define SPMMC_SD_CLOCK_DELAY GENMASK(2, 0)
+#define SPMMC_SD_WRITE_DATA_DELAY GENMASK(6, 4)
+#define SPMMC_SD_WRITE_COMMAND_DELAY GENMASK(10, 8)
+#define SPMMC_SD_READ_RESPONSE_DELAY GENMASK(14, 12)
+#define SPMMC_SD_READ_DATA_DELAY GENMASK(18, 16)
+#define SPMMC_SD_READ_CRC_DELAY GENMASK(22, 20)
+
+#define SPMMC_SD_PIODATATX_REG 0x00BC
+#define SPMMC_SD_PIODATARX_REG 0x00C0
+#define SPMMC_SD_CMDBUF0_3_REG 0x00C4
+#define SPMMC_SD_CMDBUF4_REG 0x00C8
+#define SPMMC_SD_RSPBUF0_3_REG 0x00CC
+#define SPMMC_SD_RSPBUF4_5_REG 0x00D0
+
+#define SPMMC_MAX_RETRIES (8 * 8)
+
+struct spmmc_tuning_info {
+ int enable_tuning;
+ int need_tuning;
+ int retried; /* how many times has been retried */
+ u32 rd_crc_dly:3;
+ u32 rd_dat_dly:3;
+ u32 rd_rsp_dly:3;
+ u32 wr_cmd_dly:3;
+ u32 wr_dat_dly:3;
+ u32 clk_dly:3;
+};
+
+#define SPMMC_DMA_MODE 0
+#define SPMMC_PIO_MODE 1
+
+struct spmmc_host {
+ void __iomem *base;
+ struct clk *clk;
+ struct reset_control *rstc;
+ struct mmc_host *mmc;
+ struct mmc_request *mrq; /* current mrq */
+ int irq;
+ int dmapio_mode;
+ struct spmmc_tuning_info tuning_info;
+ int dma_int_threshold;
+ int dma_use_int;
+};
+
+static inline int spmmc_wait_finish(struct spmmc_host *host)
+{
+ u32 state;
+
+ return readl_poll_timeout(host->base + SPMMC_SD_STATE_REG, state,
+ (state & SPMMC_SDSTATE_FINISH),
+ SPMMC_POLL_DELAY_US, SPMMC_TIMEOUT_US);
+}
+
+static inline int spmmc_wait_sdstatus(struct spmmc_host *host, unsigned int status_bit)
+{
+ u32 status;
+
+ return readl_poll_timeout(host->base + SPMMC_SD_STATUS_REG, status,
+ (status & status_bit),
+ SPMMC_POLL_DELAY_US, SPMMC_TIMEOUT_US);
+}
+
+#define spmmc_wait_rspbuf_full(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_RSP_BUF_FULL)
+#define spmmc_wait_rxbuf_full(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_RX_DATA_BUF_FULL)
+#define spmmc_wait_txbuf_empty(host) spmmc_wait_sdstatus(host, SPMMC_SDSTATUS_TX_DATA_BUF_EMPTY)
+
+static void spmmc_get_rsp(struct spmmc_host *host, struct mmc_command *cmd)
+{
+ u32 value0_3, value4_5;
+
+ if (!(cmd->flags & MMC_RSP_PRESENT))
+ return;
+ if (cmd->flags & MMC_RSP_136) {
+ if (spmmc_wait_rspbuf_full(host))
+ return;
+ value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG);
+ value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff;
+ cmd->resp[0] = (value0_3 << 8) | (value4_5 >> 8);
+ cmd->resp[1] = value4_5 << 24;
+ value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG);
+ value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff;
+ cmd->resp[1] |= value0_3 >> 8;
+ cmd->resp[2] = value0_3 << 24;
+ cmd->resp[2] |= value4_5 << 8;
+ value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG);
+ value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff;
+ cmd->resp[2] |= value0_3 >> 24;
+ cmd->resp[3] = value0_3 << 8;
+ cmd->resp[3] |= value4_5 >> 8;
+ } else {
+ if (spmmc_wait_rspbuf_full(host))
+ return;
+ value0_3 = readl(host->base + SPMMC_SD_RSPBUF0_3_REG);
+ value4_5 = readl(host->base + SPMMC_SD_RSPBUF4_5_REG) & 0xffff;
+ cmd->resp[0] = (value0_3 << 8) | (value4_5 >> 8);
+ cmd->resp[1] = value4_5 << 24;
+ }
+}
+
+static void spmmc_set_bus_clk(struct spmmc_host *host, int clk)
+{
+ unsigned int clkdiv;
+ int f_min = host->mmc->f_min;
+ int f_max = host->mmc->f_max;
+ u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG);
+
+ if (clk < f_min)
+ clk = f_min;
+ if (clk > f_max)
+ clk = f_max;
+
+ clkdiv = (clk_get_rate(host->clk) + clk) / clk - 1;
+ if (clkdiv > 0xfff)
+ clkdiv = 0xfff;
+ value &= ~SPMMC_CLOCK_DIVISION;
+ value |= FIELD_PREP(SPMMC_CLOCK_DIVISION, clkdiv);
+ writel(value, host->base + SPMMC_SD_CONFIG0_REG);
+}
+
+static void spmmc_set_bus_timing(struct spmmc_host *host, unsigned int timing)
+{
+ u32 value = readl(host->base + SPMMC_SD_CONFIG1_REG);
+ int clkdiv = FIELD_GET(SPMMC_CLOCK_DIVISION, readl(host->base + SPMMC_SD_CONFIG0_REG));
+ int delay = clkdiv / 2 < 7 ? clkdiv / 2 : 7;
+ int hs_en = 1, ddr_enabled = 0;
+
+ switch (timing) {
+ case MMC_TIMING_LEGACY:
+ hs_en = 0;
+ break;
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_SDR50:
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ hs_en = 1;
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ ddr_enabled = 1;
+ break;
+ case MMC_TIMING_MMC_DDR52:
+ ddr_enabled = 1;
+ break;
+ default:
+ hs_en = 0;
+ break;
+ }
+
+ if (hs_en) {
+ value |= SPMMC_SD_HIGH_SPEED_EN;
+ writel(value, host->base + SPMMC_SD_CONFIG1_REG);
+ value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG);
+ value &= ~SPMMC_SD_WRITE_DATA_DELAY;
+ value |= FIELD_PREP(SPMMC_SD_WRITE_DATA_DELAY, delay);
+ value &= ~SPMMC_SD_WRITE_COMMAND_DELAY;
+ value |= FIELD_PREP(SPMMC_SD_WRITE_COMMAND_DELAY, delay);
+ writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG);
+ } else {
+ value &= ~SPMMC_SD_HIGH_SPEED_EN;
+ writel(value, host->base + SPMMC_SD_CONFIG1_REG);
+ }
+ if (ddr_enabled) {
+ value = readl(host->base + SPMMC_SD_CONFIG0_REG);
+ value |= SPMMC_SD_DDR_MODE;
+ writel(value, host->base + SPMMC_SD_CONFIG0_REG);
+ } else {
+ value = readl(host->base + SPMMC_SD_CONFIG0_REG);
+ value &= ~SPMMC_SD_DDR_MODE;
+ writel(value, host->base + SPMMC_SD_CONFIG0_REG);
+ }
+}
+
+static void spmmc_set_bus_width(struct spmmc_host *host, int width)
+{
+ u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG);
+
+ switch (width) {
+ case MMC_BUS_WIDTH_8:
+ value &= ~SPMMC_SD_DATA_WD;
+ value |= SPMMC_MMC8_EN;
+ break;
+ case MMC_BUS_WIDTH_4:
+ value |= SPMMC_SD_DATA_WD;
+ value &= ~SPMMC_MMC8_EN;
+ break;
+ default:
+ value &= ~SPMMC_SD_DATA_WD;
+ value &= ~SPMMC_MMC8_EN;
+ break;
+ }
+ writel(value, host->base + SPMMC_SD_CONFIG0_REG);
+}
+
+/*
+ * select the working mode of controller: sd/sdio/emmc
+ */
+static void spmmc_set_sdmmc_mode(struct spmmc_host *host)
+{
+ u32 value = readl(host->base + SPMMC_SD_CONFIG0_REG);
+
+ value |= SPMMC_SD_MMC_MODE;
+ value &= ~SPMMC_SDIO_MODE;
+ writel(value, host->base + SPMMC_SD_CONFIG0_REG);
+}
+
+static void spmmc_sw_reset(struct spmmc_host *host)
+{
+ u32 value;
+
+ /*
+ * Must reset dma operation first, or it will
+ * be stuck on sd_state == 0x1c00 because of
+ * a controller software reset bug
+ */
+ value = readl(host->base + SPMMC_HW_DMA_CTRL_REG);
+ value |= SPMMC_DMAIDLE;
+ writel(value, host->base + SPMMC_HW_DMA_CTRL_REG);
+ value &= ~SPMMC_DMAIDLE;
+ writel(value, host->base + SPMMC_HW_DMA_CTRL_REG);
+ value = readl(host->base + SPMMC_HW_DMA_CTRL_REG);
+ value |= SPMMC_HW_DMA_RST;
+ writel(value, host->base + SPMMC_HW_DMA_CTRL_REG);
+ writel(0x7, host->base + SPMMC_SD_RST_REG);
+ readl_poll_timeout_atomic(host->base + SPMMC_SD_HW_STATE_REG, value,
+ !(value & BIT(6)), 1, SPMMC_TIMEOUT_US);
+}
+
+static void spmmc_prepare_cmd(struct spmmc_host *host, struct mmc_command *cmd)
+{
+ u32 value;
+
+ /* add start bit, according to spec, command format */
+ value = ((cmd->opcode | 0x40) << 24) | (cmd->arg >> 8);
+ writel(value, host->base + SPMMC_SD_CMDBUF0_3_REG);
+ writeb(cmd->arg & 0xff, host->base + SPMMC_SD_CMDBUF4_REG);
+
+ /* disable interrupt if needed */
+ value = readl(host->base + SPMMC_SD_INT_REG);
+ value |= SPMMC_SDINT_SDCMPCLR;
+ value &= ~SPMMC_SDINT_SDCMPEN;
+ writel(value, host->base + SPMMC_SD_INT_REG);
+
+ value = readl(host->base + SPMMC_SD_CONFIG0_REG);
+ value &= ~SPMMC_SD_TRANS_MODE;
+ value |= SPMMC_SD_CMD_DUMMY;
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ value |= SPMMC_SD_AUTO_RESPONSE;
+ } else {
+ value &= ~SPMMC_SD_AUTO_RESPONSE;
+ writel(value, host->base + SPMMC_SD_CONFIG0_REG);
+
+ return;
+ }
+ /*
+ * Currently, host is not capable of checking R2's CRC7,
+ * thus, enable crc7 check only for 48 bit response commands
+ */
+ if (cmd->flags & MMC_RSP_CRC && !(cmd->flags & MMC_RSP_136))
+ value |= SPMMC_SD_RSP_CHK_EN;
+ else
+ value &= ~SPMMC_SD_RSP_CHK_EN;
+
+ if (cmd->flags & MMC_RSP_136)
+ value |= SPMMC_SD_RSP_TYPE;
+ else
+ value &= ~SPMMC_SD_RSP_TYPE;
+ writel(value, host->base + SPMMC_SD_CONFIG0_REG);
+}
+
+static void spmmc_prepare_data(struct spmmc_host *host, struct mmc_data *data)
+{
+ u32 value, srcdst;
+
+ writel(data->blocks - 1, host->base + SPMMC_SD_PAGE_NUM_REG);
+ writel(data->blksz - 1, host->base + SPMMC_SD_BLOCKSIZE_REG);
+ value = readl(host->base + SPMMC_SD_CONFIG0_REG);
+ if (data->flags & MMC_DATA_READ) {
+ value &= ~SPMMC_SD_TRANS_MODE;
+ value |= FIELD_PREP(SPMMC_SD_TRANS_MODE, 2);
+ value &= ~SPMMC_SD_AUTO_RESPONSE;
+ value &= ~SPMMC_SD_CMD_DUMMY;
+ srcdst = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
+ srcdst &= ~SPMMC_DMA_SOURCE;
+ srcdst |= FIELD_PREP(SPMMC_DMA_SOURCE, 0x2);
+ srcdst &= ~SPMMC_DMA_DESTINATION;
+ srcdst |= FIELD_PREP(SPMMC_DMA_DESTINATION, 0x1);
+ writel(srcdst, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
+ } else {
+ value &= ~SPMMC_SD_TRANS_MODE;
+ value |= FIELD_PREP(SPMMC_SD_TRANS_MODE, 1);
+ srcdst = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
+ srcdst &= ~SPMMC_DMA_SOURCE;
+ srcdst |= FIELD_PREP(SPMMC_DMA_SOURCE, 0x1);
+ srcdst &= ~SPMMC_DMA_DESTINATION;
+ srcdst |= FIELD_PREP(SPMMC_DMA_DESTINATION, 0x2);
+ writel(srcdst, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
+ }
+
+ value |= SPMMC_SD_LEN_MODE;
+ if (host->dmapio_mode == SPMMC_DMA_MODE) {
+ struct scatterlist *sg;
+ dma_addr_t dma_addr;
+ unsigned int dma_size;
+ int i, count = 1;
+
+ count = dma_map_sg(host->mmc->parent, data->sg, data->sg_len,
+ mmc_get_dma_dir(data));
+ if (!count || count > SPMMC_MAX_DMA_MEMORY_SECTORS) {
+ data->error = -EINVAL;
+
+ return;
+ }
+ for_each_sg(data->sg, sg, count, i) {
+ dma_addr = sg_dma_address(sg);
+ dma_size = sg_dma_len(sg) / data->blksz - 1;
+ if (i == 0) {
+ writel(dma_addr, host->base + SPMMC_DMA_BASE_ADDR_REG);
+ writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_0_SIZE_REG);
+ } else if (i == 1) {
+ writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_1_ADDR_REG);
+ writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_1_LENG_REG);
+ } else if (i == 2) {
+ writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_2_ADDR_REG);
+ writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_2_LENG_REG);
+ } else if (i == 3) {
+ writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_3_ADDR_REG);
+ writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_3_LENG_REG);
+ } else if (i == 4) {
+ writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_4_ADDR_REG);
+ writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_4_LENG_REG);
+ } else if (i == 5) {
+ writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_5_ADDR_REG);
+ writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_5_LENG_REG);
+ } else if (i == 6) {
+ writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_6_ADDR_REG);
+ writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_6_LENG_REG);
+ } else if (i == 7) {
+ writel(dma_addr, host->base + SPMMC_SDRAM_SECTOR_7_ADDR_REG);
+ writel(dma_size, host->base + SPMMC_SDRAM_SECTOR_7_LENG_REG);
+ }
+ }
+ value &= ~SPMMC_SD_PIO_MODE;
+ writel(value, host->base + SPMMC_SD_CONFIG0_REG);
+ /* enable interrupt if needed */
+ if (data->blksz * data->blocks > host->dma_int_threshold) {
+ host->dma_use_int = 1;
+ value = readl(host->base + SPMMC_SD_INT_REG);
+ value &= ~SPMMC_SDINT_SDCMPEN;
+ value |= FIELD_PREP(SPMMC_SDINT_SDCMPEN, 1); /* sdcmpen */
+ writel(value, host->base + SPMMC_SD_INT_REG);
+ }
+ } else {
+ value |= SPMMC_SD_PIO_MODE;
+ value |= SPMMC_RX4_EN;
+ writel(value, host->base + SPMMC_SD_CONFIG0_REG);
+ }
+}
+
+static inline void spmmc_trigger_transaction(struct spmmc_host *host)
+{
+ u32 value = readl(host->base + SPMMC_SD_CTRL_REG);
+
+ value |= SPMMC_NEW_COMMAND_TRIGGER;
+ writel(value, host->base + SPMMC_SD_CTRL_REG);
+}
+
+static void spmmc_send_stop_cmd(struct spmmc_host *host)
+{
+ struct mmc_command stop = {};
+ u32 value;
+
+ stop.opcode = MMC_STOP_TRANSMISSION;
+ stop.arg = 0;
+ stop.flags = MMC_RSP_R1B;
+ spmmc_prepare_cmd(host, &stop);
+ value = readl(host->base + SPMMC_SD_INT_REG);
+ value &= ~SPMMC_SDINT_SDCMPEN;
+ value |= FIELD_PREP(SPMMC_SDINT_SDCMPEN, 0);
+ writel(value, host->base + SPMMC_SD_INT_REG);
+ spmmc_trigger_transaction(host);
+ readl_poll_timeout(host->base + SPMMC_SD_STATE_REG, value,
+ (value & SPMMC_SDSTATE_FINISH), 1, SPMMC_TIMEOUT_US);
+}
+
+static int spmmc_check_error(struct spmmc_host *host, struct mmc_request *mrq)
+{
+ int ret = 0;
+ struct mmc_command *cmd = mrq->cmd;
+ struct mmc_data *data = mrq->data;
+
+ u32 value = readl(host->base + SPMMC_SD_STATE_REG);
+ u32 crc_token = FIELD_GET(SPMMC_CRCTOKEN_CHECK_RESULT, value);
+
+ if (value & SPMMC_SDSTATE_ERROR) {
+ u32 timing_cfg0 = 0;
+
+ value = readl(host->base + SPMMC_SD_STATUS_REG);
+
+ if (host->tuning_info.enable_tuning) {
+ timing_cfg0 = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG);
+ host->tuning_info.rd_crc_dly = FIELD_GET(SPMMC_SD_READ_CRC_DELAY,
+ timing_cfg0);
+ host->tuning_info.rd_dat_dly = FIELD_GET(SPMMC_SD_READ_DATA_DELAY,
+ timing_cfg0);
+ host->tuning_info.rd_rsp_dly = FIELD_GET(SPMMC_SD_READ_RESPONSE_DELAY,
+ timing_cfg0);
+ host->tuning_info.wr_cmd_dly = FIELD_GET(SPMMC_SD_WRITE_COMMAND_DELAY,
+ timing_cfg0);
+ host->tuning_info.wr_dat_dly = FIELD_GET(SPMMC_SD_WRITE_DATA_DELAY,
+ timing_cfg0);
+ }
+
+ if (value & SPMMC_SDSTATUS_RSP_TIMEOUT) {
+ ret = -ETIMEDOUT;
+ host->tuning_info.wr_cmd_dly++;
+ } else if (value & SPMMC_SDSTATUS_RSP_CRC7_ERROR) {
+ ret = -EILSEQ;
+ host->tuning_info.rd_rsp_dly++;
+ } else if (data) {
+ if ((value & SPMMC_SDSTATUS_STB_TIMEOUT)) {
+ ret = -ETIMEDOUT;
+ host->tuning_info.rd_dat_dly++;
+ } else if (value & SPMMC_SDSTATUS_RDATA_CRC16_ERROR) {
+ ret = -EILSEQ;
+ host->tuning_info.rd_dat_dly++;
+ } else if (value & SPMMC_SDSTATUS_CARD_CRC_CHECK_TIMEOUT) {
+ ret = -ETIMEDOUT;
+ host->tuning_info.rd_crc_dly++;
+ } else if (value & SPMMC_SDSTATUS_CRC_TOKEN_CHECK_ERROR) {
+ ret = -EILSEQ;
+ if (crc_token == 0x5)
+ host->tuning_info.wr_dat_dly++;
+ else
+ host->tuning_info.rd_crc_dly++;
+ }
+ }
+ cmd->error = ret;
+ if (data) {
+ data->error = ret;
+ data->bytes_xfered = 0;
+ }
+ if (!host->tuning_info.need_tuning && host->tuning_info.enable_tuning)
+ cmd->retries = SPMMC_MAX_RETRIES;
+ spmmc_sw_reset(host);
+
+ if (host->tuning_info.enable_tuning) {
+ timing_cfg0 &= ~SPMMC_SD_READ_CRC_DELAY;
+ timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY,
+ host->tuning_info.rd_crc_dly);
+ timing_cfg0 &= ~SPMMC_SD_READ_DATA_DELAY;
+ timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY,
+ host->tuning_info.rd_dat_dly);
+ timing_cfg0 &= ~SPMMC_SD_READ_RESPONSE_DELAY;
+ timing_cfg0 |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY,
+ host->tuning_info.rd_rsp_dly);
+ timing_cfg0 &= ~SPMMC_SD_WRITE_COMMAND_DELAY;
+ timing_cfg0 |= FIELD_PREP(SPMMC_SD_WRITE_COMMAND_DELAY,
+ host->tuning_info.wr_cmd_dly);
+ timing_cfg0 &= ~SPMMC_SD_WRITE_DATA_DELAY;
+ timing_cfg0 |= FIELD_PREP(SPMMC_SD_WRITE_DATA_DELAY,
+ host->tuning_info.wr_dat_dly);
+ writel(timing_cfg0, host->base + SPMMC_SD_TIMING_CONFIG0_REG);
+ }
+ } else if (data) {
+ data->error = 0;
+ data->bytes_xfered = data->blocks * data->blksz;
+ }
+ host->tuning_info.need_tuning = ret;
+
+ return ret;
+}
+
+/*
+ * the strategy is:
+ * 1. if several continuous delays are acceptable, we choose a middle one;
+ * 2. otherwise, we choose the first one.
+ */
+static inline int spmmc_find_best_delay(u8 candidate_dly)
+{
+ int f, w, value;
+
+ if (!candidate_dly)
+ return 0;
+ f = ffs(candidate_dly) - 1;
+ w = hweight8(candidate_dly);
+ value = ((1 << w) - 1) << f;
+ if (0xff == (value & ~candidate_dly))
+ return (f + w / 2);
+ else
+ return (f);
+}
+
+static void spmmc_xfer_data_pio(struct spmmc_host *host, struct mmc_data *data)
+{
+ u32 *buf;
+ int data_left = data->blocks * data->blksz;
+ int consumed, remain;
+
+ struct sg_mapping_iter sg_miter;
+ unsigned int flags = 0;
+
+ if (data->flags & MMC_DATA_WRITE)
+ flags |= SG_MITER_FROM_SG;
+ else
+ flags |= SG_MITER_TO_SG;
+ sg_miter_start(&sg_miter, data->sg, data->sg_len, flags);
+ while (data_left > 0) {
+ consumed = 0;
+ if (!sg_miter_next(&sg_miter))
+ break;
+ buf = sg_miter.addr;
+ remain = sg_miter.length;
+ do {
+ if (data->flags & MMC_DATA_WRITE) {
+ if (spmmc_wait_txbuf_empty(host))
+ goto done;
+ writel(*buf, host->base + SPMMC_SD_PIODATATX_REG);
+ } else {
+ if (spmmc_wait_rxbuf_full(host))
+ goto done;
+ *buf = readl(host->base + SPMMC_SD_PIODATARX_REG);
+ }
+ buf++;
+ /* tx/rx 4 bytes one time in pio mode */
+ consumed += 4;
+ remain -= 4;
+ } while (remain);
+ sg_miter.consumed = consumed;
+ data_left -= consumed;
+ }
+done:
+ sg_miter_stop(&sg_miter);
+}
+
+static void spmmc_controller_init(struct spmmc_host *host)
+{
+ u32 value;
+ int ret = reset_control_assert(host->rstc);
+
+ if (!ret) {
+ usleep_range(1000, 1250);
+ ret = reset_control_deassert(host->rstc);
+ }
+
+ value = readl(host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
+ value &= ~SPMMC_MEDIA_TYPE;
+ value |= FIELD_PREP(SPMMC_MEDIA_TYPE, SPMMC_MEDIA_SD);
+ writel(value, host->base + SPMMC_CARD_MEDIATYPE_SRCDST_REG);
+}
+
+/*
+ * 1. unmap scatterlist if needed;
+ * 2. get response & check error conditions;
+ * 3. notify mmc layer the request is done
+ */
+static void spmmc_finish_request(struct spmmc_host *host, struct mmc_request *mrq)
+{
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ if (!mrq)
+ return;
+
+ cmd = mrq->cmd;
+ data = mrq->data;
+
+ if (data && SPMMC_DMA_MODE == host->dmapio_mode) {
+ dma_unmap_sg(host->mmc->parent, data->sg, data->sg_len, mmc_get_dma_dir(data));
+ host->dma_use_int = 0;
+ }
+
+ spmmc_get_rsp(host, cmd);
+ spmmc_check_error(host, mrq);
+ if (mrq->stop)
+ spmmc_send_stop_cmd(host);
+
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+}
+
+/* Interrupt Service Routine */
+static irqreturn_t spmmc_irq(int irq, void *dev_id)
+{
+ struct spmmc_host *host = dev_id;
+ u32 value = readl(host->base + SPMMC_SD_INT_REG);
+
+ if ((value & SPMMC_SDINT_SDCMP) && (value & SPMMC_SDINT_SDCMPEN)) {
+ value &= ~SPMMC_SDINT_SDCMPEN;
+ value |= SPMMC_SDINT_SDCMPCLR;
+ writel(value, host->base + SPMMC_SD_INT_REG);
+ return IRQ_WAKE_THREAD;
+ }
+ return IRQ_HANDLED;
+}
+
+static void spmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct spmmc_host *host = mmc_priv(mmc);
+ struct mmc_data *data;
+ struct mmc_command *cmd;
+
+ host->mrq = mrq;
+ data = mrq->data;
+ cmd = mrq->cmd;
+
+ spmmc_prepare_cmd(host, cmd);
+ /* we need manually read response R2. */
+ if (cmd->flags & MMC_RSP_136) {
+ spmmc_trigger_transaction(host);
+ spmmc_get_rsp(host, cmd);
+ spmmc_wait_finish(host);
+ spmmc_check_error(host, mrq);
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, mrq);
+ } else {
+ if (data)
+ spmmc_prepare_data(host, data);
+
+ if (host->dmapio_mode == SPMMC_PIO_MODE && data) {
+ u32 value;
+ /* pio data transfer do not use interrupt */
+ value = readl(host->base + SPMMC_SD_INT_REG);
+ value &= ~SPMMC_SDINT_SDCMPEN;
+ writel(value, host->base + SPMMC_SD_INT_REG);
+ spmmc_trigger_transaction(host);
+ spmmc_xfer_data_pio(host, data);
+ spmmc_wait_finish(host);
+ spmmc_finish_request(host, mrq);
+ } else {
+ if (host->dma_use_int) {
+ spmmc_trigger_transaction(host);
+ } else {
+ spmmc_trigger_transaction(host);
+ spmmc_wait_finish(host);
+ spmmc_finish_request(host, mrq);
+ }
+ }
+ }
+}
+
+static void spmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct spmmc_host *host = (struct spmmc_host *)mmc_priv(mmc);
+
+ spmmc_set_bus_clk(host, ios->clock);
+ spmmc_set_bus_timing(host, ios->timing);
+ spmmc_set_bus_width(host, ios->bus_width);
+ /* ensure mode is correct, because we might have hw reset the controller */
+ spmmc_set_sdmmc_mode(host);
+}
+
+/*
+ * Return values for the get_cd callback should be:
+ * 0 for a absent card
+ * 1 for a present card
+ * -ENOSYS when not supported (equal to NULL callback)
+ * or a negative errno value when something bad happened
+ */
+static int spmmc_get_cd(struct mmc_host *mmc)
+{
+ int ret = 0;
+
+ if (mmc_can_gpio_cd(mmc))
+ ret = mmc_gpio_get_cd(mmc);
+
+ if (ret < 0)
+ ret = 0;
+
+ return ret;
+}
+
+static int spmmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct spmmc_host *host = mmc_priv(mmc);
+ u8 smpl_dly = 0, candidate_dly = 0;
+ u32 value;
+
+ host->tuning_info.enable_tuning = 0;
+ do {
+ value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG);
+ value &= ~SPMMC_SD_READ_RESPONSE_DELAY;
+ value |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, smpl_dly);
+ value &= ~SPMMC_SD_READ_DATA_DELAY;
+ value |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, smpl_dly);
+ value &= ~SPMMC_SD_READ_CRC_DELAY;
+ value |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, smpl_dly);
+ writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG);
+
+ if (!mmc_send_tuning(mmc, opcode, NULL)) {
+ candidate_dly |= (1 << smpl_dly);
+ break;
+ }
+ } while (smpl_dly++ <= SPMMC_MAX_TUNABLE_DLY);
+ host->tuning_info.enable_tuning = 1;
+
+ if (candidate_dly) {
+ smpl_dly = spmmc_find_best_delay(candidate_dly);
+ value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG);
+ value &= ~SPMMC_SD_READ_RESPONSE_DELAY;
+ value |= FIELD_PREP(SPMMC_SD_READ_RESPONSE_DELAY, smpl_dly);
+ value &= ~SPMMC_SD_READ_DATA_DELAY;
+ value |= FIELD_PREP(SPMMC_SD_READ_DATA_DELAY, smpl_dly);
+ value &= ~SPMMC_SD_READ_CRC_DELAY;
+ value |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, smpl_dly);
+ writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static const struct mmc_host_ops spmmc_ops = {
+ .request = spmmc_request,
+ .set_ios = spmmc_set_ios,
+ .get_cd = spmmc_get_cd,
+ .execute_tuning = spmmc_execute_tuning,
+};
+
+static irqreturn_t spmmc_func_finish_req(int irq, void *dev_id)
+{
+ struct spmmc_host *host = dev_id;
+
+ spmmc_finish_request(host, host->mrq);
+
+ return IRQ_HANDLED;
+}
+
+static int spmmc_drv_probe(struct platform_device *pdev)
+{
+ struct mmc_host *mmc;
+ struct resource *res;
+ struct spmmc_host *host;
+ int ret = 0;
+
+ mmc = mmc_alloc_host(sizeof(*host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto probe_free_host;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->dmapio_mode = SPMMC_DMA_MODE;
+ host->dma_int_threshold = 1024;
+
+ host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(host->base))
+ return PTR_ERR(host->base);
+
+ host->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(host->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(host->clk), "clk get fail\n");
+
+ host->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+ if (IS_ERR(host->rstc))
+ return dev_err_probe(&pdev->dev, PTR_ERR(host->rstc), "rst get fail\n");
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq <= 0)
+ return host->irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, host->irq,
+ spmmc_irq, spmmc_func_finish_req, IRQF_SHARED,
+ NULL, host);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(host->clk);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "failed to enable clk\n");
+
+ ret = mmc_of_parse(mmc);
+ if (ret)
+ goto probe_free_host;
+
+ mmc->ops = &spmmc_ops;
+ mmc->f_min = SPMMC_MIN_CLK;
+ if (mmc->f_max > SPMMC_MAX_CLK)
+ mmc->f_max = SPMMC_MAX_CLK;
+
+ ret = mmc_regulator_get_supply(mmc);
+ if (ret)
+ goto probe_free_host;
+
+ if (!mmc->ocr_avail)
+ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->max_seg_size = SPMMC_MAX_BLK_COUNT * 512;
+ mmc->max_segs = SPMMC_MAX_DMA_MEMORY_SECTORS;
+ mmc->max_req_size = SPMMC_MAX_BLK_COUNT * 512;
+ mmc->max_blk_size = 512;
+ mmc->max_blk_count = SPMMC_MAX_BLK_COUNT;
+
+ dev_set_drvdata(&pdev->dev, host);
+ spmmc_controller_init(host);
+ spmmc_set_sdmmc_mode(host);
+ host->tuning_info.enable_tuning = 1;
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ mmc_add_host(mmc);
+
+ return ret;
+
+probe_free_host:
+ if (mmc)
+ mmc_free_host(mmc);
+
+ return ret;
+}
+
+static int spmmc_drv_remove(struct platform_device *dev)
+{
+ struct spmmc_host *host = platform_get_drvdata(dev);
+
+ mmc_remove_host(host->mmc);
+ pm_runtime_get_sync(&dev->dev);
+ clk_disable_unprepare(host->clk);
+ pm_runtime_put_noidle(&dev->dev);
+ pm_runtime_disable(&dev->dev);
+ platform_set_drvdata(dev, NULL);
+ mmc_free_host(host->mmc);
+
+ return 0;
+}
+
+static int spmmc_pm_runtime_suspend(struct device *dev)
+{
+ struct spmmc_host *host;
+
+ host = dev_get_drvdata(dev);
+ clk_disable_unprepare(host->clk);
+
+ return 0;
+}
+
+static int spmmc_pm_runtime_resume(struct device *dev)
+{
+ struct spmmc_host *host;
+
+ host = dev_get_drvdata(dev);
+
+ return clk_prepare_enable(host->clk);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(spmmc_pm_ops, spmmc_pm_runtime_suspend,
+ spmmc_pm_runtime_resume, NULL);
+
+static const struct of_device_id spmmc_of_table[] = {
+ {
+ .compatible = "sunplus,sp7021-mmc",
+ },
+ {/* sentinel */}
+};
+MODULE_DEVICE_TABLE(of, spmmc_of_table);
+
+static struct platform_driver spmmc_driver = {
+ .probe = spmmc_drv_probe,
+ .remove = spmmc_drv_remove,
+ .driver = {
+ .name = "spmmc",
+ .pm = pm_ptr(&spmmc_pm_ops),
+ .of_match_table = spmmc_of_table,
+ },
+};
+module_platform_driver(spmmc_driver);
+
+MODULE_AUTHOR("Tony Huang <[email protected]>");
+MODULE_AUTHOR("Li-hao Kuo <[email protected]>");
+MODULE_DESCRIPTION("Sunplus MMC controller driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index b16e12e62e72..3db9f32d6a7b 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -1492,9 +1492,11 @@ static int sunxi_mmc_remove(struct platform_device *pdev)
struct sunxi_mmc_host *host = mmc_priv(mmc);
mmc_remove_host(mmc);
- pm_runtime_force_suspend(&pdev->dev);
- disable_irq(host->irq);
- sunxi_mmc_disable(host);
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev)) {
+ disable_irq(host->irq);
+ sunxi_mmc_disable(host);
+ }
dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
mmc_free_host(mmc);
diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c
index 63917070b1a7..b5a2f2f25ad9 100644
--- a/drivers/mmc/host/tifm_sd.c
+++ b/drivers/mmc/host/tifm_sd.c
@@ -116,7 +116,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
unsigned char *buf;
unsigned int pos = 0, val;
- buf = kmap_atomic(pg) + off;
+ buf = kmap_local_page(pg) + off;
if (host->cmd_flags & DATA_CARRY) {
buf[pos++] = host->bounce_buf_data[0];
host->cmd_flags &= ~DATA_CARRY;
@@ -132,7 +132,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg,
}
buf[pos++] = (val >> 8) & 0xff;
}
- kunmap_atomic(buf - off);
+ kunmap_local(buf - off);
}
static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
@@ -142,7 +142,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
unsigned char *buf;
unsigned int pos = 0, val;
- buf = kmap_atomic(pg) + off;
+ buf = kmap_local_page(pg) + off;
if (host->cmd_flags & DATA_CARRY) {
val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00);
writel(val, sock->addr + SOCK_MMCSD_DATA);
@@ -159,7 +159,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg,
val |= (buf[pos++] << 8) & 0xff00;
writel(val, sock->addr + SOCK_MMCSD_DATA);
}
- kunmap_atomic(buf - off);
+ kunmap_local(buf - off);
}
static void tifm_sd_transfer_data(struct tifm_sd *host)
@@ -210,13 +210,13 @@ static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off,
struct page *src, unsigned int src_off,
unsigned int count)
{
- unsigned char *src_buf = kmap_atomic(src) + src_off;
- unsigned char *dst_buf = kmap_atomic(dst) + dst_off;
+ unsigned char *src_buf = kmap_local_page(src) + src_off;
+ unsigned char *dst_buf = kmap_local_page(dst) + dst_off;
memcpy(dst_buf, src_buf, count);
- kunmap_atomic(dst_buf - dst_off);
- kunmap_atomic(src_buf - src_off);
+ kunmap_local(dst_buf - dst_off);
+ kunmap_local(src_buf - src_off);
}
static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data)
@@ -264,16 +264,13 @@ static int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data)
unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz;
unsigned int dma_len, dma_blk_cnt, dma_off;
struct scatterlist *sg = NULL;
- unsigned long flags;
if (host->sg_pos == host->sg_len)
return 1;
if (host->cmd_flags & DATA_CARRY) {
host->cmd_flags &= ~DATA_CARRY;
- local_irq_save(flags);
tifm_sd_bounce_block(host, r_data);
- local_irq_restore(flags);
if (host->sg_pos == host->sg_len)
return 1;
}
@@ -300,11 +297,9 @@ static int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data)
if (dma_blk_cnt)
sg = &r_data->sg[host->sg_pos];
else if (dma_len) {
- if (r_data->flags & MMC_DATA_WRITE) {
- local_irq_save(flags);
+ if (r_data->flags & MMC_DATA_WRITE)
tifm_sd_bounce_block(host, r_data);
- local_irq_restore(flags);
- } else
+ else
host->cmd_flags |= DATA_CARRY;
sg = &host->bounce_buf;
@@ -506,7 +501,6 @@ static void tifm_sd_card_event(struct tifm_dev *sock)
unsigned int host_status = 0;
int cmd_error = 0;
struct mmc_command *cmd = NULL;
- unsigned long flags;
spin_lock(&sock->lock);
host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock));
@@ -570,9 +564,7 @@ static void tifm_sd_card_event(struct tifm_dev *sock)
if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF
| TIFM_MMCSD_BRS)) {
- local_irq_save(flags);
tifm_sd_transfer_data(host);
- local_irq_restore(flags);
host_status &= ~TIFM_MMCSD_AE;
}
}
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
deleted file mode 100644
index 53a2ad9a24b8..000000000000
--- a/drivers/mmc/host/tmio_mmc.c
+++ /dev/null
@@ -1,227 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Driver for the MMC / SD / SDIO cell found in:
- *
- * TC6393XB TC6391XB TC6387XB T7L66XB ASIC3
- *
- * Copyright (C) 2017 Renesas Electronics Corporation
- * Copyright (C) 2017 Horms Solutions, Simon Horman
- * Copyright (C) 2007 Ian Molton
- * Copyright (C) 2004 Ian Molton
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/mfd/core.h>
-#include <linux/mfd/tmio.h>
-#include <linux/mmc/host.h>
-#include <linux/module.h>
-#include <linux/pagemap.h>
-#include <linux/scatterlist.h>
-
-#include "tmio_mmc.h"
-
-/* Registers specific to this variant */
-#define CTL_SDIO_REGS 0x100
-#define CTL_CLK_AND_WAIT_CTL 0x138
-#define CTL_RESET_SDIO 0x1e0
-
-static void tmio_mmc_clk_start(struct tmio_mmc_host *host)
-{
- sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
- sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
-
- usleep_range(10000, 11000);
- sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0100);
- usleep_range(10000, 11000);
-}
-
-static void tmio_mmc_clk_stop(struct tmio_mmc_host *host)
-{
- sd_ctrl_write16(host, CTL_CLK_AND_WAIT_CTL, 0x0000);
- usleep_range(10000, 11000);
-
- sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
- sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
-
- usleep_range(10000, 11000);
-}
-
-static void tmio_mmc_set_clock(struct tmio_mmc_host *host,
- unsigned int new_clock)
-{
- unsigned int divisor;
- u32 clk = 0;
- int clk_sel;
-
- if (new_clock == 0) {
- tmio_mmc_clk_stop(host);
- return;
- }
-
- divisor = host->pdata->hclk / new_clock;
-
- /* bit7 set: 1/512, ... bit0 set: 1/4, all bits clear: 1/2 */
- clk_sel = (divisor <= 1);
- clk = clk_sel ? 0 : (roundup_pow_of_two(divisor) >> 2);
-
- host->pdata->set_clk_div(host->pdev, clk_sel);
-
- sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
- sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
- sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & CLK_CTL_DIV_MASK);
- usleep_range(10000, 11000);
-
- tmio_mmc_clk_start(host);
-}
-
-static void tmio_mmc_reset(struct tmio_mmc_host *host, bool preserve)
-{
- sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000);
- usleep_range(10000, 11000);
- sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0001);
- usleep_range(10000, 11000);
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int tmio_mmc_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- const struct mfd_cell *cell = mfd_get_cell(pdev);
- int ret;
-
- ret = pm_runtime_force_suspend(dev);
-
- /* Tell MFD core it can disable us now.*/
- if (!ret && cell->disable)
- cell->disable(pdev);
-
- return ret;
-}
-
-static int tmio_mmc_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- const struct mfd_cell *cell = mfd_get_cell(pdev);
- int ret = 0;
-
- /* Tell the MFD core we are ready to be enabled */
- if (cell->resume)
- ret = cell->resume(pdev);
-
- if (!ret)
- ret = pm_runtime_force_resume(dev);
-
- return ret;
-}
-#endif
-
-static int tmio_mmc_probe(struct platform_device *pdev)
-{
- const struct mfd_cell *cell = mfd_get_cell(pdev);
- struct tmio_mmc_data *pdata;
- struct tmio_mmc_host *host;
- struct resource *res;
- int ret = -EINVAL, irq;
-
- if (pdev->num_resources != 2)
- goto out;
-
- pdata = pdev->dev.platform_data;
- if (!pdata || !pdata->hclk)
- goto out;
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- ret = irq;
- goto out;
- }
-
- /* Tell the MFD core we are ready to be enabled */
- if (cell->enable) {
- ret = cell->enable(pdev);
- if (ret)
- goto out;
- }
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -EINVAL;
- goto cell_disable;
- }
-
- host = tmio_mmc_host_alloc(pdev, pdata);
- if (IS_ERR(host)) {
- ret = PTR_ERR(host);
- goto cell_disable;
- }
-
- /* SD control register space size is 0x200, 0x400 for bus_shift=1 */
- host->bus_shift = resource_size(res) >> 10;
- host->set_clock = tmio_mmc_set_clock;
- host->reset = tmio_mmc_reset;
-
- host->mmc->f_max = pdata->hclk;
- host->mmc->f_min = pdata->hclk / 512;
-
- ret = tmio_mmc_host_probe(host);
- if (ret)
- goto host_free;
-
- ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq,
- IRQF_TRIGGER_FALLING,
- dev_name(&pdev->dev), host);
- if (ret)
- goto host_remove;
-
- pr_info("%s at 0x%p irq %d\n", mmc_hostname(host->mmc), host->ctl, irq);
-
- return 0;
-
-host_remove:
- tmio_mmc_host_remove(host);
-host_free:
- tmio_mmc_host_free(host);
-cell_disable:
- if (cell->disable)
- cell->disable(pdev);
-out:
- return ret;
-}
-
-static int tmio_mmc_remove(struct platform_device *pdev)
-{
- const struct mfd_cell *cell = mfd_get_cell(pdev);
- struct tmio_mmc_host *host = platform_get_drvdata(pdev);
-
- tmio_mmc_host_remove(host);
- if (cell->disable)
- cell->disable(pdev);
-
- return 0;
-}
-
-/* ------------------- device registration ----------------------- */
-
-static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume)
- SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
- tmio_mmc_host_runtime_resume, NULL)
-};
-
-static struct platform_driver tmio_mmc_driver = {
- .driver = {
- .name = "tmio-mmc",
- .probe_type = PROBE_PREFER_ASYNCHRONOUS,
- .pm = &tmio_mmc_dev_pm_ops,
- },
- .probe = tmio_mmc_probe,
- .remove = tmio_mmc_remove,
-};
-
-module_platform_driver(tmio_mmc_driver);
-
-MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver");
-MODULE_AUTHOR("Ian Molton <[email protected]>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:tmio-mmc");
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 501613c74406..de56e6534aea 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -128,6 +128,7 @@ struct tmio_mmc_dma_ops {
/* optional */
void (*end)(struct tmio_mmc_host *host); /* held host->lock */
+ bool (*dma_irq)(struct tmio_mmc_host *host);
};
struct tmio_mmc_host {
@@ -204,20 +205,6 @@ void tmio_mmc_enable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
void tmio_mmc_disable_mmc_irqs(struct tmio_mmc_host *host, u32 i);
irqreturn_t tmio_mmc_irq(int irq, void *devid);
-static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg,
- unsigned long *flags)
-{
- local_irq_save(*flags);
- return kmap_atomic(sg_page(sg)) + sg->offset;
-}
-
-static inline void tmio_mmc_kunmap_atomic(struct scatterlist *sg,
- unsigned long *flags, void *virt)
-{
- kunmap_atomic(virt - sg->offset);
- local_irq_restore(*flags);
-}
-
#ifdef CONFIG_PM
int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev);
diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c
index 437048bb8027..e24c3d284515 100644
--- a/drivers/mmc/host/tmio_mmc_core.c
+++ b/drivers/mmc/host/tmio_mmc_core.c
@@ -412,7 +412,6 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
void *sg_virt;
unsigned short *buf;
unsigned int count;
- unsigned long flags;
if (host->dma_on) {
pr_err("PIO IRQ in DMA mode!\n");
@@ -422,8 +421,8 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
return;
}
- sg_virt = tmio_mmc_kmap_atomic(host->sg_ptr, &flags);
- buf = (unsigned short *)(sg_virt + host->sg_off);
+ sg_virt = kmap_local_page(sg_page(host->sg_ptr));
+ buf = (unsigned short *)(sg_virt + host->sg_ptr->offset + host->sg_off);
count = host->sg_ptr->length - host->sg_off;
if (count > data->blksz)
@@ -437,7 +436,7 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
host->sg_off += count;
- tmio_mmc_kunmap_atomic(host->sg_ptr, &flags, sg_virt);
+ kunmap_local(sg_virt);
if (host->sg_off == host->sg_ptr->length)
tmio_mmc_next_sg(host);
@@ -446,11 +445,11 @@ static void tmio_mmc_pio_irq(struct tmio_mmc_host *host)
static void tmio_mmc_check_bounce_buffer(struct tmio_mmc_host *host)
{
if (host->sg_ptr == &host->bounce_sg) {
- unsigned long flags;
- void *sg_vaddr = tmio_mmc_kmap_atomic(host->sg_orig, &flags);
+ void *sg_virt = kmap_local_page(sg_page(host->sg_orig));
- memcpy(sg_vaddr, host->bounce_buf, host->bounce_sg.length);
- tmio_mmc_kunmap_atomic(host->sg_orig, &flags, sg_vaddr);
+ memcpy(sg_virt + host->sg_orig->offset, host->bounce_buf,
+ host->bounce_sg.length);
+ kunmap_local(sg_virt);
}
}
@@ -670,6 +669,9 @@ static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host, int ireg,
return true;
}
+ if (host->dma_ops && host->dma_ops->dma_irq && host->dma_ops->dma_irq(host))
+ return true;
+
return false;
}
diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c
index 8d037c2071ab..497791ffada6 100644
--- a/drivers/mmc/host/toshsd.c
+++ b/drivers/mmc/host/toshsd.c
@@ -651,7 +651,9 @@ static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret)
goto unmap;
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto free_irq;
base = pci_resource_start(pdev, 0);
dev_dbg(&pdev->dev, "MMIO %pa, IRQ %d\n", &base, pdev->irq);
@@ -660,6 +662,8 @@ static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
+free_irq:
+ free_irq(pdev->irq, host);
unmap:
pci_iounmap(pdev, host->ioaddr);
release:
diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c
index 88662a90ed96..a2b0d9461665 100644
--- a/drivers/mmc/host/via-sdmmc.c
+++ b/drivers/mmc/host/via-sdmmc.c
@@ -1151,7 +1151,9 @@ static int via_sd_probe(struct pci_dev *pcidev,
pcidev->subsystem_device == 0x3891)
sdhost->quirks = VIA_CRDR_QUIRK_300MS_PWRDELAY;
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto unmap;
return 0;
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index 97beece62fec..72f65f32abbc 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -2049,6 +2049,7 @@ static void vub300_enable_sdio_irq(struct mmc_host *mmc, int enable)
return;
kref_get(&vub300->kref);
if (enable) {
+ set_current_state(TASK_RUNNING);
mutex_lock(&vub300->irq_mutex);
if (vub300->irqs_queued) {
vub300->irqs_queued -= 1;
@@ -2064,6 +2065,7 @@ static void vub300_enable_sdio_irq(struct mmc_host *mmc, int enable)
vub300_queue_poll_work(vub300, 0);
}
mutex_unlock(&vub300->irq_mutex);
+ set_current_state(TASK_INTERRUPTIBLE);
} else {
vub300->irq_enabled = 0;
}
@@ -2299,14 +2301,14 @@ static int vub300_probe(struct usb_interface *interface,
0x0000, 0x0000, &vub300->system_port_status,
sizeof(vub300->system_port_status), 1000);
if (retval < 0) {
- goto error4;
+ goto error5;
} else if (sizeof(vub300->system_port_status) == retval) {
vub300->card_present =
(0x0001 & vub300->system_port_status.port_flags) ? 1 : 0;
vub300->read_only =
(0x0010 & vub300->system_port_status.port_flags) ? 1 : 0;
} else {
- goto error4;
+ goto error5;
}
usb_set_intfdata(interface, vub300);
INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread);
@@ -2329,8 +2331,13 @@ static int vub300_probe(struct usb_interface *interface,
"USB vub300 remote SDIO host controller[%d]"
"connected with no SD/SDIO card inserted\n",
interface_to_InterfaceNumber(interface));
- mmc_add_host(mmc);
+ retval = mmc_add_host(mmc);
+ if (retval)
+ goto error6;
+
return 0;
+error6:
+ del_timer_sync(&vub300->inactivity_timer);
error5:
mmc_free_host(mmc);
/*
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index 67ecd342fe5f..521af9251f33 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -267,7 +267,7 @@ static inline int wbsd_next_sg(struct wbsd_host *host)
static inline char *wbsd_map_sg(struct wbsd_host *host)
{
- return kmap_atomic(sg_page(host->cur_sg)) + host->cur_sg->offset;
+ return kmap_local_page(sg_page(host->cur_sg)) + host->cur_sg->offset;
}
static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data)
@@ -439,7 +439,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
* End of scatter list entry?
*/
if (host->remain == 0) {
- kunmap_atomic(buffer);
+ kunmap_local(buffer);
/*
* Get next entry. Check if last.
*/
@@ -451,7 +451,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host)
}
}
}
- kunmap_atomic(buffer);
+ kunmap_local(buffer);
/*
* This is a very dirty hack to solve a
@@ -505,7 +505,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
* End of scatter list entry?
*/
if (host->remain == 0) {
- kunmap_atomic(buffer);
+ kunmap_local(buffer);
/*
* Get next entry. Check if last.
*/
@@ -517,7 +517,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host)
}
}
}
- kunmap_atomic(buffer);
+ kunmap_local(buffer);
/*
* The controller stops sending interrupts for
@@ -1698,7 +1698,17 @@ static int wbsd_init(struct device *dev, int base, int irq, int dma,
*/
wbsd_init_device(host);
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret) {
+ if (!pnp)
+ wbsd_chip_poweroff(host);
+
+ wbsd_release_resources(host);
+ wbsd_free_mmc(dev);
+
+ mmc_free_host(mmc);
+ return ret;
+ }
pr_info("%s: W83L51xD", mmc_hostname(mmc));
if (host->chip_id != 0)
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
index 9b5c503e3a3f..9aa3027ca25e 100644
--- a/drivers/mmc/host/wmt-sdmmc.c
+++ b/drivers/mmc/host/wmt-sdmmc.c
@@ -856,11 +856,15 @@ static int wmt_mci_probe(struct platform_device *pdev)
/* configure the controller to a known 'ready' state */
wmt_reset_hardware(mmc);
- mmc_add_host(mmc);
+ ret = mmc_add_host(mmc);
+ if (ret)
+ goto fail7;
dev_info(&pdev->dev, "WMT SDHC Controller initialized\n");
return 0;
+fail7:
+ clk_disable_unprepare(priv->clk_sdmmc);
fail6:
clk_put(priv->clk_sdmmc);
fail5_and_a_half: