diff options
Diffstat (limited to 'drivers/mmc')
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, ®_offset); + of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, ®_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: |