diff options
Diffstat (limited to 'drivers/mmc/host')
104 files changed, 6189 insertions, 2872 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 6824131b69b1..4f060d3e5636 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -56,7 +56,7 @@ config MMC_STM32_SDMMC config MMC_PXA tristate "Intel PXA25x/26x/27x Multimedia Card Interface support" - depends on ARCH_PXA + depends on ARCH_PXA || COMPILE_TEST help This selects the Intel(R) PXA(R) Multimedia card Interface. If you have a PXA(R) platform with a Multimedia Card slot, @@ -250,6 +250,20 @@ config MMC_SDHCI_OF_DWCMSHC If you have a controller with this interface, say Y or M here. If unsure, say N. +config MMC_SDHCI_OF_K1 + tristate "SDHCI OF support for the SpacemiT K1 SoC" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on MMC_SDHCI_PLTFM + depends on OF + depends on COMMON_CLK + help + This selects the Secure Digital Host Controller Interface (SDHCI) + found in the SpacemiT K1 SoC. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_OF_SPARX5 tristate "SDHCI OF support for the MCHP Sparx5 SoC" depends on MMC_SDHCI_PLTFM @@ -301,14 +315,14 @@ config MMC_SDHCI_ESDHC_MCF config MMC_SDHCI_ESDHC_IMX tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller" - depends on ARCH_MXC || COMPILE_TEST + depends on ARCH_MXC || ARCH_S32 || COMPILE_TEST depends on MMC_SDHCI_PLTFM depends on OF select MMC_SDHCI_IO_ACCESSORS select MMC_CQHCI help This selects the Freescale eSDHC/uSDHC controller support - found on i.MX25, i.MX35 i.MX5x and i.MX6x. + found on i.MX25, i.MX35, i.MX5x, i.MX6x, and S32G. If you have a controller with this interface, say Y or M here. @@ -345,7 +359,7 @@ 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 + often referred 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 @@ -387,7 +401,7 @@ config MMC_SDHCI_SPEAR depends on OF help This selects the Secure Digital Host Controller Interface (SDHCI) - often referrered to as the HSMMC block in some of the ST SPEAR range + often referred to as the HSMMC block in some of the ST SPEAR range of SoC If you have a controller with this interface, say Y or M here. @@ -415,6 +429,20 @@ config MMC_SDHCI_BCM_KONA If you have a controller with this interface, say Y or M here. +config MMC_SDHCI_BST + tristate "SDHCI support for Black Sesame Technologies BST C1200 controller" + depends on ARCH_BST || COMPILE_TEST + depends on MMC_SDHCI_PLTFM + depends on OF + help + This selects the Secure Digital Host Controller Interface (SDHCI) + for Black Sesame Technologies BST C1200 SoC. The controller is + based on Synopsys DesignWare Cores Mobile Storage Controller but + requires platform-specific workarounds for hardware limitations. + + If you have a controller with this interface, say Y or M here. + If unsure, say N. + config MMC_SDHCI_F_SDH30 tristate "SDHCI support for Fujitsu Semiconductor F_SDH30" depends on MMC_SDHCI_PLTFM @@ -490,6 +518,7 @@ config MMC_MESON_MX_SDIO depends on ARCH_MESON || COMPILE_TEST depends on COMMON_CLK depends on OF_ADDRESS + select REGMAP_MMIO help This selects support for the SD/MMC Host Controller on Amlogic Meson6, Meson8 and Meson8b SoCs. @@ -594,7 +623,7 @@ config MMC_SDHCI_MSM config MMC_MXC tristate "Freescale i.MX21/27/31 or MPC512x Multimedia Card support" - depends on ARCH_MXC || PPC_MPC512x + depends on ARCH_MXC || PPC_MPC512x || COMPILE_TEST help This selects the Freescale i.MX21, i.MX27, i.MX31 or MPC512x Multimedia Card Interface. If you have an i.MX or MPC512x platform @@ -691,8 +720,8 @@ config MMC_TMIO_CORE config MMC_SDHI tristate "Renesas SDHI SD/SDIO controller support" depends on SUPERH || ARCH_RENESAS || COMPILE_TEST + depends on (RESET_CONTROLLER && REGULATOR) || !OF select MMC_TMIO_CORE - select RESET_CONTROLLER if ARCH_RENESAS help This provides support for the SDHI SD/SDIO controller found in Renesas SuperH, ARM and ARM64 based SoCs @@ -852,7 +881,8 @@ config MMC_DW_PCI config MMC_DW_ROCKCHIP tristate "Rockchip specific extensions for Synopsys DW Memory Card Interface" - depends on MMC_DW && ARCH_ROCKCHIP + depends on MMC_DW + depends on ARCH_ROCKCHIP || COMPILE_TEST select MMC_DW_PLTFM help This selects support for Rockchip SoC specific extensions to the @@ -934,8 +964,8 @@ config MMC_USHC config MMC_WMT tristate "Wondermedia SD/MMC Host Controller support" - depends on ARCH_VT8500 - default y + depends on ARCH_VT8500 || COMPILE_TEST + default ARCH_VT8500 help This selects support for the SD/MMC Host Controller on Wondermedia WM8505/WM8650 based SoCs. @@ -1028,7 +1058,7 @@ config MMC_MTK config MMC_SDHCI_MICROCHIP_PIC32 tristate "Microchip PIC32MZDA SDHCI support" - depends on MMC_SDHCI && PIC32MZDA && MMC_SDHCI_PLTFM + depends on MMC_SDHCI && MMC_SDHCI_PLTFM && (PIC32MZDA || COMPILE_TEST) help This selects the Secure Digital Host Controller Interface (SDHCI) for PIC32MZDA platform. @@ -1097,6 +1127,20 @@ config MMC_OWL This selects support for the SD/MMC Host Controller on Actions Semi Owl SoCs. +config MMC_LOONGSON2 + tristate "Loongson-2K SD/SDIO/eMMC Host Interface support" + depends on LOONGARCH || COMPILE_TEST + depends on HAS_DMA + select REGMAP_MMIO + help + This selects support for the SD/SDIO/eMMC Host Controller on + Loongson-2K series CPUs. + + To compile this driver as a module, choose M here: the + module will be called mmc_loongson2. + + If unsure, say N. + config MMC_SDHCI_EXTERNAL_DMA bool diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 5147467ec825..ee412e6b84d6 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_UHS2) += sdhci-uhs2.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o +obj-$(CONFIG_MMC_SDHCI_BST) += sdhci-of-bst.o sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \ sdhci-pci-dwc-mshc.o sdhci-pci-gli.o obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o @@ -72,6 +73,7 @@ obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o obj-$(CONFIG_MMC_TOSHIBA_PCI) += toshsd.o obj-$(CONFIG_MMC_BCM2835) += bcm2835.o obj-$(CONFIG_MMC_OWL) += owl-mmc.o +obj-$(CONFIG_MMC_LOONGSON2) += loongson2-mmc.o obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o @@ -88,6 +90,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_AT91) += sdhci-of-at91.o obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_OF_DWCMSHC) += sdhci-of-dwcmshc.o +obj-$(CONFIG_MMC_SDHCI_OF_K1) += sdhci-of-k1.o obj-$(CONFIG_MMC_SDHCI_OF_SPARX5) += sdhci-of-sparx5.o obj-$(CONFIG_MMC_SDHCI_OF_MA35D1) += sdhci-of-ma35d1.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o diff --git a/drivers/mmc/host/alcor.c b/drivers/mmc/host/alcor.c index b6b6dd677ae5..721db54739c1 100644 --- a/drivers/mmc/host/alcor.c +++ b/drivers/mmc/host/alcor.c @@ -20,6 +20,7 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/platform_device.h> +#include <linux/string_choices.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> @@ -208,7 +209,7 @@ static void alcor_trf_block_pio(struct alcor_sdmmc_host *host, bool read) len = min(host->sg_miter.length, blksize); dev_dbg(host->dev, "PIO, %s block size: 0x%zx\n", - read ? "read" : "write", blksize); + str_read_write(read), blksize); host->sg_miter.consumed = len; host->blocks--; @@ -1083,7 +1084,7 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) struct alcor_sdmmc_host *host; int ret; - mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) { dev_err(&pdev->dev, "Can't allocate MMC\n"); return -ENOMEM; @@ -1101,11 +1102,9 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) ret = devm_request_threaded_irq(&pdev->dev, priv->irq, alcor_irq, alcor_irq_thread, IRQF_SHARED, DRV_NAME_ALCOR_PCI_SDMMC, host); - - if (ret) { - dev_err(&pdev->dev, "Failed to get irq for data line\n"); - goto free_host; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to get irq for data line\n"); mutex_init(&host->cmd_mutex); INIT_DELAYED_WORK(&host->timeout_work, alcor_timeout_timer); @@ -1114,15 +1113,8 @@ static int alcor_pci_sdmmc_drv_probe(struct platform_device *pdev) alcor_hw_init(host); dev_set_drvdata(&pdev->dev, host); - ret = mmc_add_host(mmc); - if (ret) - goto free_host; - return 0; - -free_host: - mmc_free_host(mmc); - return ret; + return mmc_add_host(mmc); } static void alcor_pci_sdmmc_drv_remove(struct platform_device *pdev) @@ -1135,10 +1127,8 @@ static void alcor_pci_sdmmc_drv_remove(struct platform_device *pdev) alcor_hw_uninit(host); mmc_remove_host(mmc); - mmc_free_host(mmc); } -#ifdef CONFIG_PM_SLEEP static int alcor_pci_sdmmc_suspend(struct device *dev) { struct alcor_sdmmc_host *host = dev_get_drvdata(dev); @@ -1159,10 +1149,9 @@ static int alcor_pci_sdmmc_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(alcor_mmc_pm_ops, alcor_pci_sdmmc_suspend, - alcor_pci_sdmmc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(alcor_mmc_pm_ops, alcor_pci_sdmmc_suspend, + alcor_pci_sdmmc_resume); static const struct platform_device_id alcor_pci_sdmmc_ids[] = { { @@ -1180,7 +1169,7 @@ static struct platform_driver alcor_pci_sdmmc_driver = { .driver = { .name = DRV_NAME_ALCOR_PCI_SDMMC, .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &alcor_mmc_pm_ops + .pm = pm_sleep_ptr(&alcor_mmc_pm_ops), }, }; module_platform_driver(alcor_pci_sdmmc_driver); diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 24fffc702a94..3b4928f5b9b2 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -38,6 +38,7 @@ #include <asm/cacheflush.h> #include <asm/io.h> #include <linux/unaligned.h> +#include <linux/string_choices.h> #define ATMCI_MAX_NR_SLOTS 2 @@ -541,7 +542,6 @@ static int atmci_regs_show(struct seq_file *s, void *v) memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE); spin_unlock_bh(&host->lock); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); seq_printf(s, "MR:\t0x%08x%s%s ", @@ -609,12 +609,12 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot) if (!root) return; - debugfs_create_file("regs", S_IRUSR, root, host, &atmci_regs_fops); - debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops); - debugfs_create_u32("state", S_IRUSR, root, &host->state); - debugfs_create_xul("pending_events", S_IRUSR, root, + debugfs_create_file("regs", 0400, root, host, &atmci_regs_fops); + debugfs_create_file("req", 0400, root, slot, &atmci_req_fops); + debugfs_create_u32("state", 0400, root, &host->state); + debugfs_create_xul("pending_events", 0400, root, &host->pending_events); - debugfs_create_xul("completed_events", S_IRUSR, root, + debugfs_create_xul("completed_events", 0400, root, &host->completed_events); } @@ -629,14 +629,13 @@ static int atmci_of_init(struct atmel_mci *host) { struct device *dev = host->dev; struct device_node *np = dev->of_node; - struct device_node *cnp; u32 slot_id; int err; if (!np) return dev_err_probe(dev, -EINVAL, "device node not found\n"); - for_each_child_of_node(np, cnp) { + for_each_child_of_node_scoped(np, cnp) { if (of_property_read_u32(cnp, "reg", &slot_id)) { dev_warn(dev, "reg property is missing for %pOF\n", cnp); continue; @@ -645,7 +644,6 @@ static int atmci_of_init(struct atmel_mci *host) if (slot_id >= ATMCI_MAX_NR_SLOTS) { dev_warn(dev, "can't have more than %d slots\n", ATMCI_MAX_NR_SLOTS); - of_node_put(cnp); break; } @@ -658,10 +656,8 @@ static int atmci_of_init(struct atmel_mci *host) "cd", GPIOD_IN, "cd-gpios"); err = PTR_ERR_OR_ZERO(host->pdata[slot_id].detect_pin); if (err) { - if (err != -ENOENT) { - of_node_put(cnp); + if (err != -ENOENT) return err; - } host->pdata[slot_id].detect_pin = NULL; } @@ -673,10 +669,8 @@ static int atmci_of_init(struct atmel_mci *host) "wp", GPIOD_IN, "wp-gpios"); err = PTR_ERR_OR_ZERO(host->pdata[slot_id].wp_pin); if (err) { - if (err != -ENOENT) { - of_node_put(cnp); + if (err != -ENOENT) return err; - } host->pdata[slot_id].wp_pin = NULL; } } @@ -714,7 +708,7 @@ static inline unsigned int atmci_convert_chksize(struct atmel_mci *host, static void atmci_timeout_timer(struct timer_list *t) { - struct atmel_mci *host = from_timer(host, t, timer); + struct atmel_mci *host = timer_container_of(host, t, timer); struct device *dev = host->dev; dev_dbg(dev, "software timeout\n"); @@ -1592,7 +1586,7 @@ static void atmci_request_end(struct atmel_mci *host, struct mmc_request *mrq) WARN_ON(host->cmd || host->data); - del_timer(&host->timer); + timer_delete(&host->timer); /* * Update the MMC clock rate if necessary. This may be @@ -1652,7 +1646,8 @@ static void atmci_command_complete(struct atmel_mci *host, static void atmci_detect_change(struct timer_list *t) { - struct atmel_mci_slot *slot = from_timer(slot, t, detect_timer); + struct atmel_mci_slot *slot = timer_container_of(slot, t, + detect_timer); bool present; bool present_old; @@ -2247,7 +2242,7 @@ static int atmci_init_slot(struct atmel_mci *host, struct atmel_mci_slot *slot; int ret; - mmc = mmc_alloc_host(sizeof(struct atmel_mci_slot), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*slot)); if (!mmc) return -ENOMEM; @@ -2263,7 +2258,7 @@ static int atmci_init_slot(struct atmel_mci *host, "slot[%u]: bus_width=%u, detect_pin=%d, " "detect_is_active_high=%s, wp_pin=%d\n", id, slot_data->bus_width, desc_to_gpio(slot_data->detect_pin), - !gpiod_is_active_low(slot_data->detect_pin) ? "true" : "false", + str_true_false(!gpiod_is_active_low(slot_data->detect_pin)), desc_to_gpio(slot_data->wp_pin)); mmc->ops = &atmci_ops; @@ -2320,10 +2315,8 @@ static int atmci_init_slot(struct atmel_mci *host, host->slot[id] = slot; mmc_regulator_get_supply(mmc); ret = mmc_add_host(mmc); - if (ret) { - mmc_free_host(mmc); + if (ret) return ret; - } if (slot->detect_pin) { timer_setup(&slot->detect_timer, atmci_detect_change, 0); @@ -2357,11 +2350,10 @@ static void atmci_cleanup_slot(struct atmel_mci_slot *slot, if (slot->detect_pin) { free_irq(gpiod_to_irq(slot->detect_pin), slot); - del_timer_sync(&slot->detect_timer); + timer_delete_sync(&slot->detect_timer); } slot->host->slot[id] = NULL; - mmc_free_host(slot->mmc); } static int atmci_configure_dma(struct atmel_mci *host) @@ -2569,7 +2561,6 @@ static int atmci_probe(struct platform_device *pdev) dev_info(dev, "Atmel MCI controller at 0x%08lx irq %d, %u slots\n", host->mapbase, irq, nr_slots); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -2585,7 +2576,7 @@ err_init_slot: pm_runtime_disable(dev); pm_runtime_put_noidle(dev); - del_timer_sync(&host->timer); + timer_delete_sync(&host->timer); if (!IS_ERR(host->dma.chan)) dma_release_channel(host->dma.chan); err_dma_probe_defer: @@ -2613,7 +2604,7 @@ static void atmci_remove(struct platform_device *pdev) atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS); atmci_readl(host, ATMCI_SR); - del_timer_sync(&host->timer); + timer_delete_sync(&host->timer); if (!IS_ERR(host->dma.chan)) dma_release_channel(host->dma.chan); @@ -2625,7 +2616,6 @@ static void atmci_remove(struct platform_device *pdev) pm_runtime_put_noidle(dev); } -#ifdef CONFIG_PM static int atmci_runtime_suspend(struct device *dev) { struct atmel_mci *host = dev_get_drvdata(dev); @@ -2645,12 +2635,10 @@ static int atmci_runtime_resume(struct device *dev) return clk_prepare_enable(host->mck); } -#endif static const struct dev_pm_ops atmci_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(atmci_runtime_suspend, atmci_runtime_resume, NULL) }; static struct platform_driver atmci_driver = { @@ -2660,7 +2648,7 @@ static struct platform_driver atmci_driver = { .name = "atmel_mci", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = atmci_dt_ids, - .pm = &atmci_dev_pm_ops, + .pm = pm_ptr(&atmci_dev_pm_ops), }, }; module_platform_driver(atmci_driver); diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index 057d42307832..cc6e05f9b96f 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -937,11 +937,10 @@ static int au1xmmc_probe(struct platform_device *pdev) struct resource *r; int ret, iflag; - mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) { dev_err(&pdev->dev, "no memory for mmc_host\n"); - ret = -ENOMEM; - goto out0; + return -ENOMEM; } host = mmc_priv(mmc); @@ -953,14 +952,14 @@ static int au1xmmc_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { dev_err(&pdev->dev, "no mmio defined\n"); - goto out1; + return ret; } host->ioarea = request_mem_region(r->start, resource_size(r), pdev->name); if (!host->ioarea) { dev_err(&pdev->dev, "mmio already in use\n"); - goto out1; + return ret; } host->iobase = ioremap(r->start, 0x3c); @@ -1109,9 +1108,6 @@ out3: out2: release_resource(host->ioarea); kfree(host->ioarea); -out1: - mmc_free_host(mmc); -out0: return ret; } @@ -1151,15 +1147,12 @@ static void au1xmmc_remove(struct platform_device *pdev) iounmap((void *)host->iobase); release_resource(host->ioarea); kfree(host->ioarea); - - mmc_free_host(host->mmc); } } -#ifdef CONFIG_PM -static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state) +static int au1xmmc_suspend(struct device *dev) { - struct au1xmmc_host *host = platform_get_drvdata(pdev); + struct au1xmmc_host *host = dev_get_drvdata(dev); __raw_writel(0, HOST_CONFIG2(host)); __raw_writel(0, HOST_CONFIG(host)); @@ -1170,27 +1163,24 @@ static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int au1xmmc_resume(struct platform_device *pdev) +static int au1xmmc_resume(struct device *dev) { - struct au1xmmc_host *host = platform_get_drvdata(pdev); + struct au1xmmc_host *host = dev_get_drvdata(dev); au1xmmc_reset_controller(host); return 0; } -#else -#define au1xmmc_suspend NULL -#define au1xmmc_resume NULL -#endif + +static DEFINE_SIMPLE_DEV_PM_OPS(au1xmmc_pmops, au1xmmc_suspend, au1xmmc_resume); static struct platform_driver au1xmmc_driver = { .probe = au1xmmc_probe, .remove = au1xmmc_remove, - .suspend = au1xmmc_suspend, - .resume = au1xmmc_resume, .driver = { .name = DRIVER_NAME, .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .pm = pm_sleep_ptr(&au1xmmc_pmops), }, }; diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index e5f151d092cd..ee63835b3ca0 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -44,6 +44,7 @@ #include <linux/scatterlist.h> #include <linux/time.h> #include <linux/workqueue.h> +#include <linux/string_choices.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> @@ -391,8 +392,7 @@ static void bcm2835_transfer_block_pio(struct bcm2835_host *host, bool is_read) if (time_after(jiffies, wait_max)) { dev_err(dev, "PIO %s timeout - EDM %08x\n", - is_read ? "read" : "write", - edm); + str_read_write(is_read), edm); hsts = SDHSTS_REW_TIME_OUT; break; } @@ -435,12 +435,12 @@ static void bcm2835_transfer_pio(struct bcm2835_host *host) SDHSTS_CRC7_ERROR | SDHSTS_FIFO_ERROR)) { dev_err(dev, "%s transfer error - HSTS %08x\n", - is_read ? "read" : "write", sdhsts); + str_read_write(is_read), sdhsts); host->data->error = -EILSEQ; } else if ((sdhsts & (SDHSTS_CMD_TIME_OUT | SDHSTS_REW_TIME_OUT))) { dev_err(dev, "%s timeout error - HSTS %08x\n", - is_read ? "read" : "write", sdhsts); + str_read_write(is_read), sdhsts); host->data->error = -ETIMEDOUT; } } @@ -503,7 +503,8 @@ void bcm2835_prepare_dma(struct bcm2835_host *host, struct mmc_data *data) DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) { - dma_unmap_sg(dma_chan->device->dev, data->sg, sg_len, dir_data); + dma_unmap_sg(dma_chan->device->dev, data->sg, data->sg_len, + dir_data); return; } @@ -1371,7 +1372,7 @@ static int bcm2835_probe(struct platform_device *pdev) int ret; dev_dbg(dev, "%s\n", __func__); - mmc = mmc_alloc_host(sizeof(*host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1450,7 +1451,6 @@ err: dev_dbg(dev, "%s -> err %d\n", __func__, ret); if (host->dma_chan_rxtx) dma_release_channel(host->dma_chan_rxtx); - mmc_free_host(mmc); return ret; } @@ -1473,8 +1473,6 @@ static void bcm2835_remove(struct platform_device *pdev) if (host->dma_chan_rxtx) dma_release_channel(host->dma_chan_rxtx); - - mmc_free_host(mmc); } static const struct of_device_id bcm2835_match[] = { diff --git a/drivers/mmc/host/cavium-octeon.c b/drivers/mmc/host/cavium-octeon.c index 0592f356b1e5..8a0daddd9200 100644 --- a/drivers/mmc/host/cavium-octeon.c +++ b/drivers/mmc/host/cavium-octeon.c @@ -148,7 +148,7 @@ static void octeon_mmc_dmar_fixup_done(struct cvm_mmc_host *host) static int octeon_mmc_probe(struct platform_device *pdev) { - struct device_node *cn, *node = pdev->dev.of_node; + struct device_node *node = pdev->dev.of_node; struct cvm_mmc_host *host; void __iomem *base; int mmc_irq[9]; @@ -268,7 +268,7 @@ static int octeon_mmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); i = 0; - for_each_child_of_node(node, cn) { + for_each_child_of_node_scoped(node, cn) { host->slot_pdev[i] = of_platform_device_create(cn, NULL, &pdev->dev); if (!host->slot_pdev[i]) { @@ -279,7 +279,6 @@ static int octeon_mmc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Error populating slots\n"); octeon_mmc_set_shared_power(host, 0); - of_node_put(cn); goto error; } i++; diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c index 2e2ff984f0b3..1373deb3f531 100644 --- a/drivers/mmc/host/cavium-thunderx.c +++ b/drivers/mmc/host/cavium-thunderx.c @@ -72,7 +72,7 @@ static int thunder_mmc_probe(struct pci_dev *pdev, if (ret) return ret; - ret = pci_request_regions(pdev, KBUILD_MODNAME); + ret = pcim_request_all_regions(pdev, KBUILD_MODNAME); if (ret) return ret; @@ -164,7 +164,6 @@ error: } } clk_disable_unprepare(host->clk); - pci_release_regions(pdev); return ret; } @@ -183,7 +182,6 @@ static void thunder_mmc_remove(struct pci_dev *pdev) writeq(dma_cfg, host->dma_base + MIO_EMM_DMA_CFG(host)); clk_disable_unprepare(host->clk); - pci_release_regions(pdev); } static const struct pci_device_id thunder_mmc_id_table[] = { diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c index 95a41983c6c0..37a88f2a0c86 100644 --- a/drivers/mmc/host/cavium.c +++ b/drivers/mmc/host/cavium.c @@ -905,9 +905,7 @@ static void cvm_mmc_set_clock(struct cvm_mmc_slot *slot, unsigned int clock) { struct mmc_host *mmc = slot->mmc; - clock = min(clock, mmc->f_max); - clock = max(clock, mmc->f_min); - slot->clock = clock; + slot->clock = clamp(clock, mmc->f_min, mmc->f_max); } static int cvm_mmc_init_lowlevel(struct cvm_mmc_slot *slot) @@ -1012,7 +1010,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) struct mmc_host *mmc; int ret, id; - mmc = mmc_alloc_host(sizeof(struct cvm_mmc_slot), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*slot)); if (!mmc) return -ENOMEM; @@ -1022,7 +1020,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) ret = cvm_mmc_of_parse(dev, slot); if (ret < 0) - goto error; + return ret; id = ret; /* Set up host parameters */ @@ -1066,12 +1064,7 @@ int cvm_mmc_of_slot_probe(struct device *dev, struct cvm_mmc_host *host) if (ret) { dev_err(dev, "mmc_add_host() returned %d\n", ret); slot->host->slot[id] = NULL; - goto error; } - return 0; - -error: - mmc_free_host(slot->mmc); return ret; } @@ -1079,6 +1072,5 @@ int cvm_mmc_of_slot_remove(struct cvm_mmc_slot *slot) { mmc_remove_host(slot->mmc); slot->host->slot[slot->bus_id] = NULL; - mmc_free_host(slot->mmc); return 0; } diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index d741c1f9cf87..31daec787495 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -8,6 +8,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/delay.h> +#include <linux/string_choices.h> #include "cb710-mmc.h" #define CB710_MMC_REQ_TIMEOUT_MS 2000 @@ -215,7 +216,7 @@ static void cb710_mmc_set_transfer_size(struct cb710_slot *slot, ((count - 1) << 16)|(blocksize - 1)); dev_vdbg(cb710_slot_dev(slot), "set up for %zu block%s of %zu bytes\n", - count, count == 1 ? "" : "s", blocksize); + count, str_plural(count), blocksize); } static void cb710_mmc_fifo_hack(struct cb710_slot *slot) @@ -663,25 +664,25 @@ static const struct mmc_host_ops cb710_mmc_host = { .get_cd = cb710_mmc_get_cd, }; -#ifdef CONFIG_PM - -static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state) +static int cb710_mmc_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct cb710_slot *slot = cb710_pdev_to_slot(pdev); cb710_mmc_enable_irq(slot, 0, ~0); return 0; } -static int cb710_mmc_resume(struct platform_device *pdev) +static int cb710_mmc_resume(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct cb710_slot *slot = cb710_pdev_to_slot(pdev); cb710_mmc_enable_irq(slot, 0, ~0); return 0; } -#endif /* CONFIG_PM */ +static DEFINE_SIMPLE_DEV_PM_OPS(cb710_mmc_pmops, cb710_mmc_suspend, cb710_mmc_resume); static int cb710_mmc_init(struct platform_device *pdev) { @@ -692,7 +693,7 @@ static int cb710_mmc_init(struct platform_device *pdev) int err; u32 val; - mmc = mmc_alloc_host(sizeof(*reader), cb710_slot_dev(slot)); + mmc = devm_mmc_alloc_host(cb710_slot_dev(slot), sizeof(*reader)); if (!mmc) return -ENOMEM; @@ -741,7 +742,6 @@ err_free_mmc: dev_dbg(cb710_slot_dev(slot), "mmc_add_host() failed: %d\n", err); cb710_set_irq_handler(slot, NULL); - mmc_free_host(mmc); return err; } @@ -764,18 +764,15 @@ static void cb710_mmc_exit(struct platform_device *pdev) cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0); cancel_work_sync(&reader->finish_req_bh_work); - - mmc_free_host(mmc); } static struct platform_driver cb710_mmc_driver = { - .driver.name = "cb710-mmc", + .driver = { + .name = "cb710-mmc", + .pm = pm_sleep_ptr(&cb710_mmc_pmops), + }, .probe = cb710_mmc_init, .remove = cb710_mmc_exit, -#ifdef CONFIG_PM - .suspend = cb710_mmc_suspend, - .resume = cb710_mmc_resume, -#endif }; module_platform_driver(cb710_mmc_driver); diff --git a/drivers/mmc/host/cqhci-crypto.c b/drivers/mmc/host/cqhci-crypto.c index cb8044093402..5a467098a0d6 100644 --- a/drivers/mmc/host/cqhci-crypto.c +++ b/drivers/mmc/host/cqhci-crypto.c @@ -84,11 +84,11 @@ static int cqhci_crypto_keyslot_program(struct blk_crypto_profile *profile, if (ccap_array[cap_idx].algorithm_id == CQHCI_CRYPTO_ALG_AES_XTS) { /* In XTS mode, the blk_crypto_key's size is already doubled */ - memcpy(cfg.crypto_key, key->raw, key->size/2); + memcpy(cfg.crypto_key, key->bytes, key->size/2); memcpy(cfg.crypto_key + CQHCI_CRYPTO_KEY_MAX_SIZE/2, - key->raw + key->size/2, key->size/2); + key->bytes + key->size/2, key->size/2); } else { - memcpy(cfg.crypto_key, key->raw, key->size); + memcpy(cfg.crypto_key, key->bytes, key->size); } cqhci_crypto_program_key(cq_host, &cfg, slot); @@ -204,6 +204,8 @@ int cqhci_crypto_init(struct cqhci_host *cq_host) /* Unfortunately, CQHCI crypto only supports 32 DUN bits. */ profile->max_dun_bytes_supported = 4; + profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW; + /* * Cache all the crypto capabilities and advertise the supported crypto * modes and data unit sizes to the block layer. diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h index ce189a1866b9..3668856531c1 100644 --- a/drivers/mmc/host/cqhci.h +++ b/drivers/mmc/host/cqhci.h @@ -93,6 +93,7 @@ /* send status config 1 */ #define CQHCI_SSC1 0x40 #define CQHCI_SSC1_CBC_MASK GENMASK(19, 16) +#define CQHCI_SSC1_CIT_MASK GENMASK(15, 0) /* send status config 2 */ #define CQHCI_SSC2 0x44 diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index cde4c4339ab7..42b0118a45a8 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -145,17 +145,17 @@ #define MAX_NR_SG 16 static unsigned rw_threshold = 32; -module_param(rw_threshold, uint, S_IRUGO); +module_param(rw_threshold, uint, 0444); MODULE_PARM_DESC(rw_threshold, "Read/Write threshold. Default = 32"); static unsigned poll_threshold = 128; -module_param(poll_threshold, uint, S_IRUGO); +module_param(poll_threshold, uint, 0444); MODULE_PARM_DESC(poll_threshold, "Polling transaction size threshold. Default = 128"); static unsigned poll_loopcount = 32; -module_param(poll_loopcount, uint, S_IRUGO); +module_param(poll_loopcount, uint, 0444); MODULE_PARM_DESC(poll_loopcount, "Maximum polling loop count. Default = 32"); @@ -588,7 +588,7 @@ static void mmc_davinci_request(struct mmc_host *mmc, struct mmc_request *req) cpu_relax(); } if (mmcst1 & MMCST1_BUSY) { - dev_err(mmc_dev(host->mmc), "still BUSY? bad ... \n"); + dev_err(mmc_dev(host->mmc), "still BUSY? bad ...\n"); req->cmd->error = -ETIMEDOUT; mmc_request_done(mmc, req); return; @@ -1203,7 +1203,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) if (!mem) return -EBUSY; - mmc = mmc_alloc_host(sizeof(struct mmc_davinci_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1212,19 +1212,16 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) host->mem_res = mem; host->base = devm_ioremap(&pdev->dev, mem->start, mem_size); - if (!host->base) { - ret = -ENOMEM; - goto ioremap_fail; - } + if (!host->base) + return -ENOMEM; host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - goto clk_get_fail; - } + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + ret = clk_prepare_enable(host->clk); if (ret) - goto clk_prepare_enable_fail; + return ret; host->mmc_input_clk = clk_get_rate(host->clk); @@ -1336,10 +1333,6 @@ cpu_freq_fail: parse_fail: dma_probe_defer: clk_disable_unprepare(host->clk); -clk_prepare_enable_fail: -clk_get_fail: -ioremap_fail: - mmc_free_host(mmc); return ret; } @@ -1352,10 +1345,8 @@ static void davinci_mmcsd_remove(struct platform_device *pdev) mmc_davinci_cpufreq_deregister(host); davinci_release_dma_channels(host); clk_disable_unprepare(host->clk); - mmc_free_host(host->mmc); } -#ifdef CONFIG_PM static int davinci_mmcsd_suspend(struct device *dev) { struct mmc_davinci_host *host = dev_get_drvdata(dev); @@ -1381,21 +1372,14 @@ static int davinci_mmcsd_resume(struct device *dev) return 0; } -static const struct dev_pm_ops davinci_mmcsd_pm = { - .suspend = davinci_mmcsd_suspend, - .resume = davinci_mmcsd_resume, -}; - -#define davinci_mmcsd_pm_ops (&davinci_mmcsd_pm) -#else -#define davinci_mmcsd_pm_ops NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(davinci_mmcsd_pm_ops, + davinci_mmcsd_suspend, davinci_mmcsd_resume); static struct platform_driver davinci_mmcsd_driver = { .driver = { .name = "davinci_mmc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = davinci_mmcsd_pm_ops, + .pm = pm_sleep_ptr(&davinci_mmcsd_pm_ops), .of_match_table = davinci_mmc_dt_ids, }, .probe = davinci_mmcsd_probe, diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c index 3cf526ab0387..81dc072d0e3f 100644 --- a/drivers/mmc/host/dw_mmc-bluefield.c +++ b/drivers/mmc/host/dw_mmc-bluefield.c @@ -73,7 +73,7 @@ static struct platform_driver dw_mci_bluefield_pltfm_driver = { .name = "dwmmc_bluefield", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_bluefield_match, - .pm = &dw_mci_pltfm_pmops, + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 53d32d0f2709..261344d3a8cf 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -27,6 +27,8 @@ enum dw_mci_exynos_type { DW_MCI_TYPE_EXYNOS5420_SMU, DW_MCI_TYPE_EXYNOS7, DW_MCI_TYPE_EXYNOS7_SMU, + DW_MCI_TYPE_EXYNOS7870, + DW_MCI_TYPE_EXYNOS7870_SMU, DW_MCI_TYPE_ARTPEC8, }; @@ -70,6 +72,12 @@ static struct dw_mci_exynos_compatible { .compatible = "samsung,exynos7-dw-mshc-smu", .ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU, }, { + .compatible = "samsung,exynos7870-dw-mshc", + .ctrl_type = DW_MCI_TYPE_EXYNOS7870, + }, { + .compatible = "samsung,exynos7870-dw-mshc-smu", + .ctrl_type = DW_MCI_TYPE_EXYNOS7870_SMU, + }, { .compatible = "axis,artpec8-dw-mshc", .ctrl_type = DW_MCI_TYPE_ARTPEC8, }, @@ -85,6 +93,8 @@ static inline u8 dw_mci_exynos_get_ciu_div(struct dw_mci *host) return EXYNOS4210_FIXED_CIU_CLK_DIV; else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) return SDMMC_CLKSEL_GET_DIV(mci_readl(host, CLKSEL64)) + 1; else @@ -100,7 +110,8 @@ static void dw_mci_exynos_config_smu(struct dw_mci *host) * set for non-ecryption mode at this time. */ if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU || - priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) { + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) { mci_writel(host, MPSBEGIN0, 0); mci_writel(host, MPSEND0, SDMMC_ENDING_SEC_NR_MAX); mci_writel(host, MPSCTRL0, SDMMC_MPSCTRL_SECURE_WRITE_BIT | @@ -126,6 +137,12 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) DQS_CTRL_GET_RD_DELAY(priv->saved_strobe_ctrl); } + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU) { + /* Quirk needed for certain Exynos SoCs */ + host->quirks |= DW_MMC_QUIRK_FIFO64_32; + } + if (priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) { /* Quirk needed for the ARTPEC-8 SoC */ host->quirks |= DW_MMC_QUIRK_EXTENDED_TMOUT; @@ -143,6 +160,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) clksel = mci_readl(host, CLKSEL64); else @@ -152,6 +171,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) mci_writel(host, CLKSEL64, clksel); else @@ -164,11 +185,10 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing) * HOLD register should be bypassed in case there is no phase shift * applied on CMD/DATA that is sent to the card. */ - if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->slot) - set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags); + if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel)) + set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags); } -#ifdef CONFIG_PM static int dw_mci_exynos_runtime_resume(struct device *dev) { struct dw_mci *host = dev_get_drvdata(dev); @@ -182,9 +202,7 @@ static int dw_mci_exynos_runtime_resume(struct device *dev) return ret; } -#endif /* CONFIG_PM */ -#ifdef CONFIG_PM_SLEEP /** * dw_mci_exynos_suspend_noirq - Exynos-specific suspend code * @dev: Device to suspend (this device) @@ -222,6 +240,8 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) clksel = mci_readl(host, CLKSEL64); else @@ -230,6 +250,8 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) if (clksel & SDMMC_CLKSEL_WAKEUP_INT) { if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) mci_writel(host, CLKSEL64, clksel); else @@ -240,7 +262,6 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing) { @@ -409,6 +430,8 @@ static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64)); else @@ -422,6 +445,8 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) clksel = mci_readl(host, CLKSEL64); else @@ -429,6 +454,8 @@ static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) clksel = SDMMC_CLKSEL_UP_SAMPLE(clksel, sample); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) mci_writel(host, CLKSEL64, clksel); else @@ -443,6 +470,8 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) clksel = mci_readl(host, CLKSEL64); else @@ -453,6 +482,8 @@ static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 || priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS7870_SMU || priv->ctrl_type == DW_MCI_TYPE_ARTPEC8) mci_writel(host, CLKSEL64, clksel); else @@ -499,11 +530,10 @@ out: return loc; } -static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_exynos_execute_tuning(struct dw_mci *host, u32 opcode) { - struct dw_mci *host = slot->host; struct dw_mci_exynos_priv_data *priv = host->priv; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; u8 start_smpl, smpl, candidates = 0; s8 found; int ret = 0; @@ -632,6 +662,10 @@ static const struct of_device_id dw_mci_exynos_match[] = { .data = &exynos_drv_data, }, { .compatible = "samsung,exynos7-dw-mshc-smu", .data = &exynos_drv_data, }, + { .compatible = "samsung,exynos7870-dw-mshc", + .data = &exynos_drv_data, }, + { .compatible = "samsung,exynos7870-dw-mshc-smu", + .data = &exynos_drv_data, }, { .compatible = "axis,artpec8-dw-mshc", .data = &artpec_drv_data, }, {}, @@ -673,11 +707,8 @@ static void dw_mci_exynos_remove(struct platform_device *pdev) } static const struct dev_pm_ops dw_mci_exynos_pmops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq, - dw_mci_exynos_resume_noirq) - SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, - dw_mci_exynos_runtime_resume, - NULL) + NOIRQ_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend_noirq, dw_mci_exynos_resume_noirq) + RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_exynos_runtime_resume, NULL) }; static struct platform_driver dw_mci_exynos_pltfm_driver = { @@ -687,7 +718,7 @@ static struct platform_driver dw_mci_exynos_pltfm_driver = { .name = "dwmmc_exynos", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_exynos_match, - .pm = &dw_mci_exynos_pmops, + .pm = pm_ptr(&dw_mci_exynos_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c index 0ccfae1b2dbe..4b723ed3259c 100644 --- a/drivers/mmc/host/dw_mmc-hi3798cv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798cv200.c @@ -57,11 +57,9 @@ static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) clk_set_phase(priv->drive_clk, 135); } -static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot, - u32 opcode) +static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci *host, u32 opcode) { static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 }; - struct dw_mci *host = slot->host; struct hi3798cv200_priv *priv = host->priv; int raise_point = -1, fall_point = -1; int err, prev_err = -1; @@ -72,7 +70,7 @@ static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot, clk_set_phase(priv->sample_clk, degrees[i]); mci_writel(host, RINTSTS, ALL_INT_CLR); - err = mmc_send_tuning(slot->mmc, opcode, NULL); + err = mmc_send_tuning(host->mmc, opcode, NULL); if (!err) found = 1; diff --git a/drivers/mmc/host/dw_mmc-hi3798mv200.c b/drivers/mmc/host/dw_mmc-hi3798mv200.c index 5791a975a944..fda417888be9 100644 --- a/drivers/mmc/host/dw_mmc-hi3798mv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798mv200.c @@ -30,13 +30,12 @@ struct dw_mci_hi3798mv200_priv { struct clk *drive_clk; struct regmap *crg_reg; u32 sap_dll_offset; - struct mmc_clk_phase_map phase_map; }; static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) { struct dw_mci_hi3798mv200_priv *priv = host->priv; - struct mmc_clk_phase phase = priv->phase_map.phase[ios->timing]; + struct mmc_clk_phase phase = host->phase_map.phase[ios->timing]; u32 val; val = mci_readl(host, ENABLE_SHIFT); @@ -74,25 +73,24 @@ static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } -static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci_slot *slot) +static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci *host) { - struct dw_mci_hi3798mv200_priv *priv = slot->host->priv; + struct dw_mci_hi3798mv200_priv *priv = host->priv; return regmap_clear_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE); } -static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci_slot *slot) +static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci *host) { - struct dw_mci_hi3798mv200_priv *priv = slot->host->priv; + struct dw_mci_hi3798mv200_priv *priv = host->priv; return regmap_set_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE); } -static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, +static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci *host, u32 opcode) { static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 }; - struct dw_mci *host = slot->host; struct dw_mci_hi3798mv200_priv *priv = host->priv; int raise_point = -1, fall_point = -1, mid; int err, prev_err = -1; @@ -101,7 +99,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, int i; int ret; - ret = dw_mci_hi3798mv200_enable_tuning(slot); + ret = dw_mci_hi3798mv200_enable_tuning(host); if (ret < 0) return ret; @@ -115,7 +113,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, * * Treat edge(flip) found as an error too. */ - err = mmc_send_tuning(slot->mmc, opcode, NULL); + err = mmc_send_tuning(host->mmc, opcode, NULL); regval = mci_readl(host, TUNING_CTRL); if (err || (regval & SDMMC_TUNING_FIND_EDGE)) err = 1; @@ -136,7 +134,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, } tuning_out: - ret = dw_mci_hi3798mv200_disable_tuning(slot); + ret = dw_mci_hi3798mv200_disable_tuning(host); if (ret < 0) return ret; @@ -159,9 +157,9 @@ tuning_out: * We don't care what timing we are tuning for, * simply use the same phase for all timing needs tuning. */ - priv->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid]; - priv->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid]; - priv->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid]; + host->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid]; + host->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid]; + host->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid]; clk_set_phase(priv->sample_clk, degrees[mid]); dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n", @@ -186,8 +184,6 @@ static int dw_mci_hi3798mv200_init(struct dw_mci *host) if (!priv) return -ENOMEM; - mmc_of_parse_clk_phase(host->dev, &priv->phase_map); - priv->sample_clk = devm_clk_get_enabled(host->dev, "ciu-sample"); if (IS_ERR(priv->sample_clk)) return dev_err_probe(host->dev, PTR_ERR(priv->sample_clk), diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 0311a37dd4ab..59b802c9f2b8 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -53,7 +53,6 @@ #define USE_DLY_MAX_SMPL (14) struct k3_priv { - int ctrl_id; u32 cur_speed; struct regmap *reg; }; @@ -127,26 +126,17 @@ static int dw_mci_hi6220_parse_dt(struct dw_mci *host) if (IS_ERR(priv->reg)) priv->reg = NULL; - priv->ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); - if (priv->ctrl_id < 0) - priv->ctrl_id = 0; - - if (priv->ctrl_id >= TIMING_MODE) - return -EINVAL; - host->priv = priv; return 0; } -static int dw_mci_hi6220_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +static int dw_mci_hi6220_switch_voltage(struct dw_mci *host, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); struct k3_priv *priv; - struct dw_mci *host; + struct mmc_host *mmc = host->mmc; int min_uv, max_uv; int ret; - host = slot->host; priv = host->priv; if (!priv || !priv->reg) @@ -199,7 +189,7 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios) host->bus_hz = clk_get_rate(host->biu_clk); } -static int dw_mci_hi6220_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_hi6220_execute_tuning(struct dw_mci *host, u32 opcode) { return 0; } @@ -213,7 +203,7 @@ static const struct dw_mci_drv_data hi6220_data = { .execute_tuning = dw_mci_hi6220_execute_tuning, }; -static void dw_mci_hs_set_timing(struct dw_mci *host, int timing, +static int dw_mci_hs_set_timing(struct dw_mci *host, int timing, int smpl_phase) { u32 drv_phase; @@ -222,10 +212,10 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing, u32 enable_shift = 0; u32 reg_value; int ctrl_id; - struct k3_priv *priv; - priv = host->priv; - ctrl_id = priv->ctrl_id; + ctrl_id = host->mmc->index; + if (ctrl_id >= TIMING_MODE) + return -EINVAL; drv_phase = hs_timing_cfg[ctrl_id][timing].drv_phase; smpl_dly = hs_timing_cfg[ctrl_id][timing].smpl_dly; @@ -262,6 +252,8 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing, /* We should delay 1ms wait for timing setting finished. */ usleep_range(1000, 2000); + + return 0; } static int dw_mci_hi3660_init(struct dw_mci *host) @@ -269,10 +261,9 @@ static int dw_mci_hi3660_init(struct dw_mci *host) mci_writel(host, CDTHRCTL, SDMMC_SET_THLD(SDCARD_RD_THRESHOLD, SDMMC_CARD_RD_THR_EN)); - dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1); host->bus_hz /= (GENCLK_DIV + 1); - return 0; + return dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1); } static int dw_mci_set_sel18(struct dw_mci *host, bool set) @@ -364,11 +355,10 @@ static int dw_mci_get_best_clksmpl(unsigned int sample_flag) return middle_range; } -static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_hi3660_execute_tuning(struct dw_mci *host, u32 opcode) { int i = 0; - struct dw_mci *host = slot->host; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; int smpl_phase = 0; u32 tuning_sample_flag = 0; int best_clksmpl = 0; @@ -398,21 +388,19 @@ static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode) return 0; } -static int dw_mci_hi3660_switch_voltage(struct mmc_host *mmc, +static int dw_mci_hi3660_switch_voltage(struct dw_mci *host, struct mmc_ios *ios) { - int ret = 0; - struct dw_mci_slot *slot = mmc_priv(mmc); struct k3_priv *priv; - struct dw_mci *host; + struct mmc_host *mmc = host->mmc; + int ret = 0; - host = slot->host; priv = host->priv; if (!priv || !priv->reg) return 0; - if (priv->ctrl_id == DWMMC_SDIO_ID) + if (mmc->index == DWMMC_SDIO_ID) return 0; if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) @@ -460,14 +448,6 @@ static int dw_mci_k3_probe(struct platform_device *pdev) return dw_mci_pltfm_register(pdev, drv_data); } -static const struct dev_pm_ops dw_mci_k3_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, - dw_mci_runtime_resume, - NULL) -}; - static struct platform_driver dw_mci_k3_pltfm_driver = { .probe = dw_mci_k3_probe, .remove = dw_mci_pltfm_remove, @@ -475,7 +455,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = { .name = "dwmmc_k3", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_k3_match, - .pm = &dw_mci_k3_dev_pm_ops, + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index e7ab699f488e..a2e1b4d75a5e 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -10,13 +10,14 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/pci.h> +#include <linux/pci-epf.h> #include <linux/pm_runtime.h> #include <linux/slab.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include "dw_mmc.h" +#include "dw_mmc-pltfm.h" -#define PCI_BAR_NO 2 #define SYNOPSYS_DW_MCI_VENDOR_ID 0x700 #define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107 /* Defining the Capabilities */ @@ -24,11 +25,8 @@ MMC_CAP_SD_HIGHSPEED | MMC_CAP_8_BIT_DATA |\ MMC_CAP_SDIO_IRQ) -static struct dw_mci_board pci_board_data = { - .caps = DW_MCI_CAPABILITIES, - .bus_hz = 33 * 1000 * 1000, - .detect_delay_ms = 200, - .fifo_depth = 32, +static const struct dw_mci_drv_data pci_drv_data = { + .common_caps = DW_MCI_CAPABILITIES, }; static int dw_mci_pci_probe(struct pci_dev *pdev, @@ -41,20 +39,20 @@ static int dw_mci_pci_probe(struct pci_dev *pdev, if (ret) return ret; - host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); - if (!host) - return -ENOMEM; + host = dw_mci_alloc_host(&pdev->dev); + if (IS_ERR(host)) + return PTR_ERR(host); host->irq = pdev->irq; host->irq_flags = IRQF_SHARED; - host->dev = &pdev->dev; - host->pdata = &pci_board_data; + host->fifo_depth = 32; + host->detect_delay_ms = 200; + host->bus_hz = 33 * 1000 * 1000; + host->drv_data = &pci_drv_data; - ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev)); - if (ret) - return ret; - - host->regs = pcim_iomap_table(pdev)[PCI_BAR_NO]; + host->regs = pcim_iomap_region(pdev, BAR_2, pci_name(pdev)); + if (IS_ERR(host->regs)) + return PTR_ERR(host->regs); pci_set_master(pdev); @@ -74,14 +72,6 @@ static void dw_mci_pci_remove(struct pci_dev *pdev) dw_mci_remove(host); } -static const struct dev_pm_ops dw_mci_pci_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, - dw_mci_runtime_resume, - NULL) -}; - static const struct pci_device_id dw_mci_pci_id[] = { { PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) }, {} @@ -94,7 +84,7 @@ static struct pci_driver dw_mci_pci_driver = { .probe = dw_mci_pci_probe, .remove = dw_mci_pci_remove, .driver = { - .pm = &dw_mci_pci_dev_pm_ops, + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index de820ffd2133..cf38bb118dc2 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -33,18 +33,16 @@ int dw_mci_pltfm_register(struct platform_device *pdev, struct dw_mci *host; struct resource *regs; - host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); - if (!host) - return -ENOMEM; + host = dw_mci_alloc_host(&pdev->dev); + if (IS_ERR(host)) + return PTR_ERR(host); host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) return host->irq; host->drv_data = drv_data; - host->dev = &pdev->dev; host->irq_flags = 0; - host->pdata = pdev->dev.platform_data; host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); if (IS_ERR(host->regs)) @@ -58,24 +56,16 @@ int dw_mci_pltfm_register(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(dw_mci_pltfm_register); -const struct dev_pm_ops dw_mci_pltfm_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, - dw_mci_runtime_resume, - NULL) -}; -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 mmc_clk_phase phase; struct regmap *sys_mgr_base_addr; - u32 clk_phase[2] = {0}, reg_offset, reg_shift; - int i, rc, hs_timing; + u32 reg_offset, reg_shift; + int hs_timing; - rc = of_property_read_variable_u32_array(np, "clk-phase-sd-hs", &clk_phase[0], 2, 0); - if (rc < 0) + phase = host->phase_map.phase[MMC_TIMING_SD_HS]; + if (!phase.valid) return 0; sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon"); @@ -87,10 +77,10 @@ static int dw_mci_socfpga_priv_init(struct dw_mci *host) 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; + phase.in_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP; + phase.out_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP; - hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1], reg_shift); + hs_timing = SYSMGR_SDMMC_CTRL_SET(phase.in_deg, phase.out_deg, reg_shift); regmap_write(sys_mgr_base_addr, reg_offset, hs_timing); return 0; @@ -136,7 +126,7 @@ static struct platform_driver dw_mci_pltfm_driver = { .name = "dw_mmc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_pltfm_match, - .pm = &dw_mci_pltfm_pmops, + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h index 64cf7522a3d4..ef1b05d484c3 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.h +++ b/drivers/mmc/host/dw_mmc-pltfm.h @@ -11,6 +11,6 @@ extern int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data); extern void dw_mci_pltfm_remove(struct platform_device *pdev); -extern const struct dev_pm_ops dw_mci_pltfm_pmops; +extern const struct dev_pm_ops dw_mci_pmops; #endif /* _DW_MMC_PLTFM_H_ */ diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index baa23b517731..c6eece4ec3fd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -6,6 +6,7 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/clk.h> +#include <linux/hw_bitfield.h> #include <linux/mmc/host.h> #include <linux/of_address.h> #include <linux/mmc/slot-gpio.h> @@ -18,14 +19,14 @@ #define RK3288_CLKGEN_DIV 2 #define SDMMC_TIMING_CON0 0x130 #define SDMMC_TIMING_CON1 0x134 +#define SDMMC_MISC_CON 0x138 +#define MEM_CLK_AUTOGATE_ENABLE BIT(5) #define ROCKCHIP_MMC_DELAY_SEL BIT(10) #define ROCKCHIP_MMC_DEGREE_MASK 0x3 #define ROCKCHIP_MMC_DEGREE_OFFSET 1 #define ROCKCHIP_MMC_DELAYNUM_OFFSET 2 #define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET) #define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60 -#define HIWORD_UPDATE(val, mask, shift) \ - ((val) << (shift) | (mask) << ((shift) + 16)) static const unsigned int freqs[] = { 100000, 200000, 300000, 400000 }; @@ -35,6 +36,8 @@ struct dw_mci_rockchip_priv_data { int default_sample_phase; int num_phases; bool internal_phase; + int sample_phase; + int drv_phase; }; /* @@ -43,7 +46,7 @@ struct dw_mci_rockchip_priv_data { */ static int rockchip_mmc_get_internal_phase(struct dw_mci *host, bool sample) { - unsigned long rate = clk_get_rate(host->ciu_clk); + unsigned long rate = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV; u32 raw_value; u16 degrees; u32 delay_num = 0; @@ -86,7 +89,7 @@ static int rockchip_mmc_get_phase(struct dw_mci *host, bool sample) static int rockchip_mmc_set_internal_phase(struct dw_mci *host, bool sample, int degrees) { - unsigned long rate = clk_get_rate(host->ciu_clk); + unsigned long rate = clk_get_rate(host->ciu_clk) / RK3288_CLKGEN_DIV; u8 nineties, remainder; u8 delay_num; u32 raw_value; @@ -148,9 +151,11 @@ static int rockchip_mmc_set_internal_phase(struct dw_mci *host, bool sample, int raw_value |= nineties; if (sample) - mci_writel(host, TIMING_CON1, HIWORD_UPDATE(raw_value, 0x07ff, 1)); + mci_writel(host, TIMING_CON1, + FIELD_PREP_WM16(GENMASK(11, 1), raw_value)); else - mci_writel(host, TIMING_CON0, HIWORD_UPDATE(raw_value, 0x07ff, 1)); + mci_writel(host, TIMING_CON0, + FIELD_PREP_WM16(GENMASK(11, 1), raw_value)); dev_dbg(host->dev, "set %s_phase(%d) delay_nums=%u actual_degrees=%d\n", sample ? "sample" : "drv", degrees, delay_num, @@ -174,7 +179,8 @@ static int rockchip_mmc_set_phase(struct dw_mci *host, bool sample, int degrees) static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) { struct dw_mci_rockchip_priv_data *priv = host->priv; - int ret; + struct mmc_clk_phase phase = host->phase_map.phase[ios->timing]; + int ret, sample_phase, drv_phase; unsigned int cclkin; u32 bus_hz; @@ -208,8 +214,15 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } /* Make sure we use phases which we can enumerate with */ - if (!IS_ERR(priv->sample_clk) && ios->timing <= MMC_TIMING_SD_HS) - rockchip_mmc_set_phase(host, true, priv->default_sample_phase); + if (!IS_ERR(priv->sample_clk)) { + /* Keep backward compatibility */ + if (ios->timing <= MMC_TIMING_SD_HS) { + sample_phase = phase.valid ? phase.in_deg : priv->default_sample_phase; + rockchip_mmc_set_phase(host, true, sample_phase); + } else if (phase.valid) { + rockchip_mmc_set_phase(host, true, phase.in_deg); + } + } /* * Set the drive phase offset based on speed mode to achieve hold times. @@ -238,15 +251,13 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * same results, for instance). */ if (!IS_ERR(priv->drv_clk)) { - int phase; - /* * In almost all cases a 90 degree phase offset will provide * sufficient hold times across all valid input clock rates * assuming delay_o is not absurd for a given SoC. We'll use * that as a default. */ - phase = 90; + drv_phase = 90; switch (ios->timing) { case MMC_TIMING_MMC_DDR52: @@ -256,7 +267,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * to get the same timings. */ if (ios->bus_width == MMC_BUS_WIDTH_8) - phase = 180; + drv_phase = 180; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: @@ -268,22 +279,24 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * SoCs measured this seems to be OK, but it doesn't * hurt to give margin here, so we use 180. */ - phase = 180; + drv_phase = 180; break; } - rockchip_mmc_set_phase(host, false, phase); + /* Use out phase from phase map first */ + if (phase.valid) + drv_phase = phase.out_deg; + rockchip_mmc_set_phase(host, false, drv_phase); } } #define TUNING_ITERATION_TO_PHASE(i, num_phases) \ (DIV_ROUND_UP((i) * 360, num_phases)) -static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_rk3288_execute_tuning(struct dw_mci *host, u32 opcode) { - struct dw_mci *host = slot->host; struct dw_mci_rockchip_priv_data *priv = host->priv; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; int ret = 0; int i; bool v, prev_v = 0, first_v; @@ -303,8 +316,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) return -EIO; } - ranges = kmalloc_array(priv->num_phases / 2 + 1, - sizeof(*ranges), GFP_KERNEL); + ranges = kmalloc_objs(*ranges, priv->num_phases / 2 + 1); if (!ranges) return -ENOMEM; @@ -469,10 +481,11 @@ static int dw_mci_rk3576_parse_dt(struct dw_mci *host) static int dw_mci_rockchip_init(struct dw_mci *host) { + struct dw_mci_rockchip_priv_data *priv = host->priv; int ret, i; - /* It is slot 8 on Rockchip SoCs */ - host->sdio_id0 = 8; + /* SDIO irq is the 8th on Rockchip SoCs */ + host->sdio_irq = 8; if (of_device_is_compatible(host->dev->of_node, "rockchip,rk3288-dw-mshc")) { host->bus_hz /= RK3288_CLKGEN_DIV; @@ -493,6 +506,9 @@ static int dw_mci_rockchip_init(struct dw_mci *host) dev_warn(host->dev, "no valid minimum freq: %d\n", ret); } + if (priv->internal_phase) + mci_writel(host, MISC_CON, MEM_CLK_AUTOGATE_ENABLE); + return 0; } @@ -567,12 +583,43 @@ static void dw_mci_rockchip_remove(struct platform_device *pdev) dw_mci_pltfm_remove(pdev); } +static int dw_mci_rockchip_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_mci *host = platform_get_drvdata(pdev); + struct dw_mci_rockchip_priv_data *priv = host->priv; + + if (priv->internal_phase) { + priv->sample_phase = rockchip_mmc_get_phase(host, true); + priv->drv_phase = rockchip_mmc_get_phase(host, false); + } + + return dw_mci_runtime_suspend(dev); +} + +static int dw_mci_rockchip_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct dw_mci *host = platform_get_drvdata(pdev); + struct dw_mci_rockchip_priv_data *priv = host->priv; + int ret; + + ret = dw_mci_runtime_resume(dev); + if (ret) + return ret; + + if (priv->internal_phase) { + rockchip_mmc_set_phase(host, true, priv->sample_phase); + rockchip_mmc_set_phase(host, false, priv->drv_phase); + mci_writel(host, MISC_CON, MEM_CLK_AUTOGATE_ENABLE); + } + + return ret; +} + static const struct dev_pm_ops dw_mci_rockchip_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, - dw_mci_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(dw_mci_rockchip_runtime_suspend, dw_mci_rockchip_runtime_resume, NULL) }; static struct platform_driver dw_mci_rockchip_pltfm_driver = { @@ -582,7 +629,7 @@ static struct platform_driver dw_mci_rockchip_pltfm_driver = { .name = "dwmmc_rockchip", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_rockchip_match, - .pm = &dw_mci_rockchip_dev_pm_ops, + .pm = pm_ptr(&dw_mci_rockchip_dev_pm_ops), }, }; diff --git a/drivers/mmc/host/dw_mmc-starfive.c b/drivers/mmc/host/dw_mmc-starfive.c index 34964b0dab21..11472a641cbe 100644 --- a/drivers/mmc/host/dw_mmc-starfive.c +++ b/drivers/mmc/host/dw_mmc-starfive.c @@ -53,11 +53,10 @@ static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase mdelay(1); } -static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, +static int dw_mci_starfive_execute_tuning(struct dw_mci *host, u32 opcode) { static const int grade = MAX_DELAY_CHAIN; - struct dw_mci *host = slot->host; int smpl_phase, smpl_raise = -1, smpl_fall = -1; int ret; @@ -65,7 +64,7 @@ static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, dw_mci_starfive_set_sample_phase(host, smpl_phase); mci_writel(host, RINTSTS, ALL_INT_CLR); - ret = mmc_send_tuning(slot->mmc, opcode, NULL); + ret = mmc_send_tuning(host->mmc, opcode, NULL); if (!ret && smpl_raise < 0) { smpl_raise = smpl_phase; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 3cbda98d08d2..20193ee7b73e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -7,35 +7,28 @@ * Copyright (C) 2009, 2010 Imagination Technologies Ltd. */ -#include <linux/blkdev.h> +#include <linux/bitops.h> #include <linux/clk.h> #include <linux/debugfs.h> +#include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/err.h> -#include <linux/init.h> #include <linux/interrupt.h> #include <linux/iopoll.h> -#include <linux/ioport.h> -#include <linux/ktime.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/prandom.h> -#include <linux/seq_file.h> -#include <linux/slab.h> -#include <linux/stat.h> -#include <linux/delay.h> #include <linux/irq.h> +#include <linux/ktime.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> #include <linux/mmc/sd.h> #include <linux/mmc/sdio.h> -#include <linux/bitops.h> -#include <linux/regulator/consumer.h> -#include <linux/of.h> #include <linux/mmc/slot-gpio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> #include "dw_mmc.h" @@ -47,8 +40,6 @@ SDMMC_INT_RESP_ERR | SDMMC_INT_HLE) #define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \ DW_MCI_CMD_ERROR_FLAGS) -#define DW_MCI_SEND_STATUS 1 -#define DW_MCI_RECV_STATUS 2 #define DW_MCI_DMA_THRESHOLD 16 #define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ @@ -107,15 +98,15 @@ struct idmac_desc { #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) { - struct dw_mci_slot *slot = s->private; + struct dw_mci *host = s->private; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_command *stop; struct mmc_data *data; /* Make sure we get a consistent snapshot */ - spin_lock_bh(&slot->host->lock); - mrq = slot->mrq; + spin_lock_bh(&host->lock); + mrq = host->mrq; if (mrq) { cmd = mrq->cmd; @@ -140,7 +131,7 @@ static int dw_mci_req_show(struct seq_file *s, void *v) stop->resp[2], stop->error); } - spin_unlock_bh(&slot->host->lock); + spin_unlock_bh(&host->lock); return 0; } @@ -165,22 +156,21 @@ static int dw_mci_regs_show(struct seq_file *s, void *v) } DEFINE_SHOW_ATTRIBUTE(dw_mci_regs); -static void dw_mci_init_debugfs(struct dw_mci_slot *slot) +static void dw_mci_init_debugfs(struct dw_mci *host) { - struct mmc_host *mmc = slot->mmc; - struct dw_mci *host = slot->host; + struct mmc_host *mmc = host->mmc; struct dentry *root; root = mmc->debugfs_root; if (!root) return; - debugfs_create_file("regs", S_IRUSR, root, host, &dw_mci_regs_fops); - debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops); - debugfs_create_u32("state", S_IRUSR, root, &host->state); - debugfs_create_xul("pending_events", S_IRUSR, root, + debugfs_create_file("regs", 0400, root, host, &dw_mci_regs_fops); + debugfs_create_file("req", 0400, root, host, &dw_mci_req_fops); + debugfs_create_u32("state", 0400, root, &host->state); + debugfs_create_xul("pending_events", 0400, root, &host->pending_events); - debugfs_create_xul("completed_events", S_IRUSR, root, + debugfs_create_xul("completed_events", 0400, root, &host->completed_events); #ifdef CONFIG_FAULT_INJECTION fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc); @@ -231,9 +221,8 @@ static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags) } } -static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) +static void mci_send_cmd(struct dw_mci *host, u32 cmd, u32 arg) { - struct dw_mci *host = slot->host; unsigned int cmd_status = 0; mci_writel(host, CMDARG, arg); @@ -244,15 +233,14 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) if (readl_poll_timeout_atomic(host->regs + SDMMC_CMD, cmd_status, !(cmd_status & SDMMC_CMD_START), 1, 500 * USEC_PER_MSEC)) - dev_err(&slot->mmc->class_dev, + dev_err(&host->mmc->class_dev, "Timeout sending command (cmd %#x arg %#x status %#x)\n", cmd, arg, cmd_status); } static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); u32 cmdr; cmd->error = -EINPROGRESS; @@ -274,8 +262,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_VOLT_SWITCH; /* Change state to continue to handle CMD11 weirdness */ - WARN_ON(slot->host->state != STATE_SENDING_CMD); - slot->host->state = STATE_SENDING_CMD11; + WARN_ON(host->state != STATE_SENDING_CMD); + host->state = STATE_SENDING_CMD11; /* * We need to disable low power mode (automatic clock stop) @@ -289,9 +277,9 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) * until the voltage change is all done. */ clk_en_a = mci_readl(host, CLKENA); - clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id); + clk_en_a &= ~SDMMC_CLKEN_LOW_PWR; mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + mci_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } @@ -311,7 +299,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_DAT_WR; } - if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &slot->flags)) + if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags)) cmdr |= SDMMC_CMD_USE_HOLD_REG; return cmdr; @@ -350,7 +338,7 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) cmdr = stop->opcode | SDMMC_CMD_STOP | SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; - if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags)) + if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags)) cmdr |= SDMMC_CMD_USE_HOLD_REG; return cmdr; @@ -480,7 +468,7 @@ static void dw_mci_dmac_complete_dma(void *arg) if ((host->use_dma == TRANS_MODE_EDMAC) && data && (data->flags & MMC_DATA_READ)) /* Invalidate cache after read */ - dma_sync_sg_for_cpu(mmc_dev(host->slot->mmc), + dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg, data->sg_len, DMA_FROM_DEVICE); @@ -575,23 +563,26 @@ static int dw_mci_idmac_init(struct dw_mci *host) return 0; } -static inline int dw_mci_prepare_desc64(struct dw_mci *host, - struct mmc_data *data, - unsigned int sg_len) +static inline int dw_mci_prepare_desc(struct dw_mci *host, struct mmc_data *data, + unsigned int sg_len, bool is_64bit) { unsigned int desc_len; - struct idmac_desc_64addr *desc_first, *desc_last, *desc; - u32 val; - int i; + struct idmac_desc *desc_first, *desc_last, *desc; + struct idmac_desc_64addr *desc64_first, *desc64_last, *desc64; + u32 val, des0; + int i, err; - desc_first = desc_last = desc = host->sg_cpu; + if (is_64bit) + desc64_first = desc64_last = desc64 = host->sg_cpu; + else + desc_first = desc_last = desc = host->sg_cpu; for (i = 0; i < sg_len; i++) { unsigned int length = sg_dma_len(&data->sg[i]); u64 mem_addr = sg_dma_address(&data->sg[i]); - for ( ; length ; desc++) { + while (length > 0) { desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? length : DW_MCI_DESC_DATA_LENGTH; @@ -603,113 +594,60 @@ static inline int dw_mci_prepare_desc64(struct dw_mci *host, * isn't still owned by IDMAC as IDMAC's write * ops and CPU's read ops are asynchronous. */ - if (readl_poll_timeout_atomic(&desc->des0, val, - !(val & IDMAC_DES0_OWN), - 10, 100 * USEC_PER_MSEC)) + if (is_64bit) + err = readl_poll_timeout_atomic(&desc64->des0, val, + IDMAC_OWN_CLR64(val), 10, 100 * USEC_PER_MSEC); + else + err = readl_poll_timeout_atomic(&desc->des0, val, + IDMAC_OWN_CLR64(val), 10, 100 * USEC_PER_MSEC); + if (err) goto err_own_bit; - /* - * Set the OWN bit and disable interrupts - * for this descriptor - */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | - IDMAC_DES0_CH; - - /* Buffer length */ - IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); - - /* Physical address to DMA to/from */ - desc->des4 = mem_addr & 0xffffffff; - desc->des5 = mem_addr >> 32; - - /* Update physical address for the next desc */ - mem_addr += desc_len; - - /* Save pointer to the last descriptor */ - desc_last = desc; - } - } - - /* Set first descriptor */ - desc_first->des0 |= IDMAC_DES0_FD; - - /* Set last descriptor */ - desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc_last->des0 |= IDMAC_DES0_LD; - - return 0; -err_own_bit: - /* restore the descriptor chain as it's polluted */ - dev_dbg(host->dev, "descriptor is still owned by IDMAC.\n"); - memset(host->sg_cpu, 0, DESC_RING_BUF_SZ); - dw_mci_idmac_init(host); - return -EINVAL; -} - - -static inline int dw_mci_prepare_desc32(struct dw_mci *host, - struct mmc_data *data, - unsigned int sg_len) -{ - unsigned int desc_len; - struct idmac_desc *desc_first, *desc_last, *desc; - u32 val; - int i; - - desc_first = desc_last = desc = host->sg_cpu; - - for (i = 0; i < sg_len; i++) { - unsigned int length = sg_dma_len(&data->sg[i]); - - u32 mem_addr = sg_dma_address(&data->sg[i]); - - for ( ; length ; desc++) { - desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? - length : DW_MCI_DESC_DATA_LENGTH; - - length -= desc_len; - - /* - * Wait for the former clear OWN bit operation - * of IDMAC to make sure that this descriptor - * isn't still owned by IDMAC as IDMAC's write - * ops and CPU's read ops are asynchronous. - */ - if (readl_poll_timeout_atomic(&desc->des0, val, - IDMAC_OWN_CLR64(val), - 10, - 100 * USEC_PER_MSEC)) - goto err_own_bit; + des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + if (is_64bit) + desc64->des0 = des0; + else + desc->des0 = cpu_to_le32(des0); /* - * Set the OWN bit and disable interrupts - * for this descriptor + * 1. Set OWN bit and disable interrupts for this descriptor + * 2. Set Buffer length + * Set physical address to DMA to/from */ - desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | - IDMAC_DES0_DIC | - IDMAC_DES0_CH); - - /* Buffer length */ - IDMAC_SET_BUFFER1_SIZE(desc, desc_len); - - /* Physical address to DMA to/from */ - desc->des2 = cpu_to_le32(mem_addr); + if (is_64bit) { + desc64->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc64, desc_len); + desc64->des4 = mem_addr & 0xffffffff; + desc64->des5 = mem_addr >> 32; + } else { + IDMAC_SET_BUFFER1_SIZE(desc, desc_len); + desc->des2 = cpu_to_le32(mem_addr); + } /* Update physical address for the next desc */ mem_addr += desc_len; /* Save pointer to the last descriptor */ - desc_last = desc; + if (is_64bit) { + desc64_last = desc64; + desc64++; + } else { + desc_last = desc; + desc++; + } } } - /* Set first descriptor */ - desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); - - /* Set last descriptor */ - desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | - IDMAC_DES0_DIC)); - desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); + /* Set the first descriptor and the last descriptor */ + if (is_64bit) { + desc64_first->des0 |= IDMAC_DES0_FD; + desc64_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc64_last->des0 |= IDMAC_DES0_LD; + } else { + desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); + desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC)); + desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); + } return 0; err_own_bit: @@ -725,11 +663,7 @@ static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) u32 temp; int ret; - if (host->dma_64bit_address == 1) - ret = dw_mci_prepare_desc64(host, host->data, sg_len); - else - ret = dw_mci_prepare_desc32(host, host->data, sg_len); - + ret = dw_mci_prepare_desc(host, host->data, sg_len, host->dma_64bit_address); if (ret) goto out; @@ -823,7 +757,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host, /* Flush cache before write */ if (host->data->flags & MMC_DATA_WRITE) - dma_sync_sg_for_device(mmc_dev(host->slot->mmc), sgl, + dma_sync_sg_for_device(mmc_dev(host->mmc), sgl, sg_elems, DMA_TO_DEVICE); dma_async_issue_pending(host->dms->ch); @@ -834,7 +768,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host, static int dw_mci_edmac_init(struct dw_mci *host) { /* Request external dma channel */ - host->dms = kzalloc(sizeof(struct dw_mci_dma_slave), GFP_KERNEL); + host->dms = kzalloc_obj(struct dw_mci_dma_slave); if (!host->dms) return -ENOMEM; @@ -913,16 +847,16 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host, static void dw_mci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - if (!slot->host->use_dma || !data) + if (!host->use_dma || !data) return; /* This data might be unmapped at this time */ data->host_cookie = COOKIE_UNMAPPED; - if (dw_mci_pre_dma_transfer(slot->host, mrq->data, + if (dw_mci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED) < 0) data->host_cookie = COOKIE_UNMAPPED; } @@ -931,14 +865,14 @@ static void dw_mci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, int err) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - if (!slot->host->use_dma || !data) + if (!host->use_dma || !data) return; if (data->host_cookie != COOKIE_UNMAPPED) - dma_unmap_sg(slot->host->dev, + dma_unmap_sg(host->dev, data->sg, data->sg_len, mmc_get_dma_dir(data)); @@ -947,43 +881,21 @@ static void dw_mci_post_req(struct mmc_host *mmc, static int dw_mci_get_cd(struct mmc_host *mmc) { - int present; - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); int gpio_cd = mmc_gpio_get_cd(mmc); - /* Use platform get_cd function, else try onboard card detect */ - if (((mmc->caps & MMC_CAP_NEEDS_POLL) - || !mmc_card_is_removable(mmc))) { - present = 1; + if (mmc->caps & MMC_CAP_NEEDS_POLL) + return 1; - if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) { - if (mmc->caps & MMC_CAP_NEEDS_POLL) { - dev_info(&mmc->class_dev, - "card is polling.\n"); - } else { - dev_info(&mmc->class_dev, - "card is non-removable.\n"); - } - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - } + if (!mmc_card_is_removable(mmc)) + return 1; - return present; - } else if (gpio_cd >= 0) - present = gpio_cd; - else - present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) - == 0 ? 1 : 0; + /* Try slot gpio detection */ + if (gpio_cd >= 0) + return !!gpio_cd; - spin_lock_bh(&host->lock); - if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &slot->flags)) - dev_dbg(&mmc->class_dev, "card is present\n"); - else if (!present && - !test_and_clear_bit(DW_MMC_CARD_PRESENT, &slot->flags)) - dev_dbg(&mmc->class_dev, "card is not present\n"); - spin_unlock_bh(&host->lock); - - return present; + /* Host native card detect */ + return !(mci_readl(host, CDETECT) & BIT(0)); } static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) @@ -1150,9 +1062,9 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->data = data; if (data->flags & MMC_DATA_READ) - host->dir_status = DW_MCI_RECV_STATUS; + host->dir_status = MMC_DATA_READ; else - host->dir_status = DW_MCI_SEND_STATUS; + host->dir_status = MMC_DATA_WRITE; dw_mci_ctrl_thld(host, data); @@ -1200,10 +1112,9 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) } } -static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) +static void dw_mci_setup_bus(struct dw_mci *host, bool force_clkinit) { - struct dw_mci *host = slot->host; - unsigned int clock = slot->clock; + unsigned int clock = host->clock; u32 div; u32 clk_en_a; u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT; @@ -1212,11 +1123,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) if (host->state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; - slot->mmc->actual_clock = 0; + host->mmc->actual_clock = 0; if (!clock) { mci_writel(host, CLKENA, 0); - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); } else if (clock != host->current_speed || force_clkinit) { div = host->bus_hz / clock; if (host->bus_hz % clock && host->bus_hz > clock) @@ -1228,14 +1139,14 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; - if ((clock != slot->__clk_old && - !test_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags)) || + if ((clock != host->clk_old && + !test_bit(DW_MMC_CARD_NEEDS_POLL, &host->flags)) || force_clkinit) { /* Silent the verbose log if calling from PM context */ if (!force_clkinit) - dev_info(&slot->mmc->class_dev, - "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", - slot->id, host->bus_hz, clock, + dev_info(&host->mmc->class_dev, + "Bus speed = %dHz (req %dHz, actual %dHZ div = %d)\n", + host->bus_hz, clock, div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); @@ -1243,9 +1154,9 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) * If card is polling, display the message only * one time at boot time. */ - if (slot->mmc->caps & MMC_CAP_NEEDS_POLL && - slot->mmc->f_min == clock) - set_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags); + if (host->mmc->caps & MMC_CAP_NEEDS_POLL && + host->mmc->f_min == clock) + set_bit(DW_MMC_CARD_NEEDS_POLL, &host->flags); } /* disable clock */ @@ -1253,33 +1164,33 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKSRC, 0); /* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); /* set clock to desired speed */ mci_writel(host, CLKDIV, div); /* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); /* enable clock; only low power if no SDIO */ - clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; - if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags)) - clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; + clk_en_a = SDMMC_CLKEN_ENABLE; + if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags)) + clk_en_a |= SDMMC_CLKEN_LOW_PWR; mci_writel(host, CLKENA, clk_en_a); /* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); /* keep the last clock value that was requested from core */ - slot->__clk_old = clock; - slot->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : + host->clk_old = clock; + host->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : host->bus_hz; } host->current_speed = clock; - /* Set the current slot bus width */ - mci_writel(host, CTYPE, (slot->ctype << slot->id)); + /* Set the current bus width */ + mci_writel(host, CTYPE, host->ctype); } static void dw_mci_set_data_timeout(struct dw_mci *host, @@ -1313,15 +1224,13 @@ static void dw_mci_set_data_timeout(struct dw_mci *host, timeout_ns, tmout >> 8); } -static void __dw_mci_start_request(struct dw_mci *host, - struct dw_mci_slot *slot, - struct mmc_command *cmd) +static void dw_mci_start_request(struct dw_mci *host, struct mmc_command *cmd) { struct mmc_request *mrq; struct mmc_data *data; u32 cmdflags; - mrq = slot->mrq; + mrq = host->mrq; host->mrq = mrq; @@ -1338,10 +1247,10 @@ static void __dw_mci_start_request(struct dw_mci *host, mci_writel(host, BLKSIZ, data->blksz); } - cmdflags = dw_mci_prepare_command(slot->mmc, cmd); + cmdflags = dw_mci_prepare_command(host->mmc, cmd); /* this is the first command, send the initialization clock */ - if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags)) + if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &host->flags)) cmdflags |= SDMMC_CMD_INIT; if (data) { @@ -1374,27 +1283,32 @@ static void __dw_mci_start_request(struct dw_mci *host, host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); } -static void dw_mci_start_request(struct dw_mci *host, - struct dw_mci_slot *slot) +static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) { - struct mmc_request *mrq = slot->mrq; + struct dw_mci *host = mmc_priv(mmc); struct mmc_command *cmd; - cmd = mrq->sbc ? mrq->sbc : mrq->cmd; - __dw_mci_start_request(host, slot, cmd); -} + WARN_ON(host->mrq); -/* must be called with host->lock held */ -static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, - struct mmc_request *mrq) -{ - dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n", - host->state); + /* + * The check for card presence and queueing of the request must be + * atomic, otherwise the card could be removed in between and the + * request wouldn't fail until another card was inserted. + */ + if (!dw_mci_get_cd(mmc)) { + mrq->cmd->error = -ENOMEDIUM; + mmc_request_done(mmc, mrq); + return; + } - slot->mrq = mrq; + spin_lock_bh(&host->lock); + dev_vdbg(&host->mmc->class_dev, "request: state=%d\n", + host->state); + + host->mrq = mrq; if (host->state == STATE_WAITING_CMD11_DONE) { - dev_warn(&slot->mmc->class_dev, + dev_warn(&host->mmc->class_dev, "Voltage change didn't complete\n"); /* * this case isn't expected to happen, so we can @@ -1406,168 +1320,119 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, if (host->state == STATE_IDLE) { host->state = STATE_SENDING_CMD; - dw_mci_start_request(host, slot); - } else { - list_add_tail(&slot->queue_node, &host->queue); - } -} - -static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; - - WARN_ON(slot->mrq); - - /* - * The check for card presence and queueing of the request must be - * atomic, otherwise the card could be removed in between and the - * request wouldn't fail until another card was inserted. - */ - - if (!dw_mci_get_cd(mmc)) { - mrq->cmd->error = -ENOMEDIUM; - mmc_request_done(mmc, mrq); - return; + cmd = mrq->sbc ? mrq->sbc : mrq->cmd; + dw_mci_start_request(host, cmd); } - spin_lock_bh(&host->lock); - - dw_mci_queue_request(host, slot, mrq); - spin_unlock_bh(&host->lock); } static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); - const struct dw_mci_drv_data *drv_data = slot->host->drv_data; + struct dw_mci *host = mmc_priv(mmc); + const struct dw_mci_drv_data *drv_data = host->drv_data; u32 regs; int ret; switch (ios->bus_width) { case MMC_BUS_WIDTH_4: - slot->ctype = SDMMC_CTYPE_4BIT; + host->ctype = SDMMC_CTYPE_4BIT; break; case MMC_BUS_WIDTH_8: - slot->ctype = SDMMC_CTYPE_8BIT; + host->ctype = SDMMC_CTYPE_8BIT; break; default: /* set default 1 bit mode */ - slot->ctype = SDMMC_CTYPE_1BIT; + host->ctype = SDMMC_CTYPE_1BIT; } - regs = mci_readl(slot->host, UHS_REG); + regs = mci_readl(host, UHS_REG); /* DDR mode set */ if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_HS400) - regs |= ((0x1 << slot->id) << 16); + regs |= BIT(16); else - regs &= ~((0x1 << slot->id) << 16); + regs &= ~BIT(16); - mci_writel(slot->host, UHS_REG, regs); - slot->host->timing = ios->timing; + mci_writel(host, UHS_REG, regs); + host->timing = ios->timing; /* * Use mirror of ios->clock to prevent race with mmc * core ios update when finding the minimum. */ - slot->clock = ios->clock; + host->clock = ios->clock; if (drv_data && drv_data->set_ios) - drv_data->set_ios(slot->host, ios); + drv_data->set_ios(host, ios); switch (ios->power_mode) { case MMC_POWER_UP: - if (!IS_ERR(mmc->supply.vmmc)) { - ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, - ios->vdd); - if (ret) { - dev_err(slot->host->dev, - "failed to enable vmmc regulator\n"); - /*return, if failed turn on vmmc*/ - return; - } + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); + if (ret) { + dev_err(host->dev, "failed to enable vmmc regulator\n"); + return; } - set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); - regs = mci_readl(slot->host, PWREN); - regs |= (1 << slot->id); - mci_writel(slot->host, PWREN, regs); + set_bit(DW_MMC_CARD_NEED_INIT, &host->flags); + regs = mci_readl(host, PWREN); + regs |= BIT(0); + mci_writel(host, PWREN, regs); break; case MMC_POWER_ON: - if (!slot->host->vqmmc_enabled) { - if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_enable(mmc->supply.vqmmc); - if (ret < 0) - dev_err(slot->host->dev, - "failed to enable vqmmc\n"); - else - slot->host->vqmmc_enabled = true; - - } else { - /* Keep track so we don't reset again */ - slot->host->vqmmc_enabled = true; - } - - /* Reset our state machine after powering on */ - dw_mci_ctrl_reset(slot->host, - SDMMC_CTRL_ALL_RESET_FLAGS); - } - + mmc_regulator_enable_vqmmc(mmc); /* Adjust clock / bus width after power is up */ - dw_mci_setup_bus(slot, false); + dw_mci_setup_bus(host, false); break; case MMC_POWER_OFF: /* Turn clock off before power goes down */ - dw_mci_setup_bus(slot, false); + dw_mci_setup_bus(host, false); if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) - regulator_disable(mmc->supply.vqmmc); - slot->host->vqmmc_enabled = false; + mmc_regulator_disable_vqmmc(mmc); - regs = mci_readl(slot->host, PWREN); - regs &= ~(1 << slot->id); - mci_writel(slot->host, PWREN, regs); + regs = mci_readl(host, PWREN); + regs &= ~BIT(0); + mci_writel(host, PWREN, regs); + /* Reset our state machine after powering off */ + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); break; default: break; } - if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) - slot->host->state = STATE_IDLE; + if (host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) + host->state = STATE_IDLE; } static int dw_mci_card_busy(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); u32 status; /* * Check the busy bit which is low when DAT[3:0] * (the data lines) are 0000 */ - status = mci_readl(slot->host, STATUS); + status = mci_readl(host, STATUS); return !!(status & SDMMC_STATUS_BUSY); } static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; u32 uhs; - u32 v18 = SDMMC_UHS_18V << slot->id; + u32 v18 = SDMMC_UHS_18V; int ret; if (drv_data && drv_data->switch_voltage) - return drv_data->switch_voltage(mmc, ios); + return drv_data->switch_voltage(host, ios); /* * Program the voltage. Note that some instances of dw_mmc may use @@ -1597,7 +1462,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) static int dw_mci_get_ro(struct mmc_host *mmc) { int read_only; - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); int gpio_ro = mmc_gpio_get_ro(mmc); /* Use platform get_ro function, else try on board write protect */ @@ -1605,7 +1470,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) read_only = gpio_ro; else read_only = - mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0; + mci_readl(host, WRTPRT) & BIT(0) ? 1 : 0; dev_dbg(&mmc->class_dev, "card is %s\n", read_only ? "read-only" : "read-write"); @@ -1615,8 +1480,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) static void dw_mci_hw_reset(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; int reset; @@ -1639,18 +1503,17 @@ static void dw_mci_hw_reset(struct mmc_host *mmc) * tRSTH >= 1us: RST_n high period */ reset = mci_readl(host, RST_N); - reset &= ~(SDMMC_RST_HWACTIVE << slot->id); + reset &= ~SDMMC_RST_HWACTIVE; mci_writel(host, RST_N, reset); usleep_range(1, 2); - reset |= SDMMC_RST_HWACTIVE << slot->id; + reset |= SDMMC_RST_HWACTIVE; mci_writel(host, RST_N, reset); usleep_range(200, 300); } -static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare) +static void dw_mci_prepare_sdio_irq(struct dw_mci *host, bool prepare) { - struct dw_mci *host = slot->host; - const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; + const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR; u32 clk_en_a_old; u32 clk_en_a; @@ -1662,23 +1525,22 @@ static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare) clk_en_a_old = mci_readl(host, CLKENA); if (prepare) { - set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + set_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags); clk_en_a = clk_en_a_old & ~clken_low_pwr; } else { - clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + clear_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags); clk_en_a = clk_en_a_old | clken_low_pwr; } if (clk_en_a != clk_en_a_old) { mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, + mci_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } } -static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb) +static void __dw_mci_enable_sdio_irq(struct dw_mci *host, int enb) { - struct dw_mci *host = slot->host; unsigned long irqflags; u32 int_mask; @@ -1687,9 +1549,9 @@ static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb) /* Enable/disable Slot Specific SDIO interrupt */ int_mask = mci_readl(host, INTMASK); if (enb) - int_mask |= SDMMC_INT_SDIO(slot->sdio_id); + int_mask |= SDMMC_INT_SDIO(host->sdio_irq); else - int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id); + int_mask &= ~SDMMC_INT_SDIO(host->sdio_irq); mci_writel(host, INTMASK, int_mask); spin_unlock_irqrestore(&host->irq_lock, irqflags); @@ -1697,11 +1559,10 @@ static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb) static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); - dw_mci_prepare_sdio_irq(slot, enb); - __dw_mci_enable_sdio_irq(slot, enb); + dw_mci_prepare_sdio_irq(host, enb); + __dw_mci_enable_sdio_irq(host, enb); /* Avoid runtime suspending the device when SDIO IRQ is enabled */ if (enb) @@ -1712,28 +1573,26 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) static void dw_mci_ack_sdio_irq(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); - __dw_mci_enable_sdio_irq(slot, 1); + __dw_mci_enable_sdio_irq(host, 1); } static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; int err = -EINVAL; if (drv_data && drv_data->execute_tuning) - err = drv_data->execute_tuning(slot, opcode); + err = drv_data->execute_tuning(host, opcode); return err; } static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; if (drv_data && drv_data->prepare_hs400_tuning) @@ -1804,7 +1663,7 @@ static bool dw_mci_reset(struct dw_mci *host) ciu_out: /* After a CTRL reset we need to have CIU set clock registers */ - mci_send_cmd(host->slot, SDMMC_CMD_UPD_CLK, 0); + mci_send_cmd(host, SDMMC_CMD_UPD_CLK, 0); return ret; } @@ -1875,8 +1734,7 @@ static void dw_mci_init_fault(struct dw_mci *host) { host->fail_data_crc = (struct fault_attr) FAULT_ATTR_INITIALIZER; - hrtimer_init(&host->fault_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - host->fault_timer.function = dw_mci_fault_timer; + hrtimer_setup(&host->fault_timer, dw_mci_fault_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); } #else static void dw_mci_init_fault(struct dw_mci *host) @@ -1896,29 +1754,16 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) __releases(&host->lock) __acquires(&host->lock) { - struct dw_mci_slot *slot; - struct mmc_host *prev_mmc = host->slot->mmc; + struct mmc_host *prev_mmc = host->mmc; WARN_ON(host->cmd || host->data); - host->slot->mrq = NULL; host->mrq = NULL; - if (!list_empty(&host->queue)) { - slot = list_entry(host->queue.next, - struct dw_mci_slot, queue_node); - list_del(&slot->queue_node); - dev_vdbg(host->dev, "list not empty: %s is next\n", - mmc_hostname(slot->mmc)); - host->state = STATE_SENDING_CMD; - dw_mci_start_request(host, slot); - } else { - dev_vdbg(host->dev, "list empty\n"); - if (host->state == STATE_SENDING_CMD11) - host->state = STATE_WAITING_CMD11_DONE; - else - host->state = STATE_IDLE; - } + if (host->state == STATE_SENDING_CMD11) + host->state = STATE_WAITING_CMD11_DONE; + else + host->state = STATE_IDLE; spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); @@ -1969,7 +1814,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) data->error = -EILSEQ; } else if (status & SDMMC_INT_EBE) { if (host->dir_status == - DW_MCI_SEND_STATUS) { + MMC_DATA_WRITE) { /* * No data CRC status was returned. * The number of bytes transferred @@ -1978,7 +1823,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) data->bytes_xfered = 0; data->error = -ETIMEDOUT; } else if (host->dir_status == - DW_MCI_RECV_STATUS) { + MMC_DATA_READ) { data->error = -EILSEQ; } } else { @@ -2041,10 +1886,10 @@ static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host) * Really be certain that the timer has stopped. This is a bit of * paranoia and could only really happen if we had really bad * interrupt latency and the interrupt routine and timeout were - * running concurrently so that the del_timer() in the interrupt + * running concurrently so that the timer_delete() in the interrupt * handler couldn't run. */ - WARN_ON(del_timer_sync(&host->cto_timer)); + WARN_ON(timer_delete_sync(&host->cto_timer)); clear_bit(EVENT_CMD_COMPLETE, &host->pending_events); return true; @@ -2056,7 +1901,7 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host) return false; /* Extra paranoia just like dw_mci_clear_pending_cmd_complete() */ - WARN_ON(del_timer_sync(&host->dto_timer)); + WARN_ON(timer_delete_sync(&host->dto_timer)); clear_bit(EVENT_DATA_COMPLETE, &host->pending_events); return true; @@ -2096,8 +1941,7 @@ static void dw_mci_work_func(struct work_struct *t) set_bit(EVENT_CMD_COMPLETE, &host->completed_events); err = dw_mci_command_complete(host, cmd); if (cmd == mrq->sbc && !err) { - __dw_mci_start_request(host, host->slot, - mrq->cmd); + dw_mci_start_request(host, mrq->cmd); goto unlock; } @@ -2124,7 +1968,7 @@ static void dw_mci_work_func(struct work_struct *t) * avoids races and keeps things simple. */ if (err != -ETIMEDOUT && - host->dir_status == DW_MCI_RECV_STATUS) { + host->dir_status == MMC_DATA_READ) { state = STATE_SENDING_DATA; continue; } @@ -2168,7 +2012,7 @@ static void dw_mci_work_func(struct work_struct *t) * If all data-related interrupts don't come * within the given time in reading data state. */ - if (host->dir_status == DW_MCI_RECV_STATUS) + if (host->dir_status == MMC_DATA_READ) dw_mci_set_drto(host); break; } @@ -2208,7 +2052,7 @@ static void dw_mci_work_func(struct work_struct *t) * interrupt doesn't come within the given time. * in reading data state. */ - if (host->dir_status == DW_MCI_RECV_STATUS) + if (host->dir_status == MMC_DATA_READ) dw_mci_set_drto(host); break; } @@ -2579,6 +2423,91 @@ static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) } } +static void dw_mci_push_data64_32(struct dw_mci *host, void *buf, int cnt) +{ + struct mmc_data *data = host->data; + int init_cnt = cnt; + + /* try and push anything in the part_buf */ + if (unlikely(host->part_buf_count)) { + int len = dw_mci_push_part_bytes(host, buf, cnt); + + buf += len; + cnt -= len; + + if (host->part_buf_count == 8) { + mci_fifo_l_writeq(host->fifo_reg, host->part_buf); + host->part_buf_count = 0; + } + } +#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (unlikely((unsigned long)buf & 0x7)) { + while (cnt >= 8) { + u64 aligned_buf[16]; + int len = min(cnt & -8, (int)sizeof(aligned_buf)); + int items = len >> 3; + int i; + /* memcpy from input buffer into aligned buffer */ + memcpy(aligned_buf, buf, len); + buf += len; + cnt -= len; + /* push data from aligned buffer into fifo */ + for (i = 0; i < items; ++i) + mci_fifo_l_writeq(host->fifo_reg, aligned_buf[i]); + } + } else +#endif + { + u64 *pdata = buf; + + for (; cnt >= 8; cnt -= 8) + mci_fifo_l_writeq(host->fifo_reg, *pdata++); + buf = pdata; + } + /* put anything remaining in the part_buf */ + if (cnt) { + dw_mci_set_part_bytes(host, buf, cnt); + /* Push data if we have reached the expected data length */ + if ((data->bytes_xfered + init_cnt) == + (data->blksz * data->blocks)) + mci_fifo_l_writeq(host->fifo_reg, host->part_buf); + } +} + +static void dw_mci_pull_data64_32(struct dw_mci *host, void *buf, int cnt) +{ +#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + if (unlikely((unsigned long)buf & 0x7)) { + while (cnt >= 8) { + /* pull data from fifo into aligned buffer */ + u64 aligned_buf[16]; + int len = min(cnt & -8, (int)sizeof(aligned_buf)); + int items = len >> 3; + int i; + + for (i = 0; i < items; ++i) + aligned_buf[i] = mci_fifo_l_readq(host->fifo_reg); + + /* memcpy from aligned buffer into output buffer */ + memcpy(buf, aligned_buf, len); + buf += len; + cnt -= len; + } + } else +#endif + { + u64 *pdata = buf; + + for (; cnt >= 8; cnt -= 8) + *pdata++ = mci_fifo_l_readq(host->fifo_reg); + buf = pdata; + } + if (cnt) { + host->part_buf = mci_fifo_l_readq(host->fifo_reg); + dw_mci_pull_final_bytes(host, buf, cnt); + } +} + static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt) { int len; @@ -2704,7 +2633,7 @@ done: static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) { - del_timer(&host->cto_timer); + timer_delete(&host->cto_timer); if (!host->cmd_status) host->cmd_status = status; @@ -2719,17 +2648,14 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) static void dw_mci_handle_cd(struct dw_mci *host) { - struct dw_mci_slot *slot = host->slot; - - mmc_detect_change(slot->mmc, - msecs_to_jiffies(host->pdata->detect_delay_ms)); + mmc_detect_change(host->mmc, + msecs_to_jiffies(host->detect_delay_ms)); } static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) { struct dw_mci *host = dev_id; u32 pending; - struct dw_mci_slot *slot = host->slot; pending = mci_readl(host, MINTSTS); /* read-only mask reg */ @@ -2748,13 +2674,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) dw_mci_cmd_interrupt(host, pending); spin_unlock(&host->irq_lock); - del_timer(&host->cmd11_timer); + timer_delete(&host->cmd11_timer); } if (pending & DW_MCI_CMD_ERROR_FLAGS) { spin_lock(&host->irq_lock); - del_timer(&host->cto_timer); + timer_delete(&host->cto_timer); mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); host->cmd_status = pending; smp_wmb(); /* drain writebuffer */ @@ -2767,7 +2693,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) spin_lock(&host->irq_lock); if (host->quirks & DW_MMC_QUIRK_EXTENDED_TMOUT) - del_timer(&host->dto_timer); + timer_delete(&host->dto_timer); /* if there is an error report DATA_ERROR */ mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); @@ -2788,13 +2714,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_DATA_OVER) { spin_lock(&host->irq_lock); - del_timer(&host->dto_timer); + timer_delete(&host->dto_timer); mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) host->data_status = pending; smp_wmb(); /* drain writebuffer */ - if (host->dir_status == DW_MCI_RECV_STATUS) { + if (host->dir_status == MMC_DATA_READ) { if (host->sg != NULL) dw_mci_read_data_pio(host, true); } @@ -2806,13 +2732,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_RXDR) { mci_writel(host, RINTSTS, SDMMC_INT_RXDR); - if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) + if (host->dir_status == MMC_DATA_READ && host->sg) dw_mci_read_data_pio(host, false); } if (pending & SDMMC_INT_TXDR) { mci_writel(host, RINTSTS, SDMMC_INT_TXDR); - if (host->dir_status == DW_MCI_SEND_STATUS && host->sg) + if (host->dir_status == MMC_DATA_WRITE && host->sg) dw_mci_write_data_pio(host); } @@ -2830,11 +2756,11 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) dw_mci_handle_cd(host); } - if (pending & SDMMC_INT_SDIO(slot->sdio_id)) { + if (pending & SDMMC_INT_SDIO(host->sdio_irq)) { mci_writel(host, RINTSTS, - SDMMC_INT_SDIO(slot->sdio_id)); - __dw_mci_enable_sdio_irq(slot, 0); - sdio_signal_irq(slot->mmc); + SDMMC_INT_SDIO(host->sdio_irq)); + __dw_mci_enable_sdio_irq(host, 0); + sdio_signal_irq(host->mmc); } } @@ -2866,29 +2792,19 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) +static int dw_mci_init_host_caps(struct dw_mci *host) { - struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; int ctrl_id; - if (host->pdata->caps) - mmc->caps = host->pdata->caps; - - if (host->pdata->pm_caps) - mmc->pm_caps = host->pdata->pm_caps; - if (drv_data) mmc->caps |= drv_data->common_caps; - if (host->dev->of_node) { - ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); - if (ctrl_id < 0) - ctrl_id = 0; - } else { + if (host->dev->of_node) + ctrl_id = mmc->index; + else ctrl_id = to_platform_device(host->dev)->id; - } if (drv_data && drv_data->caps) { if (ctrl_id >= drv_data->num_caps) { @@ -2899,9 +2815,6 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) mmc->caps |= drv_data->caps[ctrl_id]; } - if (host->pdata->caps2) - mmc->caps2 = host->pdata->caps2; - /* if host has set a minimum_freq, we should respect it */ if (host->minimum_speed) mmc->f_min = host->minimum_speed; @@ -2918,40 +2831,30 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) return 0; } -static int dw_mci_init_slot(struct dw_mci *host) +static int dw_mci_init_host(struct dw_mci *host) { - struct mmc_host *mmc; - struct dw_mci_slot *slot; + struct mmc_host *mmc = host->mmc; int ret; - mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); - if (!mmc) - return -ENOMEM; - - slot = mmc_priv(mmc); - slot->id = 0; - slot->sdio_id = host->sdio_id0 + slot->id; - slot->mmc = mmc; - slot->host = host; - host->slot = slot; - mmc->ops = &dw_mci_ops; /*if there are external regulators, get them*/ ret = mmc_regulator_get_supply(mmc); if (ret) - goto err_host_allocated; + return ret; if (!mmc->ocr_avail) mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ret = mmc_of_parse(mmc); if (ret) - goto err_host_allocated; + return ret; - ret = dw_mci_init_slot_caps(slot); + mmc_of_parse_clk_phase(host->dev, &host->phase_map); + + ret = dw_mci_init_host_caps(host); if (ret) - goto err_host_allocated; + return ret; /* Useful defaults if platform data is unset. */ if (host->use_dma == TRANS_MODE_IDMAC) { @@ -2977,29 +2880,28 @@ static int dw_mci_init_slot(struct dw_mci *host) mmc->max_seg_size = mmc->max_req_size; } + if (mmc->caps & MMC_CAP_NEEDS_POLL) + dev_info(&mmc->class_dev, "card is polling.\n"); + else if (!mmc_card_is_removable(mmc)) + dev_info(&mmc->class_dev, "card is non-removable.\n"); + dw_mci_get_cd(mmc); ret = mmc_add_host(mmc); if (ret) - goto err_host_allocated; + return ret; #if defined(CONFIG_DEBUG_FS) - dw_mci_init_debugfs(slot); + dw_mci_init_debugfs(host); #endif return 0; - -err_host_allocated: - mmc_free_host(mmc); - return ret; } -static void dw_mci_cleanup_slot(struct dw_mci_slot *slot) +static void dw_mci_cleanup_host(struct dw_mci *host) { /* Debugfs stuff is cleaned up by mmc core */ - mmc_remove_host(slot->mmc); - slot->host->slot = NULL; - mmc_free_host(slot->mmc); + mmc_remove_host(host->mmc); } static void dw_mci_init_dma(struct dw_mci *host) @@ -3041,9 +2943,8 @@ static void dw_mci_init_dma(struct dw_mci *host) host->dma_64bit_address = 1; dev_info(host->dev, "IDMAC supports 64-bit address mode.\n"); - if (!dma_set_mask(host->dev, DMA_BIT_MASK(64))) - dma_set_coherent_mask(host->dev, - DMA_BIT_MASK(64)); + if (dma_set_mask_and_coherent(host->dev, DMA_BIT_MASK(64))) + dev_info(host->dev, "Fail to set 64-bit DMA mask"); } else { /* host supports IDMAC in 32-bit address mode */ host->dma_64bit_address = 0; @@ -3095,7 +2996,7 @@ no_dma: static void dw_mci_cmd11_timer(struct timer_list *t) { - struct dw_mci *host = from_timer(host, t, cmd11_timer); + struct dw_mci *host = timer_container_of(host, t, cmd11_timer); if (host->state != STATE_SENDING_CMD11) { dev_warn(host->dev, "Unexpected CMD11 timeout\n"); @@ -3109,7 +3010,7 @@ static void dw_mci_cmd11_timer(struct timer_list *t) static void dw_mci_cto_timer(struct timer_list *t) { - struct dw_mci *host = from_timer(host, t, cto_timer); + struct dw_mci *host = timer_container_of(host, t, cto_timer); unsigned long irqflags; u32 pending; @@ -3164,7 +3065,7 @@ exit: static void dw_mci_dto_timer(struct timer_list *t) { - struct dw_mci *host = from_timer(host, t, dto_timer); + struct dw_mci *host = timer_container_of(host, t, dto_timer); unsigned long irqflags; u32 pending; @@ -3213,54 +3114,43 @@ exit: spin_unlock_irqrestore(&host->irq_lock, irqflags); } -#ifdef CONFIG_OF -static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) +static int dw_mci_parse_dt(struct dw_mci *host) { - struct dw_mci_board *pdata; struct device *dev = host->dev; const struct dw_mci_drv_data *drv_data = host->drv_data; int ret; u32 clock_frequency; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - /* find reset controller when exist */ - pdata->rstc = devm_reset_control_get_optional_exclusive(dev, "reset"); - if (IS_ERR(pdata->rstc)) - return ERR_CAST(pdata->rstc); + host->rstc = devm_reset_control_get_optional_exclusive(dev, "reset"); + if (IS_ERR(host->rstc)) + return PTR_ERR(host->rstc); - if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth)) + if (!host->fifo_depth && device_property_read_u32(dev, "fifo-depth", &host->fifo_depth)) dev_info(dev, "fifo-depth property not found, using value of FIFOTH register as default\n"); - device_property_read_u32(dev, "card-detect-delay", - &pdata->detect_delay_ms); + if (!host->detect_delay_ms) + device_property_read_u32(dev, "card-detect-delay", + &host->detect_delay_ms); - device_property_read_u32(dev, "data-addr", &host->data_addr_override); + if (!host->data_addr_override) + device_property_read_u32(dev, "data-addr", &host->data_addr_override); if (device_property_present(dev, "fifo-watermark-aligned")) host->wm_aligned = true; - if (!device_property_read_u32(dev, "clock-frequency", &clock_frequency)) - pdata->bus_hz = clock_frequency; + if (!host->bus_hz && !device_property_read_u32(dev, "clock-frequency", &clock_frequency)) + host->bus_hz = clock_frequency; if (drv_data && drv_data->parse_dt) { ret = drv_data->parse_dt(host); if (ret) - return ERR_PTR(ret); + return ret; } - return pdata; -} - -#else /* CONFIG_OF */ -static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) -{ - return ERR_PTR(-EINVAL); + return 0; } -#endif /* CONFIG_OF */ static void dw_mci_enable_cd(struct dw_mci *host) { @@ -3268,13 +3158,13 @@ static void dw_mci_enable_cd(struct dw_mci *host) u32 temp; /* - * No need for CD if all slots have a non-error GPIO + * No need for CD if host has a non-error GPIO * as well as broken card detection is found. */ - if (host->slot->mmc->caps & MMC_CAP_NEEDS_POLL) + if (host->mmc->caps & MMC_CAP_NEEDS_POLL) return; - if (mmc_gpio_get_cd(host->slot->mmc) < 0) { + if (mmc_gpio_get_cd(host->mmc) < 0) { spin_lock_irqsave(&host->irq_lock, irqflags); temp = mci_readl(host, INTMASK); temp |= SDMMC_INT_CD; @@ -3283,18 +3173,32 @@ static void dw_mci_enable_cd(struct dw_mci *host) } } +struct dw_mci *dw_mci_alloc_host(struct device *dev) +{ + struct mmc_host *mmc; + struct dw_mci *host; + + mmc = devm_mmc_alloc_host(dev, sizeof(struct dw_mci)); + if (!mmc) + return ERR_PTR(-ENOMEM); + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dev = dev; + + return host; +} +EXPORT_SYMBOL(dw_mci_alloc_host); + int dw_mci_probe(struct dw_mci *host) { const struct dw_mci_drv_data *drv_data = host->drv_data; int width, i, ret = 0; u32 fifo_size; - if (!host->pdata) { - host->pdata = dw_mci_parse_dt(host); - if (IS_ERR(host->pdata)) - return dev_err_probe(host->dev, PTR_ERR(host->pdata), - "platform data not available\n"); - } + ret = dw_mci_parse_dt(host); + if (ret) + return dev_err_probe(host->dev, ret, "parse dt failed\n"); host->biu_clk = devm_clk_get(host->dev, "biu"); if (IS_ERR(host->biu_clk)) { @@ -3317,8 +3221,6 @@ int dw_mci_probe(struct dw_mci *host) ret = PTR_ERR(host->ciu_clk); if (ret == -EPROBE_DEFER) goto err_clk_biu; - - host->bus_hz = host->pdata->bus_hz; } else { ret = clk_prepare_enable(host->ciu_clk); if (ret) { @@ -3326,12 +3228,12 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_biu; } - if (host->pdata->bus_hz) { - ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz); + if (host->bus_hz) { + ret = clk_set_rate(host->ciu_clk, host->bus_hz); if (ret) dev_warn(host->dev, "Unable to set bus rate to %uHz\n", - host->pdata->bus_hz); + host->bus_hz); } host->bus_hz = clk_get_rate(host->ciu_clk); } @@ -3343,10 +3245,10 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_ciu; } - if (host->pdata->rstc) { - reset_control_assert(host->pdata->rstc); + if (host->rstc) { + reset_control_assert(host->rstc); usleep_range(10, 50); - reset_control_deassert(host->pdata->rstc); + reset_control_deassert(host->rstc); } if (drv_data && drv_data->init) { @@ -3364,7 +3266,6 @@ int dw_mci_probe(struct dw_mci *host) spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); - INIT_LIST_HEAD(&host->queue); dw_mci_init_fault(host); @@ -3379,8 +3280,13 @@ int dw_mci_probe(struct dw_mci *host) width = 16; host->data_shift = 1; } else if (i == 2) { - host->push_data = dw_mci_push_data64; - host->pull_data = dw_mci_pull_data64; + if ((host->quirks & DW_MMC_QUIRK_FIFO64_32)) { + host->push_data = dw_mci_push_data64_32; + host->pull_data = dw_mci_pull_data64_32; + } else { + host->push_data = dw_mci_push_data64; + host->pull_data = dw_mci_pull_data64; + } width = 64; host->data_shift = 3; } else { @@ -3400,7 +3306,6 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_ciu; } - host->dma_ops = host->pdata->dma_ops; dw_mci_init_dma(host); /* Clear the interrupts for the host controller */ @@ -3414,7 +3319,7 @@ int dw_mci_probe(struct dw_mci *host) * FIFO threshold settings RxMark = fifo_size / 2 - 1, * Tx Mark = fifo_size / 2 DMA Size = 8 */ - if (!host->pdata->fifo_depth) { + if (!host->fifo_depth) { /* * Power-on value of RX_WMark is FIFO_DEPTH-1, but this may * have been overwritten by the bootloader, just like we're @@ -3424,7 +3329,7 @@ int dw_mci_probe(struct dw_mci *host) fifo_size = mci_readl(host, FIFOTH); fifo_size = 1 + ((fifo_size >> 16) & 0xfff); } else { - fifo_size = host->pdata->fifo_depth; + fifo_size = host->fifo_depth; } host->fifo_depth = fifo_size; host->fifoth_val = @@ -3469,14 +3374,13 @@ int dw_mci_probe(struct dw_mci *host) "DW MMC controller at irq %d,%d bit host data width,%u deep fifo\n", host->irq, width, fifo_size); - /* We need at least one slot to succeed */ - ret = dw_mci_init_slot(host); + ret = dw_mci_init_host(host); if (ret) { - dev_dbg(host->dev, "slot %d init failed\n", i); + dev_dbg(host->dev, "host init failed\n"); goto err_dmaunmap; } - /* Now that slots are all setup, we can enable card detect */ + /* Now that host is setup, we can enable card detect */ dw_mci_enable_cd(host); return 0; @@ -3485,7 +3389,7 @@ err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - reset_control_assert(host->pdata->rstc); + reset_control_assert(host->rstc); err_clk_ciu: clk_disable_unprepare(host->ciu_clk); @@ -3499,9 +3403,8 @@ EXPORT_SYMBOL(dw_mci_probe); void dw_mci_remove(struct dw_mci *host) { - dev_dbg(host->dev, "remove slot\n"); - if (host->slot) - dw_mci_cleanup_slot(host->slot); + dev_dbg(host->dev, "remove host\n"); + dw_mci_cleanup_host(host); mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ @@ -3513,16 +3416,13 @@ void dw_mci_remove(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - reset_control_assert(host->pdata->rstc); + reset_control_assert(host->rstc); clk_disable_unprepare(host->ciu_clk); clk_disable_unprepare(host->biu_clk); } EXPORT_SYMBOL(dw_mci_remove); - - -#ifdef CONFIG_PM int dw_mci_runtime_suspend(struct device *dev) { struct dw_mci *host = dev_get_drvdata(dev); @@ -3532,9 +3432,8 @@ int dw_mci_runtime_suspend(struct device *dev) clk_disable_unprepare(host->ciu_clk); - if (host->slot && - (mmc_can_gpio_cd(host->slot->mmc) || - !mmc_card_is_removable(host->slot->mmc))) + if (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc)) clk_disable_unprepare(host->biu_clk); return 0; @@ -3546,9 +3445,8 @@ int dw_mci_runtime_resume(struct device *dev) int ret = 0; struct dw_mci *host = dev_get_drvdata(dev); - if (host->slot && - (mmc_can_gpio_cd(host->slot->mmc) || - !mmc_card_is_removable(host->slot->mmc))) { + if (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc)) { ret = clk_prepare_enable(host->biu_clk); if (ret) return ret; @@ -3564,8 +3462,11 @@ int dw_mci_runtime_resume(struct device *dev) goto err; } - if (host->use_dma && host->dma_ops->init) - host->dma_ops->init(host); + if (host->use_dma && host->dma_ops->init) { + ret = host->dma_ops->init(host); + if (ret) + return ret; + } /* * Restore the initial value at FIFOTH register @@ -3584,44 +3485,35 @@ int dw_mci_runtime_resume(struct device *dev) mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); - if (host->slot && host->slot->mmc->pm_flags & MMC_PM_KEEP_POWER) - dw_mci_set_ios(host->slot->mmc, &host->slot->mmc->ios); + if (host->mmc->pm_flags & MMC_PM_KEEP_POWER) + dw_mci_set_ios(host->mmc, &host->mmc->ios); /* Force setup bus to guarantee available clock output */ - dw_mci_setup_bus(host->slot, true); + dw_mci_setup_bus(host, true); /* Re-enable SDIO interrupts. */ - if (sdio_irq_claimed(host->slot->mmc)) - __dw_mci_enable_sdio_irq(host->slot, 1); + if (sdio_irq_claimed(host->mmc)) + __dw_mci_enable_sdio_irq(host, 1); - /* Now that slots are all setup, we can enable card detect */ + /* Now that host is setup, we can enable card detect */ dw_mci_enable_cd(host); return 0; err: - if (host->slot && - (mmc_can_gpio_cd(host->slot->mmc) || - !mmc_card_is_removable(host->slot->mmc))) + if (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc)) clk_disable_unprepare(host->biu_clk); return ret; } EXPORT_SYMBOL(dw_mci_runtime_resume); -#endif /* CONFIG_PM */ -static int __init dw_mci_init(void) -{ - pr_info("Synopsys Designware Multimedia Card Interface Driver\n"); - return 0; -} - -static void __exit dw_mci_exit(void) -{ -} - -module_init(dw_mci_init); -module_exit(dw_mci_exit); +const struct dev_pm_ops dw_mci_pmops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(dw_mci_pmops); MODULE_DESCRIPTION("DW Multimedia Card Interface driver"); MODULE_AUTHOR("NXP Semiconductor VietNam"); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 6447b916990d..42e58be74ce0 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -43,8 +43,6 @@ enum dw_mci_cookie { COOKIE_MAPPED, /* mapped by prepare_data() of dwmmc */ }; -struct mmc_data; - enum { TRANS_MODE_PIO = 0, TRANS_MODE_IDMAC, @@ -57,14 +55,14 @@ struct dw_mci_dma_slave { }; /** - * struct dw_mci - MMC controller state shared between all slots + * struct dw_mci - MMC controller state * @lock: Spinlock protecting the queue and associated data. * @irq_lock: Spinlock protecting the INTMASK setting. * @regs: Pointer to MMIO registers. * @fifo_reg: Pointer to MMIO registers for data FIFO * @sg: Scatterlist entry currently being processed by PIO code, if any. * @sg_miter: PIO mapping scatterlist iterator. - * @mrq: The request currently being processed on @slot, + * @mrq: The request currently being processed on @host, * or NULL if the controller is idle. * @cmd: The command currently being sent to the card, or NULL. * @data: The data currently being transferred, or NULL if no data @@ -78,7 +76,7 @@ struct dw_mci_dma_slave { * @dma_64bit_address: Whether DMA supports 64-bit address mode or not. * @sg_dma: Bus address of DMA buffer. * @sg_cpu: Virtual address of DMA buffer. - * @dma_ops: Pointer to platform-specific DMA callbacks. + * @dma_ops: Pointer to DMA callbacks. * @cmd_status: Snapshot of SR taken upon completion of the current * @ring_size: Buffer size for idma descriptors. * command. Only valid when EVENT_CMD_COMPLETE is pending. @@ -96,7 +94,6 @@ struct dw_mci_dma_slave { * @completed_events: Bitmask of events which the state machine has * processed. * @state: BH work state. - * @queue: List of slots waiting for access to the controller. * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus * rate and timeout calculations. * @current_speed: Configured rate of the controller. @@ -104,12 +101,10 @@ struct dw_mci_dma_slave { * @fifoth_val: The value of FIFOTH register. * @verid: Denote Version ID. * @dev: Device associated with the MMC controller. - * @pdata: Platform data associated with the MMC controller. * @drv_data: Driver specific data for identified variant of the controller * @priv: Implementation defined private data. * @biu_clk: Pointer to bus interface unit clock instance. * @ciu_clk: Pointer to card interface unit clock instance. - * @slot: Slots sharing this MMC controller. * @fifo_depth: depth of FIFO. * @data_addr_override: override fifo reg offset with this value. * @wm_aligned: force fifo watermark equal with data length in PIO mode. @@ -121,23 +116,28 @@ struct dw_mci_dma_slave { * @push_data: Pointer to FIFO push function. * @pull_data: Pointer to FIFO pull function. * @quirks: Set of quirks that apply to specific versions of the IP. - * @vqmmc_enabled: Status of vqmmc, should be true or false. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. - * @sdio_id0: Number of slot0 in the SDIO interrupt registers. + * @sdio_irq: SDIO interrupt bit in interrupt registers. * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. * @cto_timer: Timer for broken command transfer over scheme. * @dto_timer: Timer for broken data transfer over scheme. + * @mmc: The mmc_host representing this dw_mci. + * @flags: Random state bits associated with the host. + * @ctype: Card type for this host. + * @clock: Clock rate configured by set_ios(). Protected by host->lock. + * @clk_old: The last clock value that was requested from core. + * @pdev: platform_device registered + * @rstc: Reset controller for this host. + * @detect_delay_ms: Delay in mS before detecting cards after interrupt. + * @phase_map: The map for recording in and out phases for each timing * * Locking * ======= * - * @lock is a softirq-safe spinlock protecting @queue as well as - * @slot, @mrq and @state. These must always be updated + * @lock is a softirq-safe spinlock protecting as well as + * @mrq and @state. These must always be updated * at the same time while holding @lock. - * The @mrq field of struct dw_mci_slot is also protected by @lock, - * and must always be written at the same time as the slot is added to - * @queue. * * @irq_lock is an irq-safe spinlock protecting the INTMASK register * to allow the interrupt handler to modify it directly. Held for only long @@ -199,7 +199,6 @@ struct dw_mci { unsigned long pending_events; unsigned long completed_events; enum dw_mci_state state; - struct list_head queue; u32 bus_hz; u32 current_speed; @@ -207,7 +206,6 @@ struct dw_mci { u32 fifoth_val; u16 verid; struct device *dev; - struct dw_mci_board *pdata; const struct dw_mci_drv_data *drv_data; void *priv; struct clk *biu_clk; @@ -228,11 +226,10 @@ struct dw_mci { void (*pull_data)(struct dw_mci *host, void *buf, int cnt); u32 quirks; - bool vqmmc_enabled; unsigned long irq_flags; /* IRQ flags */ int irq; - int sdio_id0; + int sdio_irq; struct timer_list cmd11_timer; struct timer_list cto_timer; @@ -242,6 +239,19 @@ struct dw_mci { struct fault_attr fail_data_crc; struct hrtimer fault_timer; #endif + struct mmc_host *mmc; + unsigned long flags; +#define DW_MMC_CARD_NEED_INIT 0 +#define DW_MMC_CARD_NO_LOW_PWR 1 +#define DW_MMC_CARD_NO_USE_HOLD 2 +#define DW_MMC_CARD_NEEDS_POLL 3 + u32 ctype; + unsigned int clock; + unsigned int clk_old; + struct platform_device *pdev; + struct reset_control *rstc; + u32 detect_delay_ms; + struct mmc_clk_phase_map phase_map; }; /* DMA ops for Internal/External DMAC interface */ @@ -255,32 +265,10 @@ struct dw_mci_dma_ops { void (*exit)(struct dw_mci *host); }; -struct dma_pdata; - -/* Board platform data */ -struct dw_mci_board { - unsigned int bus_hz; /* Clock speed at the cclk_in pad */ - - u32 caps; /* Capabilities */ - u32 caps2; /* More capabilities */ - u32 pm_caps; /* PM capabilities */ - /* - * Override fifo depth. If 0, autodetect it from the FIFOTH register, - * but note that this may not be reliable after a bootloader has used - * it. - */ - unsigned int fifo_depth; - - /* delay in mS before detecting cards after interrupt */ - u32 detect_delay_ms; - - struct reset_control *rstc; - struct dw_mci_dma_ops *dma_ops; - struct dma_pdata *data; -}; - /* Support for longer data read timeout */ #define DW_MMC_QUIRK_EXTENDED_TMOUT BIT(0) +/* Force 32-bit access to the FIFO */ +#define DW_MMC_QUIRK_FIFO64_32 BIT(1) #define DW_MMC_240A 0x240a #define DW_MMC_280A 0x280a @@ -394,7 +382,6 @@ struct dw_mci_board { #define SDMMC_INT_CMD_DONE BIT(2) #define SDMMC_INT_RESP_ERR BIT(1) #define SDMMC_INT_CD BIT(0) -#define SDMMC_INT_ERROR 0xbfc2 /* Command register defines */ #define SDMMC_CMD_START BIT(31) #define SDMMC_CMD_USE_HOLD_REG BIT(29) @@ -472,87 +459,48 @@ struct dw_mci_board { #define mci_fifo_writel(__value, __reg) __raw_writel(__reg, __value) #define mci_fifo_writeq(__value, __reg) __raw_writeq(__reg, __value) +/* + * Some dw_mmc devices have 64-bit FIFOs, but expect them to be + * accessed using two 32-bit accesses. If such controller is used + * with a 64-bit kernel, this has to be done explicitly. + */ +static inline u64 mci_fifo_l_readq(void __iomem *addr) +{ + u64 ans; + u32 proxy[2]; + + proxy[0] = mci_fifo_readl(addr); + proxy[1] = mci_fifo_readl(addr + 4); + memcpy(&ans, proxy, 8); + return ans; +} + +static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value) +{ + u32 proxy[2]; + + memcpy(proxy, &value, 8); + mci_fifo_writel(addr, proxy[0]); + mci_fifo_writel(addr + 4, proxy[1]); +} + /* Register access macros */ #define mci_readl(dev, reg) \ readl_relaxed((dev)->regs + SDMMC_##reg) #define mci_writel(dev, reg, value) \ writel_relaxed((value), (dev)->regs + SDMMC_##reg) -/* 16-bit FIFO access macros */ -#define mci_readw(dev, reg) \ - readw_relaxed((dev)->regs + SDMMC_##reg) -#define mci_writew(dev, reg, value) \ - writew_relaxed((value), (dev)->regs + SDMMC_##reg) - -/* 64-bit FIFO access macros */ -#ifdef readq -#define mci_readq(dev, reg) \ - readq_relaxed((dev)->regs + SDMMC_##reg) -#define mci_writeq(dev, reg, value) \ - writeq_relaxed((value), (dev)->regs + SDMMC_##reg) -#else -/* - * Dummy readq implementation for architectures that don't define it. - * - * We would assume that none of these architectures would configure - * the IP block with a 64bit FIFO width, so this code will never be - * executed on those machines. Defining these macros here keeps the - * rest of the code free from ifdefs. - */ -#define mci_readq(dev, reg) \ - (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg)) -#define mci_writeq(dev, reg, value) \ - (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value)) - +#ifndef readq #define __raw_writeq(__value, __reg) \ (*(volatile u64 __force *)(__reg) = (__value)) #define __raw_readq(__reg) (*(volatile u64 __force *)(__reg)) #endif +extern struct dw_mci *dw_mci_alloc_host(struct device *device); extern int dw_mci_probe(struct dw_mci *host); extern void dw_mci_remove(struct dw_mci *host); -#ifdef CONFIG_PM extern int dw_mci_runtime_suspend(struct device *device); extern int dw_mci_runtime_resume(struct device *device); -#endif - -/** - * struct dw_mci_slot - MMC slot state - * @mmc: The mmc_host representing this slot. - * @host: The MMC controller this slot is using. - * @ctype: Card type for this slot. - * @mrq: mmc_request currently being processed or waiting to be - * processed, or NULL when the slot is idle. - * @queue_node: List node for placing this node in the @queue list of - * &struct dw_mci. - * @clock: Clock rate configured by set_ios(). Protected by host->lock. - * @__clk_old: The last clock value that was requested from core. - * Keeping track of this helps us to avoid spamming the console. - * @flags: Random state bits associated with the slot. - * @id: Number of this slot. - * @sdio_id: Number of this slot in the SDIO interrupt registers. - */ -struct dw_mci_slot { - struct mmc_host *mmc; - struct dw_mci *host; - - u32 ctype; - - struct mmc_request *mrq; - struct list_head queue_node; - - unsigned int clock; - unsigned int __clk_old; - - unsigned long flags; -#define DW_MMC_CARD_PRESENT 0 -#define DW_MMC_CARD_NEED_INIT 1 -#define DW_MMC_CARD_NO_LOW_PWR 2 -#define DW_MMC_CARD_NO_USE_HOLD 3 -#define DW_MMC_CARD_NEEDS_POLL 4 - int id; - int sdio_id; -}; /** * dw_mci driver data - dw-mshc implementation specific driver data. @@ -579,10 +527,10 @@ struct dw_mci_drv_data { int (*init)(struct dw_mci *host); void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); int (*parse_dt)(struct dw_mci *host); - int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode); + int (*execute_tuning)(struct dw_mci *host, u32 opcode); int (*prepare_hs400_tuning)(struct dw_mci *host, struct mmc_ios *ios); - int (*switch_voltage)(struct mmc_host *mmc, + int (*switch_voltage)(struct dw_mci *host, struct mmc_ios *ios); void (*set_data_timeout)(struct dw_mci *host, unsigned int timeout_ns); diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 596012d5afac..6a3c26b7c82d 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -641,7 +641,8 @@ poll_timeout: static void jz4740_mmc_timeout(struct timer_list *t) { - struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer); + struct jz4740_mmc_host *host = timer_container_of(host, t, + timeout_timer); if (!test_and_clear_bit(0, &host->waiting)) return; @@ -862,7 +863,7 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) if (host->req && cmd && irq_reg) { if (test_and_clear_bit(0, &host->waiting)) { - del_timer(&host->timeout_timer); + timer_delete(&host->timeout_timer); if (status & JZ_MMC_STATUS_TIMEOUT_RES) { cmd->error = -ETIMEDOUT; @@ -1042,7 +1043,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) struct mmc_host *mmc; struct jz4740_mmc_host *host; - mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) { dev_err(&pdev->dev, "Failed to alloc mmc host structure\n"); return -ENOMEM; @@ -1051,34 +1052,27 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host = mmc_priv(mmc); /* Default if no match is JZ4740 */ - host->version = (enum jz4740_mmc_version)device_get_match_data(&pdev->dev); + host->version = (unsigned long)device_get_match_data(&pdev->dev); ret = mmc_of_parse(mmc); - if (ret) { - dev_err_probe(&pdev->dev, ret, "could not parse device properties\n"); - goto err_free_host; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "could not parse device properties\n"); mmc_regulator_get_supply(mmc); host->irq = platform_get_irq(pdev, 0); - if (host->irq < 0) { - ret = host->irq; - goto err_free_host; - } + if (host->irq < 0) + return host->irq; host->clk = devm_clk_get(&pdev->dev, "mmc"); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - dev_err(&pdev->dev, "Failed to get mmc clock\n"); - goto err_free_host; - } + if (IS_ERR(host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(host->clk), + "Failed to get mmc clock\n"); host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->mem_res); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto err_free_host; - } + if (IS_ERR(host->base)) + return PTR_ERR(host->base); mmc->ops = &jz4740_mmc_ops; if (!mmc->f_max) @@ -1118,10 +1112,8 @@ static int jz4740_mmc_probe(struct platform_device* pdev) ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, dev_name(&pdev->dev), host); - if (ret) { - dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); - goto err_free_host; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to request irq\n"); jz4740_mmc_clock_disable(host); timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); @@ -1152,9 +1144,6 @@ err_release_dma: jz4740_mmc_release_dma_channels(host); err_free_irq: free_irq(host->irq, host); -err_free_host: - mmc_free_host(mmc); - return ret; } @@ -1162,7 +1151,7 @@ static void jz4740_mmc_remove(struct platform_device *pdev) { struct jz4740_mmc_host *host = platform_get_drvdata(pdev); - del_timer_sync(&host->timeout_timer); + timer_delete_sync(&host->timeout_timer); jz4740_mmc_set_irq_enabled(host, 0xff, false); jz4740_mmc_reset(host); @@ -1172,8 +1161,6 @@ static void jz4740_mmc_remove(struct platform_device *pdev) if (host->use_dma) jz4740_mmc_release_dma_channels(host); - - mmc_free_host(host->mmc); } static int jz4740_mmc_suspend(struct device *dev) diff --git a/drivers/mmc/host/litex_mmc.c b/drivers/mmc/host/litex_mmc.c index b338ccfa8f33..d2f19c2dc673 100644 --- a/drivers/mmc/host/litex_mmc.c +++ b/drivers/mmc/host/litex_mmc.c @@ -506,11 +506,6 @@ use_polling: return 0; } -static void litex_mmc_free_host_wrapper(void *mmc) -{ - mmc_free_host(mmc); -} - static int litex_mmc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -525,15 +520,10 @@ static int litex_mmc_probe(struct platform_device *pdev) * If for some reason we need to modify max_blk_count, we must also * re-calculate `max_[req,seg]_size = max_blk_size * max_blk_count;` */ - mmc = mmc_alloc_host(sizeof(struct litex_mmc_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; - ret = devm_add_action_or_reset(dev, litex_mmc_free_host_wrapper, mmc); - if (ret) - return dev_err_probe(dev, ret, - "Can't register mmc_free_host action\n"); - host = mmc_priv(mmc); host->mmc = mmc; diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c new file mode 100644 index 000000000000..f553e92fd9e5 --- /dev/null +++ b/drivers/mmc/host/loongson2-mmc.c @@ -0,0 +1,1056 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Loongson-2K MMC/SDIO controller driver + * + * Copyright (C) 2018-2025 Loongson Technology Corporation Limited. + * + */ + +#include <linux/bitfield.h> +#include <linux/bitrev.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dmaengine.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/mmc/core.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.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/regmap.h> + +#define LOONGSON2_MMC_REG_CTL 0x00 /* Control Register */ +#define LOONGSON2_MMC_REG_PRE 0x04 /* Prescaler Register */ +#define LOONGSON2_MMC_REG_CARG 0x08 /* Command Register */ +#define LOONGSON2_MMC_REG_CCTL 0x0c /* Command Control Register */ +#define LOONGSON2_MMC_REG_CSTS 0x10 /* Command Status Register */ +#define LOONGSON2_MMC_REG_RSP0 0x14 /* Command Response Register 0 */ +#define LOONGSON2_MMC_REG_RSP1 0x18 /* Command Response Register 1 */ +#define LOONGSON2_MMC_REG_RSP2 0x1c /* Command Response Register 2 */ +#define LOONGSON2_MMC_REG_RSP3 0x20 /* Command Response Register 3 */ +#define LOONGSON2_MMC_REG_TIMER 0x24 /* Data Timeout Register */ +#define LOONGSON2_MMC_REG_BSIZE 0x28 /* Block Size Register */ +#define LOONGSON2_MMC_REG_DCTL 0x2c /* Data Control Register */ +#define LOONGSON2_MMC_REG_DCNT 0x30 /* Data Counter Register */ +#define LOONGSON2_MMC_REG_DSTS 0x34 /* Data Status Register */ +#define LOONGSON2_MMC_REG_FSTS 0x38 /* FIFO Status Register */ +#define LOONGSON2_MMC_REG_INT 0x3c /* Interrupt Register */ +#define LOONGSON2_MMC_REG_DATA 0x40 /* Data Register */ +#define LOONGSON2_MMC_REG_IEN 0x64 /* Interrupt Enable Register */ + +/* EMMC DLL Mode Registers */ +#define LOONGSON2_MMC_REG_DLLVAL 0xf0 /* DLL Master Lock-value Register */ +#define LOONGSON2_MMC_REG_DLLCTL 0xf4 /* DLL Control Register */ +#define LOONGSON2_MMC_REG_DELAY 0xf8 /* DLL Delayed Parameter Register */ +#define LOONGSON2_MMC_REG_SEL 0xfc /* Bus Mode Selection Register */ + +/* Exclusive DMA R/W Registers */ +#define LOONGSON2_MMC_REG_WDMA_LO 0x400 +#define LOONGSON2_MMC_REG_WDMA_HI 0x404 +#define LOONGSON2_MMC_REG_RDMA_LO 0x800 +#define LOONGSON2_MMC_REG_RDMA_HI 0x804 + +/* Bitfields of control register */ +#define LOONGSON2_MMC_CTL_ENCLK BIT(0) +#define LOONGSON2_MMC_CTL_EXTCLK BIT(1) +#define LOONGSON2_MMC_CTL_RESET BIT(8) + +/* Bitfields of prescaler register */ +#define LOONGSON2_MMC_PRE GENMASK(9, 0) +#define LOONGSON2_MMC_PRE_EN BIT(31) + +/* Bitfields of command control register */ +#define LOONGSON2_MMC_CCTL_INDEX GENMASK(5, 0) +#define LOONGSON2_MMC_CCTL_HOST BIT(6) +#define LOONGSON2_MMC_CCTL_START BIT(8) +#define LOONGSON2_MMC_CCTL_WAIT_RSP BIT(9) +#define LOONGSON2_MMC_CCTL_LONG_RSP BIT(10) +#define LOONGSON2_MMC_CCTL_ABORT BIT(12) +#define LOONGSON2_MMC_CCTL_CHECK BIT(13) +#define LOONGSON2_MMC_CCTL_SDIO BIT(14) +#define LOONGSON2_MMC_CCTL_CMD6 BIT(18) + +/* Bitfields of command status register */ +#define LOONGSON2_MMC_CSTS_INDEX GENMASK(7, 0) +#define LOONGSON2_MMC_CSTS_ON BIT(8) +#define LOONGSON2_MMC_CSTS_RSP BIT(9) +#define LOONGSON2_MMC_CSTS_TIMEOUT BIT(10) +#define LOONGSON2_MMC_CSTS_END BIT(11) +#define LOONGSON2_MMC_CSTS_CRC_ERR BIT(12) +#define LOONGSON2_MMC_CSTS_AUTO_STOP BIT(13) +#define LOONGSON2_MMC_CSTS_FIN BIT(14) + +/* Bitfields of data timeout register */ +#define LOONGSON2_MMC_DTIMR GENMASK(23, 0) + +/* Bitfields of block size register */ +#define LOONGSON2_MMC_BSIZE GENMASK(11, 0) + +/* Bitfields of data control register */ +#define LOONGSON2_MMC_DCTL_BNUM GENMASK(11, 0) +#define LOONGSON2_MMC_DCTL_START BIT(14) +#define LOONGSON2_MMC_DCTL_ENDMA BIT(15) +#define LOONGSON2_MMC_DCTL_WIDE BIT(16) +#define LOONGSON2_MMC_DCTL_RWAIT BIT(17) +#define LOONGSON2_MMC_DCTL_IO_SUSPEND BIT(18) +#define LOONGSON2_MMC_DCTL_IO_RESUME BIT(19) +#define LOONGSON2_MMC_DCTL_RW_RESUME BIT(20) +#define LOONGSON2_MMC_DCTL_8BIT_BUS BIT(26) + +/* Bitfields of sata counter register */ +#define LOONGSON2_MMC_DCNT_BNUM GENMASK(11, 0) +#define LOONGSON2_MMC_DCNT_BYTE GENMASK(23, 12) + +/* Bitfields of command status register */ +#define LOONGSON2_MMC_DSTS_RXON BIT(0) +#define LOONGSON2_MMC_DSTS_TXON BIT(1) +#define LOONGSON2_MMC_DSTS_SBITERR BIT(2) +#define LOONGSON2_MMC_DSTS_BUSYFIN BIT(3) +#define LOONGSON2_MMC_DSTS_XFERFIN BIT(4) +#define LOONGSON2_MMC_DSTS_DTIMEOUT BIT(5) +#define LOONGSON2_MMC_DSTS_RXCRC BIT(6) +#define LOONGSON2_MMC_DSTS_TXCRC BIT(7) +#define LOONGSON2_MMC_DSTS_IRQ BIT(8) +#define LOONGSON2_MMC_DSTS_START BIT(13) +#define LOONGSON2_MMC_DSTS_RESUME BIT(15) +#define LOONGSON2_MMC_DSTS_SUSPEND BIT(16) + +/* Bitfields of FIFO Status Register */ +#define LOONGSON2_MMC_FSTS_TXFULL BIT(11) + +/* Bitfields of interrupt register */ +#define LOONGSON2_MMC_INT_DFIN BIT(0) +#define LOONGSON2_MMC_INT_DTIMEOUT BIT(1) +#define LOONGSON2_MMC_INT_RXCRC BIT(2) +#define LOONGSON2_MMC_INT_TXCRC BIT(3) +#define LOONGSON2_MMC_INT_PROGERR BIT(4) +#define LOONGSON2_MMC_INT_SDIOIRQ BIT(5) +#define LOONGSON2_MMC_INT_CSENT BIT(6) +#define LOONGSON2_MMC_INT_CTIMEOUT BIT(7) +#define LOONGSON2_MMC_INT_RESPCRC BIT(8) +#define LOONGSON2_MMC_INT_BUSYEND BIT(9) + +/* Bitfields of interrupt enable register */ +#define LOONGSON2_MMC_IEN_DFIN BIT(0) +#define LOONGSON2_MMC_IEN_DTIMEOUT BIT(1) +#define LOONGSON2_MMC_IEN_RXCRC BIT(2) +#define LOONGSON2_MMC_IEN_TXCRC BIT(3) +#define LOONGSON2_MMC_IEN_PROGERR BIT(4) +#define LOONGSON2_MMC_IEN_SDIOIRQ BIT(5) +#define LOONGSON2_MMC_IEN_CSENT BIT(6) +#define LOONGSON2_MMC_IEN_CTIMEOUT BIT(7) +#define LOONGSON2_MMC_IEN_RESPCRC BIT(8) +#define LOONGSON2_MMC_IEN_BUSYEND BIT(9) + +#define LOONGSON2_MMC_IEN_ALL GENMASK(9, 0) +#define LOONGSON2_MMC_INT_CLEAR GENMASK(9, 0) + +/* Bitfields of DLL master lock-value register */ +#define LOONGSON2_MMC_DLLVAL_DONE BIT(8) + +/* Bitfields of DLL control register */ +#define LOONGSON2_MMC_DLLCTL_TIME GENMASK(7, 0) +#define LOONGSON2_MMC_DLLCTL_INCRE GENMASK(15, 8) +#define LOONGSON2_MMC_DLLCTL_START GENMASK(23, 16) +#define LOONGSON2_MMC_DLLCTL_CLK_MODE BIT(24) +#define LOONGSON2_MMC_DLLCTL_START_BIT BIT(25) +#define LOONGSON2_MMC_DLLCTL_TIME_BPASS GENMASK(29, 26) + +#define LOONGSON2_MMC_DELAY_PAD GENMASK(7, 0) +#define LOONGSON2_MMC_DELAY_RD GENMASK(15, 8) + +#define LOONGSON2_MMC_SEL_DATA BIT(0) /* 0: SDR, 1: DDR */ +#define LOONGSON2_MMC_SEL_BUS BIT(0) /* 0: EMMC, 1: SDIO */ + +/* Internal dma controller registers */ + +/* Bitfields of Global Configuration Register */ +#define LOONGSON2_MMC_DMA_64BIT_EN BIT(0) /* 1: 64 bit support */ +#define LOONGSON2_MMC_DMA_UNCOHERENT_EN BIT(1) /* 0: cache, 1: uncache */ +#define LOONGSON2_MMC_DMA_ASK_VALID BIT(2) +#define LOONGSON2_MMC_DMA_START BIT(3) /* DMA start operation */ +#define LOONGSON2_MMC_DMA_STOP BIT(4) /* DMA stop operation */ +#define LOONGSON2_MMC_DMA_CONFIG_MASK GENMASK_ULL(4, 0) /* DMA controller config bits mask */ + +/* Bitfields of ndesc_addr field of HW descriptor */ +#define LOONGSON2_MMC_DMA_DESC_EN BIT(0) /*1: The next descriptor is valid */ +#define LOONGSON2_MMC_DMA_DESC_ADDR_LOW GENMASK(31, 1) + +/* Bitfields of cmd field of HW descriptor */ +#define LOONGSON2_MMC_DMA_INT BIT(1) /* Enable DMA interrupts */ +#define LOONGSON2_MMC_DMA_DATA_DIR BIT(12) /* 1: write to device, 0: read from device */ + +#define LOONGSON2_MMC_DLLVAL_TIMEOUT_US 4000 +#define LOONGSON2_MMC_TXFULL_TIMEOUT_US 500 + +/* + * Due to a hardware design flaw, the Loongson-2K0300 may fail to recognize the + * CMD48 (SD_READ_EXTR_SINGLE) interrupt. + */ +#define LOONGSON2_MMC_CMD48_QUIRK BIT(0) + +/* Loongson-2K1000 SDIO2 DMA routing register */ +#define LS2K1000_SDIO_DMA_MASK GENMASK(17, 15) +#define LS2K1000_DMA0_CONF 0x0 +#define LS2K1000_DMA1_CONF 0x1 +#define LS2K1000_DMA2_CONF 0x2 +#define LS2K1000_DMA3_CONF 0x3 +#define LS2K1000_DMA4_CONF 0x4 + +/* Loongson-2K0500 SDIO2 DMA routing register */ +#define LS2K0500_SDIO_DMA_MASK GENMASK(15, 14) +#define LS2K0500_DMA0_CONF 0x1 +#define LS2K0500_DMA1_CONF 0x2 +#define LS2K0500_DMA2_CONF 0x3 + +enum loongson2_mmc_state { + STATE_NONE, + STATE_FINALIZE, + STATE_CMDSENT, + STATE_RSPFIN, + STATE_XFERFINISH, + STATE_XFERFINISH_RSPFIN, +}; + +struct loongson2_dma_desc { + u32 ndesc_addr; + u32 mem_addr; + u32 apb_addr; + u32 len; + u32 step_len; + u32 step_times; + u32 cmd; + u32 stats; + u32 high_ndesc_addr; + u32 high_mem_addr; + u32 reserved[2]; +} __packed; + +struct loongson2_mmc_host { + struct device *dev; + struct mmc_request *mrq; + struct regmap *regmap; + struct resource *res; + struct clk *clk; + u32 current_clk; + void *sg_cpu; + dma_addr_t sg_dma; + int dma_complete; + struct dma_chan *chan; + int cmd_is_stop; + int bus_width; + spinlock_t lock; /* Prevent races with irq handler */ + enum loongson2_mmc_state state; + const struct loongson2_mmc_pdata *pdata; +}; + +struct loongson2_mmc_pdata { + u32 flags; + const struct regmap_config *regmap_config; + void (*reorder_cmd_data)(struct loongson2_mmc_host *host, struct mmc_command *cmd); + void (*fix_data_timeout)(struct loongson2_mmc_host *host, struct mmc_command *cmd); + int (*setting_dma)(struct loongson2_mmc_host *host, struct platform_device *pdev); + int (*prepare_dma)(struct loongson2_mmc_host *host, struct mmc_data *data); + void (*release_dma)(struct loongson2_mmc_host *host, struct device *dev); +}; + +static void loongson2_mmc_send_command(struct loongson2_mmc_host *host, + struct mmc_command *cmd) +{ + u32 cctrl; + + if (cmd->data) + host->state = STATE_XFERFINISH_RSPFIN; + else if (cmd->flags & MMC_RSP_PRESENT) + host->state = STATE_RSPFIN; + else + host->state = STATE_CMDSENT; + + regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, cmd->arg); + + cctrl = FIELD_PREP(LOONGSON2_MMC_CCTL_INDEX, cmd->opcode); + cctrl |= LOONGSON2_MMC_CCTL_HOST | LOONGSON2_MMC_CCTL_START; + + if (cmd->opcode == SD_SWITCH && cmd->data) + cctrl |= LOONGSON2_MMC_CCTL_CMD6; + + if (cmd->flags & MMC_RSP_PRESENT) + cctrl |= LOONGSON2_MMC_CCTL_WAIT_RSP; + + if (cmd->flags & MMC_RSP_136) + cctrl |= LOONGSON2_MMC_CCTL_LONG_RSP; + + regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, cctrl); +} + +static int loongson2_mmc_setup_data(struct loongson2_mmc_host *host, + struct mmc_data *data) +{ + u32 dctrl; + + if ((data->blksz & 3) != 0) + return -EINVAL; + + dctrl = FIELD_PREP(LOONGSON2_MMC_DCTL_BNUM, data->blocks); + dctrl |= LOONGSON2_MMC_DCTL_START | LOONGSON2_MMC_DCTL_ENDMA; + + if (host->bus_width == MMC_BUS_WIDTH_4) + dctrl |= LOONGSON2_MMC_DCTL_WIDE; + else if (host->bus_width == MMC_BUS_WIDTH_8) + dctrl |= LOONGSON2_MMC_DCTL_8BIT_BUS; + + regmap_write(host->regmap, LOONGSON2_MMC_REG_DCTL, dctrl); + regmap_write(host->regmap, LOONGSON2_MMC_REG_BSIZE, data->blksz); + regmap_write(host->regmap, LOONGSON2_MMC_REG_TIMER, U32_MAX); + + return 0; +} + +static int loongson2_mmc_prepare_dma(struct loongson2_mmc_host *host, + struct mmc_data *data) +{ + int ret; + + if (!data) + return 0; + + ret = loongson2_mmc_setup_data(host, data); + if (ret) + return ret; + + host->dma_complete = 0; + + return host->pdata->prepare_dma(host, data); +} + +static void loongson2_mmc_send_request(struct mmc_host *mmc) +{ + int ret; + struct loongson2_mmc_host *host = mmc_priv(mmc); + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + + ret = loongson2_mmc_prepare_dma(host, cmd->data); + if (ret) { + dev_err(host->dev, "DMA data prepared failed with %d\n", ret); + cmd->error = ret; + cmd->data->error = ret; + mmc_request_done(mmc, mrq); + return; + } + + if (host->pdata->fix_data_timeout) + host->pdata->fix_data_timeout(host, cmd); + + loongson2_mmc_send_command(host, cmd); + + /* Fix deselect card */ + if (cmd->opcode == MMC_SELECT_CARD && cmd->arg == 0) { + cmd->error = 0; + mmc_request_done(mmc, mrq); + } +} + +static irqreturn_t loongson2_mmc_irq_worker(int irq, void *devid) +{ + struct loongson2_mmc_host *host = (struct loongson2_mmc_host *)devid; + struct mmc_host *mmc = mmc_from_priv(host); + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = host->cmd_is_stop ? mrq->stop : mrq->cmd; + + if (cmd->data) + dma_unmap_sg(mmc_dev(mmc), cmd->data->sg, cmd->data->sg_len, + mmc_get_dma_dir(cmd->data)); + + if (cmd->data && !cmd->error && + !cmd->data->error && !host->dma_complete) + return IRQ_HANDLED; + + /* Read response from controller. */ + regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP0, &cmd->resp[0]); + regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP1, &cmd->resp[1]); + regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP2, &cmd->resp[2]); + regmap_read(host->regmap, LOONGSON2_MMC_REG_RSP3, &cmd->resp[3]); + + /* Cleanup controller */ + regmap_write(host->regmap, LOONGSON2_MMC_REG_CARG, 0); + regmap_write(host->regmap, LOONGSON2_MMC_REG_CCTL, 0); + + 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; + loongson2_mmc_send_request(mmc); + return IRQ_HANDLED; + } + + /* If we have no data transfer we are finished here */ + if (!mrq->data) + goto request_done; + + /* Calculate the amount 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; + } + +request_done: + host->state = STATE_NONE; + host->mrq = NULL; + mmc_request_done(mmc, mrq); + return IRQ_HANDLED; +} + +static irqreturn_t loongson2_mmc_irq(int irq, void *devid) +{ + struct loongson2_mmc_host *host = (struct loongson2_mmc_host *)devid; + struct mmc_host *mmc = mmc_from_priv(host); + struct mmc_command *cmd; + unsigned long iflags; + u32 dsts, imsk; + + regmap_read(host->regmap, LOONGSON2_MMC_REG_INT, &imsk); + regmap_read(host->regmap, LOONGSON2_MMC_REG_DSTS, &dsts); + + if ((dsts & LOONGSON2_MMC_DSTS_IRQ) && + (imsk & LOONGSON2_MMC_INT_SDIOIRQ)) { + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_INT, + LOONGSON2_MMC_INT_SDIOIRQ, LOONGSON2_MMC_INT_SDIOIRQ); + + sdio_signal_irq(mmc); + return IRQ_HANDLED; + } + + spin_lock_irqsave(&host->lock, iflags); + + if (host->state == STATE_NONE || host->state == STATE_FINALIZE || !host->mrq) + goto irq_out; + + cmd = host->cmd_is_stop ? host->mrq->stop : host->mrq->cmd; + if (!cmd) + goto irq_out; + + cmd->error = 0; + + if (imsk & LOONGSON2_MMC_INT_CTIMEOUT) { + cmd->error = -ETIMEDOUT; + goto close_transfer; + } + + if (imsk & LOONGSON2_MMC_INT_CSENT) { + if (host->state == STATE_RSPFIN || host->state == STATE_CMDSENT) + goto close_transfer; + + if (host->state == STATE_XFERFINISH_RSPFIN) + host->state = STATE_XFERFINISH; + } + + if (!cmd->data) + goto irq_out; + + if (imsk & (LOONGSON2_MMC_INT_RXCRC | LOONGSON2_MMC_INT_TXCRC)) { + cmd->data->error = -EILSEQ; + goto close_transfer; + } + + if (imsk & LOONGSON2_MMC_INT_DTIMEOUT) { + cmd->data->error = -ETIMEDOUT; + goto close_transfer; + } + + if (imsk & LOONGSON2_MMC_INT_DFIN) { + if (host->state == STATE_XFERFINISH) { + host->dma_complete = 1; + goto close_transfer; + } + + if (host->state == STATE_XFERFINISH_RSPFIN) + host->state = STATE_RSPFIN; + } + +irq_out: + regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk); + spin_unlock_irqrestore(&host->lock, iflags); + return IRQ_HANDLED; + +close_transfer: + host->state = STATE_FINALIZE; + host->pdata->reorder_cmd_data(host, cmd); + regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, imsk); + spin_unlock_irqrestore(&host->lock, iflags); + return IRQ_WAKE_THREAD; +} + +static void loongson2_mmc_dll_mode_init(struct loongson2_mmc_host *host) +{ + u32 val, pad_delay, delay; + int ret; + + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_SEL, + LOONGSON2_MMC_SEL_DATA, LOONGSON2_MMC_SEL_DATA); + + val = FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME, 0xc8) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_INCRE, 0x1) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START, 0x1) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_CLK_MODE, 0x1) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_START_BIT, 0x1) + | FIELD_PREP(LOONGSON2_MMC_DLLCTL_TIME_BPASS, 0xf); + + regmap_write(host->regmap, LOONGSON2_MMC_REG_DLLCTL, val); + + ret = regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_DLLVAL, val, + (val & LOONGSON2_MMC_DLLVAL_DONE), 0, + LOONGSON2_MMC_DLLVAL_TIMEOUT_US); + if (ret < 0) + return; + + regmap_read(host->regmap, LOONGSON2_MMC_REG_DLLVAL, &val); + pad_delay = FIELD_GET(GENMASK(7, 1), val); + + delay = FIELD_PREP(LOONGSON2_MMC_DELAY_PAD, pad_delay) + | FIELD_PREP(LOONGSON2_MMC_DELAY_RD, pad_delay + 1); + + regmap_write(host->regmap, LOONGSON2_MMC_REG_DELAY, delay); +} + +static void loongson2_mmc_set_clk(struct loongson2_mmc_host *host, struct mmc_ios *ios) +{ + u32 pre; + + pre = DIV_ROUND_UP(host->current_clk, ios->clock); + if (pre > 255) + pre = 255; + + regmap_write(host->regmap, LOONGSON2_MMC_REG_PRE, pre | LOONGSON2_MMC_PRE_EN); + + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL, + LOONGSON2_MMC_CTL_ENCLK, LOONGSON2_MMC_CTL_ENCLK); + + /* EMMC DLL mode setting */ + if (ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_DDR52) + loongson2_mmc_dll_mode_init(host); +} + +static void loongson2_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct loongson2_mmc_host *host = mmc_priv(mmc); + int ret; + + if (ios->power_mode == MMC_POWER_UP) { + if (!IS_ERR(mmc->supply.vmmc)) { + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); + if (ret) { + dev_err(host->dev, "failed to enable vmmc regulator\n"); + return; /* return, if failed turn on vmmc */ + } + } + regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_RESET); + mdelay(10); + regmap_write(host->regmap, LOONGSON2_MMC_REG_CTL, LOONGSON2_MMC_CTL_EXTCLK); + regmap_write(host->regmap, LOONGSON2_MMC_REG_INT, LOONGSON2_MMC_IEN_ALL); + regmap_write(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_CLEAR); + } else if (ios->power_mode == MMC_POWER_OFF) { + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_CTL, + LOONGSON2_MMC_CTL_RESET, LOONGSON2_MMC_CTL_RESET); + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + return; + } + + loongson2_mmc_set_clk(host, ios); + + host->bus_width = ios->bus_width; +} + +static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct loongson2_mmc_host *host = mmc_priv(mmc); + + if ((host->pdata->flags & LOONGSON2_MMC_CMD48_QUIRK) && + mrq->cmd->opcode == SD_READ_EXTR_SINGLE) { + mmc_request_done(mmc, mrq); + return; + } + + host->cmd_is_stop = 0; + host->mrq = mrq; + loongson2_mmc_send_request(mmc); +} + +static void loongson2_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct loongson2_mmc_host *host = mmc_priv(mmc); + + regmap_update_bits(host->regmap, LOONGSON2_MMC_REG_IEN, LOONGSON2_MMC_INT_SDIOIRQ, enable); +} + +static void loongson2_mmc_ack_sdio_irq(struct mmc_host *mmc) +{ + loongson2_mmc_enable_sdio_irq(mmc, 1); +} + +static struct mmc_host_ops loongson2_mmc_ops = { + .request = loongson2_mmc_request, + .set_ios = loongson2_mmc_set_ios, + .get_ro = mmc_gpio_get_ro, + .get_cd = mmc_gpio_get_cd, + .enable_sdio_irq = loongson2_mmc_enable_sdio_irq, + .ack_sdio_irq = loongson2_mmc_ack_sdio_irq, +}; + +static const struct regmap_config ls2k0500_mmc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LOONGSON2_MMC_REG_IEN, +}; + +static int loongson2_reorder_cmd_list[] = { SD_APP_SEND_SCR, SD_APP_SEND_NUM_WR_BLKS, + SD_APP_SD_STATUS, MMC_SEND_WRITE_PROT, SD_SWITCH }; + +/* + * According to SD spec, ACMD13, ACMD22, ACMD51 and CMD30 + * response datas has different byte order with usual data packets. + * However sdio controller will send these datas in usual data format, + * so we need to adjust these datas to a protocol consistent byte order. + */ +static void ls2k0500_mmc_reorder_cmd_data(struct loongson2_mmc_host *host, + struct mmc_command *cmd) +{ + struct scatterlist *sg; + u32 *data; + int i, j; + + if (mmc_cmd_type(cmd) != MMC_CMD_ADTC) + return; + + for (i = 0; i < ARRAY_SIZE(loongson2_reorder_cmd_list); i++) + if (cmd->opcode == loongson2_reorder_cmd_list[i]) + break; + + if (i == ARRAY_SIZE(loongson2_reorder_cmd_list)) + return; + + for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { + data = sg_virt(&sg[i]); + for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++) + if (cmd->opcode == SD_SWITCH) + data[j] = bitrev8x4(data[j]); + else + data[j] = (__force u32)cpu_to_be32(data[j]); + } +} + +static int loongson2_mmc_prepare_external_dma(struct loongson2_mmc_host *host, + struct mmc_data *data) +{ + struct mmc_host *mmc = mmc_from_priv(host); + struct dma_slave_config dma_conf = { }; + struct dma_async_tx_descriptor *desc; + int ret; + + ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, + mmc_get_dma_dir(data)); + if (!ret) + return -ENOMEM; + + dma_conf.src_addr = host->res->start + LOONGSON2_MMC_REG_DATA, + dma_conf.dst_addr = host->res->start + LOONGSON2_MMC_REG_DATA, + dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + dma_conf.direction = !(data->flags & MMC_DATA_WRITE) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; + + dmaengine_slave_config(host->chan, &dma_conf); + desc = dmaengine_prep_slave_sg(host->chan, data->sg, data->sg_len, + dma_conf.direction, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!desc) + goto unmap_exit; + + dmaengine_submit(desc); + dma_async_issue_pending(host->chan); + + return 0; + +unmap_exit: + dma_unmap_sg(mmc_dev(mmc), data->sg, data->sg_len, mmc_get_dma_dir(data)); + return -ENOMEM; +} + +static void loongson2_mmc_release_external_dma(struct loongson2_mmc_host *host, + struct device *dev) +{ + dma_release_channel(host->chan); +} + +static int ls2k0500_mmc_set_external_dma(struct loongson2_mmc_host *host, + struct platform_device *pdev) +{ + int ret, val; + void __iomem *regs; + + regs = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + val = readl(regs); + val |= FIELD_PREP(LS2K0500_SDIO_DMA_MASK, LS2K0500_DMA2_CONF); + writel(val, regs); + + host->chan = dma_request_chan(&pdev->dev, "rx-tx"); + ret = PTR_ERR_OR_ZERO(host->chan); + if (ret) { + dev_err(&pdev->dev, "Cannot get DMA channel.\n"); + return ret; + } + + return 0; +} + +static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host, + struct platform_device *pdev) +{ + int ret, val; + void __iomem *regs; + + regs = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + val = readl(regs); + val |= FIELD_PREP(LS2K1000_SDIO_DMA_MASK, LS2K1000_DMA1_CONF); + writel(val, regs); + + host->chan = dma_request_chan(&pdev->dev, "rx-tx"); + ret = PTR_ERR_OR_ZERO(host->chan); + if (ret) { + dev_err(&pdev->dev, "Cannot get DMA channel.\n"); + return ret; + } + + return 0; +} + +static const struct regmap_config ls2k2000_mmc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = LOONGSON2_MMC_REG_RDMA_HI, +}; + +static void ls2k2000_mmc_reorder_cmd_data(struct loongson2_mmc_host *host, + struct mmc_command *cmd) +{ + struct scatterlist *sg; + u32 *data; + int i, j; + + if (cmd->opcode != SD_SWITCH || mmc_cmd_type(cmd) != MMC_CMD_ADTC) + return; + + for_each_sg(cmd->data->sg, sg, cmd->data->sg_len, i) { + data = sg_virt(&sg[i]); + for (j = 0; j < (sg_dma_len(&sg[i]) / 4); j++) + data[j] = bitrev8x4(data[j]); + } +} + +/* + * This is a controller hardware defect. Single/multiple block write commands + * must be sent after the TX FULL flag is set, otherwise a data timeout interrupt + * will occur. + */ +static void ls2k2000_mmc_fix_data_timeout(struct loongson2_mmc_host *host, + struct mmc_command *cmd) +{ + int val; + + if (cmd->opcode != MMC_WRITE_BLOCK && cmd->opcode != MMC_WRITE_MULTIPLE_BLOCK) + return; + + regmap_read_poll_timeout(host->regmap, LOONGSON2_MMC_REG_FSTS, val, + (val & LOONGSON2_MMC_FSTS_TXFULL), 0, + LOONGSON2_MMC_TXFULL_TIMEOUT_US); +} + +static int loongson2_mmc_prepare_internal_dma(struct loongson2_mmc_host *host, + struct mmc_data *data) +{ + struct loongson2_dma_desc *pdes = (struct loongson2_dma_desc *)host->sg_cpu; + struct mmc_host *mmc = mmc_from_priv(host); + dma_addr_t next_desc = host->sg_dma; + struct scatterlist *sg; + int reg_lo, reg_hi; + u64 dma_order; + int i, ret; + + ret = dma_map_sg(mmc_dev(mmc), data->sg, data->sg_len, + mmc_get_dma_dir(data)); + if (!ret) + return -ENOMEM; + + for_each_sg(data->sg, sg, data->sg_len, i) { + pdes[i].len = sg_dma_len(&sg[i]) / 4; + pdes[i].step_len = 0; + pdes[i].step_times = 1; + pdes[i].mem_addr = lower_32_bits(sg_dma_address(&sg[i])); + pdes[i].high_mem_addr = upper_32_bits(sg_dma_address(&sg[i])); + pdes[i].apb_addr = host->res->start + LOONGSON2_MMC_REG_DATA; + pdes[i].cmd = LOONGSON2_MMC_DMA_INT; + + if (data->flags & MMC_DATA_READ) { + reg_lo = LOONGSON2_MMC_REG_RDMA_LO; + reg_hi = LOONGSON2_MMC_REG_RDMA_HI; + } else { + pdes[i].cmd |= LOONGSON2_MMC_DMA_DATA_DIR; + reg_lo = LOONGSON2_MMC_REG_WDMA_LO; + reg_hi = LOONGSON2_MMC_REG_WDMA_HI; + } + + next_desc += sizeof(struct loongson2_dma_desc); + pdes[i].ndesc_addr = lower_32_bits(next_desc) | + LOONGSON2_MMC_DMA_DESC_EN; + pdes[i].high_ndesc_addr = upper_32_bits(next_desc); + } + + /* Setting the last descriptor enable bit */ + pdes[i - 1].ndesc_addr &= ~LOONGSON2_MMC_DMA_DESC_EN; + + dma_order = (host->sg_dma & ~LOONGSON2_MMC_DMA_CONFIG_MASK) | + LOONGSON2_MMC_DMA_64BIT_EN | + LOONGSON2_MMC_DMA_START; + + regmap_write(host->regmap, reg_hi, upper_32_bits(dma_order)); + regmap_write(host->regmap, reg_lo, lower_32_bits(dma_order)); + + return 0; +} + +static int ls2k2000_mmc_set_internal_dma(struct loongson2_mmc_host *host, + struct platform_device *pdev) +{ + host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, + &host->sg_dma, GFP_KERNEL); + if (!host->sg_cpu) + return -ENOMEM; + + return 0; +} + +static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host, + struct device *dev) +{ + dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); +} + +static struct loongson2_mmc_pdata ls2k0300_mmc_pdata = { + .flags = LOONGSON2_MMC_CMD48_QUIRK, + .regmap_config = &ls2k2000_mmc_regmap_config, + .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data, + .fix_data_timeout = ls2k2000_mmc_fix_data_timeout, + .setting_dma = ls2k2000_mmc_set_internal_dma, + .prepare_dma = loongson2_mmc_prepare_internal_dma, + .release_dma = loongson2_mmc_release_internal_dma, +}; + +static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = { + .flags = 0, + .regmap_config = &ls2k0500_mmc_regmap_config, + .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, + .setting_dma = ls2k0500_mmc_set_external_dma, + .prepare_dma = loongson2_mmc_prepare_external_dma, + .release_dma = loongson2_mmc_release_external_dma, +}; + +static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = { + .flags = 0, + .regmap_config = &ls2k0500_mmc_regmap_config, + .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, + .setting_dma = ls2k1000_mmc_set_external_dma, + .prepare_dma = loongson2_mmc_prepare_external_dma, + .release_dma = loongson2_mmc_release_external_dma, +}; + +static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = { + .flags = 0, + .regmap_config = &ls2k2000_mmc_regmap_config, + .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data, + .fix_data_timeout = ls2k2000_mmc_fix_data_timeout, + .setting_dma = ls2k2000_mmc_set_internal_dma, + .prepare_dma = loongson2_mmc_prepare_internal_dma, + .release_dma = loongson2_mmc_release_internal_dma, +}; + +static int loongson2_mmc_resource_request(struct platform_device *pdev, + struct loongson2_mmc_host *host) +{ + struct device *dev = &pdev->dev; + void __iomem *base; + int ret, irq; + + base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->res); + if (IS_ERR(base)) + return PTR_ERR(base); + + host->regmap = devm_regmap_init_mmio(dev, base, host->pdata->regmap_config); + if (IS_ERR(host->regmap)) + return PTR_ERR(host->regmap); + + host->clk = devm_clk_get_optional_enabled(dev, NULL); + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); + + if (host->clk) { + ret = devm_clk_rate_exclusive_get(dev, host->clk); + if (ret) + return ret; + + host->current_clk = clk_get_rate(host->clk); + } else { + /* For ACPI, the clock is accessed via the clock-frequency attribute. */ + device_property_read_u32(dev, "clock-frequency", &host->current_clk); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, loongson2_mmc_irq, + loongson2_mmc_irq_worker, + IRQF_ONESHOT, "loongson2-mmc", host); + if (ret) + return ret; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (ret) + return ret; + + return host->pdata->setting_dma(host, pdev); +} + +static int loongson2_mmc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct loongson2_mmc_host *host; + struct mmc_host *mmc; + int ret; + + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); + if (!mmc) + return -ENOMEM; + + platform_set_drvdata(pdev, mmc); + + host = mmc_priv(mmc); + host->state = STATE_NONE; + spin_lock_init(&host->lock); + + host->pdata = device_get_match_data(dev); + if (!host->pdata) + return dev_err_probe(dev, -EINVAL, "Failed to get match data\n"); + + ret = loongson2_mmc_resource_request(pdev, host); + if (ret) + return dev_err_probe(dev, ret, "Failed to request resource\n"); + + mmc->ops = &loongson2_mmc_ops; + mmc->f_min = DIV_ROUND_UP(host->current_clk, 256); + mmc->f_max = host->current_clk; + mmc->max_blk_count = 4095; + mmc->max_blk_size = 4095; + mmc->max_req_size = mmc->max_blk_count * mmc->max_blk_size; + mmc->max_segs = 1; + mmc->max_seg_size = mmc->max_req_size; + + /* Process SDIO IRQs through the sdio_irq_work. */ + if (mmc->caps & MMC_CAP_SDIO_IRQ) + mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; + + ret = mmc_regulator_get_supply(mmc); + if (ret || mmc->ocr_avail == 0) { + dev_warn(dev, "Can't get voltage, defaulting to 3.3V\n"); + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + } + + ret = mmc_of_parse(mmc); + if (ret) { + dev_err(dev, "Failed to parse device node\n"); + goto free_dma; + } + + ret = mmc_add_host(mmc); + if (ret) { + dev_err(dev, "Failed to add mmc host\n"); + goto free_dma; + } + + return 0; + +free_dma: + host->pdata->release_dma(host, dev); + return ret; +} + +static void loongson2_mmc_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct loongson2_mmc_host *host = mmc_priv(mmc); + + mmc_remove_host(mmc); + host->pdata->release_dma(host, &pdev->dev); +} + +static const struct of_device_id loongson2_mmc_of_ids[] = { + { .compatible = "loongson,ls2k0300-mmc", .data = &ls2k0300_mmc_pdata }, + { .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata }, + { .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata }, + { .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata }, + { }, +}; +MODULE_DEVICE_TABLE(of, loongson2_mmc_of_ids); + +static int loongson2_mmc_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct loongson2_mmc_host *host = mmc_priv(mmc); + + clk_disable_unprepare(host->clk); + + return 0; +} + +static int loongson2_mmc_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct loongson2_mmc_host *host = mmc_priv(mmc); + + return clk_prepare_enable(host->clk); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(loongson2_mmc_pm_ops, loongson2_mmc_suspend, loongson2_mmc_resume); + +static struct platform_driver loongson2_mmc_driver = { + .driver = { + .name = "loongson2-mmc", + .of_match_table = loongson2_mmc_of_ids, + .pm = pm_ptr(&loongson2_mmc_pm_ops), + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, + .probe = loongson2_mmc_probe, + .remove = loongson2_mmc_remove, +}; + +module_platform_driver(loongson2_mmc_driver); + +MODULE_DESCRIPTION("Loongson-2K SD/SDIO/eMMC Interface driver"); +MODULE_AUTHOR("Loongson Technology Corporation Limited"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/meson-mx-sdhc-clkc.c b/drivers/mmc/host/meson-mx-sdhc-clkc.c index cbd17a596cd2..6d619bd0a8dc 100644 --- a/drivers/mmc/host/meson-mx-sdhc-clkc.c +++ b/drivers/mmc/host/meson-mx-sdhc-clkc.c @@ -84,10 +84,8 @@ static int meson_mx_sdhc_gate_clk_hw_register(struct device *dev, return ret; clk_bulk_data[bulk_index].clk = devm_clk_hw_get_clk(dev, hw, name_suffix); - if (IS_ERR(clk_bulk_data[bulk_index].clk)) - return PTR_ERR(clk_bulk_data[bulk_index].clk); - return 0; + return PTR_ERR_OR_ZERO(clk_bulk_data[bulk_index].clk); } int meson_mx_sdhc_register_clkc(struct device *dev, void __iomem *base, diff --git a/drivers/mmc/host/meson-mx-sdhc-mmc.c b/drivers/mmc/host/meson-mx-sdhc-mmc.c index b4e56ccffca2..fb49ea71289e 100644 --- a/drivers/mmc/host/meson-mx-sdhc-mmc.c +++ b/drivers/mmc/host/meson-mx-sdhc-mmc.c @@ -757,11 +757,6 @@ static void meson_mx_sdhc_init_hw(struct mmc_host *mmc) regmap_write(host->regmap, MESON_SDHC_ISTA, MESON_SDHC_ISTA_ALL_IRQS); } -static void meason_mx_mmc_free_host(void *data) -{ - mmc_free_host(data); -} - static int meson_mx_sdhc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -770,16 +765,10 @@ static int meson_mx_sdhc_probe(struct platform_device *pdev) void __iomem *base; int ret, irq; - mmc = mmc_alloc_host(sizeof(*host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; - ret = devm_add_action_or_reset(dev, meason_mx_mmc_free_host, mmc); - if (ret) { - dev_err(dev, "Failed to register mmc_free_host action\n"); - return ret; - } - host = mmc_priv(mmc); host->mmc = mmc; diff --git a/drivers/mmc/host/meson-mx-sdio.c b/drivers/mmc/host/meson-mx-sdio.c index ad351805eed4..5921e2cb2180 100644 --- a/drivers/mmc/host/meson-mx-sdio.c +++ b/drivers/mmc/host/meson-mx-sdio.c @@ -19,6 +19,7 @@ #include <linux/ioport.h> #include <linux/platform_device.h> #include <linux/of_platform.h> +#include <linux/regmap.h> #include <linux/timer.h> #include <linux/types.h> @@ -98,17 +99,16 @@ #define MESON_MX_SDIO_RESPONSE_CRC16_BITS (16 - 1) #define MESON_MX_SDIO_MAX_SLOTS 3 +struct meson_mx_mmc_host_clkc { + struct clk_divider cfg_div; + struct clk_fixed_factor fixed_div2; +}; + struct meson_mx_mmc_host { struct device *controller_dev; - struct clk *parent_clk; - struct clk *core_clk; - struct clk_divider cfg_div; struct clk *cfg_div_clk; - struct clk_fixed_factor fixed_factor; - struct clk *fixed_factor_clk; - - void __iomem *base; + struct regmap *regmap; int irq; spinlock_t irq_lock; @@ -122,22 +122,10 @@ struct meson_mx_mmc_host { int error; }; -static void meson_mx_mmc_mask_bits(struct mmc_host *mmc, char reg, u32 mask, - u32 val) -{ - struct meson_mx_mmc_host *host = mmc_priv(mmc); - u32 regval; - - regval = readl(host->base + reg); - regval &= ~mask; - regval |= (val & mask); - - writel(regval, host->base + reg); -} - static void meson_mx_mmc_soft_reset(struct meson_mx_mmc_host *host) { - writel(MESON_MX_SDIO_IRQC_SOFT_RESET, host->base + MESON_MX_SDIO_IRQC); + regmap_write(host->regmap, MESON_MX_SDIO_IRQC, + MESON_MX_SDIO_IRQC_SOFT_RESET); udelay(2); } @@ -158,7 +146,7 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc, struct meson_mx_mmc_host *host = mmc_priv(mmc); unsigned int pack_size; unsigned long irqflags, timeout; - u32 mult, send = 0, ext = 0; + u32 send = 0, ext = 0; host->cmd = cmd; @@ -215,25 +203,22 @@ static void meson_mx_mmc_start_cmd(struct mmc_host *mmc, spin_lock_irqsave(&host->irq_lock, irqflags); - mult = readl(host->base + MESON_MX_SDIO_MULT); - mult &= ~MESON_MX_SDIO_MULT_PORT_SEL_MASK; - mult |= FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK, host->slot_id); - mult |= BIT(31); - writel(mult, host->base + MESON_MX_SDIO_MULT); + regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT, + MESON_MX_SDIO_MULT_PORT_SEL_MASK | BIT(31), + FIELD_PREP(MESON_MX_SDIO_MULT_PORT_SEL_MASK, + host->slot_id) | BIT(31)); /* enable the CMD done interrupt */ - meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQC, - MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN, - MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN); + regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQC, + MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN); /* clear pending interrupts */ - meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_IRQS, - MESON_MX_SDIO_IRQS_CMD_INT, - MESON_MX_SDIO_IRQS_CMD_INT); + regmap_set_bits(host->regmap, MESON_MX_SDIO_IRQS, + MESON_MX_SDIO_IRQS_CMD_INT); - writel(cmd->arg, host->base + MESON_MX_SDIO_ARGU); - writel(ext, host->base + MESON_MX_SDIO_EXT); - writel(send, host->base + MESON_MX_SDIO_SEND); + regmap_write(host->regmap, MESON_MX_SDIO_ARGU, cmd->arg); + regmap_write(host->regmap, MESON_MX_SDIO_EXT, ext); + regmap_write(host->regmap, MESON_MX_SDIO_SEND, send); spin_unlock_irqrestore(&host->irq_lock, irqflags); @@ -263,14 +248,13 @@ static void meson_mx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->bus_width) { case MMC_BUS_WIDTH_1: - meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF, - MESON_MX_SDIO_CONF_BUS_WIDTH, 0); + regmap_clear_bits(host->regmap, MESON_MX_SDIO_CONF, + MESON_MX_SDIO_CONF_BUS_WIDTH); break; case MMC_BUS_WIDTH_4: - meson_mx_mmc_mask_bits(mmc, MESON_MX_SDIO_CONF, - MESON_MX_SDIO_CONF_BUS_WIDTH, - MESON_MX_SDIO_CONF_BUS_WIDTH); + regmap_set_bits(host->regmap, MESON_MX_SDIO_CONF, + MESON_MX_SDIO_CONF_BUS_WIDTH); break; case MMC_BUS_WIDTH_8: @@ -351,8 +335,8 @@ static void meson_mx_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; if (mrq->data) - writel(sg_dma_address(mrq->data->sg), - host->base + MESON_MX_SDIO_ADDR); + regmap_write(host->regmap, MESON_MX_SDIO_ADDR, + sg_dma_address(mrq->data->sg)); if (mrq->sbc) meson_mx_mmc_start_cmd(mmc, mrq->sbc); @@ -364,24 +348,26 @@ static void meson_mx_mmc_read_response(struct mmc_host *mmc, struct mmc_command *cmd) { struct meson_mx_mmc_host *host = mmc_priv(mmc); - u32 mult; - int i, resp[4]; + unsigned int i, resp[4]; - mult = readl(host->base + MESON_MX_SDIO_MULT); - mult |= MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX; - mult &= ~MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK; - mult |= FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK, 0); - writel(mult, host->base + MESON_MX_SDIO_MULT); + regmap_update_bits(host->regmap, MESON_MX_SDIO_MULT, + MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX | + MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK, + MESON_MX_SDIO_MULT_WR_RD_OUT_INDEX | + FIELD_PREP(MESON_MX_SDIO_MULT_RESP_READ_INDEX_MASK, + 0)); if (cmd->flags & MMC_RSP_136) { for (i = 0; i <= 3; i++) - resp[3 - i] = readl(host->base + MESON_MX_SDIO_ARGU); + regmap_read(host->regmap, MESON_MX_SDIO_ARGU, + &resp[3 - i]); + cmd->resp[0] = (resp[0] << 8) | ((resp[1] >> 24) & 0xff); cmd->resp[1] = (resp[1] << 8) | ((resp[2] >> 24) & 0xff); cmd->resp[2] = (resp[2] << 8) | ((resp[3] >> 24) & 0xff); cmd->resp[3] = (resp[3] << 8); } else if (cmd->flags & MMC_RSP_PRESENT) { - cmd->resp[0] = readl(host->base + MESON_MX_SDIO_ARGU); + regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &cmd->resp[0]); } } @@ -422,8 +408,8 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data) spin_lock(&host->irq_lock); - irqs = readl(host->base + MESON_MX_SDIO_IRQS); - send = readl(host->base + MESON_MX_SDIO_SEND); + regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs); + regmap_read(host->regmap, MESON_MX_SDIO_SEND, &send); if (irqs & MESON_MX_SDIO_IRQS_CMD_INT) ret = meson_mx_mmc_process_cmd_irq(host, irqs, send); @@ -431,7 +417,7 @@ static irqreturn_t meson_mx_mmc_irq(int irq, void *data) ret = IRQ_HANDLED; /* finally ACK all pending interrupts */ - writel(irqs, host->base + MESON_MX_SDIO_IRQS); + regmap_write(host->regmap, MESON_MX_SDIO_IRQS, irqs); spin_unlock(&host->irq_lock); @@ -446,12 +432,11 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data) if (WARN_ON(!cmd)) return IRQ_HANDLED; - del_timer_sync(&host->cmd_timeout); + timer_delete_sync(&host->cmd_timeout); if (cmd->data) { dma_unmap_sg(mmc_dev(host->mmc), cmd->data->sg, - cmd->data->sg_len, - mmc_get_dma_dir(cmd->data)); + cmd->data->sg_len, mmc_get_dma_dir(cmd->data)); cmd->data->bytes_xfered = cmd->data->blksz * cmd->data->blocks; } @@ -467,16 +452,16 @@ static irqreturn_t meson_mx_mmc_irq_thread(int irq, void *irq_data) static void meson_mx_mmc_timeout(struct timer_list *t) { - struct meson_mx_mmc_host *host = from_timer(host, t, cmd_timeout); + struct meson_mx_mmc_host *host = timer_container_of(host, t, + cmd_timeout); unsigned long irqflags; - u32 irqc; + u32 irqs, argu; spin_lock_irqsave(&host->irq_lock, irqflags); /* disable the CMD interrupt */ - irqc = readl(host->base + MESON_MX_SDIO_IRQC); - irqc &= ~MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN; - writel(irqc, host->base + MESON_MX_SDIO_IRQC); + regmap_clear_bits(host->regmap, MESON_MX_SDIO_IRQC, + MESON_MX_SDIO_IRQC_ARC_CMD_INT_EN); spin_unlock_irqrestore(&host->irq_lock, irqflags); @@ -487,10 +472,12 @@ static void meson_mx_mmc_timeout(struct timer_list *t) if (!host->cmd) return; + regmap_read(host->regmap, MESON_MX_SDIO_IRQS, &irqs); + regmap_read(host->regmap, MESON_MX_SDIO_ARGU, &argu); + dev_dbg(mmc_dev(host->mmc), "Timeout on CMD%u (IRQS = 0x%08x, ARGU = 0x%08x)\n", - host->cmd->opcode, readl(host->base + MESON_MX_SDIO_IRQS), - readl(host->base + MESON_MX_SDIO_ARGU)); + host->cmd->opcode, irqs, argu); host->cmd->error = -ETIMEDOUT; @@ -506,23 +493,30 @@ static struct mmc_host_ops meson_mx_mmc_ops = { static struct platform_device *meson_mx_mmc_slot_pdev(struct device *parent) { - struct device_node *slot_node; - struct platform_device *pdev; + struct platform_device *pdev = NULL; + + for_each_available_child_of_node_scoped(parent->of_node, slot_node) { + if (!of_device_is_compatible(slot_node, "mmc-slot")) + continue; + + /* + * TODO: the MMC core framework currently does not support + * controllers with multiple slots properly. So we only + * register the first slot for now. + */ + if (pdev) { + dev_warn(parent, + "more than one 'mmc-slot' compatible child found - using the first one and ignoring all subsequent ones\n"); + break; + } - /* - * TODO: the MMC core framework currently does not support - * controllers with multiple slots properly. So we only register - * the first slot for now - */ - slot_node = of_get_compatible_child(parent->of_node, "mmc-slot"); - if (!slot_node) { - dev_warn(parent, "no 'mmc-slot' sub-node found\n"); - return ERR_PTR(-ENOENT); + pdev = of_platform_device_create(slot_node, NULL, parent); + if (!pdev) + dev_err(parent, + "Failed to create platform device for mmc-slot node '%pOF'\n", + slot_node); } - pdev = of_platform_device_create(slot_node, NULL, parent); - of_node_put(slot_node); - return pdev; } @@ -532,16 +526,14 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host) struct device *slot_dev = mmc_dev(mmc); int ret; - if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id)) { - dev_err(slot_dev, "missing 'reg' property\n"); - return -EINVAL; - } + if (of_property_read_u32(slot_dev->of_node, "reg", &host->slot_id)) + return dev_err_probe(slot_dev, -EINVAL, + "missing 'reg' property\n"); - if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS) { - dev_err(slot_dev, "invalid 'reg' property value %d\n", - host->slot_id); - return -EINVAL; - } + if (host->slot_id >= MESON_MX_SDIO_MAX_SLOTS) + return dev_err_probe(slot_dev, -EINVAL, + "invalid 'reg' property value %d\n", + host->slot_id); /* Get regulators and the supported OCR mask */ ret = mmc_regulator_get_supply(mmc); @@ -560,8 +552,7 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host) /* Get the min and max supported clock rates */ mmc->f_min = clk_round_rate(host->cfg_div_clk, 1); - mmc->f_max = clk_round_rate(host->cfg_div_clk, - clk_get_rate(host->parent_clk)); + mmc->f_max = clk_round_rate(host->cfg_div_clk, ULONG_MAX); mmc->caps |= MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY; mmc->ops = &meson_mx_mmc_ops; @@ -577,70 +568,89 @@ static int meson_mx_mmc_add_host(struct meson_mx_mmc_host *host) return 0; } -static int meson_mx_mmc_register_clks(struct meson_mx_mmc_host *host) +static struct clk *meson_mx_mmc_register_clk(struct device *dev, + void __iomem *base) { - struct clk_init_data init; - const char *clk_div_parent, *clk_fixed_factor_parent; - - clk_fixed_factor_parent = __clk_get_name(host->parent_clk); - init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL, - "%s#fixed_factor", - dev_name(host->controller_dev)); - if (!init.name) - return -ENOMEM; + const char *fixed_div2_name, *cfg_div_name; + struct meson_mx_mmc_host_clkc *host_clkc; + struct clk *clk; + int ret; - init.ops = &clk_fixed_factor_ops; - init.flags = 0; - init.parent_names = &clk_fixed_factor_parent; - init.num_parents = 1; - host->fixed_factor.div = 2; - host->fixed_factor.mult = 1; - host->fixed_factor.hw.init = &init; - - host->fixed_factor_clk = devm_clk_register(host->controller_dev, - &host->fixed_factor.hw); - if (WARN_ON(IS_ERR(host->fixed_factor_clk))) - return PTR_ERR(host->fixed_factor_clk); - - clk_div_parent = __clk_get_name(host->fixed_factor_clk); - init.name = devm_kasprintf(host->controller_dev, GFP_KERNEL, - "%s#div", dev_name(host->controller_dev)); - if (!init.name) - return -ENOMEM; + /* use a dedicated memory allocation for the clock controller to + * prevent use-after-free as meson_mx_mmc_host is free'd before + * dev (controller dev, not mmc_host->dev) is free'd. + */ + host_clkc = devm_kzalloc(dev, sizeof(*host_clkc), GFP_KERNEL); + if (!host_clkc) + return ERR_PTR(-ENOMEM); + + fixed_div2_name = devm_kasprintf(dev, GFP_KERNEL, "%s#fixed_div2", + dev_name(dev)); + if (!fixed_div2_name) + return ERR_PTR(-ENOMEM); + + host_clkc->fixed_div2.div = 2; + host_clkc->fixed_div2.mult = 1; + host_clkc->fixed_div2.hw.init = CLK_HW_INIT_FW_NAME(fixed_div2_name, + "clkin", + &clk_fixed_factor_ops, + 0); + ret = devm_clk_hw_register(dev, &host_clkc->fixed_div2.hw); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to register %s clock\n", + fixed_div2_name); + + cfg_div_name = devm_kasprintf(dev, GFP_KERNEL, "%s#div", dev_name(dev)); + if (!cfg_div_name) + return ERR_PTR(-ENOMEM); + + host_clkc->cfg_div.reg = base + MESON_MX_SDIO_CONF; + host_clkc->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT; + host_clkc->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH; + host_clkc->cfg_div.hw.init = CLK_HW_INIT_HW(cfg_div_name, + &host_clkc->fixed_div2.hw, + &clk_divider_ops, + CLK_DIVIDER_ALLOW_ZERO); + ret = devm_clk_hw_register(dev, &host_clkc->cfg_div.hw); + if (ret) + return dev_err_ptr_probe(dev, ret, + "Failed to register %s clock\n", + cfg_div_name); - init.ops = &clk_divider_ops; - init.flags = CLK_SET_RATE_PARENT; - init.parent_names = &clk_div_parent; - init.num_parents = 1; - host->cfg_div.reg = host->base + MESON_MX_SDIO_CONF; - host->cfg_div.shift = MESON_MX_SDIO_CONF_CMD_CLK_DIV_SHIFT; - host->cfg_div.width = MESON_MX_SDIO_CONF_CMD_CLK_DIV_WIDTH; - host->cfg_div.hw.init = &init; - host->cfg_div.flags = CLK_DIVIDER_ALLOW_ZERO; - - host->cfg_div_clk = devm_clk_register(host->controller_dev, - &host->cfg_div.hw); - if (WARN_ON(IS_ERR(host->cfg_div_clk))) - return PTR_ERR(host->cfg_div_clk); + clk = devm_clk_hw_get_clk(dev, &host_clkc->cfg_div.hw, "cfg_div_clk"); + if (IS_ERR(clk)) + return dev_err_ptr_probe(dev, PTR_ERR(clk), + "Failed to get the cfg_div clock\n"); - return 0; + return clk; } static int meson_mx_mmc_probe(struct platform_device *pdev) { + const struct regmap_config meson_mx_sdio_regmap_config = { + .reg_bits = 8, + .val_bits = 32, + .reg_stride = 4, + .max_register = MESON_MX_SDIO_EXT, + }; struct platform_device *slot_pdev; struct mmc_host *mmc; struct meson_mx_mmc_host *host; + struct clk *core_clk; + void __iomem *base; int ret, irq; u32 conf; + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + slot_pdev = meson_mx_mmc_slot_pdev(&pdev->dev); if (!slot_pdev) return -ENODEV; - else if (IS_ERR(slot_pdev)) - return PTR_ERR(slot_pdev); - mmc = mmc_alloc_host(sizeof(*host), &slot_pdev->dev); + mmc = devm_mmc_alloc_host(&slot_pdev->dev, sizeof(*host)); if (!mmc) { ret = -ENOMEM; goto error_unregister_slot_pdev; @@ -655,51 +665,48 @@ static int meson_mx_mmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); - host->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto error_free_mmc; + host->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &meson_mx_sdio_regmap_config); + if (IS_ERR(host->regmap)) { + ret = dev_err_probe(host->controller_dev, PTR_ERR(host->regmap), + "Failed to initialize regmap\n"); + goto error_unregister_slot_pdev; } irq = platform_get_irq(pdev, 0); if (irq < 0) { ret = irq; - goto error_free_mmc; + goto error_unregister_slot_pdev; } ret = devm_request_threaded_irq(host->controller_dev, irq, meson_mx_mmc_irq, meson_mx_mmc_irq_thread, IRQF_ONESHOT, NULL, host); - if (ret) - goto error_free_mmc; - - host->core_clk = devm_clk_get(host->controller_dev, "core"); - if (IS_ERR(host->core_clk)) { - ret = PTR_ERR(host->core_clk); - goto error_free_mmc; + if (ret) { + dev_err_probe(host->controller_dev, ret, + "Failed to request IRQ\n"); + goto error_unregister_slot_pdev; } - host->parent_clk = devm_clk_get(host->controller_dev, "clkin"); - if (IS_ERR(host->parent_clk)) { - ret = PTR_ERR(host->parent_clk); - goto error_free_mmc; + core_clk = devm_clk_get_enabled(host->controller_dev, "core"); + if (IS_ERR(core_clk)) { + ret = dev_err_probe(host->controller_dev, PTR_ERR(core_clk), + "Failed to get and enable 'core' clock\n"); + goto error_unregister_slot_pdev; } - ret = meson_mx_mmc_register_clks(host); - if (ret) - goto error_free_mmc; - - ret = clk_prepare_enable(host->core_clk); - if (ret) { - dev_err(host->controller_dev, "Failed to enable core clock\n"); - goto error_free_mmc; + host->cfg_div_clk = meson_mx_mmc_register_clk(&pdev->dev, base); + if (IS_ERR(host->cfg_div_clk)) { + ret = PTR_ERR(host->cfg_div_clk); + goto error_unregister_slot_pdev; } ret = clk_prepare_enable(host->cfg_div_clk); if (ret) { - dev_err(host->controller_dev, "Failed to enable MMC clock\n"); - goto error_disable_core_clk; + dev_err_probe(host->controller_dev, ret, + "Failed to enable MMC (cfg div) clock\n"); + goto error_unregister_slot_pdev; } conf = 0; @@ -707,22 +714,18 @@ static int meson_mx_mmc_probe(struct platform_device *pdev) conf |= FIELD_PREP(MESON_MX_SDIO_CONF_M_ENDIAN_MASK, 0x3); conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_NWR_MASK, 0x2); conf |= FIELD_PREP(MESON_MX_SDIO_CONF_WRITE_CRC_OK_STATUS_MASK, 0x2); - writel(conf, host->base + MESON_MX_SDIO_CONF); + regmap_write(host->regmap, MESON_MX_SDIO_CONF, conf); meson_mx_mmc_soft_reset(host); ret = meson_mx_mmc_add_host(host); if (ret) - goto error_disable_clks; + goto error_disable_div_clk; return 0; -error_disable_clks: +error_disable_div_clk: clk_disable_unprepare(host->cfg_div_clk); -error_disable_core_clk: - clk_disable_unprepare(host->core_clk); -error_free_mmc: - mmc_free_host(mmc); error_unregister_slot_pdev: of_platform_device_destroy(&slot_pdev->dev, NULL); return ret; @@ -733,16 +736,13 @@ static void meson_mx_mmc_remove(struct platform_device *pdev) struct meson_mx_mmc_host *host = platform_get_drvdata(pdev); struct device *slot_dev = mmc_dev(host->mmc); - del_timer_sync(&host->cmd_timeout); + timer_delete_sync(&host->cmd_timeout); mmc_remove_host(host->mmc); of_platform_device_destroy(slot_dev, NULL); clk_disable_unprepare(host->cfg_div_clk); - clk_disable_unprepare(host->core_clk); - - mmc_free_host(host->mmc); } static const struct of_device_id meson_mx_mmc_of_match[] = { diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 47443fb5eb33..b471a7795b4d 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -563,10 +563,10 @@ mmc_spi_setup_data_message(struct mmc_spi_host *host, bool multiple, bool write) * the next token (next data block, or STOP_TRAN). We can try to * minimize I/O ops by using a single read to collect end-of-busy. */ - if (multiple || write) { + if (write) { t = &host->early_status; memset(t, 0, sizeof(*t)); - t->len = write ? sizeof(scratch->status) : 1; + t->len = sizeof(scratch->status); t->tx_buf = host->ones; t->rx_buf = scratch->status; t->cs_change = 1; @@ -1185,7 +1185,7 @@ static int mmc_spi_probe(struct spi_device *spi) goto nomem; memset(ones, 0xff, MMC_SPI_BLOCKSIZE); - mmc = mmc_alloc_host(sizeof(*host), &spi->dev); + mmc = devm_mmc_alloc_host(&spi->dev, sizeof(*host)); if (!mmc) goto nomem; @@ -1236,7 +1236,7 @@ static int mmc_spi_probe(struct spi_device *spi) } /* Preallocate buffers */ - host->data = kmalloc(sizeof(*host->data), GFP_KERNEL); + host->data = kmalloc_obj(*host->data); if (!host->data) goto fail_nobuf1; @@ -1305,7 +1305,6 @@ fail_glue_init: kfree(host->data); fail_nobuf1: mmc_spi_put_pdata(spi); - mmc_free_host(mmc); nomem: kfree(ones); return status; @@ -1328,7 +1327,6 @@ static void mmc_spi_remove(struct spi_device *spi) spi->max_speed_hz = mmc->f_max; mmc_spi_put_pdata(spi); - mmc_free_host(mmc); } static const struct spi_device_id mmc_spi_dev_ids[] = { diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index b790c3c3c8f9..e500051bd572 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -2082,7 +2082,6 @@ static void mmci_enable_sdio_irq(struct mmc_host *mmc, int enable) spin_unlock_irqrestore(&host->lock, flags); if (!enable) { - pm_runtime_mark_last_busy(mmc_dev(mmc)); pm_runtime_put_autosuspend(mmc_dev(mmc)); } } @@ -2223,7 +2222,7 @@ static int mmci_probe(struct amba_device *dev, return -ENOMEM; } - mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); + mmc = devm_mmc_alloc_host(&dev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -2234,7 +2233,7 @@ static int mmci_probe(struct amba_device *dev, ret = mmci_of_parse(np, mmc); if (ret) - goto host_free; + return ret; /* * Some variant (STM32) doesn't have opendrain bit, nevertheless @@ -2242,19 +2241,15 @@ static int mmci_probe(struct amba_device *dev, */ if (!variant->opendrain) { host->pinctrl = devm_pinctrl_get(&dev->dev); - if (IS_ERR(host->pinctrl)) { - dev_err(&dev->dev, "failed to get pinctrl"); - ret = PTR_ERR(host->pinctrl); - goto host_free; - } + if (IS_ERR(host->pinctrl)) + return dev_err_probe(&dev->dev, PTR_ERR(host->pinctrl), + "failed to get pinctrl\n"); host->pins_opendrain = pinctrl_lookup_state(host->pinctrl, MMCI_PINCTRL_STATE_OPENDRAIN); - if (IS_ERR(host->pins_opendrain)) { - dev_err(mmc_dev(mmc), "Can't select opendrain pins\n"); - ret = PTR_ERR(host->pins_opendrain); - goto host_free; - } + if (IS_ERR(host->pins_opendrain)) + return dev_err_probe(&dev->dev, PTR_ERR(host->pins_opendrain), + "Can't select opendrain pins\n"); } host->hw_designer = amba_manf(dev); @@ -2263,14 +2258,12 @@ static int mmci_probe(struct amba_device *dev, dev_dbg(mmc_dev(mmc), "revision = 0x%01x\n", host->hw_revision); host->clk = devm_clk_get(&dev->dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - goto host_free; - } + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); ret = clk_prepare_enable(host->clk); if (ret) - goto host_free; + return ret; if (variant->qcom_fifo) host->get_rx_fifocnt = mmci_qcom_get_rx_fifocnt; @@ -2491,8 +2484,6 @@ static int mmci_probe(struct amba_device *dev, clk_disable: clk_disable_unprepare(host->clk); - host_free: - mmc_free_host(mmc); return ret; } @@ -2522,11 +2513,9 @@ static void mmci_remove(struct amba_device *dev) mmci_dma_release(host); clk_disable_unprepare(host->clk); - mmc_free_host(mmc); } } -#ifdef CONFIG_PM static void mmci_save(struct mmci_host *host) { unsigned long flags; @@ -2591,12 +2580,10 @@ static int mmci_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops mmci_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(mmci_runtime_suspend, mmci_runtime_resume, NULL) }; static const struct amba_id mmci_ids[] = { @@ -2685,7 +2672,7 @@ MODULE_DEVICE_TABLE(amba, mmci_ids); static struct amba_driver mmci_driver = { .drv = { .name = DRIVER_NAME, - .pm = &mmci_dev_pm_ops, + .pm = pm_ptr(&mmci_dev_pm_ops), .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = mmci_probe, diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c index 3da6112fbe39..67371389cc33 100644 --- a/drivers/mmc/host/mmci_qcom_dml.c +++ b/drivers/mmc/host/mmci_qcom_dml.c @@ -109,6 +109,7 @@ static int of_get_dml_pipe_index(struct device_node *np, const char *name) &dma_spec)) return -ENODEV; + of_node_put(dma_spec.np); if (dma_spec.args_count) return dma_spec.args[0]; diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index a12048e5de63..3dd8f232052f 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -558,41 +558,33 @@ static int moxart_probe(struct platform_device *pdev) int irq, ret; u32 i; - mmc = mmc_alloc_host(sizeof(struct moxart_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) { - dev_err(dev, "mmc_alloc_host failed\n"); - ret = -ENOMEM; - goto out_mmc; + dev_err(dev, "devm_mmc_alloc_host failed\n"); + return -ENOMEM; } ret = of_address_to_resource(node, 0, &res_mmc); - if (ret) { - dev_err(dev, "of_address_to_resource failed\n"); - goto out_mmc; - } + if (ret) + return dev_err_probe(dev, ret, + "of_address_to_resource failed\n"); irq = irq_of_parse_and_map(node, 0); - if (irq <= 0) { - dev_err(dev, "irq_of_parse_and_map failed\n"); - ret = -EINVAL; - goto out_mmc; - } + if (irq <= 0) + return dev_err_probe(dev, -EINVAL, + "irq_of_parse_and_map failed\n"); clk = devm_clk_get(dev, NULL); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - goto out_mmc; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); reg_mmc = devm_ioremap_resource(dev, &res_mmc); - if (IS_ERR(reg_mmc)) { - ret = PTR_ERR(reg_mmc); - goto out_mmc; - } + if (IS_ERR(reg_mmc)) + return PTR_ERR(reg_mmc); ret = mmc_of_parse(mmc); if (ret) - goto out_mmc; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -686,9 +678,6 @@ out: dma_release_channel(host->dma_chan_tx); if (!IS_ERR_OR_NULL(host->dma_chan_rx)) dma_release_channel(host->dma_chan_rx); -out_mmc: - if (mmc) - mmc_free_host(mmc); return ret; } @@ -707,7 +696,6 @@ static void moxart_remove(struct platform_device *pdev) writel(0, host->base + REG_POWER_CONTROL); writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF, host->base + REG_CLOCK_CONTROL); - mmc_free_host(mmc); } static const struct of_device_id moxart_mmc_match[] = { diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 345ea91629e0..b2680cc054bd 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -5,6 +5,7 @@ */ #include <linux/module.h> +#include <linux/bitfield.h> #include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> @@ -83,6 +84,7 @@ #define EMMC51_CFG0 0x204 #define EMMC50_CFG0 0x208 #define EMMC50_CFG1 0x20c +#define EMMC50_CFG2 0x21c #define EMMC50_CFG3 0x220 #define SDC_FIFO_CFG 0x228 #define CQHCI_SETTING 0x7fc @@ -201,9 +203,10 @@ #define SDC_CFG_DTOC GENMASK(31, 24) /* RW */ /* SDC_STS mask */ -#define SDC_STS_SDCBUSY BIT(0) /* RW */ -#define SDC_STS_CMDBUSY BIT(1) /* RW */ -#define SDC_STS_SWR_COMPL BIT(31) /* RW */ +#define SDC_STS_SDCBUSY BIT(0) /* RW */ +#define SDC_STS_CMDBUSY BIT(1) /* RW */ +#define SDC_STS_SPM_RESOURCE_RELEASE BIT(3) /* RW */ +#define SDC_STS_SWR_COMPL BIT(31) /* RW */ /* SDC_ADV_CFG0 mask */ #define SDC_DAT1_IRQ_TRIGGER BIT(19) /* RW */ @@ -233,7 +236,9 @@ /* MSDC_PATCH_BIT mask */ #define MSDC_PATCH_BIT_ODDSUPP BIT(1) /* RW */ +#define MSDC_PATCH_BIT_DIS_WRMON BIT(2) /* RW */ #define MSDC_PATCH_BIT_RD_DAT_SEL BIT(3) /* RW */ +#define MSDC_PATCH_BIT_DESCUP_SEL BIT(6) /* RW */ #define MSDC_INT_DAT_LATCH_CK_SEL GENMASK(9, 7) #define MSDC_CKGEN_MSDC_DLY_SEL GENMASK(14, 10) #define MSDC_PATCH_BIT_IODSSEL BIT(16) /* RW */ @@ -246,10 +251,22 @@ #define MSDC_PATCH_BIT_SPCPUSH BIT(29) /* RW */ #define MSDC_PATCH_BIT_DECRCTMO BIT(30) /* RW */ -#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */ +/* MSDC_PATCH_BIT1 mask */ +#define MSDC_PB1_WRDAT_CRC_TACNTR GENMASK(2, 0) /* RW */ +#define MSDC_PATCH_BIT1_CMDTA GENMASK(5, 3) /* RW */ #define MSDC_PB1_BUSY_CHECK_SEL BIT(7) /* RW */ #define MSDC_PATCH_BIT1_STOP_DLY GENMASK(11, 8) /* RW */ - +#define MSDC_PB1_DDR_CMD_FIX_SEL BIT(14) /* RW */ +#define MSDC_PB1_SINGLE_BURST BIT(16) /* RW */ +#define MSDC_PB1_RSVD20 GENMASK(18, 17) /* RW */ +#define MSDC_PB1_AUTO_SYNCST_CLR BIT(19) /* RW */ +#define MSDC_PB1_MARK_POP_WATER BIT(20) /* RW */ +#define MSDC_PB1_LP_DCM_EN BIT(21) /* RW */ +#define MSDC_PB1_RSVD3 BIT(22) /* RW */ +#define MSDC_PB1_AHB_GDMA_HCLK BIT(23) /* RW */ +#define MSDC_PB1_MSDC_CLK_ENFEAT GENMASK(31, 24) /* RW */ + +/* MSDC_PATCH_BIT2 mask */ #define MSDC_PATCH_BIT2_CFGRESP BIT(15) /* RW */ #define MSDC_PATCH_BIT2_CFGCRCSTS BIT(28) /* RW */ #define MSDC_PB2_SUPPORT_64G BIT(1) /* RW */ @@ -291,7 +308,10 @@ /* EMMC50_CFG1 mask */ #define EMMC50_CFG1_DS_CFG BIT(28) /* RW */ -#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */ +/* EMMC50_CFG2 mask */ +#define EMMC50_CFG2_AXI_SET_LEN GENMASK(27, 24) /* RW */ + +#define EMMC50_CFG3_OUTS_WR GENMASK(4, 0) /* RW */ #define SDC_FIFO_CFG_WRVALIDSEL BIT(24) /* RW */ #define SDC_FIFO_CFG_RDVALIDSEL BIT(25) /* RW */ @@ -429,6 +449,7 @@ struct mtk_mmc_compatible { bool use_internal_cd; bool support_new_tx; bool support_new_rx; + bool support_spm_res_release; }; struct msdc_tune_para { @@ -654,6 +675,25 @@ static const struct mtk_mmc_compatible mt8516_compat = { .stop_dly_sel = 3, }; +static const struct mtk_mmc_compatible mt8189_compat = { + .clk_div_bits = 12, + .recheck_sdio_irq = false, + .hs400_tune = false, + .needs_top_base = true, + .pad_tune_reg = MSDC_PAD_TUNE0, + .async_fifo = true, + .data_tune = false, + .busy_check = true, + .stop_clk_fix = true, + .stop_dly_sel = 3, + .pop_en_cnt = 8, + .enhance_rx = true, + .support_64g = true, + .support_new_tx = false, + .support_new_rx = false, + .support_spm_res_release = true, +}; + static const struct mtk_mmc_compatible mt8196_compat = { .clk_div_bits = 12, .recheck_sdio_irq = false, @@ -684,6 +724,7 @@ static const struct of_device_id msdc_of_ids[] = { { .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat}, { .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat}, { .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat}, + { .compatible = "mediatek,mt8189-mmc", .data = &mt8189_compat}, { .compatible = "mediatek,mt8196-mmc", .data = &mt8196_compat}, { .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat}, @@ -827,12 +868,18 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma, static void msdc_prepare_data(struct msdc_host *host, struct mmc_data *data) { if (!(data->host_cookie & MSDC_PREPARE_FLAG)) { - data->host_cookie |= MSDC_PREPARE_FLAG; data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len, mmc_get_dma_dir(data)); + if (data->sg_count) + data->host_cookie |= MSDC_PREPARE_FLAG; } } +static bool msdc_data_prepared(struct mmc_data *data) +{ + return data->host_cookie & MSDC_PREPARE_FLAG; +} + static void msdc_unprepare_data(struct msdc_host *host, struct mmc_data *data) { if (data->host_cookie & MSDC_ASYNC_FLAG) @@ -927,15 +974,15 @@ static int msdc_ungate_clock(struct msdc_host *host) static void msdc_new_tx_setting(struct msdc_host *host) { + u32 val; + if (!host->top_base) return; - sdr_set_bits(host->top_base + LOOP_TEST_CONTROL, - TEST_LOOP_DSCLK_MUX_SEL); - sdr_set_bits(host->top_base + LOOP_TEST_CONTROL, - TEST_LOOP_LATCH_MUX_SEL); - sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL, - TEST_HS400_CMD_LOOP_MUX_SEL); + val = readl(host->top_base + LOOP_TEST_CONTROL); + val |= TEST_LOOP_DSCLK_MUX_SEL; + val |= TEST_LOOP_LATCH_MUX_SEL; + val &= ~TEST_HS400_CMD_LOOP_MUX_SEL; switch (host->timing) { case MMC_TIMING_LEGACY: @@ -945,19 +992,18 @@ static void msdc_new_tx_setting(struct msdc_host *host) case MMC_TIMING_UHS_SDR25: case MMC_TIMING_UHS_DDR50: case MMC_TIMING_MMC_DDR52: - sdr_clr_bits(host->top_base + LOOP_TEST_CONTROL, - LOOP_EN_SEL_CLK); + val &= ~LOOP_EN_SEL_CLK; break; case MMC_TIMING_UHS_SDR50: case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: case MMC_TIMING_MMC_HS400: - sdr_set_bits(host->top_base + LOOP_TEST_CONTROL, - LOOP_EN_SEL_CLK); + val |= LOOP_EN_SEL_CLK; break; default: break; } + writel(val, host->top_base + LOOP_TEST_CONTROL); } static void msdc_set_mclk(struct msdc_host *host, unsigned char timing, u32 hz) @@ -1190,7 +1236,7 @@ static void msdc_start_data(struct msdc_host *host, struct mmc_command *cmd, host->data = data; read = data->flags & MMC_DATA_READ; - mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); + mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT); msdc_dma_setup(host, &host->dma, data); sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask); sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1); @@ -1420,7 +1466,7 @@ static void msdc_start_command(struct msdc_host *host, WARN_ON(host->cmd); host->cmd = cmd; - mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); + mod_delayed_work(system_percpu_wq, &host->req_timeout, DAT_TIMEOUT); if (!msdc_cmd_is_ready(host, mrq, cmd)) return; @@ -1465,8 +1511,19 @@ static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(!host->hsq_en && host->mrq); host->mrq = mrq; - if (mrq->data) + if (mrq->data) { msdc_prepare_data(host, mrq->data); + if (!msdc_data_prepared(mrq->data)) { + host->mrq = NULL; + /* + * Failed to prepare DMA area, fail fast before + * starting any commands. + */ + mrq->cmd->error = -ENOSPC; + mmc_request_done(mmc_from_priv(host), mrq); + return; + } + } /* if SBC is required, we have HW option and SW option. * if HW option is enabled, and SBC does not have "special" flags, @@ -1816,7 +1873,7 @@ static irqreturn_t msdc_irq(int irq, void *dev_id) static void msdc_init_hw(struct msdc_host *host) { - u32 val; + u32 val, pb1_val, pb2_val; u32 tune_reg = host->dev_comp->pad_tune_reg; struct mmc_host *mmc = mmc_from_priv(host); @@ -1869,71 +1926,115 @@ static void msdc_init_hw(struct msdc_host *host) } writel(0, host->base + MSDC_IOCON); sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 0); - writel(0x403c0046, host->base + MSDC_PATCH_BIT); - sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1); - writel(0xffff4089, host->base + MSDC_PATCH_BIT1); - sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL); + + /* + * Patch bit 0 and 1 are completely rewritten, but for patch bit 2 + * defaults are retained and, if necessary, only some bits are fixed + * up: read the PB2 register here for later usage in this function. + */ + pb2_val = readl(host->base + MSDC_PATCH_BIT2); + + /* Enable odd number support for 8-bit data bus */ + val = MSDC_PATCH_BIT_ODDSUPP; + + /* Disable SD command register write monitor */ + val |= MSDC_PATCH_BIT_DIS_WRMON; + + /* Issue transfer done interrupt after GPD update */ + val |= MSDC_PATCH_BIT_DESCUP_SEL; + + /* Extend R1B busy detection delay (in clock cycles) */ + val |= FIELD_PREP(MSDC_PATCH_BIT_BUSYDLY, 15); + + /* Enable CRC phase timeout during data write operation */ + val |= MSDC_PATCH_BIT_DECRCTMO; + + /* Set CKGEN delay to one stage */ + val |= FIELD_PREP(MSDC_CKGEN_MSDC_DLY_SEL, 1); + + /* First MSDC_PATCH_BIT setup is done: pull the trigger! */ + writel(val, host->base + MSDC_PATCH_BIT); + + /* Set wr data, crc status, cmd response turnaround period for UHS104 */ + pb1_val = FIELD_PREP(MSDC_PB1_WRDAT_CRC_TACNTR, 1); + pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_CMDTA, 1); + pb1_val |= MSDC_PB1_DDR_CMD_FIX_SEL; + + /* Support 'single' burst type only when AXI_LEN is 0 */ + sdr_get_field(host->base + EMMC50_CFG2, EMMC50_CFG2_AXI_SET_LEN, &val); + if (!val) + pb1_val |= MSDC_PB1_SINGLE_BURST; + + /* Set auto sync state clear, block gap stop clk */ + pb1_val |= MSDC_PB1_RSVD20 | MSDC_PB1_AUTO_SYNCST_CLR | MSDC_PB1_MARK_POP_WATER; + + /* Set low power DCM, use HCLK for GDMA, use MSDC CLK for everything else */ + pb1_val |= MSDC_PB1_LP_DCM_EN | MSDC_PB1_RSVD3 | + MSDC_PB1_AHB_GDMA_HCLK | MSDC_PB1_MSDC_CLK_ENFEAT; + + /* If needed, enable R1b command busy check at controller init time */ + if (!host->dev_comp->busy_check) + pb1_val |= MSDC_PB1_BUSY_CHECK_SEL; if (host->dev_comp->stop_clk_fix) { if (host->dev_comp->stop_dly_sel) - sdr_set_field(host->base + MSDC_PATCH_BIT1, - MSDC_PATCH_BIT1_STOP_DLY, - host->dev_comp->stop_dly_sel); + pb1_val |= FIELD_PREP(MSDC_PATCH_BIT1_STOP_DLY, + host->dev_comp->stop_dly_sel); - if (host->dev_comp->pop_en_cnt) - sdr_set_field(host->base + MSDC_PATCH_BIT2, - MSDC_PB2_POP_EN_CNT, - host->dev_comp->pop_en_cnt); + if (host->dev_comp->pop_en_cnt) { + pb2_val &= ~MSDC_PB2_POP_EN_CNT; + pb2_val |= FIELD_PREP(MSDC_PB2_POP_EN_CNT, + host->dev_comp->pop_en_cnt); + } - sdr_clr_bits(host->base + SDC_FIFO_CFG, - SDC_FIFO_CFG_WRVALIDSEL); - sdr_clr_bits(host->base + SDC_FIFO_CFG, - SDC_FIFO_CFG_RDVALIDSEL); + sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_WRVALIDSEL); + sdr_clr_bits(host->base + SDC_FIFO_CFG, SDC_FIFO_CFG_RDVALIDSEL); } - if (host->dev_comp->busy_check) - sdr_clr_bits(host->base + MSDC_PATCH_BIT1, BIT(7)); - if (host->dev_comp->async_fifo) { - sdr_set_field(host->base + MSDC_PATCH_BIT2, - MSDC_PB2_RESPWAIT, 3); - if (host->dev_comp->enhance_rx) { - if (host->top_base) - sdr_set_bits(host->top_base + EMMC_TOP_CONTROL, - SDC_RX_ENH_EN); - else - sdr_set_bits(host->base + SDC_ADV_CFG0, - SDC_RX_ENHANCE_EN); + /* Set CMD response timeout multiplier to 65 + (16 * 3) cycles */ + pb2_val &= ~MSDC_PB2_RESPWAIT; + pb2_val |= FIELD_PREP(MSDC_PB2_RESPWAIT, 3); + + /* eMMC4.5: Select async FIFO path for CMD resp and CRC status */ + pb2_val &= ~MSDC_PATCH_BIT2_CFGRESP; + pb2_val |= MSDC_PATCH_BIT2_CFGCRCSTS; + + if (!host->dev_comp->enhance_rx) { + /* eMMC4.5: Delay 2T for CMD resp and CRC status EN signals */ + pb2_val &= ~(MSDC_PB2_RESPSTSENSEL | MSDC_PB2_CRCSTSENSEL); + pb2_val |= FIELD_PREP(MSDC_PB2_RESPSTSENSEL, 2); + pb2_val |= FIELD_PREP(MSDC_PB2_CRCSTSENSEL, 2); + } else if (host->top_base) { + sdr_set_bits(host->top_base + EMMC_TOP_CONTROL, SDC_RX_ENH_EN); } else { - sdr_set_field(host->base + MSDC_PATCH_BIT2, - MSDC_PB2_RESPSTSENSEL, 2); - sdr_set_field(host->base + MSDC_PATCH_BIT2, - MSDC_PB2_CRCSTSENSEL, 2); + sdr_set_bits(host->base + SDC_ADV_CFG0, SDC_RX_ENHANCE_EN); } - /* use async fifo, then no need tune internal delay */ - sdr_clr_bits(host->base + MSDC_PATCH_BIT2, - MSDC_PATCH_BIT2_CFGRESP); - sdr_set_bits(host->base + MSDC_PATCH_BIT2, - MSDC_PATCH_BIT2_CFGCRCSTS); } if (host->dev_comp->support_64g) - sdr_set_bits(host->base + MSDC_PATCH_BIT2, - MSDC_PB2_SUPPORT_64G); + pb2_val |= MSDC_PB2_SUPPORT_64G; + + /* Patch Bit 1/2 setup is done: pull the trigger! */ + writel(pb1_val, host->base + MSDC_PATCH_BIT1); + writel(pb2_val, host->base + MSDC_PATCH_BIT2); + sdr_set_bits(host->base + EMMC50_CFG0, EMMC50_CFG_CFCSTS_SEL); + if (host->dev_comp->data_tune) { if (host->top_base) { - sdr_set_bits(host->top_base + EMMC_TOP_CONTROL, - PAD_DAT_RD_RXDLY_SEL); - sdr_clr_bits(host->top_base + EMMC_TOP_CONTROL, - DATA_K_VALUE_SEL); - sdr_set_bits(host->top_base + EMMC_TOP_CMD, - PAD_CMD_RD_RXDLY_SEL); + u32 top_ctl_val = readl(host->top_base + EMMC_TOP_CONTROL); + u32 top_cmd_val = readl(host->top_base + EMMC_TOP_CMD); + + top_cmd_val |= PAD_CMD_RD_RXDLY_SEL; + top_ctl_val |= PAD_DAT_RD_RXDLY_SEL; + top_ctl_val &= ~DATA_K_VALUE_SEL; if (host->tuning_step > PAD_DELAY_HALF) { - sdr_set_bits(host->top_base + EMMC_TOP_CONTROL, - PAD_DAT_RD_RXDLY2_SEL); - sdr_set_bits(host->top_base + EMMC_TOP_CMD, - PAD_CMD_RD_RXDLY2_SEL); + top_cmd_val |= PAD_CMD_RD_RXDLY2_SEL; + top_ctl_val |= PAD_DAT_RD_RXDLY2_SEL; } + + writel(top_ctl_val, host->top_base + EMMC_TOP_CONTROL); + writel(top_cmd_val, host->top_base + EMMC_TOP_CMD); } else { sdr_set_bits(host->base + tune_reg, MSDC_PAD_TUNE_RD_SEL | @@ -2143,15 +2244,17 @@ static inline void msdc_set_cmd_delay(struct msdc_host *host, u32 value) u32 tune_reg = host->dev_comp->pad_tune_reg; if (host->top_base) { + u32 regval = readl(host->top_base + EMMC_TOP_CMD); + + regval &= ~(PAD_CMD_RXDLY | PAD_CMD_RXDLY2); + if (value < PAD_DELAY_HALF) { - sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY, value); - sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2, 0); + regval |= FIELD_PREP(PAD_CMD_RXDLY, value); } else { - sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY, - PAD_DELAY_HALF - 1); - sdr_set_field(host->top_base + EMMC_TOP_CMD, PAD_CMD_RXDLY2, - value - PAD_DELAY_HALF); + regval |= FIELD_PREP(PAD_CMD_RXDLY, PAD_DELAY_HALF - 1); + regval |= FIELD_PREP(PAD_CMD_RXDLY2, value - PAD_DELAY_HALF); } + writel(regval, host->top_base + EMMC_TOP_CMD); } else { if (value < PAD_DELAY_HALF) { sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_CMDRDLY, value); @@ -2171,17 +2274,18 @@ static inline void msdc_set_data_delay(struct msdc_host *host, u32 value) u32 tune_reg = host->dev_comp->pad_tune_reg; if (host->top_base) { + u32 regval = readl(host->top_base + EMMC_TOP_CONTROL); + + regval &= ~(PAD_DAT_RD_RXDLY | PAD_DAT_RD_RXDLY2); + if (value < PAD_DELAY_HALF) { - sdr_set_field(host->top_base + EMMC_TOP_CONTROL, - PAD_DAT_RD_RXDLY, value); - sdr_set_field(host->top_base + EMMC_TOP_CONTROL, - PAD_DAT_RD_RXDLY2, 0); + regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, value); + regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value); } else { - sdr_set_field(host->top_base + EMMC_TOP_CONTROL, - PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1); - sdr_set_field(host->top_base + EMMC_TOP_CONTROL, - PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF); + regval |= FIELD_PREP(PAD_DAT_RD_RXDLY, PAD_DELAY_HALF - 1); + regval |= FIELD_PREP(PAD_DAT_RD_RXDLY2, value - PAD_DELAY_HALF); } + writel(regval, host->top_base + EMMC_TOP_CONTROL); } else { if (value < PAD_DELAY_HALF) { sdr_set_field(host->base + tune_reg, MSDC_PAD_TUNE_DATRRDLY, value); @@ -2977,7 +3081,7 @@ static int msdc_drv_probe(struct platform_device *pdev) mmc->f_min = DIV_ROUND_UP(host->src_clk_freq, 4 * 4095); if (!(mmc->caps & MMC_CAP_NONREMOVABLE) && - !mmc_can_gpio_cd(mmc) && + !mmc_host_can_gpio_cd(mmc) && host->dev_comp->use_internal_cd) { /* * Is removable but no GPIO declared, so @@ -3196,7 +3300,7 @@ static void msdc_restore_reg(struct msdc_host *host) __msdc_enable_sdio_irq(host, 1); } -static int __maybe_unused msdc_runtime_suspend(struct device *dev) +static int msdc_runtime_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct msdc_host *host = mmc_priv(mmc); @@ -3214,11 +3318,15 @@ static int __maybe_unused msdc_runtime_suspend(struct device *dev) __msdc_enable_sdio_irq(host, 0); } + + if (host->dev_comp->support_spm_res_release) + sdr_set_bits(host->base + SDC_STS, SDC_STS_SPM_RESOURCE_RELEASE); + msdc_gate_clock(host); return 0; } -static int __maybe_unused msdc_runtime_resume(struct device *dev) +static int msdc_runtime_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct msdc_host *host = mmc_priv(mmc); @@ -3241,7 +3349,7 @@ static int __maybe_unused msdc_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused msdc_suspend(struct device *dev) +static int msdc_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct msdc_host *host = mmc_priv(mmc); @@ -3266,7 +3374,7 @@ static int __maybe_unused msdc_suspend(struct device *dev) return pm_runtime_force_suspend(dev); } -static int __maybe_unused msdc_resume(struct device *dev) +static int msdc_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct msdc_host *host = mmc_priv(mmc); @@ -3278,8 +3386,8 @@ static int __maybe_unused msdc_resume(struct device *dev) } static const struct dev_pm_ops msdc_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(msdc_suspend, msdc_resume) - SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(msdc_suspend, msdc_resume) + RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL) }; static struct platform_driver mt_msdc_driver = { @@ -3289,7 +3397,7 @@ static struct platform_driver mt_msdc_driver = { .name = "mtk-msdc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = msdc_of_ids, - .pm = &msdc_dev_pm_ops, + .pm = pm_ptr(&msdc_dev_pm_ops), }, }; diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index b92f3ba38663..79df2fa89a3f 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -292,7 +292,7 @@ static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data, host->pio_ptr = NULL; host->pio_size = 0; } else { - dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags, + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, mmc_get_dma_dir(data)); } @@ -464,7 +464,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev) struct mmc_command *cmd = mrq->cmd; u32 err_status = 0; - del_timer(&host->timer); + timer_delete(&host->timer); host->mrq = NULL; host->intr_en &= MVSD_NOR_CARD_INT; @@ -509,7 +509,7 @@ static irqreturn_t mvsd_irq(int irq, void *dev) static void mvsd_timeout_timer(struct timer_list *t) { - struct mvsd_host *host = from_timer(host, t, timer); + struct mvsd_host *host = timer_container_of(host, t, timer); void __iomem *iobase = host->base; struct mmc_request *mrq; unsigned long flags; @@ -706,11 +706,9 @@ static int mvsd_probe(struct platform_device *pdev) if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto out; - } + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); + if (!mmc) + return -ENOMEM; host = mmc_priv(mmc); host->mmc = mmc; @@ -724,11 +722,9 @@ static int mvsd_probe(struct platform_device *pdev) * fixed rate clock). */ host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(host->clk)) { - dev_err(&pdev->dev, "no clock associated\n"); - ret = -EINVAL; - goto out; - } + if (IS_ERR(host->clk)) + return dev_err_probe(&pdev->dev, -EINVAL, "no clock associated\n"); + clk_prepare_enable(host->clk); mmc->ops = &mvsd_ops; @@ -787,12 +783,7 @@ static int mvsd_probe(struct platform_device *pdev) return 0; out: - if (mmc) { - if (!IS_ERR(host->clk)) - clk_disable_unprepare(host->clk); - mmc_free_host(mmc); - } - + clk_disable_unprepare(host->clk); return ret; } @@ -803,12 +794,11 @@ static void mvsd_remove(struct platform_device *pdev) struct mvsd_host *host = mmc_priv(mmc); mmc_remove_host(mmc); - del_timer_sync(&host->timer); + timer_delete_sync(&host->timer); mvsd_power_down(host); if (!IS_ERR(host->clk)) clk_disable_unprepare(host->clk); - mmc_free_host(mmc); } static const struct of_device_id mvsdio_dt_ids[] = { diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 0a9affd12532..c405cfb8b269 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -352,7 +352,7 @@ static void mxcmci_dma_callback(void *data) struct mxcmci_host *host = data; u32 stat; - del_timer(&host->watchdog); + timer_delete(&host->watchdog); stat = mxcmci_readl(host, MMC_REG_STATUS); @@ -737,7 +737,7 @@ static irqreturn_t mxcmci_irq(int irq, void *devid) mxcmci_cmd_done(host, stat); if (mxcmci_use_dma(host) && (stat & STATUS_WRITE_OP_DONE)) { - del_timer(&host->watchdog); + timer_delete(&host->watchdog); mxcmci_data_done(host, stat); } @@ -955,7 +955,7 @@ static bool filter(struct dma_chan *chan, void *param) static void mxcmci_watchdog(struct timer_list *t) { - struct mxcmci_host *host = from_timer(host, t, watchdog); + struct mxcmci_host *host = timer_container_of(host, t, watchdog); struct mmc_request *req = host->req; unsigned int stat = mxcmci_readl(host, MMC_REG_STATUS); @@ -1005,23 +1005,21 @@ static int mxcmci_probe(struct platform_device *pdev) if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; host = mmc_priv(mmc); host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto out_free; - } + if (IS_ERR(host->base)) + return PTR_ERR(host->base); host->phys_base = res->start; ret = mmc_of_parse(mmc); if (ret) - goto out_free; + return ret; mmc->ops = &mxcmci_ops; /* For devicetree parsing, the bus width is read from devicetree */ @@ -1054,7 +1052,7 @@ static int mxcmci_probe(struct platform_device *pdev) ret = mmc_regulator_get_supply(mmc); if (ret) - goto out_free; + return ret; if (!mmc->ocr_avail) { if (pdata && pdata->ocr_avail) @@ -1070,20 +1068,16 @@ static int mxcmci_probe(struct platform_device *pdev) host->default_irq_mask = 0; host->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(host->clk_ipg)) { - ret = PTR_ERR(host->clk_ipg); - goto out_free; - } + if (IS_ERR(host->clk_ipg)) + return PTR_ERR(host->clk_ipg); host->clk_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(host->clk_per)) { - ret = PTR_ERR(host->clk_per); - goto out_free; - } + if (IS_ERR(host->clk_per)) + return PTR_ERR(host->clk_per); ret = clk_prepare_enable(host->clk_per); if (ret) - goto out_free; + return ret; ret = clk_prepare_enable(host->clk_ipg); if (ret) @@ -1169,9 +1163,6 @@ out_clk_put: out_clk_per_put: clk_disable_unprepare(host->clk_per); -out_free: - mmc_free_host(mmc); - return ret; } @@ -1190,8 +1181,6 @@ static void mxcmci_remove(struct platform_device *pdev) clk_disable_unprepare(host->clk_per); clk_disable_unprepare(host->clk_ipg); - - mmc_free_host(mmc); } static int mxcmci_suspend(struct device *dev) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 80e6f48c83aa..7c7c52d9e8e7 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -569,7 +569,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) if (irq_err < 0) return irq_err; - mmc = mmc_alloc_host(sizeof(struct mxs_mmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -577,10 +577,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) ssp = &host->ssp; ssp->dev = &pdev->dev; ssp->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(ssp->base)) { - ret = PTR_ERR(ssp->base); - goto out_mmc_free; - } + if (IS_ERR(ssp->base)) + return PTR_ERR(ssp->base); ssp->devid = (enum mxs_ssp_id)of_device_get_match_data(&pdev->dev); @@ -590,26 +588,23 @@ static int mxs_mmc_probe(struct platform_device *pdev) reg_vmmc = devm_regulator_get(&pdev->dev, "vmmc"); if (!IS_ERR(reg_vmmc)) { ret = regulator_enable(reg_vmmc); - if (ret) { - dev_err(&pdev->dev, - "Failed to enable vmmc regulator: %d\n", ret); - goto out_mmc_free; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to enable vmmc regulator\n"); ret = devm_add_action_or_reset(&pdev->dev, mxs_mmc_regulator_disable, reg_vmmc); if (ret) - goto out_mmc_free; + return ret; } ssp->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(ssp->clk)) { - ret = PTR_ERR(ssp->clk); - goto out_mmc_free; - } + if (IS_ERR(ssp->clk)) + return PTR_ERR(ssp->clk); + ret = clk_prepare_enable(ssp->clk); if (ret) - goto out_mmc_free; + return ret; ret = mxs_mmc_reset(host); if (ret) { @@ -668,8 +663,6 @@ out_free_dma: dma_release_channel(ssp->dmach); out_clk_disable: clk_disable_unprepare(ssp->clk); -out_mmc_free: - mmc_free_host(mmc); return ret; } @@ -685,11 +678,8 @@ static void mxs_mmc_remove(struct platform_device *pdev) dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); - - mmc_free_host(mmc); } -#ifdef CONFIG_PM_SLEEP static int mxs_mmc_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); @@ -708,9 +698,8 @@ static int mxs_mmc_resume(struct device *dev) return clk_prepare_enable(ssp->clk); } -#endif -static SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(mxs_mmc_pm_ops, mxs_mmc_suspend, mxs_mmc_resume); static struct platform_driver mxs_mmc_driver = { .probe = mxs_mmc_probe, @@ -718,7 +707,7 @@ static struct platform_driver mxs_mmc_driver = { .driver = { .name = DRIVER_NAME, .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &mxs_mmc_pm_ops, + .pm = pm_sleep_ptr(&mxs_mmc_pm_ops), .of_match_table = mxs_mmc_dt_ids, }, }; diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c index 05939f30a5ae..8131a1703f28 100644 --- a/drivers/mmc/host/of_mmc_spi.c +++ b/drivers/mmc/host/of_mmc_spi.c @@ -57,7 +57,7 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) if (dev->platform_data || !dev_fwnode(dev)) return dev->platform_data; - oms = kzalloc(sizeof(*oms), GFP_KERNEL); + oms = kzalloc_obj(*oms); if (!oms) return NULL; diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 62252ad4e20d..527b89a5ed70 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -214,7 +214,7 @@ static void mmc_omap_select_slot(struct mmc_omap_slot *slot, int claimed) host->mmc = slot->mmc; spin_unlock_irqrestore(&host->slot_lock, flags); no_claim: - del_timer(&host->clk_timer); + timer_delete(&host->clk_timer); if (host->current_slot != slot || !claimed) mmc_omap_fclk_offdelay(host->current_slot); @@ -273,7 +273,7 @@ static void mmc_omap_release_slot(struct mmc_omap_slot *slot, int clk_enabled) /* Keeps clock running for at least 8 cycles on valid freq */ mod_timer(&host->clk_timer, jiffies + HZ/10); else { - del_timer(&host->clk_timer); + timer_delete(&host->clk_timer); mmc_omap_fclk_offdelay(slot); mmc_omap_fclk_enable(host, 0); } @@ -326,7 +326,7 @@ mmc_omap_show_cover_switch(struct device *dev, struct device_attribute *attr, "closed"); } -static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); +static DEVICE_ATTR(cover_switch, 0444, mmc_omap_show_cover_switch, NULL); static ssize_t mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, @@ -338,7 +338,7 @@ mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%s\n", slot->pdata->name); } -static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); +static DEVICE_ATTR(slot_name, 0444, mmc_omap_show_slot_name, NULL); static void mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) @@ -564,7 +564,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) { host->cmd = NULL; - del_timer(&host->cmd_abort_timer); + timer_delete(&host->cmd_abort_timer); if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) { @@ -639,7 +639,8 @@ static void mmc_omap_abort_command(struct work_struct *work) static void mmc_omap_cmd_timer(struct timer_list *t) { - struct mmc_omap_host *host = from_timer(host, t, cmd_abort_timer); + struct mmc_omap_host *host = timer_container_of(host, t, + cmd_abort_timer); unsigned long flags; spin_lock_irqsave(&host->slot_lock, flags); @@ -655,7 +656,7 @@ mmc_omap_cmd_timer(struct timer_list *t) static void mmc_omap_clk_timer(struct timer_list *t) { - struct mmc_omap_host *host = from_timer(host, t, clk_timer); + struct mmc_omap_host *host = timer_container_of(host, t, clk_timer); mmc_omap_fclk_enable(host, 0); } @@ -836,7 +837,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } if (cmd_error && host->data) { - del_timer(&host->cmd_abort_timer); + timer_delete(&host->cmd_abort_timer); host->abort = 1; OMAP_MMC_WRITE(host, IE, 0); disable_irq_nosync(host->irq); @@ -879,7 +880,7 @@ void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed) static void mmc_omap_cover_timer(struct timer_list *t) { - struct mmc_omap_slot *slot = from_timer(slot, t, cover_timer); + struct mmc_omap_slot *slot = timer_container_of(slot, t, cover_timer); queue_work(system_bh_wq, &slot->cover_bh_work); } @@ -1258,7 +1259,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id) struct mmc_host *mmc; int r; - mmc = mmc_alloc_host(sizeof(struct mmc_omap_slot), host->dev); + mmc = devm_mmc_alloc_host(host->dev, sizeof(*slot)); if (mmc == NULL) return -ENOMEM; @@ -1275,11 +1276,13 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id) if (IS_ERR(slot->vsd)) return dev_err_probe(host->dev, PTR_ERR(slot->vsd), "error looking up VSD GPIO\n"); + slot->vio = devm_gpiod_get_index_optional(host->dev, "vio", id, GPIOD_OUT_LOW); if (IS_ERR(slot->vio)) return dev_err_probe(host->dev, PTR_ERR(slot->vio), "error looking up VIO GPIO\n"); + slot->cover = devm_gpiod_get_index_optional(host->dev, "cover", id, GPIOD_IN); if (IS_ERR(slot->cover)) @@ -1344,7 +1347,6 @@ err_remove_slot_name: device_remove_file(&mmc->class_dev, &dev_attr_slot_name); err_remove_host: mmc_remove_host(mmc); - mmc_free_host(mmc); return r; } @@ -1358,11 +1360,10 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot) device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); cancel_work_sync(&slot->cover_bh_work); - del_timer_sync(&slot->cover_timer); + timer_delete_sync(&slot->cover_timer); flush_workqueue(slot->host->mmc_omap_wq); mmc_remove_host(mmc); - mmc_free_host(mmc); } static int mmc_omap_probe(struct platform_device *pdev) @@ -1476,7 +1477,7 @@ static int mmc_omap_probe(struct platform_device *pdev) host->nr_slots = pdata->nr_slots; host->reg_shift = (mmc_omap7xx() ? 1 : 2); - host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0); + host->mmc_omap_wq = alloc_workqueue("mmc_omap", WQ_PERCPU, 0); if (!host->mmc_omap_wq) { ret = -ENOMEM; goto err_plat_cleanup; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 59e36e0ebbbf..58c881f2725b 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -620,8 +620,6 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host) OMAP_HSMMC_WRITE(host->base, CON, con & ~OD); } -#ifdef CONFIG_PM - /* * Restore the MMC host context, if it was lost as result of a * power state change. @@ -689,6 +687,7 @@ out: return 0; } +#ifdef CONFIG_PM /* * Save the MMC host context (store the number of power state changes so far). */ @@ -747,7 +746,7 @@ omap_hsmmc_show_slot_name(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%s\n", mmc_pdata(host)->name); } -static DEVICE_ATTR(slot_name, S_IRUGO, omap_hsmmc_show_slot_name, NULL); +static DEVICE_ATTR(slot_name, 0444, omap_hsmmc_show_slot_name, NULL); /* * Configure the response type and send the cmd. @@ -1663,7 +1662,6 @@ static int mmc_regs_show(struct seq_file *s, void *data) seq_printf(s, "CAPA:\t\t0x%08x\n", OMAP_HSMMC_READ(host->base, CAPA)); - pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); return 0; @@ -1674,7 +1672,7 @@ DEFINE_SHOW_ATTRIBUTE(mmc_regs); static void omap_hsmmc_debugfs(struct mmc_host *mmc) { if (mmc->debugfs_root) - debugfs_create_file("regs", S_IRUSR, mmc->debugfs_root, + debugfs_create_file("regs", 0400, mmc->debugfs_root, mmc, &mmc_regs_fops); } @@ -1798,15 +1796,13 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - mmc = mmc_alloc_host(sizeof(struct omap_hsmmc_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto err; - } + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); + if (!mmc) + return -ENOMEM; ret = mmc_of_parse(mmc); if (ret) - goto err1; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -1842,7 +1838,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) if (IS_ERR(host->fclk)) { ret = PTR_ERR(host->fclk); host->fclk = NULL; - goto err1; + return ret; } if (host->pdata->controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ) { @@ -1956,7 +1952,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) } omap_hsmmc_debugfs(mmc); - pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); return 0; @@ -1973,9 +1968,6 @@ err_irq: pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); clk_disable_unprepare(host->dbclk); -err1: - mmc_free_host(mmc); -err: return ret; } @@ -1995,11 +1987,8 @@ static void omap_hsmmc_remove(struct platform_device *pdev) pm_runtime_disable(host->dev); device_init_wakeup(&pdev->dev, false); clk_disable_unprepare(host->dbclk); - - mmc_free_host(host->mmc); } -#ifdef CONFIG_PM_SLEEP static int omap_hsmmc_suspend(struct device *dev) { struct omap_hsmmc_host *host = dev_get_drvdata(dev); @@ -2038,13 +2027,10 @@ static int omap_hsmmc_resume(struct device *dev) if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) omap_hsmmc_conf_bus_power(host); - pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); return 0; } -#endif -#ifdef CONFIG_PM static int omap_hsmmc_runtime_suspend(struct device *dev) { struct omap_hsmmc_host *host; @@ -2112,11 +2098,10 @@ static int omap_hsmmc_runtime_resume(struct device *dev) spin_unlock_irqrestore(&host->irq_lock, flags); return 0; } -#endif static const struct dev_pm_ops omap_hsmmc_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume) - SET_RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(omap_hsmmc_suspend, omap_hsmmc_resume) + RUNTIME_PM_OPS(omap_hsmmc_runtime_suspend, omap_hsmmc_runtime_resume, NULL) }; static struct platform_driver omap_hsmmc_driver = { @@ -2125,7 +2110,7 @@ static struct platform_driver omap_hsmmc_driver = { .driver = { .name = DRIVER_NAME, .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &omap_hsmmc_dev_pm_ops, + .pm = pm_ptr(&omap_hsmmc_dev_pm_ops), .of_match_table = of_match_ptr(omap_mmc_of_match), }, }; diff --git a/drivers/mmc/host/owl-mmc.c b/drivers/mmc/host/owl-mmc.c index 797ef48d9204..dc585726b66e 100644 --- a/drivers/mmc/host/owl-mmc.c +++ b/drivers/mmc/host/owl-mmc.c @@ -567,7 +567,7 @@ static int owl_mmc_probe(struct platform_device *pdev) struct resource *res; int ret; - mmc = mmc_alloc_host(sizeof(struct owl_mmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*owl_host)); if (!mmc) { dev_err(&pdev->dev, "mmc alloc host failed\n"); return -ENOMEM; @@ -580,24 +580,18 @@ static int owl_mmc_probe(struct platform_device *pdev) spin_lock_init(&owl_host->lock); owl_host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(owl_host->base)) { - ret = PTR_ERR(owl_host->base); - goto err_free_host; - } + if (IS_ERR(owl_host->base)) + return PTR_ERR(owl_host->base); owl_host->clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(owl_host->clk)) { - dev_err(&pdev->dev, "No clock defined\n"); - ret = PTR_ERR(owl_host->clk); - goto err_free_host; - } + if (IS_ERR(owl_host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(owl_host->clk), + "No clock defined\n"); owl_host->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL); - if (IS_ERR(owl_host->reset)) { - dev_err(&pdev->dev, "Could not get reset control\n"); - ret = PTR_ERR(owl_host->reset); - goto err_free_host; - } + if (IS_ERR(owl_host->reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(owl_host->reset), + "Could not get reset control\n"); mmc->ops = &owl_mmc_ops; mmc->max_blk_count = 512; @@ -616,16 +610,14 @@ static int owl_mmc_probe(struct platform_device *pdev) ret = mmc_of_parse(mmc); if (ret) - goto err_free_host; + return ret; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; owl_host->dma = dma_request_chan(&pdev->dev, "mmc"); - if (IS_ERR(owl_host->dma)) { - dev_err(owl_host->dev, "Failed to get external DMA channel.\n"); - ret = PTR_ERR(owl_host->dma); - goto err_free_host; - } + if (IS_ERR(owl_host->dma)) + return dev_err_probe(&pdev->dev, PTR_ERR(owl_host->dma), + "Failed to get external DMA channel.\n"); dev_info(&pdev->dev, "Using %s for DMA transfers\n", dma_chan_name(owl_host->dma)); @@ -662,8 +654,6 @@ static int owl_mmc_probe(struct platform_device *pdev) err_release_channel: dma_release_channel(owl_host->dma); -err_free_host: - mmc_free_host(mmc); return ret; } @@ -676,7 +666,6 @@ static void owl_mmc_remove(struct platform_device *pdev) mmc_remove_host(mmc); disable_irq(owl_host->irq); dma_release_channel(owl_host->dma); - mmc_free_host(mmc); } static const struct of_device_id owl_mmc_of_match[] = { diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 2d0ad006913d..b5ea058ed467 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -615,11 +615,9 @@ static int pxamci_probe(struct platform_device *pdev) if (irq < 0) return irq; - mmc = mmc_alloc_host(sizeof(struct pxamci_host), dev); - if (!mmc) { - ret = -ENOMEM; - goto out; - } + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); + if (!mmc) + return -ENOMEM; mmc->ops = &pxamci_ops; @@ -646,7 +644,7 @@ static int pxamci_probe(struct platform_device *pdev) ret = pxamci_of_init(pdev, mmc); if (ret) - goto out; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -654,11 +652,9 @@ static int pxamci_probe(struct platform_device *pdev) host->clkrt = CLKRT_OFF; host->clk = devm_clk_get(dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - host->clk = NULL; - goto out; - } + if (IS_ERR(host->clk)) + return dev_err_probe(dev, PTR_ERR(host->clk), + "Failed to acquire clock\n"); host->clkrate = clk_get_rate(host->clk); @@ -670,7 +666,7 @@ static int pxamci_probe(struct platform_device *pdev) ret = pxamci_init_ocr(host); if (ret < 0) - goto out; + return ret; mmc->caps = 0; host->cmdat = 0; @@ -686,10 +682,8 @@ static int pxamci_probe(struct platform_device *pdev) host->imask = MMC_I_MASK_ALL; host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto out; - } + if (IS_ERR(host->base)) + return PTR_ERR(host->base); host->res = r; /* @@ -704,51 +698,41 @@ static int pxamci_probe(struct platform_device *pdev) ret = devm_request_irq(dev, irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) - goto out; + return ret; platform_set_drvdata(pdev, mmc); - host->dma_chan_rx = dma_request_chan(dev, "rx"); - if (IS_ERR(host->dma_chan_rx)) { - dev_err(dev, "unable to request rx dma channel\n"); - ret = PTR_ERR(host->dma_chan_rx); - host->dma_chan_rx = NULL; - goto out; - } + host->dma_chan_rx = devm_dma_request_chan(dev, "rx"); + if (IS_ERR(host->dma_chan_rx)) + return dev_err_probe(dev, PTR_ERR(host->dma_chan_rx), + "unable to request rx dma channel\n"); - host->dma_chan_tx = dma_request_chan(dev, "tx"); - if (IS_ERR(host->dma_chan_tx)) { - dev_err(dev, "unable to request tx dma channel\n"); - ret = PTR_ERR(host->dma_chan_tx); - host->dma_chan_tx = NULL; - goto out; - } + + host->dma_chan_tx = devm_dma_request_chan(dev, "tx"); + if (IS_ERR(host->dma_chan_tx)) + return dev_err_probe(dev, PTR_ERR(host->dma_chan_tx), + "unable to request tx dma channel\n"); if (host->pdata) { host->detect_delay_ms = host->pdata->detect_delay_ms; host->power = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); - if (IS_ERR(host->power)) { - ret = PTR_ERR(host->power); - dev_err(dev, "Failed requesting gpio_power\n"); - goto out; - } + if (IS_ERR(host->power)) + return dev_err_probe(dev, PTR_ERR(host->power), + "Failed requesting gpio_power\n"); /* FIXME: should we pass detection delay to debounce? */ ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0); - if (ret && ret != -ENOENT) { - dev_err(dev, "Failed requesting gpio_cd\n"); - goto out; - } + if (ret && ret != -ENOENT) + return dev_err_probe(dev, ret, "Failed requesting gpio_cd\n"); if (!host->pdata->gpio_card_ro_invert) mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; ret = mmc_gpiod_request_ro(mmc, "wp", 0, 0); - if (ret && ret != -ENOENT) { - dev_err(dev, "Failed requesting gpio_ro\n"); - goto out; - } + if (ret && ret != -ENOENT) + return dev_err_probe(dev, ret, "Failed requesting gpio_ro\n"); + if (!ret) host->use_ro_gpio = true; @@ -765,20 +749,8 @@ static int pxamci_probe(struct platform_device *pdev) if (ret) { if (host->pdata && host->pdata->exit) host->pdata->exit(dev, mmc); - goto out; } - return 0; - -out: - if (host) { - if (host->dma_chan_rx) - dma_release_channel(host->dma_chan_rx); - if (host->dma_chan_tx) - dma_release_channel(host->dma_chan_tx); - } - if (mmc) - mmc_free_host(mmc); return ret; } @@ -801,10 +773,6 @@ static void pxamci_remove(struct platform_device *pdev) dmaengine_terminate_all(host->dma_chan_rx); dmaengine_terminate_all(host->dma_chan_tx); - dma_release_channel(host->dma_chan_rx); - dma_release_channel(host->dma_chan_tx); - - mmc_free_host(mmc); } } diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index f12a87442338..afc36a407c2c 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -9,6 +9,7 @@ #ifndef RENESAS_SDHI_H #define RENESAS_SDHI_H +#include <linux/device.h> #include <linux/dmaengine.h> #include <linux/platform_device.h> #include <linux/workqueue.h> @@ -85,6 +86,7 @@ struct renesas_sdhi { u32 scc_tappos_hs400; const u8 *adjust_hs400_calib_table; bool needs_adjust_hs400; + bool card_is_sdio; /* Tuning values: 1 for success, 0 for failure */ DECLARE_BITMAP(taps, BITS_PER_LONG); @@ -95,6 +97,7 @@ struct renesas_sdhi { struct reset_control *rstc; struct tmio_mmc_host *host; + struct regulator_dev *rdev; }; #define host_to_priv(host) \ @@ -105,4 +108,6 @@ int renesas_sdhi_probe(struct platform_device *pdev, const struct renesas_sdhi_of_data *of_data, const struct renesas_sdhi_quirks *quirks); void renesas_sdhi_remove(struct platform_device *pdev); +int renesas_sdhi_suspend(struct device *dev); +int renesas_sdhi_resume(struct device *dev); #endif diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index f73b84bae0c4..f9ec78d699f4 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -26,12 +26,16 @@ #include <linux/mmc/mmc.h> #include <linux/mmc/slot-gpio.h> #include <linux/module.h> +#include <linux/mux/consumer.h> #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/pinctrl-state.h> #include <linux/platform_data/tmio.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/of_regulator.h> #include <linux/reset.h> #include <linux/sh_dma.h> #include <linux/slab.h> @@ -220,7 +224,11 @@ static void renesas_sdhi_set_clock(struct tmio_mmc_host *host, clk &= ~0xff; } - sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk & CLK_CTL_DIV_MASK); + clock = clk & CLK_CTL_DIV_MASK; + if (clock != CLK_CTL_DIV_MASK) + host->mmc->actual_clock /= (1 << (ffs(clock) + 1)); + + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clock); if (!(host->pdata->flags & TMIO_MMC_MIN_RCAR2)) usleep_range(10000, 11000); @@ -581,12 +589,24 @@ static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve) if (!preserve) { if (priv->rstc) { + u32 sd_status; + /* + * HW reset might have toggled the regulator state in + * HW which regulator core might be unaware of so save + * and restore the regulator state during HW reset. + */ + if (priv->rdev) + sd_status = sd_ctrl_read32(host, CTL_SD_STATUS); + reset_control_reset(priv->rstc); /* Unknown why but without polling reset status, it will hang */ read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100, false, priv->rstc); /* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */ sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); + if (priv->rdev) + sd_ctrl_write32(host, CTL_SD_STATUS, sd_status); + priv->needs_adjust_hs400 = false; renesas_sdhi_set_clock(host, host->clk_cache); @@ -672,9 +692,8 @@ static int renesas_sdhi_select_tuning(struct tmio_mmc_host *host) /* Set SCC */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, priv->tap_set); - /* Enable auto re-tuning */ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, - SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN | + (priv->card_is_sdio ? 0 : SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN) | sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); return 0; @@ -764,6 +783,14 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_ if (bad_taps & BIT(new_tap % priv->tap_num)) return test_bit(error_tap % priv->tap_num, priv->smpcmp); } else { + if (!priv->card_is_sdio && + !(val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR)) { + u32 smpcmp = sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_SMPCMP); + + /* DAT1 is unmatched because of an SDIO irq */ + if (smpcmp & (BIT(17) | BIT(1))) + return false; + } if (val & SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) return true; /* need retune */ else if (val & SH_MOBILE_SDHI_SCC_RVSREQ_REQTAPUP) @@ -814,11 +841,14 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host, if (mmc_doing_tune(host->mmc)) return false; - if (((mrq->cmd->error == -ETIMEDOUT) || - (mrq->data && mrq->data->error == -ETIMEDOUT)) && - ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || - (host->ops.get_cd && host->ops.get_cd(host->mmc)))) - ret |= true; + /* mrq can be NULL to check SCC error on SDIO irq without any request */ + if (mrq) { + if (((mrq->cmd->error == -ETIMEDOUT) || + (mrq->data && mrq->data->error == -ETIMEDOUT)) && + ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || + (host->ops.get_cd && host->ops.get_cd(host->mmc)))) + ret |= true; + } if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) & SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN) @@ -829,6 +859,28 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host, return ret; } +static void renesas_sdhi_init_card(struct mmc_host *mmc, struct mmc_card *card) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + struct renesas_sdhi *priv = host_to_priv(host); + + /* + * This controller cannot do auto-retune with SDIO irqs, so we + * then need to enforce manual correction. However, when tuning, + * mmc->card is not populated yet, so we don't know if the card + * is SDIO. init_card provides this information earlier, so we + * keep a copy of it. + */ + priv->card_is_sdio = mmc_card_sdio(card); +} + +static void renesas_sdhi_sdio_irq(struct tmio_mmc_host *host) +{ + /* This controller requires retune when an SDIO irq occurs */ + if (renesas_sdhi_check_scc_error(host, NULL)) + mmc_retune_needed(host->mmc); +} + static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit) { int timeout = 1000; @@ -904,6 +956,102 @@ static void renesas_sdhi_enable_dma(struct tmio_mmc_host *host, bool enable) renesas_sdhi_sdbuf_width(host, enable ? width : 16); } +static const unsigned int renesas_sdhi_vqmmc_voltages[] = { + 3300000, 1800000 +}; + +static int renesas_sdhi_regulator_disable(struct regulator_dev *rdev) +{ + struct tmio_mmc_host *host = rdev_get_drvdata(rdev); + u32 sd_status; + + sd_status = sd_ctrl_read32(host, CTL_SD_STATUS); + sd_status &= ~SD_STATUS_PWEN; + sd_ctrl_write32(host, CTL_SD_STATUS, sd_status); + + return 0; +} + +static int renesas_sdhi_regulator_enable(struct regulator_dev *rdev) +{ + struct tmio_mmc_host *host = rdev_get_drvdata(rdev); + u32 sd_status; + + sd_status = sd_ctrl_read32(host, CTL_SD_STATUS); + sd_status |= SD_STATUS_PWEN; + sd_ctrl_write32(host, CTL_SD_STATUS, sd_status); + + return 0; +} + +static int renesas_sdhi_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct tmio_mmc_host *host = rdev_get_drvdata(rdev); + u32 sd_status; + + sd_status = sd_ctrl_read32(host, CTL_SD_STATUS); + + return (sd_status & SD_STATUS_PWEN) ? 1 : 0; +} + +static int renesas_sdhi_regulator_get_voltage(struct regulator_dev *rdev) +{ + struct tmio_mmc_host *host = rdev_get_drvdata(rdev); + u32 sd_status; + + sd_status = sd_ctrl_read32(host, CTL_SD_STATUS); + + return (sd_status & SD_STATUS_IOVS) ? 1800000 : 3300000; +} + +static int renesas_sdhi_regulator_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV, + unsigned int *selector) +{ + struct tmio_mmc_host *host = rdev_get_drvdata(rdev); + u32 sd_status; + + sd_status = sd_ctrl_read32(host, CTL_SD_STATUS); + if (min_uV >= 1700000 && max_uV <= 1950000) { + sd_status |= SD_STATUS_IOVS; + *selector = 1; + } else { + sd_status &= ~SD_STATUS_IOVS; + *selector = 0; + } + sd_ctrl_write32(host, CTL_SD_STATUS, sd_status); + + return 0; +} + +static int renesas_sdhi_regulator_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + if (selector >= ARRAY_SIZE(renesas_sdhi_vqmmc_voltages)) + return -EINVAL; + + return renesas_sdhi_vqmmc_voltages[selector]; +} + +static const struct regulator_ops renesas_sdhi_regulator_voltage_ops = { + .enable = renesas_sdhi_regulator_enable, + .disable = renesas_sdhi_regulator_disable, + .is_enabled = renesas_sdhi_regulator_is_enabled, + .list_voltage = renesas_sdhi_regulator_list_voltage, + .get_voltage = renesas_sdhi_regulator_get_voltage, + .set_voltage = renesas_sdhi_regulator_set_voltage, +}; + +static const struct regulator_desc renesas_sdhi_vqmmc_regulator = { + .name = "sdhi-vqmmc-regulator", + .of_match = of_match_ptr("vqmmc-regulator"), + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &renesas_sdhi_regulator_voltage_ops, + .volt_table = renesas_sdhi_vqmmc_voltages, + .n_voltages = ARRAY_SIZE(renesas_sdhi_vqmmc_voltages), +}; + int renesas_sdhi_probe(struct platform_device *pdev, const struct tmio_mmc_dma_ops *dma_ops, const struct renesas_sdhi_of_data *of_data, @@ -911,7 +1059,11 @@ int renesas_sdhi_probe(struct platform_device *pdev, { struct tmio_mmc_data *mmd = pdev->dev.platform_data; struct tmio_mmc_data *mmc_data; + struct regulator_config rcfg = { .dev = &pdev->dev, }; + struct regulator_dev *rdev; struct renesas_sdhi_dma *dma_priv; + struct device *dev = &pdev->dev; + struct mux_state *mux_state; struct tmio_mmc_host *host; struct renesas_sdhi *priv; int num_irqs, irq, ret, i; @@ -954,7 +1106,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, if (IS_ERR(priv->clk_cd)) return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cd), "cannot get cd clock"); - priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + priv->rstc = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL); if (IS_ERR(priv->rstc)) return PTR_ERR(priv->rstc); @@ -966,6 +1118,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, "state_uhs"); } + mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL); + if (IS_ERR(mux_state)) + return PTR_ERR(mux_state); + host = tmio_mmc_host_alloc(pdev, mmc_data); if (IS_ERR(host)) return PTR_ERR(host); @@ -999,7 +1155,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); /* For some SoC, we disable internal WP. GPIO may override this */ - if (mmc_can_gpio_ro(host->mmc)) + if (mmc_host_can_gpio_ro(host->mmc)) mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT; /* SDR speeds are only available on Gen2+ */ @@ -1051,7 +1207,20 @@ int renesas_sdhi_probe(struct platform_device *pdev, ret = renesas_sdhi_clk_enable(host); if (ret) - goto efree; + return ret; + + rcfg.of_node = of_get_available_child_by_name(dev->of_node, "vqmmc-regulator"); + if (rcfg.of_node) { + rcfg.driver_data = priv->host; + rdev = devm_regulator_register(dev, &renesas_sdhi_vqmmc_regulator, &rcfg); + of_node_put(rcfg.of_node); + if (IS_ERR(rdev)) { + dev_err(dev, "regulator register failed err=%ld", PTR_ERR(rdev)); + ret = PTR_ERR(rdev); + goto edisclk; + } + priv->rdev = rdev; + } ver = sd_ctrl_read16(host, CTL_VERSION); /* GEN2_SDR104 is first known SDHI to use 32bit block count */ @@ -1101,6 +1270,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, dev_warn(&host->pdev->dev, "Unknown clock rate for tuning\n"); host->check_retune = renesas_sdhi_check_scc_error; + host->sdio_irq = renesas_sdhi_sdio_irq; + host->ops.init_card = renesas_sdhi_init_card; host->ops.execute_tuning = renesas_sdhi_execute_tuning; host->ops.prepare_hs400_tuning = renesas_sdhi_prepare_hs400_tuning; host->ops.hs400_downgrade = renesas_sdhi_disable_scc; @@ -1109,29 +1280,24 @@ int renesas_sdhi_probe(struct platform_device *pdev, sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, host->sdcard_irq_mask_all); - num_irqs = platform_irq_count(pdev); - if (num_irqs < 0) { - ret = num_irqs; - goto eirq; - } - /* There must be at least one IRQ source */ - if (!num_irqs) { - ret = -ENXIO; - goto eirq; + num_irqs = platform_irq_count(pdev); + if (num_irqs <= 0) { + ret = num_irqs ?: -ENOENT; + goto edisclk; } for (i = 0; i < num_irqs; i++) { irq = platform_get_irq(pdev, i); if (irq < 0) { ret = irq; - goto eirq; + goto edisclk; } ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, 0, dev_name(&pdev->dev), host); if (ret) - goto eirq; + goto edisclk; } ret = tmio_mmc_host_probe(host); @@ -1143,13 +1309,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, return ret; -eirq: - tmio_mmc_host_remove(host); edisclk: renesas_sdhi_clk_disable(host); -efree: - tmio_mmc_host_free(host); - return ret; } EXPORT_SYMBOL_GPL(renesas_sdhi_probe); @@ -1160,9 +1321,44 @@ void renesas_sdhi_remove(struct platform_device *pdev) tmio_mmc_host_remove(host); renesas_sdhi_clk_disable(host); - tmio_mmc_host_free(host); } EXPORT_SYMBOL_GPL(renesas_sdhi_remove); +int renesas_sdhi_suspend(struct device *dev) +{ + struct tmio_mmc_host *host = dev_get_drvdata(dev); + struct renesas_sdhi *priv = host_to_priv(host); + int ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + return ret; + + ret = reset_control_assert(priv->rstc); + if (ret) + pm_runtime_force_resume(dev); + + return ret; +} +EXPORT_SYMBOL_GPL(renesas_sdhi_suspend); + +int renesas_sdhi_resume(struct device *dev) +{ + struct tmio_mmc_host *host = dev_get_drvdata(dev); + struct renesas_sdhi *priv = host_to_priv(host); + int ret; + + ret = reset_control_deassert(priv->rstc); + if (ret) + return ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + reset_control_assert(priv->rstc); + + return ret; +} +EXPORT_SYMBOL_GPL(renesas_sdhi_resume); + MODULE_DESCRIPTION("Renesas SDHI core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 4b389e92399e..f6ebb7bc7ede 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -18,7 +18,6 @@ #include <linux/pagemap.h> #include <linux/platform_data/tmio.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> #include <linux/scatterlist.h> #include <linux/sys_soc.h> @@ -107,7 +106,8 @@ static const struct renesas_sdhi_of_data of_data_rza2 = { static const struct renesas_sdhi_of_data of_data_rcar_gen3 = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | - TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, + TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | + TMIO_MMC_64BIT_DATA_PORT, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY, .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE, @@ -123,7 +123,8 @@ static const struct renesas_sdhi_of_data of_data_rcar_gen3 = { static const struct renesas_sdhi_of_data of_data_rcar_gen3_no_sdh_fallback = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | - TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, + TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | + TMIO_MMC_64BIT_DATA_PORT, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY, .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT | MMC_CAP2_MERGE_CAPABLE, @@ -598,18 +599,17 @@ static int renesas_sdhi_internal_dmac_probe(struct platform_device *pdev) } static const struct dev_pm_ops renesas_sdhi_internal_dmac_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, - tmio_mmc_host_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(renesas_sdhi_suspend, renesas_sdhi_resume) + RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, + tmio_mmc_host_runtime_resume, + NULL) }; static struct platform_driver renesas_internal_dmac_sdhi_driver = { .driver = { .name = "renesas_sdhi_internal_dmac", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &renesas_sdhi_internal_dmac_dev_pm_ops, + .pm = pm_ptr(&renesas_sdhi_internal_dmac_dev_pm_ops), .of_match_table = renesas_sdhi_internal_dmac_of_match, }, .probe = renesas_sdhi_internal_dmac_probe, diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c index 822a310c9bba..9215600f03a2 100644 --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -60,7 +60,8 @@ static struct renesas_sdhi_scc rcar_gen2_scc_taps[] = { static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL | - TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, + TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2 | + TMIO_MMC_32BIT_DATA_PORT, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23 | MMC_CAP_WAIT_WHILE_BUSY, .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, @@ -455,19 +456,15 @@ static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev) of_device_get_match_data(&pdev->dev), NULL); } -static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, - tmio_mmc_host_runtime_resume, - NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(renesas_sdhi_sys_dmac_dev_pm_ops, + tmio_mmc_host_runtime_suspend, + tmio_mmc_host_runtime_resume, NULL); static struct platform_driver renesas_sys_dmac_sdhi_driver = { .driver = { .name = "sh_mobile_sdhi", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &renesas_sdhi_sys_dmac_dev_pm_ops, + .pm = pm_ptr(&renesas_sdhi_sys_dmac_dev_pm_ops), .of_match_table = renesas_sdhi_sys_dmac_of_match, }, .probe = renesas_sdhi_sys_dmac_probe, diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 0c6eb60a95fd..8dfbc62f165b 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1181,79 +1181,6 @@ static int sdmmc_get_cd(struct mmc_host *mmc) return cd; } -static int sd_wait_voltage_stable_1(struct realtek_pci_sdmmc *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - u8 stat; - - /* Reference to Signal Voltage Switch Sequence in SD spec. - * Wait for a period of time so that the card can drive SD_CMD and - * SD_DAT[3:0] to low after sending back CMD11 response. - */ - mdelay(1); - - /* SD_CMD, SD_DAT[3:0] should be driven to low by card; - * If either one of SD_CMD,SD_DAT[3:0] is not low, - * abort the voltage switch sequence; - */ - err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); - if (err < 0) - return err; - - if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS)) - return -EINVAL; - - /* Stop toggle SD clock */ - err = rtsx_pci_write_register(pcr, SD_BUS_STAT, - 0xFF, SD_CLK_FORCE_STOP); - if (err < 0) - return err; - - return 0; -} - -static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - u8 stat, mask, val; - - /* Wait 1.8V output of voltage regulator in card stable */ - msleep(50); - - /* Toggle SD clock again */ - err = rtsx_pci_write_register(pcr, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN); - if (err < 0) - return err; - - /* Wait for a period of time so that the card can drive - * SD_DAT[3:0] to high at 1.8V - */ - msleep(20); - - /* SD_CMD, SD_DAT[3:0] should be pulled high by host */ - err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); - if (err < 0) - return err; - - mask = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS; - val = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS; - if ((stat & mask) != val) { - dev_dbg(sdmmc_dev(host), - "%s: SD_BUS_STAT = 0x%x\n", __func__, stat); - rtsx_pci_write_register(pcr, SD_BUS_STAT, - SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); - rtsx_pci_write_register(pcr, CARD_CLK_EN, 0xFF, 0); - return -EINVAL; - } - - return 0; -} - static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) { struct realtek_pci_sdmmc *host = mmc_priv(mmc); @@ -1281,7 +1208,9 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) voltage = OUTPUT_1V8; if (voltage == OUTPUT_1V8) { - err = sd_wait_voltage_stable_1(host); + /* Stop toggle SD clock */ + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + 0xFF, SD_CLK_FORCE_STOP); if (err < 0) goto out; } @@ -1290,22 +1219,57 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) if (err < 0) goto out; - if (voltage == OUTPUT_1V8) { - err = sd_wait_voltage_stable_2(host); - if (err < 0) - goto out; - } - out: /* Stop toggle SD clock in idle */ - err = rtsx_pci_write_register(pcr, SD_BUS_STAT, - SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); + if (err < 0) + rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); mutex_unlock(&pcr->pcr_mutex); return err; } +static int sdmmc_card_busy(struct mmc_host *mmc) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + int err; + u8 stat; + u8 mask = SD_DAT3_STATUS | SD_DAT2_STATUS | SD_DAT1_STATUS + | SD_DAT0_STATUS; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, + SD_CLK_TOGGLE_EN); + if (err) + goto out; + + mdelay(1); + + err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); + if (err) + goto out; + + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); +out: + mutex_unlock(&pcr->pcr_mutex); + + if (err) + return err; + + /* check if any pin between dat[0:3] is low */ + if ((stat & mask) != mask) + return 1; + else + return 0; +} + static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct realtek_pci_sdmmc *host = mmc_priv(mmc); @@ -1418,6 +1382,7 @@ static const struct mmc_host_ops realtek_pci_sdmmc_ops = { .get_ro = sdmmc_get_ro, .get_cd = sdmmc_get_cd, .start_signal_voltage_switch = sdmmc_switch_voltage, + .card_busy = sdmmc_card_busy, .execute_tuning = sdmmc_execute_tuning, .init_sd_express = sdmmc_init_sd_express, }; @@ -1498,7 +1463,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller found\n"); - mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1529,7 +1494,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) if (ret) { pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); - mmc_free_host(mmc); return ret; } @@ -1572,8 +1536,6 @@ static void rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) pm_runtime_dont_use_autosuspend(&pdev->dev); pm_runtime_disable(&pdev->dev); - mmc_free_host(mmc); - dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller has been removed\n"); } diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index d229c2b83ea9..84674659a84d 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -48,7 +48,7 @@ struct rtsx_usb_sdmmc { bool ddr_mode; unsigned char power_mode; - + u16 ocp_stat; #ifdef RTSX_USB_USE_LEDS_CLASS struct led_classdev led; char led_name[32]; @@ -789,12 +789,20 @@ static int sdmmc_get_cd(struct mmc_host *mmc) if (err) goto no_card; + /* get OCP status */ + host->ocp_stat = (val >> 4) & 0x03; + if (val & SD_CD) { host->card_exist = true; return 1; } no_card: + /* clear OCP status */ + if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) { + rtsx_usb_write_register(ucr, OCPCTL, MS_OCP_CLEAR, MS_OCP_CLEAR); + host->ocp_stat = 0; + } host->card_exist = false; return 0; } @@ -818,7 +826,11 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) cmd->error = -ENOMEDIUM; goto finish_detect_card; } - + /* check OCP stat */ + if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) { + cmd->error = -ENOMEDIUM; + goto finish_detect_card; + } mutex_lock(&ucr->dev_mutex); mutex_lock(&host->host_mutex); @@ -952,6 +964,10 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host) struct rtsx_ucr *ucr = host->ucr; int err; + if (host->ocp_stat & (MS_OCP_NOW | MS_OCP_EVER)) { + dev_dbg(sdmmc_dev(host), "over current\n"); + return -EIO; + } dev_dbg(sdmmc_dev(host), "%s\n", __func__); rtsx_usb_init_cmd(ucr); rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL); @@ -978,8 +994,18 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host) usleep_range(800, 1000); rtsx_usb_init_cmd(ucr); + /* WA OCP issue: after OCP, there were problems with reopen card power */ + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, POWER_MASK, POWER_ON); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, FPDCTL, SSC_POWER_MASK, SSC_POWER_DOWN); + err = rtsx_usb_send_cmd(ucr, MODE_C, 100); + if (err) + return err; + msleep(20); + rtsx_usb_write_register(ucr, FPDCTL, SSC_POWER_MASK, SSC_POWER_ON); + usleep_range(180, 200); + rtsx_usb_init_cmd(ucr); rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_PWR_CTL, - POWER_MASK|LDO3318_PWR_MASK, POWER_ON|LDO_ON); + LDO3318_PWR_MASK, LDO_ON); rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN); @@ -1010,29 +1036,45 @@ static int sd_power_off(struct rtsx_usb_sdmmc *host) return sd_pull_ctl_disable_qfn24(ucr); } -static int sd_set_power_mode(struct rtsx_usb_sdmmc *host, +static void sd_set_power_mode(struct rtsx_usb_sdmmc *host, unsigned char power_mode) { int err; - - if (power_mode != MMC_POWER_OFF) - power_mode = MMC_POWER_ON; + struct rtsx_ucr *ucr = host->ucr; if (power_mode == host->power_mode) - return 0; + return; - if (power_mode == MMC_POWER_OFF) { + switch (power_mode) { + case MMC_POWER_OFF: err = sd_power_off(host); + if (err) + dev_dbg(sdmmc_dev(host), "power-off (err = %d)\n", err); pm_runtime_put_noidle(sdmmc_dev(host)); - } else { + break; + + case MMC_POWER_UP: pm_runtime_get_noresume(sdmmc_dev(host)); err = sd_power_on(host); - } + if (err) + dev_dbg(sdmmc_dev(host), "power-on (err = %d)\n", err); + /* issue the clock signals to card at least 74 clocks */ + rtsx_usb_write_register(ucr, SD_BUS_STAT, SD_CLK_TOGGLE_EN, SD_CLK_TOGGLE_EN); + break; + + case MMC_POWER_ON: + /* stop to send the clock signals */ + rtsx_usb_write_register(ucr, SD_BUS_STAT, SD_CLK_TOGGLE_EN, 0x00); + break; - if (!err) - host->power_mode = power_mode; + case MMC_POWER_UNDEFINED: + break; - return err; + default: + break; + } + + host->power_mode = power_mode; } static int sd_set_timing(struct rtsx_usb_sdmmc *host, @@ -1316,6 +1358,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host) mmc->max_req_size = 524288; host->power_mode = MMC_POWER_OFF; + host->ocp_stat = 0; } static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev) @@ -1334,7 +1377,7 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev) dev_dbg(&(pdev->dev), ": Realtek USB SD/MMC controller found\n"); - mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1368,7 +1411,6 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev) #ifdef RTSX_USB_USE_LEDS_CLASS led_classdev_unregister(&host->led); #endif - mmc_free_host(mmc); pm_runtime_disable(&pdev->dev); return ret; } @@ -1406,7 +1448,6 @@ static void rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev) led_classdev_unregister(&host->led); #endif - mmc_free_host(mmc); pm_runtime_disable(&pdev->dev); platform_set_drvdata(pdev, NULL); @@ -1414,7 +1455,6 @@ static void rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev) ": Realtek USB SD/MMC module has been removed\n"); } -#ifdef CONFIG_PM static int rtsx_usb_sdmmc_runtime_suspend(struct device *dev) { struct rtsx_usb_sdmmc *host = dev_get_drvdata(dev); @@ -1432,11 +1472,9 @@ static int rtsx_usb_sdmmc_runtime_resume(struct device *dev) mmc_detect_change(host->mmc, 0); return 0; } -#endif static const struct dev_pm_ops rtsx_usb_sdmmc_dev_pm_ops = { - SET_RUNTIME_PM_OPS(rtsx_usb_sdmmc_runtime_suspend, - rtsx_usb_sdmmc_runtime_resume, NULL) + RUNTIME_PM_OPS(rtsx_usb_sdmmc_runtime_suspend, rtsx_usb_sdmmc_runtime_resume, NULL) }; static const struct platform_device_id rtsx_usb_sdmmc_ids[] = { @@ -1455,7 +1493,7 @@ static struct platform_driver rtsx_usb_sdmmc_driver = { .driver = { .name = "rtsx_usb_sdmmc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &rtsx_usb_sdmmc_dev_pm_ops, + .pm = pm_ptr(&rtsx_usb_sdmmc_dev_pm_ops), }, }; module_platform_driver(rtsx_usb_sdmmc_driver); diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index e6c5c82f64fa..84c7054607fc 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -948,7 +948,6 @@ err_free: if (c->slot && c->slot->free_slot) c->slot->free_slot(pdev); - sdhci_free_host(c->host); return err; } @@ -972,12 +971,9 @@ static void sdhci_acpi_remove(struct platform_device *pdev) if (c->slot && c->slot->free_slot) c->slot->free_slot(pdev); - - sdhci_free_host(c->host); } -static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed( - struct device *dev) +static void sdhci_acpi_reset_signal_voltage_if_needed(struct device *dev) { struct sdhci_acpi_host *c = dev_get_drvdata(dev); struct sdhci_host *host = c->host; @@ -992,8 +988,6 @@ static void __maybe_unused sdhci_acpi_reset_signal_voltage_if_needed( } } -#ifdef CONFIG_PM_SLEEP - static int sdhci_acpi_suspend(struct device *dev) { struct sdhci_acpi_host *c = dev_get_drvdata(dev); @@ -1020,22 +1014,15 @@ static int sdhci_acpi_resume(struct device *dev) return sdhci_resume_host(c->host); } -#endif - -#ifdef CONFIG_PM - static int sdhci_acpi_runtime_suspend(struct device *dev) { struct sdhci_acpi_host *c = dev_get_drvdata(dev); struct sdhci_host *host = c->host; - int ret; if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); sdhci_acpi_reset_signal_voltage_if_needed(dev); return 0; @@ -1047,15 +1034,13 @@ static int sdhci_acpi_runtime_resume(struct device *dev) sdhci_acpi_byt_setting(&c->pdev->dev); - return sdhci_runtime_resume_host(c->host, 0); + sdhci_runtime_resume_host(c->host, 0); + return 0; } -#endif - static const struct dev_pm_ops sdhci_acpi_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume) - SET_RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend, - sdhci_acpi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(sdhci_acpi_suspend, sdhci_acpi_resume) + RUNTIME_PM_OPS(sdhci_acpi_runtime_suspend, sdhci_acpi_runtime_resume, NULL) }; static struct platform_driver sdhci_acpi_driver = { @@ -1063,7 +1048,7 @@ static struct platform_driver sdhci_acpi_driver = { .name = "sdhci-acpi", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .acpi_match_table = sdhci_acpi_ids, - .pm = &sdhci_acpi_pm_ops, + .pm = pm_ptr(&sdhci_acpi_pm_ops), }, .probe = sdhci_acpi_probe, .remove = sdhci_acpi_remove, diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index fda911fb28e5..0f2a84f769b6 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -304,8 +304,6 @@ err_clk_disable: clk_disable_unprepare(pltfm_priv->clk); err_pltfm_free: - sdhci_pltfm_free(pdev); - dev_err(dev, "Probing of sdhci-pltfm failed: %d\n", ret); return ret; } diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 48cdcba0f39c..57e45951644e 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -31,35 +31,116 @@ #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200 -#define SDIO_CFG_CQ_CAPABILITY 0x4c -#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12) - #define SDIO_CFG_CTRL 0x0 #define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31) #define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30) - +#define SDIO_CFG_OP_DLY 0x34 +#define SDIO_CFG_OP_DLY_DEFAULT 0x80000003 +#define SDIO_CFG_CQ_CAPABILITY 0x4c +#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12) +#define SDIO_CFG_SD_PIN_SEL 0x44 +#define SDIO_CFG_V1_SD_PIN_SEL 0x54 +#define SDIO_CFG_PHY_SW_MODE_0_RX_CTRL 0x7C #define SDIO_CFG_MAX_50MHZ_MODE 0x1ac #define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31) #define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0) +#define SDIO_BOOT_MAIN_CTL 0x0 + #define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V) /* Select all SD UHS type I SDR speed above 50MB/s */ #define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104) -struct sdhci_brcmstb_priv { - void __iomem *cfg_regs; - unsigned int flags; - struct clk *base_clk; - u32 base_freq_hz; +enum cfg_core_ver { + SDIO_CFG_CORE_V1 = 1, + SDIO_CFG_CORE_V2, +}; + +struct sdhci_brcmstb_saved_regs { + u32 sd_pin_sel; + u32 phy_sw_mode0_rxctrl; + u32 max_50mhz_mode; + u32 boot_main_ctl; }; struct brcmstb_match_priv { void (*cfginit)(struct sdhci_host *host); void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios); + void (*save_restore_regs)(struct mmc_host *mmc, int save); struct sdhci_ops *ops; const unsigned int flags; }; +struct sdhci_brcmstb_priv { + void __iomem *cfg_regs; + void __iomem *boot_regs; + struct sdhci_brcmstb_saved_regs saved_regs; + unsigned int flags; + struct clk *base_clk; + u32 base_freq_hz; + const struct brcmstb_match_priv *match_priv; +}; + +static void sdhci_brcmstb_save_regs(struct mmc_host *mmc, enum cfg_core_ver ver) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); + struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs; + void __iomem *cr = priv->cfg_regs; + bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE; + + if (is_emmc && priv->boot_regs) + sr->boot_main_ctl = readl(priv->boot_regs + SDIO_BOOT_MAIN_CTL); + + if (ver == SDIO_CFG_CORE_V1) { + sr->sd_pin_sel = readl(cr + SDIO_CFG_V1_SD_PIN_SEL); + return; + } + + sr->sd_pin_sel = readl(cr + SDIO_CFG_SD_PIN_SEL); + sr->phy_sw_mode0_rxctrl = readl(cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL); + sr->max_50mhz_mode = readl(cr + SDIO_CFG_MAX_50MHZ_MODE); +} + +static void sdhci_brcmstb_restore_regs(struct mmc_host *mmc, enum cfg_core_ver ver) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); + struct sdhci_brcmstb_saved_regs *sr = &priv->saved_regs; + void __iomem *cr = priv->cfg_regs; + bool is_emmc = mmc->caps & MMC_CAP_NONREMOVABLE; + + if (is_emmc && priv->boot_regs) + writel(sr->boot_main_ctl, priv->boot_regs + SDIO_BOOT_MAIN_CTL); + + if (ver == SDIO_CFG_CORE_V1) { + writel(sr->sd_pin_sel, cr + SDIO_CFG_V1_SD_PIN_SEL); + return; + } + + writel(sr->sd_pin_sel, cr + SDIO_CFG_SD_PIN_SEL); + writel(sr->phy_sw_mode0_rxctrl, cr + SDIO_CFG_PHY_SW_MODE_0_RX_CTRL); + writel(sr->max_50mhz_mode, cr + SDIO_CFG_MAX_50MHZ_MODE); +} + +static void sdhci_brcmstb_save_restore_regs_v1(struct mmc_host *mmc, int save) +{ + if (save) + sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V1); + else + sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V1); +} + +static void sdhci_brcmstb_save_restore_regs_v2(struct mmc_host *mmc, int save) +{ + if (save) + sdhci_brcmstb_save_regs(mmc, SDIO_CFG_CORE_V2); + else + sdhci_brcmstb_restore_regs(mmc, SDIO_CFG_CORE_V2); +} + static inline void enable_clock_gating(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -212,6 +293,21 @@ static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host) } } +static void sdhci_brcmstb_set_72116_uhs_signaling(struct sdhci_host *host, unsigned int timing) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); + u32 reg; + + /* no change to SDIO_CFG_OP_DLY_DEFAULT when using preset clk rate */ + if (!(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) + return; + + reg = (timing == MMC_TIMING_MMC_HS200) ? 0 : SDIO_CFG_OP_DLY_DEFAULT; + writel(reg, priv->cfg_regs + SDIO_CFG_OP_DLY); + sdhci_set_uhs_signaling(host, timing); +} + static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc) { sdhci_dumpregs(mmc_priv(mmc)); @@ -252,6 +348,13 @@ static struct sdhci_ops sdhci_brcmstb_ops_2712 = { .set_uhs_signaling = sdhci_set_uhs_signaling, }; +static struct sdhci_ops sdhci_brcmstb_ops_72116 = { + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_brcmstb_set_72116_uhs_signaling, +}; + static struct sdhci_ops sdhci_brcmstb_ops_7216 = { .set_clock = sdhci_brcmstb_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -277,19 +380,33 @@ static struct brcmstb_match_priv match_priv_7425 = { .ops = &sdhci_brcmstb_ops, }; +static struct brcmstb_match_priv match_priv_74371 = { + .flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT, + .ops = &sdhci_brcmstb_ops, +}; + static struct brcmstb_match_priv match_priv_7445 = { .flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT, + .save_restore_regs = sdhci_brcmstb_save_restore_regs_v1, .ops = &sdhci_brcmstb_ops, }; +static struct brcmstb_match_priv match_priv_72116 = { + .flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT, + .save_restore_regs = sdhci_brcmstb_save_restore_regs_v1, + .ops = &sdhci_brcmstb_ops_72116, +}; + static const struct brcmstb_match_priv match_priv_7216 = { .flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE, + .save_restore_regs = sdhci_brcmstb_save_restore_regs_v2, .hs400es = sdhci_brcmstb_hs400es, .ops = &sdhci_brcmstb_ops_7216, }; static struct brcmstb_match_priv match_priv_74165b0 = { .flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE, + .save_restore_regs = sdhci_brcmstb_save_restore_regs_v2, .hs400es = sdhci_brcmstb_hs400es, .ops = &sdhci_brcmstb_ops_74165b0, }; @@ -297,7 +414,9 @@ static struct brcmstb_match_priv match_priv_74165b0 = { static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = { { .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 }, { .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 }, + { .compatible = "brcm,bcm74371-sdhci", .data = &match_priv_74371 }, { .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 }, + { .compatible = "brcm,bcm72116-sdhci", .data = &match_priv_72116 }, { .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 }, { .compatible = "brcm,bcm74165b0-sdhci", .data = &match_priv_74165b0 }, {}, @@ -395,6 +514,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); priv = sdhci_pltfm_priv(pltfm_host); + priv->match_priv = match->data; if (device_property_read_bool(&pdev->dev, "supports-cqe")) { priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE; match_priv->ops->irq = sdhci_brcmstb_cqhci_irq; @@ -412,6 +532,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) if (res) goto err; + /* map non-standard BOOT registers if present */ + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) { + priv->boot_regs = devm_platform_get_and_ioremap_resource(pdev, 2, NULL); + if (IS_ERR(priv->boot_regs)) + priv->boot_regs = NULL; + } + /* * Automatic clock gating does not work for SD cards that may * voltage switch so only enable it for non-removable devices. @@ -485,7 +612,6 @@ add_host: return res; err: - sdhci_pltfm_free(pdev); clk_disable_unprepare(base_clk); return res; } @@ -497,14 +623,18 @@ static void sdhci_brcmstb_shutdown(struct platform_device *pdev) MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match); -#ifdef CONFIG_PM_SLEEP static int sdhci_brcmstb_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); + const struct brcmstb_match_priv *match_priv = priv->match_priv; + int ret; + if (match_priv->save_restore_regs) + match_priv->save_restore_regs(host->mmc, 1); + clk_disable_unprepare(priv->base_clk); if (host->mmc->caps2 & MMC_CAP2_CQE) { ret = cqhci_suspend(host->mmc); @@ -520,6 +650,7 @@ static int sdhci_brcmstb_resume(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); + const struct brcmstb_match_priv *match_priv = priv->match_priv; int ret; ret = sdhci_pltfm_resume(dev); @@ -536,22 +667,22 @@ static int sdhci_brcmstb_resume(struct device *dev) ret = clk_set_rate(priv->base_clk, priv->base_freq_hz); } + if (match_priv->save_restore_regs) + match_priv->save_restore_regs(host->mmc, 0); + if (host->mmc->caps2 & MMC_CAP2_CQE) ret = cqhci_resume(host->mmc); return ret; } -#endif -static const struct dev_pm_ops sdhci_brcmstb_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(sdhci_brcmstb_suspend, sdhci_brcmstb_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_brcmstb_pmops, sdhci_brcmstb_suspend, sdhci_brcmstb_resume); static struct platform_driver sdhci_brcmstb_driver = { .driver = { .name = "sdhci-brcmstb", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &sdhci_brcmstb_pmops, + .pm = pm_sleep_ptr(&sdhci_brcmstb_pmops), .of_match_table = of_match_ptr(sdhci_brcm_of_match), }, .probe = sdhci_brcmstb_probe, diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index a94b297fcf2a..435603c8c00b 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -36,6 +36,24 @@ #define SDHCI_CDNS_HRS06_MODE_MMC_HS400 0x5 #define SDHCI_CDNS_HRS06_MODE_MMC_HS400ES 0x6 +/* Read block gap */ +#define SDHCI_CDNS_HRS37 0x94 /* interface mode select */ +#define SDHCI_CDNS_HRS37_MODE_DS 0x0 +#define SDHCI_CDNS_HRS37_MODE_HS 0x1 +#define SDHCI_CDNS_HRS37_MODE_UDS_SDR12 0x8 +#define SDHCI_CDNS_HRS37_MODE_UDS_SDR25 0x9 +#define SDHCI_CDNS_HRS37_MODE_UDS_SDR50 0xa +#define SDHCI_CDNS_HRS37_MODE_UDS_SDR104 0xb +#define SDHCI_CDNS_HRS37_MODE_UDS_DDR50 0xc +#define SDHCI_CDNS_HRS37_MODE_MMC_LEGACY 0x20 +#define SDHCI_CDNS_HRS37_MODE_MMC_SDR 0x21 +#define SDHCI_CDNS_HRS37_MODE_MMC_DDR 0x22 +#define SDHCI_CDNS_HRS37_MODE_MMC_HS200 0x23 +#define SDHCI_CDNS_HRS37_MODE_MMC_HS400 0x24 +#define SDHCI_CDNS_HRS37_MODE_MMC_HS400ES 0x25 +#define SDHCI_CDNS_HRS38 0x98 /* Read block gap coefficient */ +#define SDHCI_CDNS_HRS38_BLKGAP_MAX 0xf + /* SRS - Slot Register Set (SDHCI-compatible) */ #define SDHCI_CDNS_SRS_BASE 0x200 @@ -144,7 +162,7 @@ static unsigned int sdhci_cdns_phy_param_count(struct device_node *np) int i; for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) - if (of_property_read_bool(np, sdhci_cdns_phy_cfgs[i].property)) + if (of_property_present(np, sdhci_cdns_phy_cfgs[i].property)) count++; return count; @@ -251,6 +269,43 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) return 0; } +/** + * sdhci_cdns_tune_blkgap() - tune multi-block read gap + * @mmc: MMC host + * + * Tune delay used in multi block read. To do so, + * try sending multi-block read command with incremented gap, unless + * it succeeds. + * + * Return: error code + */ +static int sdhci_cdns_tune_blkgap(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_cdns_priv *priv = sdhci_pltfm_priv(pltfm_host); + void __iomem *hrs37_reg = priv->hrs_addr + SDHCI_CDNS_HRS37; + void __iomem *hrs38_reg = priv->hrs_addr + SDHCI_CDNS_HRS38; + int ret; + u32 gap; + + /* Currently only needed in HS200 mode */ + if (host->timing != MMC_TIMING_MMC_HS200) + return 0; + + writel(SDHCI_CDNS_HRS37_MODE_MMC_HS200, hrs37_reg); + + for (gap = 0; gap <= SDHCI_CDNS_HRS38_BLKGAP_MAX; gap++) { + writel(gap, hrs38_reg); + ret = mmc_read_tuning(mmc, 512, 32); + if (!ret) + break; + } + + dev_dbg(mmc_dev(mmc), "read block gap tune %s, gap %d\n", ret ? "failed" : "OK", gap); + return ret; +} + /* * In SD mode, software must not use the hardware tuning and instead perform * an almost identical procedure to eMMC. @@ -261,6 +316,7 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode) int max_streak = 0; int end_of_streak = 0; int i; + int ret; /* * Do not execute tuning for UHS_SDR50 or UHS_DDR50. @@ -288,7 +344,11 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode) return -EIO; } - return sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2); + ret = sdhci_cdns_set_tune_val(host, end_of_streak - max_streak / 2); + if (ret) + return ret; + + return sdhci_cdns_tune_blkgap(host->mmc); } static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host, @@ -433,6 +493,13 @@ static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = { }, }; +static const struct sdhci_cdns_drv_data sdhci_eyeq_drv_data = { + .pltfm_data = { + .ops = &sdhci_cdns_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, +}; + static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = { .pltfm_data = { .ops = &sdhci_cdns_ops, @@ -515,7 +582,7 @@ static int sdhci_cdns_probe(struct platform_device *pdev) if (data->init) { ret = data->init(pdev); if (ret) - goto free; + return ret; } sdhci_enable_v4_mode(host); __sdhci_read_caps(host, &version, NULL, NULL); @@ -524,36 +591,26 @@ static int sdhci_cdns_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto free; + return ret; sdhci_cdns_phy_param_parse(dev->of_node, priv); ret = sdhci_cdns_phy_init(priv); if (ret) - goto free; + return ret; if (host->mmc->caps & MMC_CAP_HW_RESET) { priv->rst_hw = devm_reset_control_get_optional_exclusive(dev, NULL); - if (IS_ERR(priv->rst_hw)) { - ret = dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw), + if (IS_ERR(priv->rst_hw)) + return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw), "reset controller error\n"); - goto free; - } if (priv->rst_hw) host->mmc_host_ops.card_hw_reset = sdhci_cdns_mmc_hw_reset; } - ret = sdhci_add_host(host); - if (ret) - goto free; - - return 0; -free: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } -#ifdef CONFIG_PM_SLEEP static int sdhci_cdns_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -580,11 +637,8 @@ disable_clk: return ret; } -#endif -static const struct dev_pm_ops sdhci_cdns_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(sdhci_pltfm_suspend, sdhci_cdns_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_cdns_pm_ops, sdhci_pltfm_suspend, sdhci_cdns_resume); static const struct of_device_id sdhci_cdns_match[] = { { @@ -595,6 +649,10 @@ static const struct of_device_id sdhci_cdns_match[] = { .compatible = "amd,pensando-elba-sd4hc", .data = &sdhci_elba_drv_data, }, + { + .compatible = "mobileye,eyeq-sd4hc", + .data = &sdhci_eyeq_drv_data, + }, { .compatible = "cdns,sd4hc" }, { /* sentinel */ } }; @@ -604,7 +662,7 @@ static struct platform_driver sdhci_cdns_driver = { .driver = { .name = "sdhci-cdns", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &sdhci_cdns_pm_ops, + .pm = pm_sleep_ptr(&sdhci_cdns_pm_ops), .of_match_table = sdhci_cdns_match, }, .probe = sdhci_cdns_probe, diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 77034b13fa66..dbfaee4a5ada 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -79,17 +79,9 @@ static int sdhci_dove_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err_sdhci_add; + return ret; - ret = sdhci_add_host(host); - if (ret) - goto err_sdhci_add; - - return 0; - -err_sdhci_add: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static const struct of_device_id sdhci_dove_of_match_table[] = { diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ff78a7c6a04c..18ecddd6df6f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -31,7 +31,9 @@ #include "cqhci.h" #define ESDHC_SYS_CTRL_DTOCV_MASK GENMASK(19, 16) +#define ESDHC_SYS_CTRL_RST_FIFO BIT(22) #define ESDHC_SYS_CTRL_IPP_RST_N BIT(23) +#define ESDHC_SYS_CTRL_RESET_TUNING BIT(28) #define ESDHC_CTRL_D3CD 0x08 #define ESDHC_BURST_LEN_EN_INCR (1 << 27) /* VENDOR SPEC register */ @@ -81,7 +83,11 @@ #define ESDHC_TUNE_CTRL_STEP 1 #define ESDHC_TUNE_CTRL_MIN 0 #define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1) - +#define ESDHC_TUNE_CTRL_STATUS_TAP_SEL_MASK GENMASK(30, 16) +#define ESDHC_TUNE_CTRL_STATUS_TAP_SEL_PRE_MASK GENMASK(30, 24) +#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK GENMASK(14, 8) +#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK GENMASK(7, 4) +#define ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK GENMASK(3, 0) /* strobe dll register */ #define ESDHC_STROBE_DLL_CTRL 0x70 #define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0) @@ -104,6 +110,7 @@ #define ESDHC_TUNING_CTRL 0xcc #define ESDHC_STD_TUNING_EN (1 << 24) +#define ESDHC_TUNING_WINDOW_MASK GENMASK(22, 20) /* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ #define ESDHC_TUNING_START_TAP_DEFAULT 0x1 #define ESDHC_TUNING_START_TAP_MASK 0x7f @@ -205,6 +212,13 @@ /* The IP does not have GPIO CD wake capabilities */ #define ESDHC_FLAG_SKIP_CD_WAKE BIT(18) +/* the controller has dummy pad for clock loopback */ +#define ESDHC_FLAG_DUMMY_PAD BIT(19) + +#define ESDHC_AUTO_TUNING_WINDOW 3 +/* 100ms timeout for data inhibit */ +#define ESDHC_DATA_INHIBIT_WAIT_US 100000 + enum wp_types { ESDHC_WP_NONE, /* no WP, neither controller nor gpio */ ESDHC_WP_CONTROLLER, /* mmc controller internal WP */ @@ -235,6 +249,8 @@ struct esdhc_platform_data { unsigned int tuning_step; /* The delay cell steps in tuning procedure */ unsigned int tuning_start_tap; /* The start delay cell point in tuning procedure */ unsigned int strobe_dll_delay_target; /* The delay cell for strobe pad (read clock) */ + unsigned int saved_tuning_delay_cell; /* save the value of tuning delay cell */ + unsigned int saved_auto_tuning_window; /* save the auto tuning window width */ }; struct esdhc_soc_data { @@ -264,35 +280,35 @@ static const struct esdhc_soc_data usdhc_imx6q_data = { }; static const struct esdhc_soc_data usdhc_imx6sl_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536 | ESDHC_FLAG_HS200 | ESDHC_FLAG_BROKEN_AUTO_CMD23, }; static const struct esdhc_soc_data usdhc_imx6sll_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_HS400 | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; static const struct esdhc_soc_data usdhc_imx6sx_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_STATE_LOST_IN_LPMODE | ESDHC_FLAG_BROKEN_AUTO_CMD23, }; static const struct esdhc_soc_data usdhc_imx6ull_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_ERR010450 | ESDHC_FLAG_STATE_LOST_IN_LPMODE, }; static const struct esdhc_soc_data usdhc_imx7d_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_HS400 | ESDHC_FLAG_STATE_LOST_IN_LPMODE @@ -307,8 +323,16 @@ static struct esdhc_soc_data usdhc_s32g2_data = { .quirks = SDHCI_QUIRK_NO_LED, }; +static struct esdhc_soc_data usdhc_s32n79_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES + | ESDHC_FLAG_SKIP_ERR004536, + .quirks = SDHCI_QUIRK_NO_LED, +}; + static struct esdhc_soc_data usdhc_imx7ulp_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_PMQOS | ESDHC_FLAG_HS400 | ESDHC_FLAG_STATE_LOST_IN_LPMODE, @@ -321,7 +345,7 @@ static struct esdhc_soc_data usdhc_imxrt1050_data = { }; static struct esdhc_soc_data usdhc_imx8qxp_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES | ESDHC_FLAG_STATE_LOST_IN_LPMODE @@ -330,13 +354,22 @@ static struct esdhc_soc_data usdhc_imx8qxp_data = { }; static struct esdhc_soc_data usdhc_imx8mm_data = { - .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES | ESDHC_FLAG_STATE_LOST_IN_LPMODE, .quirks = SDHCI_QUIRK_NO_LED, }; +static struct esdhc_soc_data usdhc_imx95_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 + | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES + | ESDHC_FLAG_STATE_LOST_IN_LPMODE + | ESDHC_FLAG_DUMMY_PAD, + .quirks = SDHCI_QUIRK_NO_LED, +}; + struct pltfm_imx_data { u32 scratchpad; struct pinctrl *pinctrl; @@ -381,8 +414,11 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx7ulp-usdhc", .data = &usdhc_imx7ulp_data, }, { .compatible = "fsl,imx8qxp-usdhc", .data = &usdhc_imx8qxp_data, }, { .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx8mm_data, }, + { .compatible = "fsl,imx94-usdhc", .data = &usdhc_imx95_data, }, + { .compatible = "fsl,imx95-usdhc", .data = &usdhc_imx95_data, }, { .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, }, { .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, }, + { .compatible = "nxp,s32n79-usdhc", .data = &usdhc_s32n79_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); @@ -717,23 +753,17 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { u32 v = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS); - u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); - if (val & SDHCI_CTRL_TUNED_CLK) { + if (val & SDHCI_CTRL_TUNED_CLK) v |= ESDHC_MIX_CTRL_SMPCLK_SEL; - } else { + else v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; - m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; - } - if (val & SDHCI_CTRL_EXEC_TUNING) { + if (val & SDHCI_CTRL_EXEC_TUNING) v |= ESDHC_MIX_CTRL_EXE_TUNE; - m |= ESDHC_MIX_CTRL_FBCLK_SEL; - } else { + else v &= ~ESDHC_MIX_CTRL_EXE_TUNE; - } writel(v, host->ioaddr + SDHCI_AUTO_CMD_STATUS); - writel(m, host->ioaddr + ESDHC_MIX_CTRL); } return; case SDHCI_TRANSFER_MODE: @@ -870,6 +900,11 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) esdhc_clrset_le(host, mask, new_val, reg); return; + case SDHCI_TIMEOUT_CONTROL: + esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK, + FIELD_PREP(ESDHC_SYS_CTRL_DTOCV_MASK, val), + ESDHC_SYSTEM_CONTROL); + return; case SDHCI_SOFTWARE_RESET: if (val & SDHCI_RESET_DATA) new_val = readl(host->ioaddr + SDHCI_HOST_CONTROL); @@ -1057,7 +1092,7 @@ 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; + u32 ctrl, tuning_ctrl, sys_ctrl; int ret; /* Reset the tuning circuit */ @@ -1066,11 +1101,25 @@ static void esdhc_reset_tuning(struct sdhci_host *host) ctrl &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN; if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { 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) { writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL); + /* + * enable the std tuning just in case it cleared in + * sdhc_esdhc_tuning_restore. + */ + tuning_ctrl = readl(host->ioaddr + ESDHC_TUNING_CTRL); + if (!(tuning_ctrl & ESDHC_STD_TUNING_EN)) { + tuning_ctrl |= ESDHC_STD_TUNING_EN; + writel(tuning_ctrl, host->ioaddr + ESDHC_TUNING_CTRL); + } + + /* set the reset tuning bit */ + sys_ctrl = readl(host->ioaddr + ESDHC_SYSTEM_CONTROL); + sys_ctrl |= ESDHC_SYS_CTRL_RESET_TUNING; + writel(sys_ctrl, host->ioaddr + ESDHC_SYSTEM_CONTROL); + ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS); ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE; @@ -1130,7 +1179,7 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) { - u32 reg; + u32 reg, sys_ctrl; u8 sw_rst; int ret; @@ -1146,13 +1195,23 @@ static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) "warning! RESET_ALL never complete before sending tuning command\n"); reg = readl(host->ioaddr + ESDHC_MIX_CTRL); - reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | - ESDHC_MIX_CTRL_FBCLK_SEL; + reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL; writel(reg, host->ioaddr + ESDHC_MIX_CTRL); - writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); + writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK, val), + host->ioaddr + ESDHC_TUNE_CTRL_STATUS); dev_dbg(mmc_dev(host->mmc), "tuning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n", val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS)); + + /* set RST_FIFO to reset the async FIFO, and wat it to self-clear */ + sys_ctrl = readl(host->ioaddr + ESDHC_SYSTEM_CONTROL); + sys_ctrl |= ESDHC_SYS_CTRL_RST_FIFO; + writel(sys_ctrl, host->ioaddr + ESDHC_SYSTEM_CONTROL); + ret = readl_poll_timeout(host->ioaddr + ESDHC_SYSTEM_CONTROL, sys_ctrl, + !(sys_ctrl & ESDHC_SYS_CTRL_RST_FIFO), 10, 100); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), + "warning! RST_FIFO not clear in 100us\n"); } static void esdhc_post_tuning(struct sdhci_host *host) @@ -1172,9 +1231,10 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) { int min, max, avg, ret; int win_length, target_min, target_max, target_win_length; + u32 clk_tune_ctrl_status, temp; - min = ESDHC_TUNE_CTRL_MIN; - max = ESDHC_TUNE_CTRL_MIN; + min = target_min = ESDHC_TUNE_CTRL_MIN; + max = target_max = ESDHC_TUNE_CTRL_MIN; target_win_length = 0; while (max < ESDHC_TUNE_CTRL_MAX) { /* find the mininum delay first which can pass tuning */ @@ -1211,6 +1271,30 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) /* use average delay to get the best timing */ avg = (target_min + target_max) / 2; esdhc_prepare_tuning(host, avg); + + /* + * adjust the delay according to tuning window, make preparation + * for the auto-tuning logic. According to hardware suggest, need + * to config the auto tuning window width to 3, to make the auto + * tuning logic have enough space to handle the sample point shift + * caused by temperature change. + */ + clk_tune_ctrl_status = FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK, + avg - ESDHC_AUTO_TUNING_WINDOW) | + FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK, + ESDHC_AUTO_TUNING_WINDOW) | + FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK, + ESDHC_AUTO_TUNING_WINDOW); + + writel(clk_tune_ctrl_status, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); + ret = readl_poll_timeout(host->ioaddr + ESDHC_TUNE_CTRL_STATUS, temp, + clk_tune_ctrl_status == + FIELD_GET(ESDHC_TUNE_CTRL_STATUS_TAP_SEL_MASK, temp), + 1, 10); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), + "clock tuning control status not set in 10us\n"); + ret = mmc_send_tuning(host->mmc, opcode, NULL); esdhc_post_tuning(host); @@ -1365,11 +1449,37 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) break; } + if (!(imx_data->socdata->flags & ESDHC_FLAG_DUMMY_PAD) && + (timing == MMC_TIMING_UHS_SDR104 || + timing == MMC_TIMING_MMC_HS200 || + timing == MMC_TIMING_MMC_HS400)) + m |= ESDHC_MIX_CTRL_FBCLK_SEL; + else + m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; + + writel(m, host->ioaddr + ESDHC_MIX_CTRL); + esdhc_change_pinstate(host, timing); } static void esdhc_reset(struct sdhci_host *host, u8 mask) { + u32 present_state; + int ret; + + /* + * For data or full reset, ensure any active data transfer completes + * before resetting to avoid system hang. + */ + if (mask & (SDHCI_RESET_DATA | SDHCI_RESET_ALL)) { + ret = readl_poll_timeout_atomic(host->ioaddr + ESDHC_PRSSTAT, present_state, + !(present_state & SDHCI_DATA_INHIBIT), 2, + ESDHC_DATA_INHIBIT_WAIT_US); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), + "timeout waiting for data transfer completion\n"); + } + sdhci_and_cqhci_reset(host, mask); sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); @@ -1385,17 +1495,6 @@ static unsigned int esdhc_get_max_timeout_count(struct sdhci_host *host) return esdhc_is_usdhc(imx_data) ? 1 << 29 : 1 << 27; } -static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) -{ - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); - - /* use maximum timeout counter */ - esdhc_clrset_le(host, ESDHC_SYS_CTRL_DTOCV_MASK, - esdhc_is_usdhc(imx_data) ? 0xF0000 : 0xE0000, - ESDHC_SYSTEM_CONTROL); -} - static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask) { int cmd_error = 0; @@ -1432,7 +1531,6 @@ static struct sdhci_ops sdhci_esdhc_ops = { .get_min_clock = esdhc_pltfm_get_min_clock, .get_max_timeout_count = esdhc_get_max_timeout_count, .get_ro = esdhc_pltfm_get_ro, - .set_timeout = esdhc_set_timeout, .set_bus_width = esdhc_pltfm_set_bus_width, .set_uhs_signaling = esdhc_set_uhs_signaling, .reset = esdhc_reset, @@ -1529,6 +1627,16 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) << ESDHC_TUNING_STEP_SHIFT; } + /* + * Config the tuning window to the hardware suggested value 3. + * This tuning window is used for auto tuning logic. The default + * tuning window is 2, here change to 3 make the window a bit + * wider, give auto tuning enough space to handle the sample + * point shift cause by temperature change. + */ + tmp &= ~ESDHC_TUNING_WINDOW_MASK; + tmp |= FIELD_PREP(ESDHC_TUNING_WINDOW_MASK, ESDHC_AUTO_TUNING_WINDOW); + /* Disable the CMD CRC check for tuning, if not, need to * add some delay after every tuning command, because * hardware standard tuning logic will directly go to next @@ -1569,6 +1677,63 @@ static void sdhci_esdhc_imx_hwinit(struct sdhci_host *host) } } +static void sdhc_esdhc_tuning_save(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 reg; + + /* + * SD/eMMC do not need this tuning save because it will re-init + * after system resume back. + * Here save the tuning delay value for SDIO device since it may + * keep power during system PM. And for usdhc, only SDR50 and + * SDR104 mode for SDIO device need to do tuning, and need to + * save/restore. + */ + if (host->timing == MMC_TIMING_UHS_SDR50 || + host->timing == MMC_TIMING_UHS_SDR104) { + reg = readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS); + reg = FIELD_GET(ESDHC_TUNE_CTRL_STATUS_TAP_SEL_PRE_MASK, reg); + imx_data->boarddata.saved_tuning_delay_cell = reg; + } +} + +static void sdhc_esdhc_tuning_restore(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 reg; + + if (host->timing == MMC_TIMING_UHS_SDR50 || + host->timing == MMC_TIMING_UHS_SDR104) { + /* + * restore the tuning delay value actually is a + * manual tuning method, so clear the standard + * tuning enable bit here. Will set back this + * ESDHC_STD_TUNING_EN in esdhc_reset_tuning() + * when trigger re-tuning. + */ + reg = readl(host->ioaddr + ESDHC_TUNING_CTRL); + reg &= ~ESDHC_STD_TUNING_EN; + writel(reg, host->ioaddr + ESDHC_TUNING_CTRL); + + reg = readl(host->ioaddr + ESDHC_MIX_CTRL); + reg |= ESDHC_MIX_CTRL_SMPCLK_SEL; + if (!(imx_data->socdata->flags & ESDHC_FLAG_DUMMY_PAD)) + reg |= ESDHC_MIX_CTRL_FBCLK_SEL; + writel(reg, host->ioaddr + ESDHC_MIX_CTRL); + + writel(FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_PRE_MASK, + imx_data->boarddata.saved_tuning_delay_cell) | + FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_OUT_MASK, + ESDHC_AUTO_TUNING_WINDOW) | + FIELD_PREP(ESDHC_TUNE_CTRL_STATUS_DLY_CELL_SET_POST_MASK, + ESDHC_AUTO_TUNING_WINDOW), + host->ioaddr + ESDHC_TUNE_CTRL_STATUS); + } +} + static void esdhc_cqe_enable(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); @@ -1657,8 +1822,6 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, of_property_read_u32(np, "fsl,strobe-dll-delay-target", &boarddata->strobe_dll_delay_target); - if (of_property_read_bool(np, "no-1-8-v")) - host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) boarddata->delay_line = 0; @@ -1677,9 +1840,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, if (ret) return ret; - /* HS400/HS400ES require 8 bit bus */ - if (!(host->mmc->caps & MMC_CAP_8_BIT_DATA)) - host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); + sdhci_get_property(pdev); if (mmc_gpio_get_cd(host->mmc) >= 0) host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; @@ -1777,6 +1938,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) * to distinguish the card type. */ host->mmc_host_ops.init_card = usdhc_init_card; + + host->max_timeout_count = 0xF; } if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) @@ -1849,7 +2012,6 @@ disable_per_clk: free_sdhci: if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) cpu_latency_qos_remove_request(&imx_data->pm_qos_req); - sdhci_pltfm_free(pdev); return err; } @@ -1873,11 +2035,8 @@ static void sdhci_esdhc_imx_remove(struct platform_device *pdev) if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) cpu_latency_qos_remove_request(&imx_data->pm_qos_req); - - sdhci_pltfm_free(pdev); } -#ifdef CONFIG_PM_SLEEP static int sdhci_esdhc_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -1885,11 +2044,14 @@ static int sdhci_esdhc_suspend(struct device *dev) struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); int ret; - if (host->mmc->caps2 & MMC_CAP2_CQE) { - ret = cqhci_suspend(host->mmc); - if (ret) - return ret; - } + /* + * Switch to runtime resume for two reasons: + * 1, there is register access (e.g., wakeup control register), so + * need to make sure gate on ipg clock. + * 2, make sure the pm_runtime_force_resume() in sdhci_esdhc_resume() really + * invoke its ->runtime_resume callback (needs_force_resume = 1). + */ + pm_runtime_get_sync(dev); if ((imx_data->socdata->flags & ESDHC_FLAG_STATE_LOST_IN_LPMODE) && (host->tuning_mode != SDHCI_TUNING_MODE_1)) { @@ -1897,49 +2059,80 @@ static int sdhci_esdhc_suspend(struct device *dev) mmc_retune_needed(host->mmc); } - if (host->tuning_mode != SDHCI_TUNING_MODE_3) - mmc_retune_needed(host->mmc); - - ret = sdhci_suspend_host(host); - if (ret) - return ret; - - ret = pinctrl_pm_select_sleep_state(dev); - if (ret) - return ret; + /* + * For the device need to keep power during system PM, need + * to save the tuning delay value just in case the usdhc + * lost power during system PM. + */ + if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) && + esdhc_is_usdhc(imx_data)) + sdhc_esdhc_tuning_save(host); + + if (device_may_wakeup(dev)) { + /* The irqs of imx are not shared. It is safe to disable */ + disable_irq(host->irq); + ret = sdhci_enable_irq_wakeups(host); + if (!ret) + dev_warn(dev, "Failed to enable irq wakeup\n"); + } else { + /* + * For the device which works as wakeup source, no need + * to change the pinctrl to sleep state. + * e.g. For SDIO device, the interrupt share with data pin, + * but the pinctrl sleep state may config the data pin to + * other function like GPIO function to save power in PM, + * which finally block the SDIO wakeup function. + */ + ret = pinctrl_pm_select_sleep_state(dev); + if (ret) + return ret; + } ret = mmc_gpio_set_cd_wake(host->mmc, true); + /* + * Make sure invoke runtime_suspend to gate off clock. + * uSDHC IP supports in-band SDIO wakeup even without clock. + */ + pm_runtime_force_suspend(dev); + return ret; } static int sdhci_esdhc_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); int ret; - ret = pinctrl_pm_select_default_state(dev); + pm_runtime_force_resume(dev); + + ret = mmc_gpio_set_cd_wake(host->mmc, false); if (ret) return ret; /* re-initialize hw state in case it's lost in low power mode */ sdhci_esdhc_imx_hwinit(host); - ret = sdhci_resume_host(host); - if (ret) - return ret; + if (host->irq_wake_enabled) { + sdhci_disable_irq_wakeups(host); + enable_irq(host->irq); + } - if (host->mmc->caps2 & MMC_CAP2_CQE) - ret = cqhci_resume(host->mmc); + /* + * restore the saved tuning delay value for the device which keep + * power during system PM. + */ + if (mmc_card_keep_power(host->mmc) && mmc_card_wake_sdio_irq(host->mmc) && + esdhc_is_usdhc(imx_data)) + sdhc_esdhc_tuning_restore(host); - if (!ret) - ret = mmc_gpio_set_cd_wake(host->mmc, false); + pm_runtime_put_autosuspend(dev); return ret; } -#endif -#ifdef CONFIG_PM static int sdhci_esdhc_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -1953,9 +2146,7 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev) return ret; } - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -1969,7 +2160,7 @@ static int sdhci_esdhc_runtime_suspend(struct device *dev) if (imx_data->socdata->flags & ESDHC_FLAG_PMQOS) cpu_latency_qos_remove_request(&imx_data->pm_qos_req); - return ret; + return 0; } static int sdhci_esdhc_runtime_resume(struct device *dev) @@ -1999,17 +2190,13 @@ static int sdhci_esdhc_runtime_resume(struct device *dev) esdhc_pltfm_set_clock(host, imx_data->actual_clock); - err = sdhci_runtime_resume_host(host, 0); - if (err) - goto disable_ipg_clk; + sdhci_runtime_resume_host(host, 0); if (host->mmc->caps2 & MMC_CAP2_CQE) err = cqhci_resume(host->mmc); return err; -disable_ipg_clk: - clk_disable_unprepare(imx_data->clk_ipg); disable_per_clk: clk_disable_unprepare(imx_data->clk_per); disable_ahb_clk: @@ -2019,12 +2206,10 @@ remove_pm_qos_request: cpu_latency_qos_remove_request(&imx_data->pm_qos_req); return err; } -#endif static const struct dev_pm_ops sdhci_esdhc_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume) - SET_RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend, - sdhci_esdhc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(sdhci_esdhc_suspend, sdhci_esdhc_resume) + RUNTIME_PM_OPS(sdhci_esdhc_runtime_suspend, sdhci_esdhc_runtime_resume, NULL) }; static struct platform_driver sdhci_esdhc_imx_driver = { @@ -2032,7 +2217,7 @@ static struct platform_driver sdhci_esdhc_imx_driver = { .name = "sdhci-esdhc-imx", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = imx_esdhc_dt_ids, - .pm = &sdhci_esdhc_pmops, + .pm = pm_ptr(&sdhci_esdhc_pmops), }, .probe = sdhci_esdhc_imx_probe, .remove = sdhci_esdhc_imx_remove, diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c index 327662ba5bd9..375fce5639d7 100644 --- a/drivers/mmc/host/sdhci-esdhc-mcf.c +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c @@ -426,28 +426,22 @@ static int sdhci_esdhc_mcf_probe(struct platform_device *pdev) host->flags |= SDHCI_AUTO_CMD12; mcf_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(mcf_data->clk_ipg)) { - err = PTR_ERR(mcf_data->clk_ipg); - goto err_exit; - } + if (IS_ERR(mcf_data->clk_ipg)) + return PTR_ERR(mcf_data->clk_ipg); mcf_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); - if (IS_ERR(mcf_data->clk_ahb)) { - err = PTR_ERR(mcf_data->clk_ahb); - goto err_exit; - } + if (IS_ERR(mcf_data->clk_ahb)) + return PTR_ERR(mcf_data->clk_ahb); mcf_data->clk_per = devm_clk_get(&pdev->dev, "per"); - if (IS_ERR(mcf_data->clk_per)) { - err = PTR_ERR(mcf_data->clk_per); - goto err_exit; - } + if (IS_ERR(mcf_data->clk_per)) + return PTR_ERR(mcf_data->clk_per); pltfm_host->clk = mcf_data->clk_per; pltfm_host->clock = clk_get_rate(pltfm_host->clk); err = clk_prepare_enable(mcf_data->clk_per); if (err) - goto err_exit; + return err; err = clk_prepare_enable(mcf_data->clk_ipg); if (err) @@ -485,9 +479,6 @@ unprep_ipg: clk_disable_unprepare(mcf_data->clk_ipg); unprep_per: clk_disable_unprepare(mcf_data->clk_per); -err_exit: - sdhci_pltfm_free(pdev); - return err; } @@ -502,8 +493,6 @@ static void sdhci_esdhc_mcf_remove(struct platform_device *pdev) clk_disable_unprepare(mcf_data->clk_ipg); clk_disable_unprepare(mcf_data->clk_ahb); clk_disable_unprepare(mcf_data->clk_per); - - sdhci_pltfm_free(pdev); } static struct platform_driver sdhci_esdhc_mcf_driver = { diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index 80b2567a488b..35ef5c5f5146 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -379,7 +379,7 @@ static int sdhci_iproc_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err; + return ret; sdhci_get_property(pdev); @@ -387,10 +387,8 @@ static int sdhci_iproc_probe(struct platform_device *pdev) if (dev->of_node) { pltfm_host->clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(pltfm_host->clk)) { - ret = PTR_ERR(pltfm_host->clk); - goto err; - } + if (IS_ERR(pltfm_host->clk)) + return PTR_ERR(pltfm_host->clk); } if (iproc_host->data->missing_caps) { @@ -399,15 +397,7 @@ static int sdhci_iproc_probe(struct platform_device *pdev) &iproc_host->data->caps1); } - ret = sdhci_add_host(host); - if (ret) - goto err; - - return 0; - -err: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static void sdhci_iproc_shutdown(struct platform_device *pdev) diff --git a/drivers/mmc/host/sdhci-milbeaut.c b/drivers/mmc/host/sdhci-milbeaut.c index a4675456f9c7..bda71d5966dc 100644 --- a/drivers/mmc/host/sdhci-milbeaut.c +++ b/drivers/mmc/host/sdhci-milbeaut.c @@ -258,7 +258,7 @@ static int sdhci_milbeaut_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err; + return ret; platform_set_drvdata(pdev, host); @@ -267,23 +267,19 @@ static int sdhci_milbeaut_probe(struct platform_device *pdev) host->irq = irq; host->ioaddr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(host->ioaddr)) { - ret = PTR_ERR(host->ioaddr); - goto err; - } + if (IS_ERR(host->ioaddr)) + return PTR_ERR(host->ioaddr); if (dev_of_node(dev)) { sdhci_get_of_property(pdev); priv->clk_iface = devm_clk_get(&pdev->dev, "iface"); - if (IS_ERR(priv->clk_iface)) { - ret = PTR_ERR(priv->clk_iface); - goto err; - } + if (IS_ERR(priv->clk_iface)) + return PTR_ERR(priv->clk_iface); ret = clk_prepare_enable(priv->clk_iface); if (ret) - goto err; + return ret; priv->clk = devm_clk_get(&pdev->dev, "core"); if (IS_ERR(priv->clk)) { @@ -308,8 +304,6 @@ err_add_host: clk_disable_unprepare(priv->clk); err_clk: clk_disable_unprepare(priv->clk_iface); -err: - sdhci_free_host(host); return ret; } @@ -324,7 +318,6 @@ static void sdhci_milbeaut_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clk_iface); clk_disable_unprepare(priv->clk); - sdhci_free_host(host); platform_set_drvdata(pdev, NULL); } diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index e3d39311fdc7..633462c0be5f 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -81,6 +81,7 @@ #define CORE_IO_PAD_PWR_SWITCH_EN BIT(15) #define CORE_IO_PAD_PWR_SWITCH BIT(16) #define CORE_HC_SELECT_IN_EN BIT(18) +#define CORE_HC_SELECT_IN_SDR50 (4 << 19) #define CORE_HC_SELECT_IN_HS400 (6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) @@ -156,6 +157,17 @@ #define CQHCI_VENDOR_CFG1 0xA00 #define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13) +/* non command queue crypto enable register*/ +#define NONCQ_CRYPTO_PARM 0x70 +#define NONCQ_CRYPTO_DUN 0x74 + +#define DISABLE_CRYPTO BIT(15) +#define CRYPTO_GENERAL_ENABLE BIT(1) +#define HC_VENDOR_SPECIFIC_FUNC4 0x260 + +#define ICE_HCI_PARAM_CCI GENMASK(7, 0) +#define ICE_HCI_PARAM_CE GENMASK(8, 8) + struct sdhci_msm_offset { u32 core_hc_mode; u32 core_mci_data_cnt; @@ -299,6 +311,7 @@ struct sdhci_msm_host { u32 dll_config; u32 ddr_config; bool vqmmc_enabled; + bool non_cqe_ice_init_done; }; static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) @@ -343,41 +356,43 @@ static void sdhci_msm_v5_variant_writel_relaxed(u32 val, writel_relaxed(val, host->ioaddr + offset); } -static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host) +static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host, + unsigned int clock, + unsigned int timing) { - struct mmc_ios ios = host->mmc->ios; /* * The SDHC requires internal clock frequency to be double the * actual clock that will be set for DDR mode. The controller * uses the faster clock(100/400MHz) for some of its parts and * send the actual required clock (50/200MHz) to the card. */ - if (ios.timing == MMC_TIMING_UHS_DDR50 || - ios.timing == MMC_TIMING_MMC_DDR52 || - ios.timing == MMC_TIMING_MMC_HS400 || + if (timing == MMC_TIMING_UHS_DDR50 || + timing == MMC_TIMING_MMC_DDR52 || + (timing == MMC_TIMING_MMC_HS400 && + clock == MMC_HS200_MAX_DTR) || host->flags & SDHCI_HS400_TUNING) return 2; return 1; } static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, - unsigned int clock) + unsigned int clock, + unsigned int timing) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); - struct mmc_ios curr_ios = host->mmc->ios; struct clk *core_clk = msm_host->bulk_clks[0].clk; unsigned long achieved_rate; unsigned int desired_rate; unsigned int mult; int rc; - mult = msm_get_clock_mult_for_bus_mode(host); + mult = msm_get_clock_mult_for_bus_mode(host, clock, timing); desired_rate = clock * mult; rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate); if (rc) { pr_err("%s: Failed to set clock at rate %u at timing %d\n", - mmc_hostname(host->mmc), desired_rate, curr_ios.timing); + mmc_hostname(host->mmc), desired_rate, timing); return; } @@ -396,7 +411,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, msm_host->clk_rate = desired_rate; pr_debug("%s: Setting clock at rate %lu at timing %d\n", - mmc_hostname(host->mmc), achieved_rate, curr_ios.timing); + mmc_hostname(host->mmc), achieved_rate, timing); } /* Platform specific tuning */ @@ -1133,6 +1148,10 @@ static bool sdhci_msm_is_tuning_needed(struct sdhci_host *host) { struct mmc_ios *ios = &host->mmc->ios; + if (ios->timing == MMC_TIMING_UHS_SDR50 && + host->flags & SDHCI_SDR50_NEEDS_TUNING) + return true; + /* * Tuning is required for SDR104, HS200 and HS400 cards and * if clock frequency is greater than 100MHz in these modes. @@ -1201,6 +1220,8 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) struct mmc_ios ios = host->mmc->ios; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + const struct sdhci_msm_offset *msm_offset = msm_host->offset; + u32 config; if (!sdhci_msm_is_tuning_needed(host)) { msm_host->use_cdr = false; @@ -1217,6 +1238,14 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) */ msm_host->tuning_done = 0; + if (ios.timing == MMC_TIMING_UHS_SDR50 && + host->flags & SDHCI_SDR50_NEEDS_TUNING) { + config = readl_relaxed(host->ioaddr + msm_offset->core_vendor_spec); + config &= ~CORE_HC_SELECT_IN_MASK; + config |= CORE_HC_SELECT_IN_EN | CORE_HC_SELECT_IN_SDR50; + writel_relaxed(config, host->ioaddr + msm_offset->core_vendor_spec); + } + /* * For HS400 tuning in HS200 timing requires: * - select MCLK/2 in VENDOR_SPEC @@ -1224,7 +1253,7 @@ static int sdhci_msm_execute_tuning(struct mmc_host *mmc, u32 opcode) */ if (host->flags & SDHCI_HS400_TUNING) { sdhci_msm_hc_select_mode(host); - msm_set_clock_rate_for_bus_mode(host, ios.clock); + msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing); host->flags &= ~SDHCI_HS400_TUNING; } @@ -1564,6 +1593,7 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = host->mmc; bool done = false; u32 val = SWITCHABLE_SIGNALING_VOLTAGE; const struct sdhci_msm_offset *msm_offset = @@ -1621,6 +1651,12 @@ static void sdhci_msm_check_power_status(struct sdhci_host *host, u32 req_type) "%s: pwr_irq for req: (%d) timed out\n", mmc_hostname(host->mmc), req_type); } + + if ((req_type & REQ_BUS_ON) && mmc->card && !mmc->ops->get_cd(mmc)) { + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + host->pwr = 0; + } + pr_debug("%s: %s: request %d done\n", mmc_hostname(host->mmc), __func__, req_type); } @@ -1679,6 +1715,13 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) udelay(10); } + if ((irq_status & CORE_PWRCTL_BUS_ON) && mmc->card && + !mmc->ops->get_cd(mmc)) { + msm_host_writel(msm_host, CORE_PWRCTL_BUS_FAIL, host, + msm_offset->core_pwrctl_ctl); + return; + } + /* Handle BUS ON/OFF*/ if (irq_status & CORE_PWRCTL_BUS_ON) { pwr_state = REQ_BUS_ON; @@ -1835,6 +1878,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_ios ios = host->mmc->ios; if (!clock) { host->mmc->actual_clock = msm_host->clk_rate = 0; @@ -1843,7 +1887,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_msm_hc_select_mode(host); - msm_set_clock_rate_for_bus_mode(host, clock); + msm_set_clock_rate_for_bus_mode(host, ios.clock, ios.timing); out: __sdhci_msm_set_clock(host, clock); } @@ -1873,7 +1917,7 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS)) return 0; - ice = of_qcom_ice_get(dev); + ice = devm_of_qcom_ice_get(dev); if (ice == ERR_PTR(-EOPNOTSUPP)) { dev_warn(dev, "Disabling inline encryption support\n"); ice = NULL; @@ -1895,6 +1939,7 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, profile->ll_ops = sdhci_msm_crypto_ops; profile->max_dun_bytes_supported = 4; + profile->key_types_supported = qcom_ice_get_supported_key_type(ice); profile->dev = dev; /* @@ -1923,7 +1968,7 @@ static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host) qcom_ice_enable(msm_host->ice); } -static __maybe_unused int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) +static int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) { if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO) return qcom_ice_resume(msm_host->ice); @@ -1931,7 +1976,7 @@ static __maybe_unused int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) return 0; } -static __maybe_unused int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host) +static int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host) { if (msm_host->mmc->caps2 & MMC_CAP2_CRYPTO) return qcom_ice_suspend(msm_host->ice); @@ -1961,16 +2006,7 @@ static int sdhci_msm_ice_keyslot_program(struct blk_crypto_profile *profile, struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); - /* Only AES-256-XTS has been tested so far. */ - if (key->crypto_cfg.crypto_mode != BLK_ENCRYPTION_MODE_AES_256_XTS) - return -EOPNOTSUPP; - - return qcom_ice_program_key(msm_host->ice, - QCOM_ICE_CRYPTO_ALG_AES_XTS, - QCOM_ICE_CRYPTO_KEY_SIZE_256, - key->raw, - key->crypto_cfg.data_unit_size / 512, - slot); + return qcom_ice_program_key(msm_host->ice, slot, key); } static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile, @@ -1983,9 +2019,111 @@ static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile, return qcom_ice_evict_key(msm_host->ice, slot); } +static int sdhci_msm_ice_derive_sw_secret(struct blk_crypto_profile *profile, + const u8 *eph_key, size_t eph_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_derive_sw_secret(msm_host->ice, eph_key, eph_key_size, + sw_secret); +} + +static int sdhci_msm_ice_import_key(struct blk_crypto_profile *profile, + const u8 *raw_key, size_t raw_key_size, + u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_import_key(msm_host->ice, raw_key, raw_key_size, lt_key); +} + +static int sdhci_msm_ice_generate_key(struct blk_crypto_profile *profile, + u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_generate_key(msm_host->ice, lt_key); +} + +static int sdhci_msm_ice_prepare_key(struct blk_crypto_profile *profile, + const u8 *lt_key, size_t lt_key_size, + u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_prepare_key(msm_host->ice, lt_key, lt_key_size, eph_key); +} + +static void sdhci_msm_non_cqe_ice_init(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = msm_host->mmc; + struct cqhci_host *cq_host = mmc->cqe_private; + u32 config; + + config = sdhci_readl(host, HC_VENDOR_SPECIFIC_FUNC4); + config &= ~DISABLE_CRYPTO; + sdhci_writel(host, config, HC_VENDOR_SPECIFIC_FUNC4); + config = cqhci_readl(cq_host, CQHCI_CFG); + config |= CRYPTO_GENERAL_ENABLE; + cqhci_writel(cq_host, config, CQHCI_CFG); +} + +static void sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = msm_host->mmc; + struct cqhci_host *cq_host = mmc->cqe_private; + unsigned int crypto_params = 0; + int key_index; + + if (mrq->crypto_ctx) { + if (!msm_host->non_cqe_ice_init_done) { + sdhci_msm_non_cqe_ice_init(host); + msm_host->non_cqe_ice_init_done = true; + } + + key_index = mrq->crypto_key_slot; + crypto_params = FIELD_PREP(ICE_HCI_PARAM_CE, 1) | + FIELD_PREP(ICE_HCI_PARAM_CCI, key_index); + + cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM); + cqhci_writel(cq_host, lower_32_bits(mrq->crypto_ctx->bc_dun[0]), + NONCQ_CRYPTO_DUN); + } else { + cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM); + } + + /* Ensure crypto configuration is written before proceeding */ + wmb(); +} + +/* + * Handle non-CQE MMC requests with ICE crypto support. + * Configures ICE registers before passing the request to + * the standard SDHCI handler. + */ +static void sdhci_msm_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct sdhci_host *host = mmc_priv(mmc); + + /* Only need to handle non-CQE crypto requests in this path */ + if (mmc->caps2 & MMC_CAP2_CRYPTO) + sdhci_msm_ice_cfg(host, mrq); + + sdhci_request(mmc, mrq); +} + static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = { .keyslot_program = sdhci_msm_ice_keyslot_program, .keyslot_evict = sdhci_msm_ice_keyslot_evict, + .derive_sw_secret = sdhci_msm_ice_derive_sw_secret, + .import_key = sdhci_msm_ice_import_key, + .generate_key = sdhci_msm_ice_generate_key, + .prepare_key = sdhci_msm_ice_prepare_key, }; #else /* CONFIG_MMC_CRYPTO */ @@ -2000,13 +2138,13 @@ static inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host) { } -static inline __maybe_unused int +static inline int sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) { return 0; } -static inline __maybe_unused int +static inline int sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host) { return 0; @@ -2529,7 +2667,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto pltfm_free; + return ret; /* * Based on the compatible string, load the required msm host info from @@ -2551,7 +2689,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_msm_gcc_reset(&pdev->dev, host); if (ret) - goto pltfm_free; + return ret; /* Setup SDCC bus voter clock. */ msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus"); @@ -2559,10 +2697,10 @@ static int sdhci_msm_probe(struct platform_device *pdev) /* Vote for max. clk rate for max. performance */ ret = clk_set_rate(msm_host->bus_clk, INT_MAX); if (ret) - goto pltfm_free; + return ret; ret = clk_prepare_enable(msm_host->bus_clk); if (ret) - goto pltfm_free; + return ret; } /* Setup main peripheral bus clock */ @@ -2733,6 +2871,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY; +#ifdef CONFIG_MMC_CRYPTO + host->mmc_host_ops.request = sdhci_msm_request; +#endif /* Set the timeout value to max possible */ host->max_timeout_count = 0xF; @@ -2753,7 +2894,6 @@ static int sdhci_msm_probe(struct platform_device *pdev) if (ret) goto pm_runtime_disable; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; @@ -2768,8 +2908,6 @@ clk_disable: bus_clk_disable: if (!IS_ERR(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); -pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -2791,10 +2929,9 @@ static void sdhci_msm_remove(struct platform_device *pdev) msm_host->bulk_clks); if (!IS_ERR(msm_host->bus_clk)) clk_disable_unprepare(msm_host->bus_clk); - sdhci_pltfm_free(pdev); } -static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) +static int sdhci_msm_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -2813,7 +2950,7 @@ static __maybe_unused int sdhci_msm_runtime_suspend(struct device *dev) return sdhci_msm_ice_suspend(msm_host); } -static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) +static int sdhci_msm_runtime_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -2849,11 +2986,8 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) } static const struct dev_pm_ops sdhci_msm_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, - sdhci_msm_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume, NULL) }; static struct platform_driver sdhci_msm_driver = { @@ -2862,7 +2996,7 @@ static struct platform_driver sdhci_msm_driver = { .driver = { .name = "sdhci_msm", .of_match_table = sdhci_msm_dt_match, - .pm = &sdhci_msm_pm_ops, + .pm = pm_ptr(&sdhci_msm_pm_ops), .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; diff --git a/drivers/mmc/host/sdhci-npcm.c b/drivers/mmc/host/sdhci-npcm.c index bee0585ba5c1..71b635dfdf1d 100644 --- a/drivers/mmc/host/sdhci-npcm.c +++ b/drivers/mmc/host/sdhci-npcm.c @@ -48,8 +48,7 @@ static int npcm_sdhci_probe(struct platform_device *pdev) pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL); if (IS_ERR(pltfm_host->clk)) { - ret = PTR_ERR(pltfm_host->clk); - goto err_sdhci; + return PTR_ERR(pltfm_host->clk); } caps = sdhci_readl(host, SDHCI_CAPABILITIES); @@ -58,17 +57,9 @@ static int npcm_sdhci_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err_sdhci; + return ret; - ret = sdhci_add_host(host); - if (ret) - goto err_sdhci; - - return 0; - -err_sdhci: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static const struct of_device_id npcm_sdhci_of_match[] = { diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 8c29676ab662..785d3acb18c5 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -99,6 +99,9 @@ #define HIWORD_UPDATE(val, mask, shift) \ ((val) << (shift) | (mask) << ((shift) + 16)) +#define CD_STABLE_TIMEOUT_US 2000000 +#define CD_STABLE_MAX_SLEEP_US 10 + /** * struct sdhci_arasan_soc_ctl_field - Field used in sdhci_arasan_soc_ctl_map * @@ -149,8 +152,7 @@ struct sdhci_arasan_clk_ops { * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. * @sampleclk_hw: Struct for the clock we might provide to a PHY. * @sampleclk: Pointer to normal 'struct clock' for sampleclk_hw. - * @clk_phase_in: Array of Input Clock Phase Delays for all speed modes - * @clk_phase_out: Array of Output Clock Phase Delays for all speed modes + * @phase_map: Struct for mmc_clk_phase_map provided. * @set_clk_delays: Function pointer for setting Clock Delays * @clk_of_data: Platform specific runtime clock data storage pointer */ @@ -159,8 +161,7 @@ struct sdhci_arasan_clk_data { struct clk *sdcardclk; struct clk_hw sampleclk_hw; struct clk *sampleclk; - int clk_phase_in[MMC_TIMING_MMC_HS400 + 1]; - int clk_phase_out[MMC_TIMING_MMC_HS400 + 1]; + struct mmc_clk_phase_map phase_map; void (*set_clk_delays)(struct sdhci_host *host); void *clk_of_data; }; @@ -206,12 +207,15 @@ struct sdhci_arasan_data { * 19MHz instead */ #define SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN BIT(2) +/* Enable CD stable check before power-up */ +#define SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE BIT(3) }; struct sdhci_arasan_of_data { const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; const struct sdhci_pltfm_data *pdata; const struct sdhci_arasan_clk_ops *clk_ops; + u32 quirks; }; static const struct sdhci_arasan_soc_ctl_map rk3399_soc_ctl_map = { @@ -514,6 +518,24 @@ static int sdhci_arasan_voltage_switch(struct mmc_host *mmc, return -EINVAL; } +static void sdhci_arasan_set_power_and_bus_voltage(struct sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); + u32 reg; + + /* + * Ensure that the card detect logic has stabilized before powering up, this is + * necessary after a host controller reset. + */ + if (mode == MMC_POWER_UP && sdhci_arasan->quirks & SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE) + read_poll_timeout(sdhci_readl, reg, reg & SDHCI_CD_STABLE, CD_STABLE_MAX_SLEEP_US, + CD_STABLE_TIMEOUT_US, false, host, SDHCI_PRESENT_STATE); + + sdhci_set_power_and_bus_voltage(host, mode, vdd); +} + static const struct sdhci_ops sdhci_arasan_ops = { .set_clock = sdhci_arasan_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, @@ -521,7 +543,7 @@ static const struct sdhci_ops sdhci_arasan_ops = { .set_bus_width = sdhci_set_bus_width, .reset = sdhci_arasan_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, - .set_power = sdhci_set_power_and_bus_voltage, + .set_power = sdhci_arasan_set_power_and_bus_voltage, .hw_reset = sdhci_arasan_hw_reset, }; @@ -570,7 +592,7 @@ static const struct sdhci_ops sdhci_arasan_cqe_ops = { .set_bus_width = sdhci_set_bus_width, .reset = sdhci_arasan_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, - .set_power = sdhci_set_power_and_bus_voltage, + .set_power = sdhci_arasan_set_power_and_bus_voltage, .irq = sdhci_arasan_cqhci_irq, }; @@ -581,7 +603,6 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = { SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, }; -#ifdef CONFIG_PM_SLEEP /** * sdhci_arasan_suspend - Suspend method for the driver * @dev: Address of the device structure @@ -675,10 +696,9 @@ static int sdhci_arasan_resume(struct device *dev) return 0; } -#endif /* ! CONFIG_PM_SLEEP */ -static SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, - sdhci_arasan_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_arasan_dev_pm_ops, sdhci_arasan_suspend, + sdhci_arasan_resume); /** * sdhci_arasan_sdcardclk_recalc_rate - Return the card clock rate @@ -1227,36 +1247,9 @@ static void sdhci_arasan_set_clk_delays(struct sdhci_host *host) struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data; clk_set_phase(clk_data->sampleclk, - clk_data->clk_phase_in[host->timing]); + clk_data->phase_map.phase[host->timing].in_deg); clk_set_phase(clk_data->sdcardclk, - clk_data->clk_phase_out[host->timing]); -} - -static void arasan_dt_read_clk_phase(struct device *dev, - struct sdhci_arasan_clk_data *clk_data, - unsigned int timing, const char *prop) -{ - struct device_node *np = dev->of_node; - - u32 clk_phase[2] = {0}; - int ret; - - /* - * Read Tap Delay values from DT, if the DT does not contain the - * Tap Values then use the pre-defined values. - */ - ret = of_property_read_variable_u32_array(np, prop, &clk_phase[0], - 2, 0); - if (ret < 0) { - dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n", - prop, clk_data->clk_phase_in[timing], - clk_data->clk_phase_out[timing]); - return; - } - - /* The values read are Input and Output Clock Delays in order */ - clk_data->clk_phase_in[timing] = clk_phase[0]; - clk_data->clk_phase_out[timing] = clk_phase[1]; + clk_data->phase_map.phase[host->timing].out_deg); } /** @@ -1293,8 +1286,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev, } for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { - clk_data->clk_phase_in[i] = zynqmp_iclk_phase[i]; - clk_data->clk_phase_out[i] = zynqmp_oclk_phase[i]; + clk_data->phase_map.phase[i].in_deg = zynqmp_iclk_phase[i]; + clk_data->phase_map.phase[i].out_deg = zynqmp_oclk_phase[i]; } } @@ -1305,8 +1298,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev, VERSAL_OCLK_PHASE; for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { - clk_data->clk_phase_in[i] = versal_iclk_phase[i]; - clk_data->clk_phase_out[i] = versal_oclk_phase[i]; + clk_data->phase_map.phase[i].in_deg = versal_iclk_phase[i]; + clk_data->phase_map.phase[i].out_deg = versal_oclk_phase[i]; } } if (of_device_is_compatible(dev->of_node, "xlnx,versal-net-emmc")) { @@ -1316,32 +1309,12 @@ static void arasan_dt_parse_clk_phases(struct device *dev, VERSAL_NET_EMMC_OCLK_PHASE; for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { - clk_data->clk_phase_in[i] = versal_net_iclk_phase[i]; - clk_data->clk_phase_out[i] = versal_net_oclk_phase[i]; + clk_data->phase_map.phase[i].in_deg = versal_net_iclk_phase[i]; + clk_data->phase_map.phase[i].out_deg = versal_net_oclk_phase[i]; } } - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY, - "clk-phase-legacy"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS, - "clk-phase-mmc-hs"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS, - "clk-phase-sd-hs"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12, - "clk-phase-uhs-sdr12"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25, - "clk-phase-uhs-sdr25"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50, - "clk-phase-uhs-sdr50"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104, - "clk-phase-uhs-sdr104"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50, - "clk-phase-uhs-ddr50"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52, - "clk-phase-mmc-ddr52"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200, - "clk-phase-mmc-hs200"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400, - "clk-phase-mmc-hs400"); + + mmc_of_parse_clk_phase(dev, &clk_data->phase_map); } static const struct sdhci_pltfm_data sdhci_arasan_pdata = { @@ -1447,6 +1420,7 @@ static const struct sdhci_arasan_clk_ops zynqmp_clk_ops = { static struct sdhci_arasan_of_data sdhci_arasan_zynqmp_data = { .pdata = &sdhci_arasan_zynqmp_pdata, .clk_ops = &zynqmp_clk_ops, + .quirks = SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE, }; static const struct sdhci_arasan_clk_ops versal_clk_ops = { @@ -1457,6 +1431,7 @@ static const struct sdhci_arasan_clk_ops versal_clk_ops = { static struct sdhci_arasan_of_data sdhci_arasan_versal_data = { .pdata = &sdhci_arasan_zynqmp_pdata, .clk_ops = &versal_clk_ops, + .quirks = SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE, }; static const struct sdhci_arasan_clk_ops versal_net_clk_ops = { @@ -1467,6 +1442,7 @@ static const struct sdhci_arasan_clk_ops versal_net_clk_ops = { static struct sdhci_arasan_of_data sdhci_arasan_versal_net_data = { .pdata = &sdhci_arasan_versal_net_pdata, .clk_ops = &versal_net_clk_ops, + .quirks = SDHCI_ARASAN_QUIRK_ENSURE_CD_STABLE, }; static struct sdhci_arasan_of_data intel_keembay_emmc_data = { @@ -1487,6 +1463,17 @@ static struct sdhci_arasan_of_data intel_keembay_sdio_data = { .clk_ops = &arasan_clk_ops, }; +static const struct sdhci_pltfm_data sdhci_arasan_axiado_pdata = { + .ops = &sdhci_arasan_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_CQE, +}; + +static struct sdhci_arasan_of_data sdhci_arasan_axiado_data = { + .pdata = &sdhci_arasan_axiado_pdata, + .clk_ops = &arasan_clk_ops, +}; + static const struct of_device_id sdhci_arasan_of_match[] = { /* SoC-specific compatible strings w/ soc_ctl_map */ { @@ -1513,6 +1500,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = { .compatible = "intel,keembay-sdhci-5.1-sdio", .data = &intel_keembay_sdio_data, }, + { + .compatible = "axiado,ax3000-sdhci-5.1-emmc", + .data = &sdhci_arasan_axiado_data, + }, /* Generic compatible below here */ { .compatible = "arasan,sdhci-8.9a", @@ -1883,34 +1874,26 @@ static int sdhci_arasan_probe(struct platform_device *pdev) sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node); of_node_put(node); - if (IS_ERR(sdhci_arasan->soc_ctl_base)) { - ret = dev_err_probe(dev, + if (IS_ERR(sdhci_arasan->soc_ctl_base)) + return dev_err_probe(dev, PTR_ERR(sdhci_arasan->soc_ctl_base), "Can't get syscon\n"); - goto err_pltfm_free; - } } sdhci_get_of_property(pdev); sdhci_arasan->clk_ahb = devm_clk_get(dev, "clk_ahb"); - if (IS_ERR(sdhci_arasan->clk_ahb)) { - ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb), + if (IS_ERR(sdhci_arasan->clk_ahb)) + return dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb), "clk_ahb clock not found.\n"); - goto err_pltfm_free; - } clk_xin = devm_clk_get(dev, "clk_xin"); - if (IS_ERR(clk_xin)) { - ret = dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n"); - goto err_pltfm_free; - } + if (IS_ERR(clk_xin)) + return dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n"); ret = clk_prepare_enable(sdhci_arasan->clk_ahb); - if (ret) { - dev_err(dev, "Unable to enable AHB clock.\n"); - goto err_pltfm_free; - } + if (ret) + return dev_err_probe(dev, ret, "Unable to enable AHB clock.\n"); /* If clock-frequency property is set, use the provided value */ if (pltfm_host->clock && @@ -1945,6 +1928,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev) if (of_device_is_compatible(np, "rockchip,rk3399-sdhci-5.1")) sdhci_arasan_update_clockmultiplier(host, 0x0); + sdhci_arasan->quirks |= data->quirks; + if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") || of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sd") || of_device_is_compatible(np, "intel,keembay-sdhci-5.1-sdio")) { @@ -1972,7 +1957,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) { - ret = dev_err_probe(dev, ret, "parsing dt failed.\n"); + dev_err_probe(dev, ret, "parsing dt failed.\n"); goto unreg_clk; } @@ -2029,8 +2014,6 @@ clk_disable_all: clk_disable_unprepare(clk_xin); clk_dis_ahb: clk_disable_unprepare(sdhci_arasan->clk_ahb); -err_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -2061,7 +2044,7 @@ static struct platform_driver sdhci_arasan_driver = { .name = "sdhci-arasan", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sdhci_arasan_of_match, - .pm = &sdhci_arasan_dev_pm_ops, + .pm = pm_sleep_ptr(&sdhci_arasan_dev_pm_ops), }, .probe = sdhci_arasan_probe, .remove = sdhci_arasan_remove, diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index d6de010551b9..f5d973783cbe 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -13,6 +13,7 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/reset.h> #include <linux/spinlock.h> #include "sdhci-pltfm.h" @@ -425,10 +426,8 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) return PTR_ERR(pltfm_host->clk); ret = clk_prepare_enable(pltfm_host->clk); - if (ret) { - dev_err(&pdev->dev, "Unable to enable SDIO clock\n"); - goto err_pltfm_free; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "Unable to enable SDIO clock\n"); ret = mmc_of_parse(host->mmc); if (ret) @@ -445,8 +444,6 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) err_sdhci_add: clk_disable_unprepare(pltfm_host->clk); -err_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -461,8 +458,6 @@ static void aspeed_sdhci_remove(struct platform_device *pdev) sdhci_remove_host(host, 0); clk_disable_unprepare(pltfm_host->clk); - - sdhci_pltfm_free(pdev); } static const struct aspeed_sdhci_pdata ast2400_sdhci_pdata = { @@ -525,7 +520,8 @@ static struct platform_driver aspeed_sdhci_driver = { static int aspeed_sdc_probe(struct platform_device *pdev) { - struct device_node *parent, *child; + struct reset_control *reset; + struct device_node *parent; struct aspeed_sdc *sdc; int ret; @@ -535,6 +531,10 @@ static int aspeed_sdc_probe(struct platform_device *pdev) spin_lock_init(&sdc->lock); + reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(reset), "unable to acquire reset\n"); + sdc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sdc->clk)) return PTR_ERR(sdc->clk); @@ -554,12 +554,11 @@ static int aspeed_sdc_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, sdc); parent = pdev->dev.of_node; - for_each_available_child_of_node(parent, child) { + for_each_available_child_of_node_scoped(parent, child) { struct platform_device *cpdev; cpdev = of_platform_device_create(child, NULL, &pdev->dev); if (!cpdev) { - of_node_put(child); ret = -ENODEV; goto err_clk; } diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index 97988ed37467..7c4ac65f247d 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -229,7 +229,6 @@ static int sdhci_at91_set_clks_presets(struct device *dev) return 0; } -#ifdef CONFIG_PM_SLEEP static int sdhci_at91_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -243,17 +242,14 @@ static int sdhci_at91_suspend(struct device *dev) return ret; } -#endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM static int sdhci_at91_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_at91_priv *priv = sdhci_pltfm_priv(pltfm_host); - int ret; - ret = sdhci_runtime_suspend_host(host); + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -262,7 +258,7 @@ static int sdhci_at91_runtime_suspend(struct device *dev) clk_disable_unprepare(priv->hclock); clk_disable_unprepare(priv->mainck); - return ret; + return 0; } static int sdhci_at91_runtime_resume(struct device *dev) @@ -300,15 +296,13 @@ static int sdhci_at91_runtime_resume(struct device *dev) } out: - return sdhci_runtime_resume_host(host, 0); + sdhci_runtime_resume_host(host, 0); + return 0; } -#endif /* CONFIG_PM */ static const struct dev_pm_ops sdhci_at91_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(sdhci_at91_suspend, pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(sdhci_at91_runtime_suspend, - sdhci_at91_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(sdhci_at91_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(sdhci_at91_runtime_suspend, sdhci_at91_runtime_resume, NULL) }; static int sdhci_at91_probe(struct platform_device *pdev) @@ -333,32 +327,26 @@ static int sdhci_at91_probe(struct platform_device *pdev) priv->mainck = devm_clk_get(&pdev->dev, "baseclk"); if (IS_ERR(priv->mainck)) { - if (soc_data->baseclk_is_generated_internally) { + if (soc_data->baseclk_is_generated_internally) priv->mainck = NULL; - } else { - dev_err(&pdev->dev, "failed to get baseclk\n"); - ret = PTR_ERR(priv->mainck); - goto sdhci_pltfm_free; - } + else + return dev_err_probe(&pdev->dev, PTR_ERR(priv->mainck), + "failed to get baseclk\n"); } priv->hclock = devm_clk_get(&pdev->dev, "hclock"); - if (IS_ERR(priv->hclock)) { - dev_err(&pdev->dev, "failed to get hclock\n"); - ret = PTR_ERR(priv->hclock); - goto sdhci_pltfm_free; - } + if (IS_ERR(priv->hclock)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->hclock), + "failed to get hclock\n"); priv->gck = devm_clk_get(&pdev->dev, "multclk"); - if (IS_ERR(priv->gck)) { - dev_err(&pdev->dev, "failed to get multclk\n"); - ret = PTR_ERR(priv->gck); - goto sdhci_pltfm_free; - } + if (IS_ERR(priv->gck)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->gck), + "failed to get multclk\n"); ret = sdhci_at91_set_clks_presets(&pdev->dev); if (ret) - goto sdhci_pltfm_free; + return ret; priv->restore_needed = false; @@ -438,8 +426,6 @@ clocks_disable_unprepare: clk_disable_unprepare(priv->gck); clk_disable_unprepare(priv->mainck); clk_disable_unprepare(priv->hclock); -sdhci_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -468,7 +454,7 @@ static struct platform_driver sdhci_at91_driver = { .name = "sdhci-at91", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sdhci_at91_dt_match, - .pm = &sdhci_at91_dev_pm_ops, + .pm = pm_ptr(&sdhci_at91_dev_pm_ops), }, .probe = sdhci_at91_probe, .remove = sdhci_at91_remove, diff --git a/drivers/mmc/host/sdhci-of-bst.c b/drivers/mmc/host/sdhci-of-bst.c new file mode 100644 index 000000000000..f8d3df715e1a --- /dev/null +++ b/drivers/mmc/host/sdhci-of-bst.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SDHCI driver for Black Sesame Technologies C1200 controller + * + * Copyright (c) 2025 Black Sesame Technologies + */ + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include "sdhci.h" +#include "sdhci-pltfm.h" + +/* SDHCI register extensions */ +#define SDHCI_CLOCK_PLL_EN 0x0008 +#define SDHCI_VENDOR_PTR_R 0xE8 + +/* BST-specific tuning parameters */ +#define BST_TUNING_COUNT 0x20 + +/* Synopsys vendor specific registers */ +#define SDHC_EMMC_CTRL_R_OFFSET 0x2C +#define MBIU_CTRL 0x510 + +/* MBIU burst control bits */ +#define BURST_INCR16_EN BIT(3) +#define BURST_INCR8_EN BIT(2) +#define BURST_INCR4_EN BIT(1) +#define BURST_EN (BURST_INCR16_EN | BURST_INCR8_EN | BURST_INCR4_EN) +#define MBIU_BURST_MASK GENMASK(3, 0) + +/* CRM (Clock/Reset/Management) register offsets */ +#define SDEMMC_CRM_BCLK_DIV_CTRL 0x08 +#define SDEMMC_CRM_TIMER_DIV_CTRL 0x0C +#define SDEMMC_CRM_RX_CLK_CTRL 0x14 +#define SDEMMC_CRM_VOL_CTRL 0x1C +#define REG_WR_PROTECT 0x88 +#define DELAY_CHAIN_SEL 0x94 + +/* CRM register values and bit definitions */ +#define REG_WR_PROTECT_KEY 0x1234abcd +#define BST_VOL_STABLE_ON BIT(7) +#define BST_TIMER_DIV_MASK GENMASK(7, 0) +#define BST_TIMER_DIV_VAL 0x20 +#define BST_TIMER_LOAD_BIT BIT(8) +#define BST_BCLK_EN_BIT BIT(10) +#define BST_RX_UPDATE_BIT BIT(11) +#define BST_EMMC_CTRL_RST_N BIT(2) /* eMMC card reset control */ + +/* Clock frequency limits */ +#define BST_DEFAULT_MAX_FREQ 200000000UL /* 200 MHz */ +#define BST_DEFAULT_MIN_FREQ 400000UL /* 400 kHz */ + +/* Clock control bit definitions */ +#define BST_CLOCK_DIV_MASK GENMASK(7, 0) +#define BST_CLOCK_DIV_SHIFT 8 +#define BST_BCLK_DIV_MASK GENMASK(9, 0) + +/* Clock frequency thresholds */ +#define BST_CLOCK_THRESHOLD_LOW 1500 + +/* Clock stability polling parameters */ +#define BST_CLK_STABLE_POLL_US 1000 /* Poll interval in microseconds */ +#define BST_CLK_STABLE_TIMEOUT_US 20000 /* Timeout for internal clock stabilization (us) */ + +struct sdhci_bst_priv { + void __iomem *crm_reg_base; +}; + +union sdhci_bst_rx_ctrl { + struct { + u32 rx_revert:1, + rx_clk_sel_sec:1, + rx_clk_div:4, + rx_clk_phase_inner:2, + rx_clk_sel_first:1, + rx_clk_phase_out:2, + rx_clk_en:1, + res0:20; + }; + u32 reg; +}; + +static u32 sdhci_bst_crm_read(struct sdhci_pltfm_host *pltfm_host, u32 offset) +{ + struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host); + + return readl(priv->crm_reg_base + offset); +} + +static void sdhci_bst_crm_write(struct sdhci_pltfm_host *pltfm_host, u32 offset, u32 value) +{ + struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host); + + writel(value, priv->crm_reg_base + offset); +} + +static int sdhci_bst_wait_int_clk(struct sdhci_host *host) +{ + u16 clk; + + if (read_poll_timeout(sdhci_readw, clk, (clk & SDHCI_CLOCK_INT_STABLE), + BST_CLK_STABLE_POLL_US, BST_CLK_STABLE_TIMEOUT_US, false, + host, SDHCI_CLOCK_CONTROL)) + return -EBUSY; + return 0; +} + +static unsigned int sdhci_bst_get_max_clock(struct sdhci_host *host) +{ + return BST_DEFAULT_MAX_FREQ; +} + +static unsigned int sdhci_bst_get_min_clock(struct sdhci_host *host) +{ + return BST_DEFAULT_MIN_FREQ; +} + +static void sdhci_bst_enable_clk(struct sdhci_host *host, unsigned int clk) +{ + struct sdhci_pltfm_host *pltfm_host; + unsigned int div; + u32 val; + union sdhci_bst_rx_ctrl rx_reg; + + pltfm_host = sdhci_priv(host); + + /* Calculate clock divider based on target frequency */ + if (clk == 0) { + div = 0; + } else if (clk < BST_DEFAULT_MIN_FREQ) { + /* Below minimum: use max divider to get closest to min freq */ + div = BST_DEFAULT_MAX_FREQ / BST_DEFAULT_MIN_FREQ; + } else if (clk <= BST_DEFAULT_MAX_FREQ) { + /* Normal range: calculate divider directly */ + div = BST_DEFAULT_MAX_FREQ / clk; + } else { + /* Above maximum: no division needed */ + div = 1; + } + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk &= ~SDHCI_CLOCK_PLL_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL); + val &= ~BST_TIMER_LOAD_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL); + val &= ~BST_TIMER_DIV_MASK; + val |= BST_TIMER_DIV_VAL; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL); + val |= BST_TIMER_LOAD_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + val &= ~BST_RX_UPDATE_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val); + + rx_reg.reg = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + + rx_reg.rx_revert = 0; + rx_reg.rx_clk_sel_sec = 1; + rx_reg.rx_clk_div = 4; + rx_reg.rx_clk_phase_inner = 2; + rx_reg.rx_clk_sel_first = 0; + rx_reg.rx_clk_phase_out = 2; + + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, rx_reg.reg); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + val |= BST_RX_UPDATE_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val); + + /* Disable clock first */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val &= ~BST_BCLK_EN_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* Setup clock divider */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val &= ~BST_BCLK_DIV_MASK; + val |= div; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* Enable clock */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val |= BST_BCLK_EN_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* RMW the clock divider bits to avoid clobbering other fields */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~(BST_CLOCK_DIV_MASK << BST_CLOCK_DIV_SHIFT); + clk |= (div & BST_CLOCK_DIV_MASK) << BST_CLOCK_DIV_SHIFT; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk |= SDHCI_CLOCK_PLL_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +} + +static void sdhci_bst_set_clock(struct sdhci_host *host, unsigned int clock) +{ + /* Turn off card/internal/PLL clocks when clock==0 to avoid idle power */ + u32 clk_reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + + if (!clock) { + clk_reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_PLL_EN); + sdhci_writew(host, clk_reg, SDHCI_CLOCK_CONTROL); + return; + } + sdhci_bst_enable_clk(host, clock); +} + +/* + * sdhci_bst_reset - Reset the SDHCI host controller with special + * handling for eMMC card reset control. + */ +static void sdhci_bst_reset(struct sdhci_host *host, u8 mask) +{ + u16 vendor_ptr, emmc_ctrl_reg; + u32 reg; + + if (host->mmc->caps2 & MMC_CAP2_NO_SD) { + vendor_ptr = sdhci_readw(host, SDHCI_VENDOR_PTR_R); + emmc_ctrl_reg = vendor_ptr + SDHC_EMMC_CTRL_R_OFFSET; + + reg = sdhci_readw(host, emmc_ctrl_reg); + reg &= ~BST_EMMC_CTRL_RST_N; + sdhci_writew(host, reg, emmc_ctrl_reg); + sdhci_reset(host, mask); + usleep_range(10, 20); + reg = sdhci_readw(host, emmc_ctrl_reg); + reg |= BST_EMMC_CTRL_RST_N; + sdhci_writew(host, reg, emmc_ctrl_reg); + } else { + sdhci_reset(host, mask); + } +} + +/* Set timeout control register to maximum value (0xE) */ +static void sdhci_bst_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) +{ + sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL); +} + +/* + * sdhci_bst_set_power - Set power mode and voltage, also configures + * MBIU burst mode control based on power state. + */ +static void sdhci_bst_set_power(struct sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + u32 reg; + u32 val; + + sdhci_set_power(host, mode, vdd); + + if (mode == MMC_POWER_OFF) { + /* Disable MBIU burst mode */ + reg = sdhci_readw(host, MBIU_CTRL); + reg &= ~BURST_EN; /* Clear all burst enable bits */ + sdhci_writew(host, reg, MBIU_CTRL); + + /* Disable CRM BCLK */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val &= ~BST_BCLK_EN_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* Disable RX clock */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + val &= ~BST_RX_UPDATE_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val); + + /* Turn off voltage stable power */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_VOL_CTRL); + val &= ~BST_VOL_STABLE_ON; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, val); + } else { + /* Configure burst mode only when powered on */ + reg = sdhci_readw(host, MBIU_CTRL); + reg &= ~MBIU_BURST_MASK; /* Clear burst related bits */ + reg |= BURST_EN; /* Enable burst mode for better bandwidth */ + sdhci_writew(host, reg, MBIU_CTRL); + } +} + +/* + * sdhci_bst_execute_tuning - Execute tuning procedure by trying different + * delay chain values and selecting the optimal one. + */ +static int sdhci_bst_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host; + int ret = 0, error; + int first_start = -1, first_end = -1, best = 0; + int second_start = -1, second_end = -1, has_failure = 0; + int i; + + pltfm_host = sdhci_priv(host); + + for (i = 0; i < BST_TUNING_COUNT; i++) { + /* Protected write */ + sdhci_bst_crm_write(pltfm_host, REG_WR_PROTECT, REG_WR_PROTECT_KEY); + /* Write tuning value */ + sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << i) - 1); + + /* Wait for internal clock stable before tuning */ + if (sdhci_bst_wait_int_clk(host)) { + dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n"); + return -EBUSY; + } + + ret = mmc_send_tuning(host->mmc, opcode, &error); + if (ret != 0) { + has_failure = 1; + } else { + if (has_failure == 0) { + if (first_start == -1) + first_start = i; + first_end = i; + } else { + if (second_start == -1) + second_start = i; + second_end = i; + } + } + } + + /* Calculate best tuning value */ + if (first_end - first_start >= second_end - second_start) + best = ((first_end - first_start) >> 1) + first_start; + else + best = ((second_end - second_start) >> 1) + second_start; + + if (best < 0) + best = 0; + + sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << best) - 1); + /* Confirm internal clock stable after setting best tuning value */ + if (sdhci_bst_wait_int_clk(host)) { + dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n"); + return -EBUSY; + } + + return 0; +} + +/* Enable voltage stable power for voltage switch */ +static void sdhci_bst_voltage_switch(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + /* Enable voltage stable power */ + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, BST_VOL_STABLE_ON); +} + +static const struct sdhci_ops sdhci_bst_ops = { + .set_clock = sdhci_bst_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_min_clock = sdhci_bst_get_min_clock, + .get_max_clock = sdhci_bst_get_max_clock, + .reset = sdhci_bst_reset, + .set_power = sdhci_bst_set_power, + .set_timeout = sdhci_bst_set_timeout, + .platform_execute_tuning = sdhci_bst_execute_tuning, + .voltage_switch = sdhci_bst_voltage_switch, +}; + +static const struct sdhci_pltfm_data sdhci_bst_pdata = { + .ops = &sdhci_bst_ops, + .quirks = SDHCI_QUIRK_BROKEN_ADMA | + SDHCI_QUIRK_DELAY_AFTER_POWER | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT, + .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50 | + SDHCI_QUIRK2_TUNING_WORK_AROUND | + SDHCI_QUIRK2_ACMD23_BROKEN, +}; + +static void sdhci_bst_free_bounce_buffer(struct sdhci_host *host) +{ + if (host->bounce_buffer) { + dma_free_coherent(mmc_dev(host->mmc), host->bounce_buffer_size, + host->bounce_buffer, host->bounce_addr); + host->bounce_buffer = NULL; + } + of_reserved_mem_device_release(mmc_dev(host->mmc)); +} + +static int sdhci_bst_alloc_bounce_buffer(struct sdhci_host *host) +{ + struct mmc_host *mmc = host->mmc; + unsigned int bounce_size; + int ret; + + /* Fixed SRAM bounce size to 32KB: verified config under 32-bit DMA addressing limit */ + bounce_size = SZ_32K; + + ret = of_reserved_mem_device_init_by_idx(mmc_dev(mmc), mmc_dev(mmc)->of_node, 0); + if (ret) { + dev_err(mmc_dev(mmc), "Failed to initialize reserved memory\n"); + return ret; + } + + host->bounce_buffer = dma_alloc_coherent(mmc_dev(mmc), bounce_size, + &host->bounce_addr, GFP_KERNEL); + if (!host->bounce_buffer) { + of_reserved_mem_device_release(mmc_dev(mmc)); + return -ENOMEM; + } + + host->bounce_buffer_size = bounce_size; + + return 0; +} + +static int sdhci_bst_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_host *host; + struct sdhci_bst_priv *priv; + int err; + + host = sdhci_pltfm_init(pdev, &sdhci_bst_pdata, sizeof(struct sdhci_bst_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); /* Get platform private data */ + + err = mmc_of_parse(host->mmc); + if (err) + return err; + + sdhci_get_of_property(pdev); + + /* Get CRM registers from the second reg entry */ + priv->crm_reg_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(priv->crm_reg_base)) { + err = PTR_ERR(priv->crm_reg_base); + return err; + } + + /* + * Silicon constraints for BST C1200: + * - System RAM base is 0x800000000 (above 32-bit addressable range) + * - The eMMC controller DMA engine is limited to 32-bit addressing + * - SMMU cannot be used on this path due to hardware design flaws + * - These are fixed in silicon and cannot be changed in software + * + * Bus/controller mapping: + * - No registers are available to reprogram the address mapping + * - The 32-bit DMA limit is a hard constraint of the controller IP + * + * Given these constraints, an SRAM-based bounce buffer in the 32-bit + * address space is required to enable eMMC DMA on this platform. + */ + err = sdhci_bst_alloc_bounce_buffer(host); + if (err) { + dev_err(&pdev->dev, "Failed to allocate bounce buffer: %d\n", err); + return err; + } + + err = sdhci_add_host(host); + if (err) + goto err_free_bounce_buffer; + + return 0; + +err_free_bounce_buffer: + sdhci_bst_free_bounce_buffer(host); + + return err; +} + +static void sdhci_bst_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + + sdhci_bst_free_bounce_buffer(host); + sdhci_pltfm_remove(pdev); +} + +static const struct of_device_id sdhci_bst_ids[] = { + { .compatible = "bst,c1200-sdhci" }, + {} +}; +MODULE_DEVICE_TABLE(of, sdhci_bst_ids); + +static struct platform_driver sdhci_bst_driver = { + .driver = { + .name = "sdhci-bst", + .of_match_table = sdhci_bst_ids, + }, + .probe = sdhci_bst_probe, + .remove = sdhci_bst_remove, +}; +module_platform_driver(sdhci_bst_driver); + +MODULE_DESCRIPTION("Black Sesame Technologies SDHCI driver (BST)"); +MODULE_AUTHOR("Black Sesame Technologies Co., Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 7ea3da45db32..0b2158a7e409 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -11,18 +11,24 @@ #include <linux/arm-smccc.h> #include <linux/bitfield.h> #include <linux/clk.h> +#include <linux/clk-provider.h> #include <linux/dma-mapping.h> #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> #include <linux/pm_runtime.h> +#include <linux/regmap.h> #include <linux/reset.h> #include <linux/sizes.h> +#include <linux/mfd/syscon.h> +#include <linux/units.h> #include "sdhci-pltfm.h" #include "cqhci.h" +#include "sdhci-cqhci.h" #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16) @@ -34,10 +40,14 @@ #define DWCMSHC_AREA1_MASK GENMASK(11, 0) /* Offset inside the vendor area 1 */ #define DWCMSHC_HOST_CTRL3 0x8 +#define DWCMSHC_HOST_CTRL3_CMD_CONFLICT BIT(0) #define DWCMSHC_EMMC_CONTROL 0x2c +/* HPE GSC SoC MSHCCS register */ +#define HPE_GSC_MSHCCS_SCGSYNCDIS BIT(18) #define DWCMSHC_CARD_IS_EMMC BIT(0) #define DWCMSHC_ENHANCED_STROBE BIT(8) #define DWCMSHC_EMMC_ATCTRL 0x40 +#define DWCMSHC_AT_STAT 0x44 /* Tuning and auto-tuning fields in AT_CTRL_R control register */ #define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */ #define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */ @@ -81,6 +91,8 @@ #define DWCMSHC_EMMC_DLL_TXCLK 0x808 #define DWCMSHC_EMMC_DLL_STRBIN 0x80c #define DECMSHC_EMMC_DLL_CMDOUT 0x810 +#define DECMSHC_EMMC_MISC_CON 0x81C +#define MISC_INTCLK_EN BIT(1) #define DWCMSHC_EMMC_DLL_STATUS0 0x840 #define DWCMSHC_EMMC_DLL_START BIT(0) #define DWCMSHC_EMMC_DLL_LOCKED BIT(8) @@ -93,7 +105,7 @@ #define DLL_TXCLK_TAPNUM_DEFAULT 0x10 #define DLL_TXCLK_TAPNUM_90_DEGREES 0xA #define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) -#define DLL_STRBIN_TAPNUM_DEFAULT 0x8 +#define DLL_STRBIN_TAPNUM_DEFAULT 0x4 #define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) #define DLL_STRBIN_DELAY_NUM_SEL BIT(26) #define DLL_STRBIN_DELAY_NUM_OFFSET 16 @@ -119,9 +131,11 @@ #define PHY_CNFG_PHY_PWRGOOD_MASK BIT_MASK(1) /* bit [1] */ #define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */ #define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */ +#define PHY_CNFG_PAD_SP_k230 0x09 /* PMOS TX drive strength for k230 */ #define PHY_CNFG_PAD_SP_SG2042 0x09 /* PMOS TX drive strength for SG2042 */ #define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */ #define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */ +#define PHY_CNFG_PAD_SN_k230 0x08 /* NMOS TX drive strength for k230 */ #define PHY_CNFG_PAD_SN_SG2042 0x08 /* NMOS TX drive strength for SG2042 */ /* PHY command/response pad settings */ @@ -144,14 +158,21 @@ #define PHY_PAD_RXSEL_3V3 0x2 /* Receiver type select for 3.3V */ #define PHY_PAD_WEAKPULL_MASK GENMASK(4, 3) /* bits [4:3] */ +#define PHY_PAD_WEAKPULL_DISABLED 0x0 /* Weak pull up and pull down disabled */ #define PHY_PAD_WEAKPULL_PULLUP 0x1 /* Weak pull up enabled */ #define PHY_PAD_WEAKPULL_PULLDOWN 0x2 /* Weak pull down enabled */ #define PHY_PAD_TXSLEW_CTRL_P_MASK GENMASK(8, 5) /* bits [8:5] */ #define PHY_PAD_TXSLEW_CTRL_P 0x3 /* Slew control for P-Type pad TX */ +#define PHY_PAD_TXSLEW_CTRL_P_k230 0x2 /* Slew control for P-Type pad TX for k230 */ #define PHY_PAD_TXSLEW_CTRL_N_MASK GENMASK(12, 9) /* bits [12:9] */ #define PHY_PAD_TXSLEW_CTRL_N 0x3 /* Slew control for N-Type pad TX */ #define PHY_PAD_TXSLEW_CTRL_N_SG2042 0x2 /* Slew control for N-Type pad TX for SG2042 */ +#define PHY_PAD_TXSLEW_CTRL_N_k230 0x2 /* Slew control for N-Type pad TX for k230 */ + +/* PHY Common DelayLine config settings */ +#define PHY_COMMDL_CNFG (DWC_MSHC_PTR_PHY_R + 0x1c) +#define PHY_COMMDL_CNFG_DLSTEP_SEL BIT(0) /* DelayLine outputs on PAD enabled */ /* PHY CLK delay line settings */ #define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d) @@ -165,7 +186,10 @@ #define PHY_SDCLKDL_DC_HS400 0x18 /* delay code for HS400 mode */ #define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20) +#define PHY_SMPLDL_CNFG_EXTDLY_EN BIT(0) #define PHY_SMPLDL_CNFG_BYPASS_EN BIT(1) +#define PHY_SMPLDL_CNFG_INPSEL_MASK GENMASK(3, 2) /* bits [3:2] */ +#define PHY_SMPLDL_CNFG_INPSEL 0x3 /* delay line input source */ /* PHY drift_cclk_rx delay line configuration setting */ #define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21) @@ -193,6 +217,19 @@ #define PHY_DLLDL_CNFG_SLV_INPSEL_MASK GENMASK(6, 5) /* bits [6:5] */ #define PHY_DLLDL_CNFG_SLV_INPSEL 0x3 /* clock source select for slave DL */ +/* PHY DLL offset setting register */ +#define PHY_DLL_OFFST_R (DWC_MSHC_PTR_PHY_R + 0x29) +/* DLL LBT setting register */ +#define PHY_DLLBT_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x2c) +/* DLL Status register */ +#define PHY_DLL_STATUS_R (DWC_MSHC_PTR_PHY_R + 0x2e) +#define DLL_LOCK_STS BIT(0)/* DLL is locked and ready */ +/* + * Captures the value of DLL's lock error status information. Value is valid + * only when LOCK_STS is set. + */ +#define DLL_ERROR_STS BIT(1) + #define FLAG_IO_FIXED_1V8 BIT(0) #define BOUNDARY_OK(addr, len) \ @@ -202,20 +239,60 @@ SDHCI_TRNS_BLK_CNT_EN | \ SDHCI_TRNS_DMA) +#define to_pltfm_data(priv, name) \ + container_of((priv)->dwcmshc_pdata, struct name##_pltfm_data, dwcmshc_pdata) + /* SMC call for BlueField-3 eMMC RST_N */ #define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007 -enum dwcmshc_rk_type { - DWCMSHC_RK3568, - DWCMSHC_RK3588, -}; +/* Canaan specific Registers */ +#define SD0_CTRL 0x00 +#define SD0_HOST_REG_VOL_STABLE BIT(4) +#define SD0_CARD_WRITE_PROT BIT(6) +#define SD1_CTRL 0x08 +#define SD1_HOST_REG_VOL_STABLE BIT(0) +#define SD1_CARD_WRITE_PROT BIT(2) + +/* Eswin specific Registers */ +#define EIC7700_CARD_CLK_STABLE BIT(28) +#define EIC7700_INT_BCLK_STABLE BIT(16) +#define EIC7700_INT_ACLK_STABLE BIT(8) +#define EIC7700_INT_TMCLK_STABLE BIT(0) +#define EIC7700_INT_CLK_STABLE (EIC7700_CARD_CLK_STABLE | \ + EIC7700_INT_ACLK_STABLE | \ + EIC7700_INT_BCLK_STABLE | \ + EIC7700_INT_TMCLK_STABLE) +#define EIC7700_HOST_VAL_STABLE BIT(0) + +/* strength definition */ +#define PHYCTRL_DR_33OHM 0xee +#define PHYCTRL_DR_40OHM 0xcc +#define PHYCTRL_DR_50OHM 0x88 +#define PHYCTRL_DR_66OHM 0x44 +#define PHYCTRL_DR_100OHM 0x00 + +#define MAX_PHASE_CODE 0xff +#define TUNING_RANGE_THRESHOLD 40 +#define PHY_CLK_MAX_DELAY_MASK 0x7f +#define PHY_DELAY_CODE_MAX 0x7f +#define PHY_DELAY_CODE_EMMC 0x17 +#define PHY_DELAY_CODE_SD 0x55 struct rk35xx_priv { struct reset_control *reset; - enum dwcmshc_rk_type devtype; u8 txclk_tapnum; }; +struct eic7700_priv { + struct reset_control *reset; + unsigned int drive_impedance; +}; + +struct k230_priv { + /* Canaan k230 specific */ + struct regmap *hi_sys_regmap; +}; + #define DWCMSHC_MAX_OTHER_CLKS 3 struct dwcmshc_priv { @@ -226,6 +303,7 @@ struct dwcmshc_priv { int num_other_clks; struct clk_bulk_data other_clks[DWCMSHC_MAX_OTHER_CLKS]; + const struct dwcmshc_pltfm_data *dwcmshc_pdata; void *priv; /* pointer to SoC private stuff */ u16 delay_line; u16 flags; @@ -233,10 +311,40 @@ struct dwcmshc_priv { struct dwcmshc_pltfm_data { const struct sdhci_pltfm_data pdata; + const struct cqhci_host_ops *cqhci_host_ops; int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); }; +struct k230_pltfm_data { + struct dwcmshc_pltfm_data dwcmshc_pdata; + bool is_emmc; + u32 ctrl_reg; + u32 vol_stable_bit; + u32 write_prot_bit; +}; + +struct rockchip_pltfm_data { + struct dwcmshc_pltfm_data dwcmshc_pdata; + /* + * The controller hardware has two known revisions documented internally: + * - Revision 0: Exclusively used by RK3566 and RK3568 SoCs. + * - Revision 1: Implemented in all other Rockchip SoCs, including RK3576, RK3588, etc. + */ + int revision; +}; + +static void dwcmshc_enable_card_clk(struct sdhci_host *host) +{ + u16 ctrl; + + ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) { + ctrl |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); + } +} + static int dwcmshc_get_enable_other_clks(struct device *dev, struct dwcmshc_priv *priv, int num_clks, @@ -288,6 +396,19 @@ static void dwcmshc_adma_write_desc(struct sdhci_host *host, void **desc, sdhci_adma_write_desc(host, desc, addr, len, cmd); } +static void dwcmshc_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + /* The dwcmshc does not comply with the SDHCI specification + * regarding the "Software Reset for CMD line should clear 'Command + * Complete' in the Normal Interrupt Status Register." Clear the bit + * here to compensate for this quirk. + */ + if (mask & SDHCI_RESET_CMD) + sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); +} + static unsigned int dwcmshc_get_max_clock(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -328,12 +449,17 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq) sdhci_request(mmc, mrq); } -static void dwcmshc_phy_1_8v_init(struct sdhci_host *host) +static void dwcmshc_phy_init(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + u32 rxsel = PHY_PAD_RXSEL_3V3; u32 val; + if (priv->flags & FLAG_IO_FIXED_1V8 || + host->mmc->ios.timing & MMC_SIGNAL_VOLTAGE_180) + rxsel = PHY_PAD_RXSEL_1V8; + /* deassert phy reset & set tx drive strength */ val = PHY_CNFG_RSTN_DEASSERT; val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP); @@ -353,7 +479,7 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host) sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R); /* configure phy pads */ - val = PHY_PAD_RXSEL_1V8; + val = rxsel; val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P); val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N); @@ -365,65 +491,22 @@ static void dwcmshc_phy_1_8v_init(struct sdhci_host *host) val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N); sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); - val = PHY_PAD_RXSEL_1V8; + val = rxsel; val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN); val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P); val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N); sdhci_writew(host, val, PHY_STBPAD_CNFG_R); /* enable data strobe mode */ - sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL), - PHY_DLLDL_CNFG_R); - - /* enable phy dll */ - sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R); -} - -static void dwcmshc_phy_3_3v_init(struct sdhci_host *host) -{ - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - u32 val; - - /* deassert phy reset & set tx drive strength */ - val = PHY_CNFG_RSTN_DEASSERT; - val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP); - val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN); - sdhci_writel(host, val, PHY_CNFG_R); + if (rxsel == PHY_PAD_RXSEL_1V8) { + u8 sel = FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL); - /* disable delay line */ - sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R); - - /* set delay line */ - sdhci_writeb(host, priv->delay_line, PHY_SDCLKDL_DC_R); - sdhci_writeb(host, PHY_DLL_CNFG2_JUMPSTEP, PHY_DLL_CNFG2_R); - - /* enable delay lane */ - val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R); - val &= ~(PHY_SDCLKDL_CNFG_UPDATE); - sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R); - - /* configure phy pads */ - val = PHY_PAD_RXSEL_3V3; - val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); - val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P); - val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N); - sdhci_writew(host, val, PHY_CMDPAD_CNFG_R); - sdhci_writew(host, val, PHY_DATAPAD_CNFG_R); - sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R); - - val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P); - val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N); - sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); - - val = PHY_PAD_RXSEL_3V3; - val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN); - val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P); - val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N); - sdhci_writew(host, val, PHY_STBPAD_CNFG_R); + sdhci_writeb(host, sel, PHY_DLLDL_CNFG_R); + } /* enable phy dll */ sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R); + } static void th1520_sdhci_set_phy(struct sdhci_host *host) @@ -433,11 +516,7 @@ static void th1520_sdhci_set_phy(struct sdhci_host *host) u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; u16 emmc_ctrl; - /* Before power on, set PHY configs */ - if (priv->flags & FLAG_IO_FIXED_1V8) - dwcmshc_phy_1_8v_init(host); - else - dwcmshc_phy_3_3v_init(host); + dwcmshc_phy_init(host); if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { emmc_ctrl = sdhci_readw(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); @@ -602,11 +681,79 @@ static void dwcmshc_cqhci_dumpregs(struct mmc_host *mmc) sdhci_dumpregs(mmc_priv(mmc)); } +static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 reg; + + /* Set Send Status Command Idle Timer to 10.66us (256 * 1 / 24) */ + reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_SSC1); + reg = (reg & ~CQHCI_SSC1_CIT_MASK) | 0x0100; + sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_SSC1); + + reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG); + reg |= CQHCI_ENABLE; + sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG); +} + +static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + u32 reg; + + reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + while (reg & SDHCI_DATA_AVAILABLE) { + sdhci_readl(host, SDHCI_BUFFER); + reg = sdhci_readl(host, SDHCI_PRESENT_STATE); + } + + sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); + + sdhci_cqe_enable(mmc); +} + +static void rk35xx_sdhci_cqe_disable(struct mmc_host *mmc, bool recovery) +{ + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + u32 ctrl; + + /* + * During CQE command transfers, command complete bit gets latched. + * So s/w should clear command complete interrupt status when CQE is + * either halted or disabled. Otherwise unexpected SDCHI legacy + * interrupt gets triggered when CQE is halted/disabled. + */ + spin_lock_irqsave(&host->lock, flags); + ctrl = sdhci_readl(host, SDHCI_INT_ENABLE); + ctrl |= SDHCI_INT_RESPONSE; + sdhci_writel(host, ctrl, SDHCI_INT_ENABLE); + sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); + spin_unlock_irqrestore(&host->lock, flags); + + sdhci_cqe_disable(mmc, recovery); +} + +static void rk35xx_sdhci_cqe_post_disable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 ctrl; + + ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG); + ctrl &= ~CQHCI_ENABLE; + sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG); +} + static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); struct rk35xx_priv *priv = dwc_priv->priv; + const struct rockchip_pltfm_data *rockchip_pdata = to_pltfm_data(dwc_priv, rockchip); u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT; u32 extra, reg; int err; @@ -629,13 +776,24 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock sdhci_set_clock(host, clock); - /* Disable cmd conflict check */ + /* Disable cmd conflict check and internal clock gate */ reg = dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3; extra = sdhci_readl(host, reg); extra &= ~BIT(0); + extra |= BIT(4); sdhci_writel(host, extra, reg); + /* Disable clock while config DLL */ + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + if (clock <= 52000000) { + if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 || + host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { + dev_err(mmc_dev(host->mmc), + "Can't reduce the clock below 52MHz in HS200/HS400 mode"); + goto enable_clk; + } + /* * Disable DLL and reset both of sample and drive clock. * The bypass bit and start bit need to be set if DLL is not locked. @@ -653,7 +811,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock DLL_STRBIN_DELAY_NUM_SEL | DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); - return; + goto enable_clk; } /* Reset DLL */ @@ -666,7 +824,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock * we must set it in higher speed mode. */ extra = DWCMSHC_EMMC_DLL_DLYENA; - if (priv->devtype == DWCMSHC_RK3568) + if (rockchip_pdata->revision == 0) extra |= DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); @@ -680,7 +838,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock 500 * USEC_PER_MSEC); if (err) { dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n"); - return; + goto enable_clk; } extra = 0x1 << 16 | /* tune clock stop en */ @@ -692,7 +850,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock host->mmc->ios.timing == MMC_TIMING_MMC_HS400) txclk_tapnum = priv->txclk_tapnum; - if ((priv->devtype == DWCMSHC_RK3588) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { + if (rockchip_pdata->revision == 1 && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES; extra = DLL_CMDOUT_SRC_CLK_NEG | @@ -713,6 +871,16 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock DLL_STRBIN_TAPNUM_DEFAULT | DLL_STRBIN_TAPNUM_FROM_SW; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); + +enable_clk: + /* + * The sdclk frequency select bits in SDHCI_CLOCK_CONTROL are not functional + * on Rockchip's SDHCI implementation. Instead, the clock frequency is fully + * controlled via external clk provider by calling clk_set_rate(). Consequently, + * passing 0 to sdhci_enable_clk() only re-enables the already-configured clock, + * which matches the hardware's actual behavior. + */ + sdhci_enable_clk(host, 0); } static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) @@ -720,6 +888,10 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); struct rk35xx_priv *priv = dwc_priv->priv; + u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON); + + if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL)) + cqhci_deactivate(host->mmc); if (mask & SDHCI_RESET_ALL && priv->reset) { reset_control_assert(priv->reset); @@ -728,6 +900,9 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) } sdhci_reset(host, mask); + + /* Enable INTERNAL CLOCK */ + sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON); } static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host, @@ -741,11 +916,6 @@ static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host, if (!priv) return -ENOMEM; - if (of_device_is_compatible(dev->of_node, "rockchip,rk3588-dwcmshc")) - priv->devtype = DWCMSHC_RK3588; - else - priv->devtype = DWCMSHC_RK3568; - priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc)); if (IS_ERR(priv->reset)) { err = PTR_ERR(priv->reset); @@ -787,6 +957,29 @@ static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv } } +static void dwcmshc_rk3576_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) +{ + struct device *dev = mmc_dev(host->mmc); + int ret; + + /* + * This works around the design of the RK3576's power domains, which + * makes the PD_NVM power domain, which the sdhci controller on the + * RK3576 is in, never come back the same way once it's run-time + * suspended once. This can happen during early kernel boot if no driver + * is using either PD_NVM or its child power domain PD_SDGMAC for a + * short moment, leading to it being turned off to save power. By + * keeping it on, sdhci suspending won't lead to PD_NVM becoming a + * candidate for getting turned off. + */ + ret = dev_pm_genpd_rpm_always_on(dev, true); + if (ret && ret != -EOPNOTSUPP) + dev_warn(dev, "failed to set PD rpm always on, SoC may hang later: %pe\n", + ERR_PTR(ret)); + + dwcmshc_rk35xx_postinit(host, dwc_priv); +} + static int th1520_execute_tuning(struct sdhci_host *host, u32 opcode) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -850,15 +1043,7 @@ static void th1520_sdhci_reset(struct sdhci_host *host, u8 mask) struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); u16 ctrl_2; - sdhci_reset(host, mask); - - /* The T-Head 1520 SoC does not comply with the SDHCI specification - * regarding the "Software Reset for CMD line should clear 'Command - * Complete' in the Normal Interrupt Status Register." Clear the bit - * here to compensate for this quirk. - */ - if (mask & SDHCI_RESET_CMD) - sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); + dwcmshc_reset(host, mask); if (priv->flags & FLAG_IO_FIXED_1V8) { ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); @@ -904,7 +1089,7 @@ static void cv18xx_sdhci_reset(struct sdhci_host *host, u8 mask) struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); u32 val, emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; - sdhci_reset(host, mask); + dwcmshc_reset(host, mask); if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { val = sdhci_readl(host, priv->vendor_specific_area1 + CV18XX_SDHCI_MSHC_CTRL); @@ -976,7 +1161,7 @@ static void cv18xx_sdhci_post_tuning(struct sdhci_host *host) val |= SDHCI_INT_DATA_AVAIL; sdhci_writel(host, val, SDHCI_INT_STATUS); - sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + dwcmshc_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); } static int cv18xx_sdhci_execute_tuning(struct sdhci_host *host, u32 opcode) @@ -1113,12 +1298,718 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host, ARRAY_SIZE(clk_ids), clk_ids); } +/* + * HPE GSC-specific vendor configuration: disable command conflict check + * and program Auto-Tuning Control register. + */ +static void dwcmshc_hpe_vendor_specific(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 atctrl; + u8 extra; + + extra = sdhci_readb(host, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); + extra &= ~DWCMSHC_HOST_CTRL3_CMD_CONFLICT; + sdhci_writeb(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); + + atctrl = AT_CTRL_AT_EN | AT_CTRL_SWIN_TH_EN | AT_CTRL_TUNE_CLK_STOP_EN | + FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, 3) | + FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY) | + FIELD_PREP(AT_CTRL_SWIN_TH_VAL_MASK, 2); + sdhci_writel(host, atctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); +} + +static void dwcmshc_hpe_set_emmc(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u16 ctrl; + + ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + ctrl |= DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); +} + +static void dwcmshc_hpe_reset(struct sdhci_host *host, u8 mask) +{ + dwcmshc_reset(host, mask); + dwcmshc_hpe_vendor_specific(host); + dwcmshc_hpe_set_emmc(host); +} + +static void dwcmshc_hpe_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) +{ + dwcmshc_set_uhs_signaling(host, timing); + dwcmshc_hpe_set_emmc(host); +} + +/* + * HPE GSC eMMC controller clock setup. + * + * The GSC SoC wires the freq_sel field of SDHCI_CLOCK_CONTROL directly to a + * clock mux rather than a divider. Force freq_sel = 1 when running at + * 200 MHz (HS200) so the mux selects the correct clock source. + */ +static void dwcmshc_hpe_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + + host->mmc->actual_clock = 0; + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) + return; + + clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); + + if (host->mmc->actual_clock == 200000000) + clk |= (1 << SDHCI_DIVIDER_SHIFT); + + sdhci_enable_clk(host, clk); +} + +/* + * HPE GSC eMMC controller init. + * + * The GSC SoC requires configuring MSHCCS. Bit 18 (SCGSyncDis) disables clock + * synchronisation for phase-select values going to the HS200 RX delay lines, + * allowing the card clock to be stopped while the delay selection settles and + * the phase shift is applied. This must be used together with the ATCTRL + * settings programmed in dwcmshc_hpe_vendor_specific(): + * AT_CTRL_R.TUNE_CLK_STOP_EN = 0x1 + * AT_CTRL_R.POST_CHANGE_DLY = 0x3 + * AT_CTRL_R.PRE_CHANGE_DLY = 0x3 + * + * The DTS node provides a syscon phandle ('hpe,gxp-sysreg') with the + * MSHCCS register offset as an argument. + */ +static int dwcmshc_hpe_gsc_init(struct device *dev, struct sdhci_host *host, + struct dwcmshc_priv *dwc_priv) +{ + unsigned int reg_offset; + struct regmap *soc_ctrl; + int ret; + + /* Disable cmd conflict check and configure auto-tuning */ + dwcmshc_hpe_vendor_specific(host); + + /* Look up the GXP sysreg syscon and MSHCCS offset */ + soc_ctrl = syscon_regmap_lookup_by_phandle_args(dev->of_node, + "hpe,gxp-sysreg", + 1, ®_offset); + if (IS_ERR(soc_ctrl)) { + dev_err(dev, "failed to get hpe,gxp-sysreg syscon\n"); + return PTR_ERR(soc_ctrl); + } + + /* Set SCGSyncDis (bit 18) to disable sync on HS200 RX delay lines */ + ret = regmap_update_bits(soc_ctrl, reg_offset, + HPE_GSC_MSHCCS_SCGSYNCDIS, + HPE_GSC_MSHCCS_SCGSYNCDIS); + if (ret) { + dev_err(dev, "failed to set SCGSyncDis in MSHCCS\n"); + return ret; + } + + sdhci_enable_v4_mode(host); + + return 0; +} + +static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + u16 clk; + + host->mmc->actual_clock = clock; + + if (clock == 0) { + sdhci_set_clock(host, clock); + return; + } + + clk_set_rate(pltfm_host->clk, clock); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + dwcmshc_enable_card_clk(host); +} + +static void sdhci_eic7700_config_phy_delay(struct sdhci_host *host, int delay) +{ + delay &= PHY_CLK_MAX_DELAY_MASK; + + /* phy clk delay line config */ + sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R); + sdhci_writeb(host, delay, PHY_SDCLKDL_DC_R); + sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R); +} + +static void sdhci_eic7700_config_phy(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + struct eic7700_priv *priv = dwc_priv->priv; + unsigned int val, drv; + + drv = FIELD_PREP(PHY_CNFG_PAD_SP_MASK, priv->drive_impedance & 0xF); + drv |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, (priv->drive_impedance >> 4) & 0xF); + + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { + val = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + val |= DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + } + + /* reset phy, config phy's pad */ + sdhci_writel(host, drv | ~PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R); + + /* configure phy pads */ + val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); + val |= PHY_PAD_RXSEL_1V8; + sdhci_writew(host, val, PHY_CMDPAD_CNFG_R); + sdhci_writew(host, val, PHY_DATAPAD_CNFG_R); + sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R); + + /* Clock PAD Setting */ + val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); + + /* PHY strobe PAD setting (EMMC only) */ + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { + val = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_SG2042); + val |= PHY_PAD_RXSEL_1V8; + sdhci_writew(host, val, PHY_STBPAD_CNFG_R); + } + usleep_range(2000, 3000); + sdhci_writel(host, drv | PHY_CNFG_RSTN_DEASSERT, PHY_CNFG_R); + sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line); +} + +static void sdhci_eic7700_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + /* after reset all, the phy's config will be clear */ + if (mask == SDHCI_RESET_ALL) + sdhci_eic7700_config_phy(host); +} + +static int sdhci_eic7700_reset_init(struct device *dev, struct eic7700_priv *priv) +{ + int ret; + + priv->reset = devm_reset_control_array_get_optional_exclusive(dev); + if (IS_ERR(priv->reset)) { + ret = PTR_ERR(priv->reset); + dev_err(dev, "failed to get reset control %d\n", ret); + return ret; + } + + ret = reset_control_assert(priv->reset); + if (ret) { + dev_err(dev, "Failed to assert reset signals: %d\n", ret); + return ret; + } + usleep_range(2000, 2100); + ret = reset_control_deassert(priv->reset); + if (ret) { + dev_err(dev, "Failed to deassert reset signals: %d\n", ret); + return ret; + } + + return ret; +} + +static unsigned int eic7700_convert_drive_impedance_ohm(struct device *dev, unsigned int dr_ohm) +{ + switch (dr_ohm) { + case 100: + return PHYCTRL_DR_100OHM; + case 66: + return PHYCTRL_DR_66OHM; + case 50: + return PHYCTRL_DR_50OHM; + case 40: + return PHYCTRL_DR_40OHM; + case 33: + return PHYCTRL_DR_33OHM; + } + + dev_warn(dev, "Invalid value %u for drive-impedance-ohms.\n", dr_ohm); + return PHYCTRL_DR_50OHM; +} + +static int sdhci_eic7700_delay_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + int delay_min = -1; + int delay_max = -1; + int cmd_error = 0; + int delay = 0; + int i = 0; + int ret; + + for (i = 0; i <= PHY_DELAY_CODE_MAX; i++) { + sdhci_eic7700_config_phy_delay(host, i); + ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); + if (ret) { + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + usleep_range(200, 210); + if (delay_min != -1 && delay_max != -1) + break; + } else { + if (delay_min == -1) { + delay_min = i; + continue; + } else { + delay_max = i; + continue; + } + } + } + if (delay_min == -1 && delay_max == -1) { + pr_err("%s: delay code tuning failed!\n", mmc_hostname(host->mmc)); + sdhci_eic7700_config_phy_delay(host, dwc_priv->delay_line); + return ret; + } + + delay = (delay_min + delay_max) / 2; + sdhci_eic7700_config_phy_delay(host, delay); + + return 0; +} + +static int sdhci_eic7700_phase_code_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + int phase_code = -1; + int code_range = -1; + bool is_sd = false; + int code_min = -1; + int code_max = -1; + int cmd_error = 0; + int ret = 0; + int i = 0; + + if ((host->mmc->caps2 & sd_caps) == sd_caps) + is_sd = true; + + for (i = 0; i <= MAX_PHASE_CODE; i++) { + /* Centered Phase code */ + sdhci_writew(host, i, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); + ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + + if (ret) { + /* SD specific range tracking */ + if (is_sd && code_min != -1 && code_max != -1) { + if (code_max - code_min > code_range) { + code_range = code_max - code_min; + phase_code = (code_min + code_max) / 2; + if (code_range > TUNING_RANGE_THRESHOLD) + break; + } + code_min = -1; + code_max = -1; + } + /* EMMC breaks after first valid range */ + if (!is_sd && code_min != -1 && code_max != -1) + break; + } else { + /* Track valid phase code range */ + if (code_min == -1) { + code_min = i; + if (!is_sd) + continue; + } + code_max = i; + if (is_sd && i == MAX_PHASE_CODE) { + if (code_max - code_min > code_range) { + code_range = code_max - code_min; + phase_code = (code_min + code_max) / 2; + } + } + } + } + + /* Handle tuning failure case */ + if ((is_sd && phase_code == -1) || + (!is_sd && code_min == -1 && code_max == -1)) { + pr_err("%s: phase code tuning failed!\n", mmc_hostname(host->mmc)); + sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); + return -EIO; + } + if (!is_sd) + phase_code = (code_min + code_max) / 2; + + sdhci_writew(host, phase_code, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); + + /* SD specific final verification */ + if (is_sd) { + ret = mmc_send_tuning(host->mmc, opcode, &cmd_error); + host->ops->reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + if (ret) { + pr_err("%s: Final phase code 0x%x verification failed!\n", + mmc_hostname(host->mmc), phase_code); + return ret; + } + } + + return 0; +} + +static int sdhci_eic7700_executing_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + int ret = 0; + u16 ctrl; + u32 val; + + ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); + ctrl &= ~SDHCI_CTRL_TUNED_CLK; + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + + val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + val |= AT_CTRL_SW_TUNE_EN; + sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + + sdhci_writew(host, 0, priv->vendor_specific_area1 + DWCMSHC_AT_STAT); + sdhci_writew(host, 0x0, SDHCI_CMD_DATA); + + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) { + ret = sdhci_eic7700_delay_tuning(host, opcode); + if (ret) + return ret; + } + + ret = sdhci_eic7700_phase_code_tuning(host, opcode); + if (ret) + return ret; + + return 0; +} + +static void sdhci_eic7700_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + u8 status; + u32 val; + int ret; + + dwcmshc_set_uhs_signaling(host, timing); + + /* here need make dll locked when in hs400 at 200MHz */ + if (timing == MMC_TIMING_MMC_HS400 && host->clock == 200000000) { + val = sdhci_readl(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + val &= ~(FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY)); + /* 2-cycle latency */ + val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, 0x2); + sdhci_writew(host, val, priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + + sdhci_writeb(host, FIELD_PREP(PHY_DLL_CNFG1_SLVDLY_MASK, PHY_DLL_CNFG1_SLVDLY) | + 0x3, PHY_DLL_CNFG1_R);/* DLL wait cycle input */ + /* DLL jump step input */ + sdhci_writeb(host, 0x02, PHY_DLL_CNFG2_R); + sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, + PHY_DLLDL_CNFG_SLV_INPSEL), PHY_DLLDL_CNFG_R); + /* Sets the value of DLL's offset input */ + sdhci_writeb(host, 0x00, PHY_DLL_OFFST_R); + /* + * Sets the value of DLL's olbt loadval input. Controls the Ibt + * timer's timeout value at which DLL runs a revalidation cycle. + */ + sdhci_writew(host, 0xffff, PHY_DLLBT_CNFG_R); + sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R); + usleep_range(100, 110); + + ret = read_poll_timeout(sdhci_readb, status, status & DLL_LOCK_STS, 100, 1000000, + false, host, PHY_DLL_STATUS_R); + if (ret) { + pr_err("%s: DLL lock timeout! status: 0x%x\n", + mmc_hostname(host->mmc), status); + return; + } + + status = sdhci_readb(host, PHY_DLL_STATUS_R); + if (status & DLL_ERROR_STS) { + pr_err("%s: DLL lock failed!err_status:0x%x\n", + mmc_hostname(host->mmc), status); + } + } +} + +static void sdhci_eic7700_set_uhs_wrapper(struct sdhci_host *host, unsigned int timing) +{ + u32 sd_caps = MMC_CAP2_NO_MMC | MMC_CAP2_NO_SDIO; + + if ((host->mmc->caps2 & sd_caps) == sd_caps) + sdhci_set_uhs_signaling(host, timing); + else + sdhci_eic7700_set_uhs_signaling(host, timing); +} + +static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) +{ + u32 emmc_caps = MMC_CAP2_NO_SD | MMC_CAP2_NO_SDIO; + unsigned int val, hsp_int_status, hsp_pwr_ctrl; + static const char * const clk_ids[] = {"axi"}; + struct of_phandle_args args; + struct eic7700_priv *priv; + struct regmap *hsp_regmap; + int ret; + + priv = devm_kzalloc(dev, sizeof(struct eic7700_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dwc_priv->priv = priv; + + ret = sdhci_eic7700_reset_init(dev, dwc_priv->priv); + if (ret) { + dev_err(dev, "failed to reset\n"); + return ret; + } + + ret = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, + ARRAY_SIZE(clk_ids), clk_ids); + if (ret) + return ret; + + ret = of_parse_phandle_with_fixed_args(dev->of_node, "eswin,hsp-sp-csr", 2, 0, &args); + if (ret) { + dev_err(dev, "Fail to parse 'eswin,hsp-sp-csr' phandle (%d)\n", ret); + return ret; + } + + hsp_regmap = syscon_node_to_regmap(args.np); + if (IS_ERR(hsp_regmap)) { + dev_err(dev, "Failed to get regmap for 'eswin,hsp-sp-csr'\n"); + of_node_put(args.np); + return PTR_ERR(hsp_regmap); + } + hsp_int_status = args.args[0]; + hsp_pwr_ctrl = args.args[1]; + of_node_put(args.np); + /* + * Assert clock stability: write EIC7700_INT_CLK_STABLE to hsp_int_status. + * This signals to the eMMC controller that platform clocks (card, ACLK, + * BCLK, TMCLK) are enabled and stable. + */ + regmap_write(hsp_regmap, hsp_int_status, EIC7700_INT_CLK_STABLE); + /* + * Assert voltage stability: write EIC7700_HOST_VAL_STABLE to hsp_pwr_ctrl. + * This signals that VDD is stable and permits transition to high-speed + * modes (e.g., UHS-I). + */ + regmap_write(hsp_regmap, hsp_pwr_ctrl, EIC7700_HOST_VAL_STABLE); + + if ((host->mmc->caps2 & emmc_caps) == emmc_caps) + dwc_priv->delay_line = PHY_DELAY_CODE_EMMC; + else + dwc_priv->delay_line = PHY_DELAY_CODE_SD; + + if (!of_property_read_u32(dev->of_node, "eswin,drive-impedance-ohms", &val)) + priv->drive_impedance = eic7700_convert_drive_impedance_ohm(dev, val); + return 0; +} + +static void dwcmshc_k230_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + + sdhci_set_clock(host, clock); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + /* + * It is necessary to enable SDHCI_PROG_CLOCK_MODE. This is a + * vendor-specific quirk. If this is not done, the eMMC will be + * unable to read or write. + */ + clk |= SDHCI_PROG_CLOCK_MODE; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +} + +static void sdhci_k230_config_phy_delay(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 val; + + sdhci_writeb(host, PHY_COMMDL_CNFG_DLSTEP_SEL, PHY_COMMDL_CNFG); + sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R); + sdhci_writeb(host, PHY_SDCLKDL_DC_INITIAL, PHY_SDCLKDL_DC_R); + + val = PHY_SMPLDL_CNFG_EXTDLY_EN; + val |= FIELD_PREP(PHY_SMPLDL_CNFG_INPSEL_MASK, PHY_SMPLDL_CNFG_INPSEL); + sdhci_writeb(host, val, PHY_SMPLDL_CNFG_R); + + sdhci_writeb(host, FIELD_PREP(PHY_ATDL_CNFG_INPSEL_MASK, PHY_ATDL_CNFG_INPSEL), + PHY_ATDL_CNFG_R); + + val = sdhci_readl(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + val |= AT_CTRL_TUNE_CLK_STOP_EN; + val |= FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, AT_CTRL_PRE_CHANGE_DLY); + val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY); + sdhci_writel(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_AT_STAT); +} + +static int dwcmshc_k230_phy_init(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 rxsel; + u32 val; + u32 reg; + int ret; + + /* reset phy */ + sdhci_writew(host, 0, PHY_CNFG_R); + + /* Disable the clock */ + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + rxsel = dwc_priv->flags & FLAG_IO_FIXED_1V8 ? PHY_PAD_RXSEL_1V8 : PHY_PAD_RXSEL_3V3; + + val = rxsel; + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230); + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); + + sdhci_writew(host, val, PHY_CMDPAD_CNFG_R); + sdhci_writew(host, val, PHY_DATAPAD_CNFG_R); + sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R); + + val = rxsel; + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230); + sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); + + val = rxsel; + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230); + sdhci_writew(host, val, PHY_STBPAD_CNFG_R); + + sdhci_k230_config_phy_delay(host); + + /* Wait max 150 ms */ + ret = read_poll_timeout(sdhci_readl, reg, + (reg & FIELD_PREP(PHY_CNFG_PHY_PWRGOOD_MASK, 1)), + 10, 150000, false, host, PHY_CNFG_R); + if (ret) { + dev_err(mmc_dev(host->mmc), "READ PHY PWRGOOD timeout!\n"); + return -ETIMEDOUT; + } + + reg = FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN_k230) | + FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP_k230); + sdhci_writel(host, reg, PHY_CNFG_R); + + /* de-assert the phy */ + reg |= PHY_CNFG_RSTN_DEASSERT; + sdhci_writel(host, reg, PHY_CNFG_R); + + return 0; +} + +static void dwcmshc_k230_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230); + u8 emmc_ctrl; + + dwcmshc_reset(host, mask); + + if (mask != SDHCI_RESET_ALL) + return; + + emmc_ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + sdhci_writeb(host, emmc_ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + + if (k230_pdata->is_emmc) + dwcmshc_k230_phy_init(host); + else + sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); +} + +static int dwcmshc_k230_init(struct device *dev, struct sdhci_host *host, + struct dwcmshc_priv *dwc_priv) +{ + const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230); + static const char * const clk_ids[] = {"block", "timer", "axi"}; + struct device_node *usb_phy_node; + struct k230_priv *k230_priv; + u32 data; + int ret; + + k230_priv = devm_kzalloc(dev, sizeof(struct k230_priv), GFP_KERNEL); + if (!k230_priv) + return -ENOMEM; + + dwc_priv->priv = k230_priv; + + usb_phy_node = of_parse_phandle(dev->of_node, "canaan,usb-phy", 0); + if (!usb_phy_node) + return dev_err_probe(dev, -ENODEV, "Failed to find canaan,usb-phy phandle\n"); + + k230_priv->hi_sys_regmap = device_node_to_regmap(usb_phy_node); + of_node_put(usb_phy_node); + + if (IS_ERR(k230_priv->hi_sys_regmap)) + return dev_err_probe(dev, PTR_ERR(k230_priv->hi_sys_regmap), + "Failed to get k230-usb-phy regmap\n"); + + ret = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, + ARRAY_SIZE(clk_ids), clk_ids); + if (ret) + return dev_err_probe(dev, ret, "Failed to get/enable k230 mmc other clocks\n"); + + if (k230_pdata->is_emmc) { + host->flags &= ~SDHCI_SIGNALING_330; + dwc_priv->flags |= FLAG_IO_FIXED_1V8; + } else { + host->mmc->caps |= MMC_CAP_SD_HIGHSPEED; + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + } + + ret = regmap_read(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, &data); + if (ret) + return dev_err_probe(dev, ret, "Failed to read control reg 0x%x\n", + k230_pdata->ctrl_reg); + + data |= k230_pdata->write_prot_bit | k230_pdata->vol_stable_bit; + ret = regmap_write(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, data); + if (ret) + return dev_err_probe(dev, ret, "Failed to write control reg 0x%x\n", + k230_pdata->ctrl_reg); + + return 0; +} + static const struct sdhci_ops sdhci_dwcmshc_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = dwcmshc_set_uhs_signaling, .get_max_clock = dwcmshc_get_max_clock, - .reset = sdhci_reset, + .reset = dwcmshc_reset, .adma_write_desc = dwcmshc_adma_write_desc, .irq = dwcmshc_cqe_irq_handler, }; @@ -1163,7 +2054,7 @@ static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = { .get_max_clock = dwcmshc_get_max_clock, .reset = th1520_sdhci_reset, .adma_write_desc = dwcmshc_adma_write_desc, - .voltage_switch = dwcmshc_phy_1_8v_init, + .voltage_switch = dwcmshc_phy_init, .platform_execute_tuning = th1520_execute_tuning, }; @@ -1187,6 +2078,28 @@ static const struct sdhci_ops sdhci_dwcmshc_sg2042_ops = { .platform_execute_tuning = th1520_execute_tuning, }; +static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = { + .set_clock = sdhci_eic7700_set_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_timeout_clock = sdhci_pltfm_clk_get_max_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_eic7700_reset, + .set_uhs_signaling = sdhci_eic7700_set_uhs_wrapper, + .set_power = sdhci_set_power_and_bus_voltage, + .irq = dwcmshc_cqe_irq_handler, + .adma_write_desc = dwcmshc_adma_write_desc, + .platform_execute_tuning = sdhci_eic7700_executing_tuning, +}; + +static const struct sdhci_ops sdhci_dwcmshc_k230_ops = { + .set_clock = dwcmshc_k230_sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .reset = dwcmshc_k230_sdhci_reset, + .adma_write_desc = dwcmshc_adma_write_desc, +}; + static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = { .pdata = { .ops = &sdhci_dwcmshc_ops, @@ -1206,16 +2119,61 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_bf3_pdata = { }; #endif -static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { - .pdata = { - .ops = &sdhci_dwcmshc_rk35xx_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, +static const struct cqhci_host_ops rk35xx_cqhci_ops = { + .pre_enable = rk35xx_sdhci_cqe_pre_enable, + .enable = rk35xx_sdhci_cqe_enable, + .disable = rk35xx_sdhci_cqe_disable, + .post_disable = rk35xx_sdhci_cqe_post_disable, + .dumpregs = dwcmshc_cqhci_dumpregs, + .set_tran_desc = dwcmshc_set_tran_desc, +}; + +static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3568_pdata = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .cqhci_host_ops = &rk35xx_cqhci_ops, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk35xx_postinit, }, - .init = dwcmshc_rk35xx_init, - .postinit = dwcmshc_rk35xx_postinit, + .revision = 0, +}; + +static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3576_pdata = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .cqhci_host_ops = &rk35xx_cqhci_ops, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk3576_postinit, + }, + .revision = 1, +}; + +static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3588_pdata = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .cqhci_host_ops = &rk35xx_cqhci_ops, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk35xx_postinit, + }, + .revision = 1, }; static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = { @@ -1244,6 +2202,66 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_sg2042_pdata = { .init = sg2042_init, }; +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_eic7700_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .init = eic7700_init, +}; + +static const struct k230_pltfm_data k230_emmc_data = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_k230_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, + }, + .init = dwcmshc_k230_init, + }, + .is_emmc = true, + .ctrl_reg = SD0_CTRL, + .vol_stable_bit = SD0_HOST_REG_VOL_STABLE, + .write_prot_bit = SD0_CARD_WRITE_PROT, +}; + +static const struct k230_pltfm_data k230_sdio_data = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_k230_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, + }, + .init = dwcmshc_k230_init, + }, + .is_emmc = false, + .ctrl_reg = SD1_CTRL, + .vol_stable_bit = SD1_HOST_REG_VOL_STABLE, + .write_prot_bit = SD1_CARD_WRITE_PROT, +}; + +static const struct sdhci_ops sdhci_dwcmshc_hpe_ops = { + .set_clock = dwcmshc_hpe_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_hpe_set_uhs_signaling, + .get_max_clock = dwcmshc_get_max_clock, + .reset = dwcmshc_hpe_reset, + .adma_write_desc = dwcmshc_adma_write_desc, + .irq = dwcmshc_cqe_irq_handler, +}; + +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_hpe_gsc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_hpe_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, + .init = dwcmshc_hpe_gsc_init, +}; + static const struct cqhci_host_ops dwcmshc_cqhci_ops = { .enable = dwcmshc_sdhci_cqe_enable, .disable = sdhci_cqe_disable, @@ -1251,7 +2269,8 @@ static const struct cqhci_host_ops dwcmshc_cqhci_ops = { .set_tran_desc = dwcmshc_set_tran_desc, }; -static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev) +static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev, + const struct dwcmshc_pltfm_data *pltfm_data) { struct cqhci_host *cq_host; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1281,7 +2300,10 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device * } cq_host->mmio = host->ioaddr + priv->vendor_specific_area2; - cq_host->ops = &dwcmshc_cqhci_ops; + if (pltfm_data->cqhci_host_ops) + cq_host->ops = pltfm_data->cqhci_host_ops; + else + cq_host->ops = &dwcmshc_cqhci_ops; /* Enable using of 128-bit task descriptors */ dma64 = host->flags & SDHCI_USE_64_BIT_DMA; @@ -1313,12 +2335,24 @@ dsbl_cqe_caps: static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { { + .compatible = "canaan,k230-emmc", + .data = &k230_emmc_data.dwcmshc_pdata, + }, + { + .compatible = "canaan,k230-sdio", + .data = &k230_sdio_data.dwcmshc_pdata, + }, + { .compatible = "rockchip,rk3588-dwcmshc", - .data = &sdhci_dwcmshc_rk35xx_pdata, + .data = &sdhci_dwcmshc_rk3588_pdata, + }, + { + .compatible = "rockchip,rk3576-dwcmshc", + .data = &sdhci_dwcmshc_rk3576_pdata, }, { .compatible = "rockchip,rk3568-dwcmshc", - .data = &sdhci_dwcmshc_rk35xx_pdata, + .data = &sdhci_dwcmshc_rk3568_pdata, }, { .compatible = "snps,dwcmshc-sdhci", @@ -1340,6 +2374,14 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { .compatible = "sophgo,sg2042-dwcmshc", .data = &sdhci_dwcmshc_sg2042_pdata, }, + { + .compatible = "eswin,eic7700-dwcmshc", + .data = &sdhci_dwcmshc_eic7700_pdata, + }, + { + .compatible = "hpe,gsc-dwcmshc", + .data = &sdhci_dwcmshc_hpe_gsc_pdata, + }, {}, }; MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); @@ -1386,17 +2428,17 @@ static int dwcmshc_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); priv = sdhci_pltfm_priv(pltfm_host); + priv->dwcmshc_pdata = pltfm_data; if (dev->of_node) { pltfm_host->clk = devm_clk_get(dev, "core"); - if (IS_ERR(pltfm_host->clk)) { - err = PTR_ERR(pltfm_host->clk); - dev_err(dev, "failed to get core clk: %d\n", err); - goto free_pltfm; - } + if (IS_ERR(pltfm_host->clk)) + return dev_err_probe(dev, PTR_ERR(pltfm_host->clk), + "failed to get core clk\n"); + err = clk_prepare_enable(pltfm_host->clk); if (err) - goto free_pltfm; + return err; priv->bus_clk = devm_clk_get(dev, "bus"); if (!IS_ERR(priv->bus_clk)) @@ -1446,7 +2488,7 @@ static int dwcmshc_probe(struct platform_device *pdev) priv->vendor_specific_area2 = sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2); - dwcmshc_cqhci_init(host, pdev); + dwcmshc_cqhci_init(host, pdev, pltfm_data); } if (pltfm_data->postinit) @@ -1469,8 +2511,6 @@ err_clk: clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); -free_pltfm: - sdhci_pltfm_free(pdev); return err; } @@ -1502,10 +2542,8 @@ static void dwcmshc_remove(struct platform_device *pdev) clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks); - sdhci_pltfm_free(pdev); } -#ifdef CONFIG_PM_SLEEP static int dwcmshc_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -1576,20 +2614,6 @@ disable_clk: clk_disable_unprepare(pltfm_host->clk); return ret; } -#endif - -#ifdef CONFIG_PM - -static void dwcmshc_enable_card_clk(struct sdhci_host *host) -{ - u16 ctrl; - - ctrl = sdhci_readw(host, SDHCI_CLOCK_CONTROL); - if ((ctrl & SDHCI_CLOCK_INT_EN) && !(ctrl & SDHCI_CLOCK_CARD_EN)) { - ctrl |= SDHCI_CLOCK_CARD_EN; - sdhci_writew(host, ctrl, SDHCI_CLOCK_CONTROL); - } -} static int dwcmshc_runtime_suspend(struct device *dev) { @@ -1609,12 +2633,9 @@ static int dwcmshc_runtime_resume(struct device *dev) return 0; } -#endif - static const struct dev_pm_ops dwcmshc_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend, dwcmshc_resume) - SET_RUNTIME_PM_OPS(dwcmshc_runtime_suspend, - dwcmshc_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(dwcmshc_suspend, dwcmshc_resume) + RUNTIME_PM_OPS(dwcmshc_runtime_suspend, dwcmshc_runtime_resume, NULL) }; static struct platform_driver sdhci_dwcmshc_driver = { @@ -1623,7 +2644,7 @@ static struct platform_driver sdhci_dwcmshc_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sdhci_dwcmshc_dt_ids, .acpi_match_table = ACPI_PTR(sdhci_dwcmshc_acpi_ids), - .pm = &dwcmshc_pmops, + .pm = pm_ptr(&dwcmshc_pmops), }, .probe = dwcmshc_probe, .remove = dwcmshc_remove, diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 002d0d59b992..8345e2c5a034 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -1234,7 +1234,6 @@ static u32 esdhc_irq(struct sdhci_host *host, u32 intmask) return intmask; } -#ifdef CONFIG_PM_SLEEP static u32 esdhc_proctl; static int esdhc_of_suspend(struct device *dev) { @@ -1260,11 +1259,8 @@ static int esdhc_of_resume(struct device *dev) } return ret; } -#endif -static SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops, - esdhc_of_suspend, - esdhc_of_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(esdhc_of_dev_pm_ops, esdhc_of_suspend, esdhc_of_resume); static const struct sdhci_ops sdhci_esdhc_be_ops = { .read_l = esdhc_be_readl, @@ -1499,18 +1495,11 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) /* call to generic mmc_of_parse to support additional capabilities */ ret = mmc_of_parse(host->mmc); if (ret) - goto err; + return ret; mmc_of_parse_voltage(host->mmc, &host->ocr_mask); - ret = sdhci_add_host(host); - if (ret) - goto err; - - return 0; - err: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static struct platform_driver sdhci_esdhc_driver = { @@ -1518,7 +1507,7 @@ static struct platform_driver sdhci_esdhc_driver = { .name = "sdhci-esdhc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sdhci_esdhc_of_match, - .pm = &esdhc_of_dev_pm_ops, + .pm = pm_sleep_ptr(&esdhc_of_dev_pm_ops), }, .probe = sdhci_esdhc_probe, .remove = sdhci_pltfm_remove, diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c new file mode 100644 index 000000000000..455656f9842d --- /dev/null +++ b/drivers/mmc/host/sdhci-of-k1.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd + * Copyright (c) 2025 Yixun Lan <dlan@gentoo.org> + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/iopoll.h> +#include <linux/init.h> +#include <linux/mmc/card.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/reset.h> +#include <linux/platform_device.h> + +#include "sdhci.h" +#include "sdhci-pltfm.h" + +#define SPACEMIT_SDHC_MMC_CTRL_REG 0x114 +#define SDHC_MISC_INT_EN BIT(1) +#define SDHC_MISC_INT BIT(2) +#define SDHC_ENHANCE_STROBE_EN BIT(8) +#define SDHC_MMC_HS400 BIT(9) +#define SDHC_MMC_HS200 BIT(10) +#define SDHC_MMC_CARD_MODE BIT(12) + +#define SPACEMIT_SDHC_TX_CFG_REG 0x11C +#define SDHC_TX_INT_CLK_SEL BIT(30) +#define SDHC_TX_MUX_SEL BIT(31) + +#define SPACEMIT_SDHC_PHY_CTRL_REG 0x160 +#define SDHC_PHY_FUNC_EN BIT(0) +#define SDHC_PHY_PLL_LOCK BIT(1) +#define SDHC_HOST_LEGACY_MODE BIT(31) + +#define SPACEMIT_SDHC_PHY_FUNC_REG 0x164 +#define SDHC_PHY_TEST_EN BIT(7) +#define SDHC_HS200_USE_RFIFO BIT(15) + +#define SPACEMIT_SDHC_PHY_DLLCFG 0x168 +#define SDHC_DLL_PREDLY_NUM GENMASK(3, 2) +#define SDHC_DLL_FULLDLY_RANGE GENMASK(5, 4) +#define SDHC_DLL_VREG_CTRL GENMASK(7, 6) +#define SDHC_DLL_ENABLE BIT(31) + +#define SPACEMIT_SDHC_PHY_DLLCFG1 0x16C +#define SDHC_DLL_REG1_CTRL GENMASK(7, 0) +#define SDHC_DLL_REG2_CTRL GENMASK(15, 8) +#define SDHC_DLL_REG3_CTRL GENMASK(23, 16) +#define SDHC_DLL_REG4_CTRL GENMASK(31, 24) + +#define SPACEMIT_SDHC_PHY_DLLSTS 0x170 +#define SDHC_DLL_LOCK_STATE BIT(0) + +#define SPACEMIT_SDHC_PHY_PADCFG_REG 0x178 +#define SDHC_PHY_DRIVE_SEL GENMASK(2, 0) +#define SDHC_RX_BIAS_CTRL BIT(5) + +struct spacemit_sdhci_host { + struct clk *clk_core; + struct clk *clk_io; +}; + +/* All helper functions will update clr/set while preserve rest bits */ +static inline void spacemit_sdhci_setbits(struct sdhci_host *host, u32 val, int reg) +{ + sdhci_writel(host, sdhci_readl(host, reg) | val, reg); +} + +static inline void spacemit_sdhci_clrbits(struct sdhci_host *host, u32 val, int reg) +{ + sdhci_writel(host, sdhci_readl(host, reg) & ~val, reg); +} + +static inline void spacemit_sdhci_clrsetbits(struct sdhci_host *host, u32 clr, u32 set, int reg) +{ + u32 val = sdhci_readl(host, reg); + + val = (val & ~clr) | set; + sdhci_writel(host, val, reg); +} + +static void spacemit_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + sdhci_reset(host, mask); + + if (mask != SDHCI_RESET_ALL) + return; + + spacemit_sdhci_setbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK, + SPACEMIT_SDHC_PHY_CTRL_REG); + + spacemit_sdhci_clrsetbits(host, SDHC_PHY_DRIVE_SEL, + SDHC_RX_BIAS_CTRL | FIELD_PREP(SDHC_PHY_DRIVE_SEL, 4), + SPACEMIT_SDHC_PHY_PADCFG_REG); + + if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) + spacemit_sdhci_setbits(host, SDHC_MMC_CARD_MODE, SPACEMIT_SDHC_MMC_CTRL_REG); +} + +static void spacemit_sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) +{ + if (timing == MMC_TIMING_MMC_HS200) + spacemit_sdhci_setbits(host, SDHC_MMC_HS200, SPACEMIT_SDHC_MMC_CTRL_REG); + + if (timing == MMC_TIMING_MMC_HS400) + spacemit_sdhci_setbits(host, SDHC_MMC_HS400, SPACEMIT_SDHC_MMC_CTRL_REG); + + sdhci_set_uhs_signaling(host, timing); + + if (!(host->mmc->caps2 & MMC_CAP2_NO_SDIO)) + spacemit_sdhci_setbits(host, SDHCI_CTRL_VDD_180, SDHCI_HOST_CONTROL2); +} + +static void spacemit_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct mmc_host *mmc = host->mmc; + + if (mmc->ios.timing <= MMC_TIMING_UHS_SDR50) + spacemit_sdhci_setbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG); + else + spacemit_sdhci_clrbits(host, SDHC_TX_INT_CLK_SEL, SPACEMIT_SDHC_TX_CFG_REG); + + sdhci_set_clock(host, clock); +}; + +static void spacemit_sdhci_phy_dll_init(struct sdhci_host *host) +{ + u32 state; + int ret; + + spacemit_sdhci_clrsetbits(host, SDHC_DLL_PREDLY_NUM | + SDHC_DLL_FULLDLY_RANGE | + SDHC_DLL_VREG_CTRL, + FIELD_PREP(SDHC_DLL_PREDLY_NUM, 1) | + FIELD_PREP(SDHC_DLL_FULLDLY_RANGE, 1) | + FIELD_PREP(SDHC_DLL_VREG_CTRL, 1), + SPACEMIT_SDHC_PHY_DLLCFG); + + spacemit_sdhci_clrsetbits(host, SDHC_DLL_REG1_CTRL, + FIELD_PREP(SDHC_DLL_REG1_CTRL, 0x92), + SPACEMIT_SDHC_PHY_DLLCFG1); + + spacemit_sdhci_setbits(host, SDHC_DLL_ENABLE, SPACEMIT_SDHC_PHY_DLLCFG); + + ret = readl_poll_timeout(host->ioaddr + SPACEMIT_SDHC_PHY_DLLSTS, state, + state & SDHC_DLL_LOCK_STATE, 2, 100); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), "fail to lock phy dll in 100us!\n"); +} + +static void spacemit_sdhci_hs400_enhanced_strobe(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + + if (!ios->enhanced_strobe) { + spacemit_sdhci_clrbits(host, SDHC_ENHANCE_STROBE_EN, SPACEMIT_SDHC_MMC_CTRL_REG); + return; + } + + spacemit_sdhci_setbits(host, SDHC_ENHANCE_STROBE_EN, SPACEMIT_SDHC_MMC_CTRL_REG); + spacemit_sdhci_phy_dll_init(host); +} + +static unsigned int spacemit_sdhci_clk_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return clk_get_rate(pltfm_host->clk); +} + +static int spacemit_sdhci_pre_select_hs400(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + spacemit_sdhci_setbits(host, SDHC_MMC_HS400, SPACEMIT_SDHC_MMC_CTRL_REG); + + return 0; +} + +static void spacemit_sdhci_post_select_hs400(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + spacemit_sdhci_phy_dll_init(host); +} + +static void spacemit_sdhci_pre_hs400_to_hs200(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + + spacemit_sdhci_clrbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK, + SPACEMIT_SDHC_PHY_CTRL_REG); + spacemit_sdhci_clrbits(host, SDHC_MMC_HS400 | SDHC_MMC_HS200 | SDHC_ENHANCE_STROBE_EN, + SPACEMIT_SDHC_MMC_CTRL_REG); + spacemit_sdhci_clrbits(host, SDHC_HS200_USE_RFIFO, SPACEMIT_SDHC_PHY_FUNC_REG); + + udelay(5); + + spacemit_sdhci_setbits(host, SDHC_PHY_FUNC_EN | SDHC_PHY_PLL_LOCK, + SPACEMIT_SDHC_PHY_CTRL_REG); +} + +static inline int spacemit_sdhci_get_clocks(struct device *dev, + struct sdhci_pltfm_host *pltfm_host) +{ + struct spacemit_sdhci_host *sdhst = sdhci_pltfm_priv(pltfm_host); + + sdhst->clk_core = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(sdhst->clk_core)) + return -EINVAL; + + sdhst->clk_io = devm_clk_get_enabled(dev, "io"); + if (IS_ERR(sdhst->clk_io)) + return -EINVAL; + + pltfm_host->clk = sdhst->clk_io; + + return 0; +} + +static inline int spacemit_sdhci_get_resets(struct device *dev) +{ + struct reset_control *rst; + + rst = devm_reset_control_get_optional_shared_deasserted(dev, "axi"); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + rst = devm_reset_control_get_optional_exclusive_deasserted(dev, "sdh"); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + return 0; +} + +static const struct sdhci_ops spacemit_sdhci_ops = { + .get_max_clock = spacemit_sdhci_clk_get_max_clock, + .reset = spacemit_sdhci_reset, + .set_bus_width = sdhci_set_bus_width, + .set_clock = spacemit_sdhci_set_clock, + .set_uhs_signaling = spacemit_sdhci_set_uhs_signaling, +}; + +static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = { + .ops = &spacemit_sdhci_ops, + .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_32BIT_ADMA_SIZE | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_BROKEN_64_BIT_DMA | + SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + +static const struct sdhci_pltfm_data spacemit_sdhci_k3_pdata = { + .ops = &spacemit_sdhci_ops, + .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_32BIT_ADMA_SIZE | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + +static const struct of_device_id spacemit_sdhci_of_match[] = { + { .compatible = "spacemit,k1-sdhci", .data = &spacemit_sdhci_k1_pdata }, + { .compatible = "spacemit,k3-sdhci", .data = &spacemit_sdhci_k3_pdata }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match); + +static int spacemit_sdhci_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spacemit_sdhci_host *sdhst; + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_host *host; + const struct sdhci_pltfm_data *data; + struct mmc_host_ops *mops; + int ret; + + data = of_device_get_match_data(&pdev->dev); + + host = sdhci_pltfm_init(pdev, data, sizeof(*sdhst)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + + ret = mmc_of_parse(host->mmc); + if (ret) + goto err_pltfm; + + sdhci_get_of_property(pdev); + + if (!(host->mmc->caps2 & MMC_CAP2_NO_MMC)) { + mops = &host->mmc_host_ops; + mops->hs400_prepare_ddr = spacemit_sdhci_pre_select_hs400; + mops->hs400_complete = spacemit_sdhci_post_select_hs400; + mops->hs400_downgrade = spacemit_sdhci_pre_hs400_to_hs200; + mops->hs400_enhanced_strobe = spacemit_sdhci_hs400_enhanced_strobe; + } + + host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY; + + ret = spacemit_sdhci_get_clocks(dev, pltfm_host); + if (ret) + goto err_pltfm; + + ret = spacemit_sdhci_get_resets(dev); + if (ret) + goto err_pltfm; + + ret = sdhci_add_host(host); + if (ret) + goto err_pltfm; + + return 0; + +err_pltfm: + return ret; +} + +static struct platform_driver spacemit_sdhci_driver = { + .driver = { + .name = "sdhci-spacemit", + .of_match_table = spacemit_sdhci_of_match, + }, + .probe = spacemit_sdhci_probe, + .remove = sdhci_pltfm_remove, +}; +module_platform_driver(spacemit_sdhci_driver); + +MODULE_DESCRIPTION("SpacemiT SDHCI platform driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci-of-ma35d1.c b/drivers/mmc/host/sdhci-of-ma35d1.c index 1e6d180100ad..287026422616 100644 --- a/drivers/mmc/host/sdhci-of-ma35d1.c +++ b/drivers/mmc/host/sdhci-of-ma35d1.c @@ -211,20 +211,18 @@ static int ma35_probe(struct platform_device *pdev) priv = sdhci_pltfm_priv(pltfm_host); pltfm_host->clk = devm_clk_get_optional_enabled(dev, NULL); - if (IS_ERR(pltfm_host->clk)) { - err = dev_err_probe(dev, PTR_ERR(pltfm_host->clk), "failed to get clk\n"); - goto err_sdhci; - } + if (IS_ERR(pltfm_host->clk)) + return dev_err_probe(dev, PTR_ERR(pltfm_host->clk), + "failed to get clk\n"); err = mmc_of_parse(host->mmc); if (err) - goto err_sdhci; + return err; priv->rst = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(priv->rst)) { - err = dev_err_probe(dev, PTR_ERR(priv->rst), "failed to get reset control\n"); - goto err_sdhci; - } + if (IS_ERR(priv->rst)) + return dev_err_probe(dev, PTR_ERR(priv->rst), + "failed to get reset control\n"); sdhci_get_of_property(pdev); @@ -255,7 +253,7 @@ static int ma35_probe(struct platform_device *pdev) err = sdhci_add_host(host); if (err) - goto err_sdhci; + return err; /* * Split data into chunks of 16 or 8 bytes for transmission. @@ -268,10 +266,6 @@ static int ma35_probe(struct platform_device *pdev) sdhci_writew(host, ctl, MA35_SDHCI_MBIUCTL); return 0; - -err_sdhci: - sdhci_pltfm_free(pdev); - return err; } static void ma35_disable_card_clk(struct sdhci_host *host) @@ -291,7 +285,6 @@ static void ma35_remove(struct platform_device *pdev) sdhci_remove_host(host, 0); ma35_disable_card_clk(host); - sdhci_pltfm_free(pdev); } static const struct of_device_id sdhci_ma35_dt_ids[] = { diff --git a/drivers/mmc/host/sdhci-of-sparx5.c b/drivers/mmc/host/sdhci-of-sparx5.c index d2aa684e786f..b3db1e2c4c0e 100644 --- a/drivers/mmc/host/sdhci-of-sparx5.c +++ b/drivers/mmc/host/sdhci-of-sparx5.c @@ -185,11 +185,9 @@ static int sdhci_sparx5_probe(struct platform_device *pdev) sdhci_sparx5->host = host; pltfm_host->clk = devm_clk_get_enabled(&pdev->dev, "core"); - if (IS_ERR(pltfm_host->clk)) { - ret = PTR_ERR(pltfm_host->clk); - dev_err(&pdev->dev, "failed to get and enable core clk: %d\n", ret); - goto free_pltfm; - } + if (IS_ERR(pltfm_host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pltfm_host->clk), + "failed to get and enable core clk\n"); if (!of_property_read_u32(np, "microchip,clock-delay", &value) && (value > 0 && value <= MSHC_DLY_CC_MAX)) @@ -199,14 +197,12 @@ static int sdhci_sparx5_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto free_pltfm; + return ret; sdhci_sparx5->cpu_ctrl = syscon_regmap_lookup_by_compatible(syscon); - if (IS_ERR(sdhci_sparx5->cpu_ctrl)) { - dev_err(&pdev->dev, "No CPU syscon regmap !\n"); - ret = PTR_ERR(sdhci_sparx5->cpu_ctrl); - goto free_pltfm; - } + if (IS_ERR(sdhci_sparx5->cpu_ctrl)) + return dev_err_probe(&pdev->dev, PTR_ERR(sdhci_sparx5->cpu_ctrl), + "No CPU syscon regmap !\n"); if (sdhci_sparx5->delay_clock >= 0) sparx5_set_delay(host, sdhci_sparx5->delay_clock); @@ -222,7 +218,7 @@ static int sdhci_sparx5_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) - goto free_pltfm; + return ret; /* Set AXI bus master to use un-cached access (for DMA) */ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA) && @@ -235,10 +231,6 @@ static int sdhci_sparx5_probe(struct platform_device *pdev) mmc_hostname(host->mmc), sdhci_readl(host, MSHC2_TYPE)); return ret; - -free_pltfm: - sdhci_pltfm_free(pdev); - return ret; } static const struct of_device_id sdhci_sparx5_of_match[] = { diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index 54d795205fb4..b5d7c1a80a92 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -1257,7 +1257,7 @@ static int sdhci_omap_probe(struct platform_device *pdev) sdhci_get_of_property(pdev); ret = mmc_of_parse(mmc); if (ret) - goto err_pltfm_free; + return ret; soc = soc_device_match(sdhci_omap_soc_devices); if (soc) { @@ -1270,26 +1270,23 @@ static int sdhci_omap_probe(struct platform_device *pdev) mmc->f_max = 48000000; } - if (!mmc_can_gpio_ro(mmc)) + if (!mmc_host_can_gpio_ro(mmc)) mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; pltfm_host->clk = devm_clk_get(dev, "fck"); - if (IS_ERR(pltfm_host->clk)) { - ret = PTR_ERR(pltfm_host->clk); - goto err_pltfm_free; - } + if (IS_ERR(pltfm_host->clk)) + return PTR_ERR(pltfm_host->clk); ret = clk_set_rate(pltfm_host->clk, mmc->f_max); - if (ret) { - dev_err(dev, "failed to set clock to %d\n", mmc->f_max); - goto err_pltfm_free; - } + if (ret) + return dev_err_probe(dev, ret, + "failed to set clock to %d\n", mmc->f_max); omap_host->pbias = devm_regulator_get_optional(dev, "pbias"); if (IS_ERR(omap_host->pbias)) { ret = PTR_ERR(omap_host->pbias); if (ret != -ENODEV) - goto err_pltfm_free; + return ret; dev_dbg(dev, "unable to get pbias regulator %d\n", ret); } omap_host->pbias_enabled = false; @@ -1339,8 +1336,8 @@ static int sdhci_omap_probe(struct platform_device *pdev) /* R1B responses is required to properly manage HW busy detection. */ mmc->caps |= MMC_CAP_NEED_RSP_BUSY; - /* Allow card power off and runtime PM for eMMC/SD card devices */ - mmc->caps |= MMC_CAP_POWER_OFF_CARD | MMC_CAP_AGGRESSIVE_PM; + /* Enable SDIO card power off. */ + mmc->caps |= MMC_CAP_POWER_OFF_CARD; ret = sdhci_setup_host(host); if (ret) @@ -1373,7 +1370,6 @@ static int sdhci_omap_probe(struct platform_device *pdev) host->mmc->pm_caps |= MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; } - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -1382,14 +1378,10 @@ err_cleanup_host: sdhci_cleanup_host(host); err_rpm_put: - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); err_rpm_disable: pm_runtime_dont_use_autosuspend(dev); pm_runtime_disable(dev); - -err_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -1406,11 +1398,9 @@ static void sdhci_omap_remove(struct platform_device *pdev) pm_runtime_put_sync(dev); /* Ensure device gets disabled despite userspace sysfs config */ pm_runtime_force_suspend(dev); - sdhci_pltfm_free(pdev); } -#ifdef CONFIG_PM -static void __maybe_unused sdhci_omap_context_save(struct sdhci_omap_host *omap_host) +static void sdhci_omap_context_save(struct sdhci_omap_host *omap_host) { omap_host->con = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); omap_host->hctl = sdhci_omap_readl(omap_host, SDHCI_OMAP_HCTL); @@ -1421,7 +1411,7 @@ static void __maybe_unused sdhci_omap_context_save(struct sdhci_omap_host *omap_ } /* Order matters here, HCTL must be restored in two phases */ -static void __maybe_unused sdhci_omap_context_restore(struct sdhci_omap_host *omap_host) +static void sdhci_omap_context_restore(struct sdhci_omap_host *omap_host) { sdhci_omap_writel(omap_host, SDHCI_OMAP_HCTL, omap_host->hctl); sdhci_omap_writel(omap_host, SDHCI_OMAP_CAPA, omap_host->capa); @@ -1433,7 +1423,7 @@ static void __maybe_unused sdhci_omap_context_restore(struct sdhci_omap_host *om sdhci_omap_writel(omap_host, SDHCI_OMAP_ISE, omap_host->ise); } -static int __maybe_unused sdhci_omap_runtime_suspend(struct device *dev) +static int sdhci_omap_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1452,7 +1442,7 @@ static int __maybe_unused sdhci_omap_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused sdhci_omap_runtime_resume(struct device *dev) +static int sdhci_omap_runtime_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1467,13 +1457,10 @@ static int __maybe_unused sdhci_omap_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops sdhci_omap_dev_pm_ops = { - SET_RUNTIME_PM_OPS(sdhci_omap_runtime_suspend, - sdhci_omap_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(sdhci_omap_runtime_suspend, sdhci_omap_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver sdhci_omap_driver = { @@ -1482,7 +1469,7 @@ static struct platform_driver sdhci_omap_driver = { .driver = { .name = "sdhci-omap", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &sdhci_omap_dev_pm_ops, + .pm = pm_ptr(&sdhci_omap_dev_pm_ops), .of_match_table = omap_sdhci_match, }, }; diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 1f0bd723f011..c347fac24515 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -20,7 +20,6 @@ #include <linux/scatterlist.h> #include <linux/io.h> #include <linux/iopoll.h> -#include <linux/gpio.h> #include <linux/gpio/machine.h> #include <linux/pm_runtime.h> #include <linux/pm_qos.h> @@ -152,18 +151,15 @@ static int sdhci_pci_runtime_suspend_host(struct sdhci_pci_chip *chip) { struct sdhci_pci_slot *slot; struct sdhci_host *host; - int i, ret; - for (i = 0; i < chip->num_slots; i++) { + for (int i = 0; i < chip->num_slots; i++) { slot = chip->slots[i]; if (!slot) continue; host = slot->host; - ret = sdhci_runtime_suspend_host(host); - if (ret) - goto err_pci_runtime_suspend; + sdhci_runtime_suspend_host(host); if (chip->rpm_retune && host->tuning_mode != SDHCI_TUNING_MODE_3) @@ -171,26 +167,18 @@ static int sdhci_pci_runtime_suspend_host(struct sdhci_pci_chip *chip) } return 0; - -err_pci_runtime_suspend: - while (--i >= 0) - sdhci_runtime_resume_host(chip->slots[i]->host, 0); - return ret; } static int sdhci_pci_runtime_resume_host(struct sdhci_pci_chip *chip) { struct sdhci_pci_slot *slot; - int i, ret; - for (i = 0; i < chip->num_slots; i++) { + for (int i = 0; i < chip->num_slots; i++) { slot = chip->slots[i]; if (!slot) continue; - ret = sdhci_runtime_resume_host(slot->host, 0); - if (ret) - return ret; + sdhci_runtime_resume_host(slot->host, 0); } return 0; @@ -610,8 +598,12 @@ static void sdhci_intel_set_power(struct sdhci_host *host, unsigned char mode, sdhci_set_power(host, mode, vdd); - if (mode == MMC_POWER_OFF) + if (mode == MMC_POWER_OFF) { + if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_APL_SD || + slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BYT_SD) + usleep_range(15000, 17500); return; + } /* * Bus power might not enable after D3 -> D0 transition due to the @@ -686,8 +678,19 @@ static int intel_start_signal_voltage_switch(struct mmc_host *mmc, return 0; } +static void sdhci_intel_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + + /* Stop card clock separately to avoid glitches on clock line */ + if (clk & SDHCI_CLOCK_CARD_EN) + sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN, SDHCI_CLOCK_CONTROL); + + sdhci_set_clock(host, clock); +} + static const struct sdhci_ops sdhci_intel_byt_ops = { - .set_clock = sdhci_set_clock, + .set_clock = sdhci_intel_set_clock, .set_power = sdhci_intel_set_power, .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_set_bus_width, @@ -697,7 +700,7 @@ static const struct sdhci_ops sdhci_intel_byt_ops = { }; static const struct sdhci_ops sdhci_intel_glk_ops = { - .set_clock = sdhci_set_clock, + .set_clock = sdhci_intel_set_clock, .set_power = sdhci_intel_set_power, .enable_dma = sdhci_pci_enable_dma, .set_bus_width = sdhci_set_bus_width, @@ -909,7 +912,8 @@ static bool glk_broken_cqhci(struct sdhci_pci_slot *slot) { return slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC && (dmi_match(DMI_BIOS_VENDOR, "LENOVO") || - dmi_match(DMI_SYS_VENDOR, "IRBIS")); + dmi_match(DMI_SYS_VENDOR, "IRBIS") || + dmi_match(DMI_SYS_VENDOR, "Positivo Tecnologia SA")); } static bool jsl_broken_hs400es(struct sdhci_pci_slot *slot) @@ -2169,7 +2173,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( ret = pcim_iomap_regions(pdev, BIT(bar), mmc_hostname(host->mmc)); if (ret) { dev_err(&pdev->dev, "cannot request region\n"); - goto cleanup; + return ERR_PTR(ret); } host->ioaddr = pcim_iomap_table(pdev)[bar]; @@ -2177,7 +2181,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( if (chip->fixes && chip->fixes->probe_slot) { ret = chip->fixes->probe_slot(slot); if (ret) - goto cleanup; + return ERR_PTR(ret); } host->mmc->pm_caps = MMC_PM_KEEP_POWER; @@ -2238,9 +2242,6 @@ remove: if (chip->fixes && chip->fixes->remove_slot) chip->fixes->remove_slot(slot, 0); -cleanup: - sdhci_free_host(host); - return ERR_PTR(ret); } @@ -2261,8 +2262,6 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) if (slot->chip->fixes && slot->chip->fixes->remove_slot) slot->chip->fixes->remove_slot(slot, dead); - - sdhci_free_host(slot->host); } int sdhci_pci_uhs2_add_host(struct sdhci_pci_slot *slot) diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c index 4c2ae71770f7..6e4084407662 100644 --- a/drivers/mmc/host/sdhci-pci-gli.c +++ b/drivers/mmc/host/sdhci-pci-gli.c @@ -68,6 +68,9 @@ #define GLI_9750_MISC_TX1_DLY_VALUE 0x5 #define SDHCI_GLI_9750_MISC_SSC_OFF BIT(26) +#define SDHCI_GLI_9750_GM_BURST_SIZE 0x510 +#define SDHCI_GLI_9750_GM_BURST_SIZE_R_OSRC_LMT GENMASK(17, 16) + #define SDHCI_GLI_9750_TUNING_CONTROL 0x540 #define SDHCI_GLI_9750_TUNING_CONTROL_EN BIT(4) #define GLI_9750_TUNING_CONTROL_EN_ON 0x1 @@ -283,10 +286,26 @@ #define PCIE_GLI_9767_UHS2_CTL2_ZC_VALUE 0xb #define PCIE_GLI_9767_UHS2_CTL2_ZC_CTL BIT(6) #define PCIE_GLI_9767_UHS2_CTL2_ZC_CTL_VALUE 0x1 +#define PCIE_GLI_9767_UHS2_CTL2_FORCE_PHY_RESETN BIT(13) +#define PCIE_GLI_9767_UHS2_CTL2_FORCE_RESETN_VALUE BIT(14) #define GLI_MAX_TUNING_LOOP 40 /* Genesys Logic chipset */ +static void sdhci_gli_mask_replay_timer_timeout(struct pci_dev *pdev) +{ + int aer; + u32 value; + + /* mask the replay timer timeout of AER */ + aer = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); + if (aer) { + pci_read_config_dword(pdev, aer + PCI_ERR_COR_MASK, &value); + value |= PCI_ERR_COR_REP_TIMER; + pci_write_config_dword(pdev, aer + PCI_ERR_COR_MASK, value); + } +} + static inline void gl9750_wt_on(struct sdhci_host *host) { u32 wt_value; @@ -329,10 +348,16 @@ static void gli_set_9750(struct sdhci_host *host) u32 misc_value; u32 parameter_value; u32 control_value; + u32 burst_value; u16 ctrl2; gl9750_wt_on(host); + /* clear R_OSRC_Lmt to avoid DMA write corruption */ + burst_value = sdhci_readl(host, SDHCI_GLI_9750_GM_BURST_SIZE); + burst_value &= ~SDHCI_GLI_9750_GM_BURST_SIZE_R_OSRC_LMT; + sdhci_writel(host, burst_value, SDHCI_GLI_9750_GM_BURST_SIZE); + driving_value = sdhci_readl(host, SDHCI_GLI_9750_DRIVING); pll_value = sdhci_readl(host, SDHCI_GLI_9750_PLL); sw_ctrl_value = sdhci_readl(host, SDHCI_GLI_9750_SW_CTRL); @@ -607,7 +632,6 @@ static void gl9750_hw_setting(struct sdhci_host *host) { struct sdhci_pci_slot *slot = sdhci_priv(host); struct pci_dev *pdev; - int aer; u32 value; pdev = slot->chip->pdev; @@ -626,12 +650,7 @@ static void gl9750_hw_setting(struct sdhci_host *host) pci_set_power_state(pdev, PCI_D0); /* mask the replay timer timeout of AER */ - aer = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); - if (aer) { - pci_read_config_dword(pdev, aer + PCI_ERR_COR_MASK, &value); - value |= PCI_ERR_COR_REP_TIMER; - pci_write_config_dword(pdev, aer + PCI_ERR_COR_MASK, value); - } + sdhci_gli_mask_replay_timer_timeout(pdev); gl9750_wt_off(host); } @@ -806,7 +825,6 @@ static void sdhci_gl9755_set_clock(struct sdhci_host *host, unsigned int clock) static void gl9755_hw_setting(struct sdhci_pci_slot *slot) { struct pci_dev *pdev = slot->chip->pdev; - int aer; u32 value; gl9755_wt_on(pdev); @@ -841,12 +859,7 @@ static void gl9755_hw_setting(struct sdhci_pci_slot *slot) pci_set_power_state(pdev, PCI_D0); /* mask the replay timer timeout of AER */ - aer = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); - if (aer) { - pci_read_config_dword(pdev, aer + PCI_ERR_COR_MASK, &value); - value |= PCI_ERR_COR_REP_TIMER; - pci_write_config_dword(pdev, aer + PCI_ERR_COR_MASK, value); - } + sdhci_gli_mask_replay_timer_timeout(pdev); gl9755_wt_off(pdev); } @@ -1177,6 +1190,65 @@ static void gl9767_set_low_power_negotiation(struct pci_dev *pdev, bool enable) gl9767_vhs_read(pdev); } +static void sdhci_gl9767_uhs2_phy_reset(struct sdhci_host *host, bool assert) +{ + struct sdhci_pci_slot *slot = sdhci_priv(host); + struct pci_dev *pdev = slot->chip->pdev; + u32 value, set, clr; + + if (assert) { + /* Assert reset, set RESETN and clean RESETN_VALUE */ + set = PCIE_GLI_9767_UHS2_CTL2_FORCE_PHY_RESETN; + clr = PCIE_GLI_9767_UHS2_CTL2_FORCE_RESETN_VALUE; + } else { + /* De-assert reset, clean RESETN and set RESETN_VALUE */ + set = PCIE_GLI_9767_UHS2_CTL2_FORCE_RESETN_VALUE; + clr = PCIE_GLI_9767_UHS2_CTL2_FORCE_PHY_RESETN; + } + + gl9767_vhs_write(pdev); + pci_read_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL2, &value); + value |= set; + pci_write_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL2, value); + value &= ~clr; + pci_write_config_dword(pdev, PCIE_GLI_9767_UHS2_CTL2, value); + gl9767_vhs_read(pdev); +} + +static void __gl9767_uhs2_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd) +{ + u8 pwr = 0; + + if (mode != MMC_POWER_OFF) { + pwr = sdhci_get_vdd_value(vdd); + if (!pwr) + WARN(1, "%s: Invalid vdd %#x\n", + mmc_hostname(host->mmc), vdd); + pwr |= SDHCI_VDD2_POWER_180; + } + + if (host->pwr == pwr) + return; + + host->pwr = pwr; + + if (pwr == 0) { + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + } else { + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); + + pwr |= SDHCI_POWER_ON; + sdhci_writeb(host, pwr & 0xf, SDHCI_POWER_CONTROL); + usleep_range(5000, 6250); + + /* Assert reset */ + sdhci_gl9767_uhs2_phy_reset(host, true); + pwr |= SDHCI_VDD2_POWER_ON; + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); + usleep_range(5000, 6250); + } +} + static void sdhci_gl9767_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pci_slot *slot = sdhci_priv(host); @@ -1203,6 +1275,11 @@ static void sdhci_gl9767_set_clock(struct sdhci_host *host, unsigned int clock) } sdhci_enable_clk(host, clk); + + if (mmc_card_uhs2(host->mmc)) + /* De-assert reset */ + sdhci_gl9767_uhs2_phy_reset(host, false); + gl9767_set_low_power_negotiation(pdev, true); } @@ -1474,7 +1551,7 @@ static void sdhci_gl9767_set_power(struct sdhci_host *host, unsigned char mode, gl9767_vhs_read(pdev); sdhci_gli_overcurrent_event_enable(host, false); - sdhci_uhs2_set_power(host, mode, vdd); + __gl9767_uhs2_set_power(host, mode, vdd); sdhci_gli_overcurrent_event_enable(host, true); } else { gl9767_vhs_write(pdev); @@ -1751,7 +1828,7 @@ cleanup: return ret; } -static void gli_set_gl9763e(struct sdhci_pci_slot *slot) +static void gl9763e_hw_setting(struct sdhci_pci_slot *slot) { struct pci_dev *pdev = slot->chip->pdev; u32 value; @@ -1780,6 +1857,9 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot) value |= FIELD_PREP(GLI_9763E_HS400_RXDLY, GLI_9763E_HS400_RXDLY_5); pci_write_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, value); + /* mask the replay timer timeout of AER */ + sdhci_gli_mask_replay_timer_timeout(pdev); + pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); value &= ~GLI_9763E_VHS_REV; value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R); @@ -1923,7 +2003,7 @@ static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot) gli_pcie_enable_msi(slot); host->mmc_host_ops.hs400_enhanced_strobe = gl9763e_hs400_enhanced_strobe; - gli_set_gl9763e(slot); + gl9763e_hw_setting(slot); sdhci_enable_v4_mode(host); return 0; diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c index d6a299f49900..6899cf6c65e7 100644 --- a/drivers/mmc/host/sdhci-pic32.c +++ b/drivers/mmc/host/sdhci-pic32.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Support of SDHCI platform devices for Microchip PIC32. * @@ -5,10 +6,6 @@ * Andrei Pistirica, Paul Thacker * * Inspired by sdhci-pltfm.c - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> @@ -18,6 +15,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/of.h> +#include <linux/platform_data/sdhci-pic32.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/slab.h> @@ -25,7 +23,6 @@ #include <linux/io.h> #include "sdhci.h" #include "sdhci-pltfm.h" -#include <linux/platform_data/sdhci-pic32.h> #define SDH_SHARED_BUS_CTRL 0x000000E0 #define SDH_SHARED_BUS_NR_CLK_PINS_MASK 0x7 @@ -157,20 +154,20 @@ static int pic32_sdhci_probe(struct platform_device *pdev) ret = plat_data->setup_dma(ADMA_FIFO_RD_THSHLD, ADMA_FIFO_WR_THSHLD); if (ret) - goto err_host; + goto err; } sdhci_pdata->sys_clk = devm_clk_get(&pdev->dev, "sys_clk"); if (IS_ERR(sdhci_pdata->sys_clk)) { ret = PTR_ERR(sdhci_pdata->sys_clk); dev_err(&pdev->dev, "Error getting clock\n"); - goto err_host; + goto err; } ret = clk_prepare_enable(sdhci_pdata->sys_clk); if (ret) { dev_err(&pdev->dev, "Error enabling clock\n"); - goto err_host; + goto err; } sdhci_pdata->base_clk = devm_clk_get(&pdev->dev, "base_clk"); @@ -203,8 +200,6 @@ err_base_clk: clk_disable_unprepare(sdhci_pdata->base_clk); err_sys_clk: clk_disable_unprepare(sdhci_pdata->sys_clk); -err_host: - sdhci_pltfm_free(pdev); err: dev_err(&pdev->dev, "pic32-sdhci probe failed: %d\n", ret); return ret; @@ -220,7 +215,6 @@ static void pic32_sdhci_remove(struct platform_device *pdev) sdhci_remove_host(host, scratch == (u32)~0); clk_disable_unprepare(sdhci_pdata->base_clk); clk_disable_unprepare(sdhci_pdata->sys_clk); - sdhci_pltfm_free(pdev); } static const struct of_device_id pic32_sdhci_id_table[] = { diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 62753d72198a..933fafe0a0ef 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -95,13 +95,6 @@ void sdhci_get_property(struct platform_device *pdev) sdhci_get_compatibility(pdev); device_property_read_u32(dev, "clock-frequency", &pltfm_host->clock); - - if (device_property_present(dev, "keep-power-in-suspend")) - host->mmc->pm_caps |= MMC_PM_KEEP_POWER; - - if (device_property_read_bool(dev, "wakeup-source") || - device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */ - host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; } EXPORT_SYMBOL_GPL(sdhci_get_property); @@ -146,20 +139,11 @@ struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(sdhci_pltfm_init); -void sdhci_pltfm_free(struct platform_device *pdev) -{ - struct sdhci_host *host = platform_get_drvdata(pdev); - - sdhci_free_host(host); -} -EXPORT_SYMBOL_GPL(sdhci_pltfm_free); - int sdhci_pltfm_init_and_add_host(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata, size_t priv_size) { struct sdhci_host *host; - int ret = 0; host = sdhci_pltfm_init(pdev, pdata, priv_size); if (IS_ERR(host)) @@ -167,11 +151,7 @@ int sdhci_pltfm_init_and_add_host(struct platform_device *pdev, sdhci_get_property(pdev); - ret = sdhci_add_host(host); - if (ret) - sdhci_pltfm_free(pdev); - - return ret; + return sdhci_add_host(host); } EXPORT_SYMBOL_GPL(sdhci_pltfm_init_and_add_host); @@ -181,7 +161,6 @@ void sdhci_pltfm_remove(struct platform_device *pdev) int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); - sdhci_pltfm_free(pdev); } EXPORT_SYMBOL_GPL(sdhci_pltfm_remove); @@ -229,19 +208,6 @@ const struct dev_pm_ops sdhci_pltfm_pmops = { }; EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops); -static int __init sdhci_pltfm_drv_init(void) -{ - pr_info("sdhci-pltfm: SDHCI platform and OF driver helper\n"); - - return 0; -} -module_init(sdhci_pltfm_drv_init); - -static void __exit sdhci_pltfm_drv_exit(void) -{ -} -module_exit(sdhci_pltfm_drv_exit); - MODULE_DESCRIPTION("SDHCI platform and OF driver helper"); MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h index b81d5b0fd616..9c32e8a289d6 100644 --- a/drivers/mmc/host/sdhci-pltfm.h +++ b/drivers/mmc/host/sdhci-pltfm.h @@ -97,7 +97,6 @@ static inline void sdhci_get_of_property(struct platform_device *pdev) extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata, size_t priv_size); -extern void sdhci_pltfm_free(struct platform_device *pdev); extern int sdhci_pltfm_init_and_add_host(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata, diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 45b6f0891c47..76346353dc55 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -258,7 +258,6 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) struct sdhci_host *host = NULL; const struct sdhci_pxa_variant *variant; - int ret; struct clk *clk, *clk_core; host = sdhci_pltfm_init(pdev, NULL, sizeof(*pxav2_host)); @@ -271,19 +270,14 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) clk = devm_clk_get_optional_enabled(dev, "io"); if (!clk) clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err_probe(dev, ret, "failed to get io clock\n"); - goto free; - } + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to get io clock\n"); pltfm_host->clk = clk; clk_core = devm_clk_get_optional_enabled(dev, "core"); - if (IS_ERR(clk_core)) { - ret = PTR_ERR(clk_core); - dev_err_probe(dev, ret, "failed to enable core clock\n"); - goto free; - } + if (IS_ERR(clk_core)) + return dev_err_probe(dev, PTR_ERR(clk_core), + "failed to enable core clock\n"); host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL @@ -332,15 +326,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) pxav2_host->pinctrl = NULL; } - ret = sdhci_add_host(host); - if (ret) - goto free; - - return 0; - -free: - sdhci_pltfm_free(pdev); - return ret; + return sdhci_add_host(host); } static struct platform_driver sdhci_pxav2_driver = { diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 990723a008ae..d082c4e21aa9 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -20,9 +20,11 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/pinctrl/consumer.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/mbus.h> +#include <linux/units.h> #include "sdhci.h" #include "sdhci-pltfm.h" @@ -51,6 +53,9 @@ struct sdhci_pxa { struct clk *clk_io; u8 power_mode; void __iomem *sdio3_conf_reg; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_uhs; }; /* @@ -313,8 +318,20 @@ static void pxav3_set_power(struct sdhci_host *host, unsigned char mode, mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); } +static void pxav3_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_pltfm_host *phost = sdhci_priv(host); + struct sdhci_pxa *pxa = sdhci_pltfm_priv(phost); + struct pinctrl_state *pins = clock < 100 * HZ_PER_MHZ ? pxa->pins_default : pxa->pins_uhs; + + if (pins) + pinctrl_select_state(pxa->pinctrl, pins); + + sdhci_set_clock(host, clock); +} + static const struct sdhci_ops pxav3_sdhci_ops = { - .set_clock = sdhci_set_clock, + .set_clock = pxav3_set_clock, .set_power = pxav3_set_power, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, .get_max_clock = sdhci_pltfm_clk_get_max_clock, @@ -366,6 +383,19 @@ static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) } #endif +static struct pinctrl_state *pxav3_lookup_pinstate(struct device *dev, struct pinctrl *pinctrl, + const char *name) +{ + struct pinctrl_state *pins = pinctrl_lookup_state(pinctrl, name); + + if (IS_ERR(pins)) { + dev_dbg(dev, "could not get pinstate '%s': %ld\n", name, PTR_ERR(pins)); + return NULL; + } + + return pins; +} + static int sdhci_pxav3_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; @@ -389,8 +419,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) pxa->clk_io = devm_clk_get(dev, NULL); if (IS_ERR(pxa->clk_io)) { dev_err(dev, "failed to get io clock\n"); - ret = PTR_ERR(pxa->clk_io); - goto err_clk_get; + return PTR_ERR(pxa->clk_io); } pltfm_host->clk = pxa->clk_io; clk_prepare_enable(pxa->clk_io); @@ -399,6 +428,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); + host->mmc->caps |= MMC_CAP_NEED_RSP_BUSY; /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; @@ -440,6 +470,15 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) host->mmc->pm_caps |= pdata->pm_caps; } + pxa->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(pxa->pinctrl)) { + pxa->pins_default = pxav3_lookup_pinstate(dev, pxa->pinctrl, "default"); + if (pxa->pins_default) + pxa->pins_uhs = pxav3_lookup_pinstate(dev, pxa->pinctrl, "state_uhs"); + } else { + dev_dbg(dev, "could not get pinctrl handle: %ld\n", PTR_ERR(pxa->pinctrl)); + } + pm_runtime_get_noresume(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, PXAV3_RPM_DELAY_MS); @@ -465,8 +504,6 @@ err_of_parse: err_mbus_win: clk_disable_unprepare(pxa->clk_io); clk_disable_unprepare(pxa->clk_core); -err_clk_get: - sdhci_pltfm_free(pdev); return ret; } @@ -484,11 +521,8 @@ static void sdhci_pxav3_remove(struct platform_device *pdev) clk_disable_unprepare(pxa->clk_io); clk_disable_unprepare(pxa->clk_core); - - sdhci_pltfm_free(pdev); } -#ifdef CONFIG_PM_SLEEP static int sdhci_pxav3_suspend(struct device *dev) { int ret; @@ -498,7 +532,6 @@ static int sdhci_pxav3_suspend(struct device *dev) if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); ret = sdhci_suspend_host(host); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; @@ -511,24 +544,18 @@ static int sdhci_pxav3_resume(struct device *dev) pm_runtime_get_sync(dev); ret = sdhci_resume_host(host); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return ret; } -#endif -#ifdef CONFIG_PM static int sdhci_pxav3_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host); - int ret; - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -550,14 +577,13 @@ static int sdhci_pxav3_runtime_resume(struct device *dev) if (!IS_ERR(pxa->clk_core)) clk_prepare_enable(pxa->clk_core); - return sdhci_runtime_resume_host(host, 0); + sdhci_runtime_resume_host(host, 0); + return 0; } -#endif static const struct dev_pm_ops sdhci_pxav3_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume) - SET_RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend, - sdhci_pxav3_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(sdhci_pxav3_suspend, sdhci_pxav3_resume) + RUNTIME_PM_OPS(sdhci_pxav3_runtime_suspend, sdhci_pxav3_runtime_resume, NULL) }; static struct platform_driver sdhci_pxav3_driver = { @@ -565,7 +591,7 @@ static struct platform_driver sdhci_pxav3_driver = { .name = "sdhci-pxav3", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = of_match_ptr(sdhci_pxav3_of_match), - .pm = &sdhci_pxav3_pmops, + .pm = pm_ptr(&sdhci_pxav3_pmops), }, .probe = sdhci_pxav3_probe, .remove = sdhci_pxav3_remove, diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index bdf4dc0d6b77..6bf66aaa86a6 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -507,15 +507,13 @@ static int sdhci_s3c_probe(struct platform_device *pdev) sc = sdhci_priv(host); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { - ret = -ENOMEM; - goto err_pdata_io_clk; - } + if (!pdata) + return -ENOMEM; if (pdev->dev.of_node) { ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata); if (ret) - goto err_pdata_io_clk; + return ret; } else { memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); } @@ -532,8 +530,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev) sc->clk_io = devm_clk_get(dev, "hsmmc"); if (IS_ERR(sc->clk_io)) { dev_err(dev, "failed to get io clock\n"); - ret = PTR_ERR(sc->clk_io); - goto err_pdata_io_clk; + return PTR_ERR(sc->clk_io); } /* enable the local io clock and keep it running for the moment. */ @@ -661,9 +658,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev) err_no_busclks: clk_disable_unprepare(sc->clk_io); - err_pdata_io_clk: - sdhci_free_host(host); - return ret; } @@ -685,11 +679,8 @@ static void sdhci_s3c_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); clk_disable_unprepare(sc->clk_io); - - sdhci_free_host(host); } -#ifdef CONFIG_PM_SLEEP static int sdhci_s3c_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -706,17 +697,14 @@ static int sdhci_s3c_resume(struct device *dev) return sdhci_resume_host(host); } -#endif -#ifdef CONFIG_PM static int sdhci_s3c_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_s3c *ourhost = to_s3c(host); struct clk *busclk = ourhost->clk_io; - int ret; - ret = sdhci_runtime_suspend_host(host); + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -724,7 +712,7 @@ static int sdhci_s3c_runtime_suspend(struct device *dev) if (ourhost->cur_clk >= 0) clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]); clk_disable_unprepare(busclk); - return ret; + return 0; } static int sdhci_s3c_runtime_resume(struct device *dev) @@ -732,20 +720,17 @@ static int sdhci_s3c_runtime_resume(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_s3c *ourhost = to_s3c(host); struct clk *busclk = ourhost->clk_io; - int ret; clk_prepare_enable(busclk); if (ourhost->cur_clk >= 0) clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]); - ret = sdhci_runtime_resume_host(host, 0); - return ret; + sdhci_runtime_resume_host(host, 0); + return 0; } -#endif static const struct dev_pm_ops sdhci_s3c_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume) - SET_RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(sdhci_s3c_suspend, sdhci_s3c_resume) + RUNTIME_PM_OPS(sdhci_s3c_runtime_suspend, sdhci_s3c_runtime_resume, NULL) }; static const struct platform_device_id sdhci_s3c_driver_ids[] = { @@ -780,7 +765,7 @@ static struct platform_driver sdhci_s3c_driver = { .name = "s3c-sdhci", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = of_match_ptr(sdhci_s3c_dt_match), - .pm = &sdhci_s3c_pmops, + .pm = pm_ptr(&sdhci_s3c_pmops), }, }; diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 770dc12b9ae9..72d21dc0cb69 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -59,7 +59,7 @@ static int sdhci_probe(struct platform_device *pdev) if (IS_ERR(host->ioaddr)) { ret = PTR_ERR(host->ioaddr); dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret); - goto err_host; + goto err; } host->hw_name = "sdhci"; @@ -67,7 +67,7 @@ static int sdhci_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { ret = host->irq; - goto err_host; + goto err; } host->quirks = SDHCI_QUIRK_BROKEN_ADMA; @@ -78,13 +78,13 @@ static int sdhci_probe(struct platform_device *pdev) if (IS_ERR(sdhci->clk)) { ret = PTR_ERR(sdhci->clk); dev_dbg(&pdev->dev, "Error getting clock\n"); - goto err_host; + goto err; } ret = clk_prepare_enable(sdhci->clk); if (ret) { dev_dbg(&pdev->dev, "Error enabling clock\n"); - goto err_host; + goto err; } ret = clk_set_rate(sdhci->clk, 50000000); @@ -110,8 +110,6 @@ static int sdhci_probe(struct platform_device *pdev) disable_clk: clk_disable_unprepare(sdhci->clk); -err_host: - sdhci_free_host(host); err: dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); return ret; @@ -130,10 +128,8 @@ static void sdhci_remove(struct platform_device *pdev) sdhci_remove_host(host, dead); clk_disable_unprepare(sdhci->clk); - sdhci_free_host(host); } -#ifdef CONFIG_PM_SLEEP static int sdhci_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -164,9 +160,8 @@ static int sdhci_resume(struct device *dev) return sdhci_resume_host(host); } -#endif -static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume); static const struct of_device_id sdhci_spear_id_table[] = { { .compatible = "st,spear300-sdhci" }, @@ -178,7 +173,7 @@ static struct platform_driver sdhci_driver = { .driver = { .name = "sdhci", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &sdhci_pm_ops, + .pm = pm_sleep_ptr(&sdhci_pm_ops), .of_match_table = sdhci_spear_id_table, }, .probe = sdhci_probe, diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index db5e253b0f79..3584a2b314a9 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -764,7 +764,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto pltfm_free; + return ret; if (!mmc_card_is_removable(host->mmc)) host->mmc_host_ops.request_atomic = sdhci_sprd_request_atomic; @@ -778,34 +778,26 @@ static int sdhci_sprd_probe(struct platform_device *pdev) if (!IS_ERR(sprd_host->pinctrl)) { sprd_host->pins_uhs = pinctrl_lookup_state(sprd_host->pinctrl, "state_uhs"); - if (IS_ERR(sprd_host->pins_uhs)) { - ret = PTR_ERR(sprd_host->pins_uhs); - goto pltfm_free; - } + if (IS_ERR(sprd_host->pins_uhs)) + return PTR_ERR(sprd_host->pins_uhs); sprd_host->pins_default = pinctrl_lookup_state(sprd_host->pinctrl, "default"); - if (IS_ERR(sprd_host->pins_default)) { - ret = PTR_ERR(sprd_host->pins_default); - goto pltfm_free; - } + if (IS_ERR(sprd_host->pins_default)) + return PTR_ERR(sprd_host->pins_default); } clk = devm_clk_get(&pdev->dev, "sdio"); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - goto pltfm_free; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); sprd_host->clk_sdio = clk; sprd_host->base_rate = clk_get_rate(sprd_host->clk_sdio); if (!sprd_host->base_rate) sprd_host->base_rate = SDHCI_SPRD_CLK_DEF_RATE; clk = devm_clk_get(&pdev->dev, "enable"); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - goto pltfm_free; - } + if (IS_ERR(clk)) + return PTR_ERR(clk); sprd_host->clk_enable = clk; clk = devm_clk_get(&pdev->dev, "2x_enable"); @@ -814,7 +806,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev) ret = clk_prepare_enable(sprd_host->clk_sdio); if (ret) - goto pltfm_free; + return ret; ret = clk_prepare_enable(sprd_host->clk_enable); if (ret) @@ -871,7 +863,6 @@ static int sdhci_sprd_probe(struct platform_device *pdev) if (ret) goto err_cleanup_host; - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; @@ -891,9 +882,6 @@ clk_disable2: clk_disable: clk_disable_unprepare(sprd_host->clk_sdio); - -pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -907,8 +895,6 @@ static void sdhci_sprd_remove(struct platform_device *pdev) clk_disable_unprepare(sprd_host->clk_sdio); clk_disable_unprepare(sprd_host->clk_enable); clk_disable_unprepare(sprd_host->clk_2x_enable); - - sdhci_pltfm_free(pdev); } static const struct of_device_id sdhci_sprd_of_match[] = { @@ -917,7 +903,6 @@ static const struct of_device_id sdhci_sprd_of_match[] = { }; MODULE_DEVICE_TABLE(of, sdhci_sprd_of_match); -#ifdef CONFIG_PM static int sdhci_sprd_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -964,13 +949,10 @@ clk_2x_disable: return ret; } -#endif static const struct dev_pm_ops sdhci_sprd_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend, - sdhci_sprd_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(sdhci_sprd_runtime_suspend, sdhci_sprd_runtime_resume, NULL) }; static struct platform_driver sdhci_sprd_driver = { @@ -980,7 +962,7 @@ static struct platform_driver sdhci_sprd_driver = { .name = "sdhci_sprd_r11", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sdhci_sprd_of_match, - .pm = &sdhci_sprd_pm_ops, + .pm = pm_ptr(&sdhci_sprd_pm_ops), }, }; module_platform_driver(sdhci_sprd_driver); diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index 4973e08a98f8..bf6685805137 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -380,13 +380,13 @@ static int sdhci_st_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) { dev_err(&pdev->dev, "Failed mmc_of_parse\n"); - goto err_of; + goto err_pltfm_init; } ret = clk_prepare_enable(clk); if (ret) { dev_err(&pdev->dev, "Failed to prepare clock\n"); - goto err_of; + goto err_pltfm_init; } ret = clk_prepare_enable(icnclk); @@ -423,8 +423,6 @@ err_out: clk_disable_unprepare(icnclk); err_icnclk: clk_disable_unprepare(clk); -err_of: - sdhci_pltfm_free(pdev); err_pltfm_init: reset_control_assert(rstc); @@ -447,7 +445,6 @@ static void sdhci_st_remove(struct platform_device *pdev) reset_control_assert(rstc); } -#ifdef CONFIG_PM_SLEEP static int sdhci_st_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -494,9 +491,8 @@ static int sdhci_st_resume(struct device *dev) return sdhci_resume_host(host); } -#endif -static SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume); static const struct of_device_id st_sdhci_match[] = { { .compatible = "st,sdhci" }, @@ -511,7 +507,7 @@ static struct platform_driver sdhci_st_driver = { .driver = { .name = "sdhci-st", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &sdhci_st_pmops, + .pm = pm_sleep_ptr(&sdhci_st_pmops), .of_match_table = st_sdhci_match, }, }; diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index b2f5c3f8b839..820ce4dae58b 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -1693,7 +1693,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) rc = mmc_of_parse(host->mmc); if (rc) - goto err_parse_dt; + return rc; if (tegra_host->soc_data->nvquirks & NVQUIRK_ENABLE_DDR50) host->mmc->caps |= MMC_CAP_1_8V_DDR; @@ -1739,7 +1739,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) if (IS_ERR(clk)) { rc = PTR_ERR(clk); if (rc == -EPROBE_DEFER) - goto err_power_req; + return rc; dev_warn(&pdev->dev, "failed to get tmclk: %d\n", rc); clk = NULL; @@ -1750,7 +1750,7 @@ static int sdhci_tegra_probe(struct platform_device *pdev) if (rc) { dev_err(&pdev->dev, "failed to enable tmclk: %d\n", rc); - goto err_power_req; + return rc; } tegra_host->tmclk = clk; @@ -1811,8 +1811,6 @@ err_rst_get: err_clk_get: clk_disable_unprepare(tegra_host->tmclk); err_power_req: -err_parse_dt: - sdhci_pltfm_free(pdev); return rc; } @@ -1831,10 +1829,9 @@ static void sdhci_tegra_remove(struct platform_device *pdev) pm_runtime_force_suspend(&pdev->dev); clk_disable_unprepare(tegra_host->tmclk); - sdhci_pltfm_free(pdev); } -static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev) +static int sdhci_tegra_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1844,7 +1841,7 @@ static int __maybe_unused sdhci_tegra_runtime_suspend(struct device *dev) return 0; } -static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev) +static int sdhci_tegra_runtime_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1852,7 +1849,6 @@ static int __maybe_unused sdhci_tegra_runtime_resume(struct device *dev) return clk_prepare_enable(pltfm_host->clk); } -#ifdef CONFIG_PM_SLEEP static int sdhci_tegra_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -1913,12 +1909,10 @@ disable_clk: pm_runtime_force_suspend(dev); return ret; } -#endif static const struct dev_pm_ops sdhci_tegra_dev_pm_ops = { - SET_RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume) + RUNTIME_PM_OPS(sdhci_tegra_runtime_suspend, sdhci_tegra_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(sdhci_tegra_suspend, sdhci_tegra_resume) }; static struct platform_driver sdhci_tegra_driver = { @@ -1926,7 +1920,7 @@ static struct platform_driver sdhci_tegra_driver = { .name = "sdhci-tegra", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sdhci_tegra_dt_match, - .pm = &sdhci_tegra_dev_pm_ops, + .pm = pm_ptr(&sdhci_tegra_dev_pm_ops), }, .probe = sdhci_tegra_probe, .remove = sdhci_tegra_remove, diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c index c53b64d50c0d..41e49c6cc751 100644 --- a/drivers/mmc/host/sdhci-uhs2.c +++ b/drivers/mmc/host/sdhci-uhs2.c @@ -99,8 +99,8 @@ void sdhci_uhs2_reset(struct sdhci_host *host, u16 mask) /* hw clears the bit when it's done */ if (read_poll_timeout_atomic(sdhci_readw, val, !(val & mask), 10, UHS2_RESET_TIMEOUT_100MS, true, host, SDHCI_UHS2_SW_RESET)) { - pr_warn("%s: %s: Reset 0x%x never completed. %s: clean reset bit.\n", __func__, - mmc_hostname(host->mmc), (int)mask, mmc_hostname(host->mmc)); + pr_debug("%s: %s: Reset 0x%x never completed. %s: clean reset bit.\n", __func__, + mmc_hostname(host->mmc), (int)mask, mmc_hostname(host->mmc)); sdhci_writeb(host, 0, SDHCI_UHS2_SW_RESET); return; } @@ -295,7 +295,8 @@ static void __sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else sdhci_uhs2_set_power(host, ios->power_mode, ios->vdd); - sdhci_set_clock(host, host->clock); + host->ops->set_clock(host, ios->clock); + host->clock = ios->clock; } static int sdhci_uhs2_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -335,8 +336,8 @@ static int sdhci_uhs2_interface_detect(struct sdhci_host *host) if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IF_DETECT), 100, UHS2_INTERFACE_DETECT_TIMEOUT_100MS, true, host, SDHCI_PRESENT_STATE)) { - pr_warn("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc)); - sdhci_dumpregs(host); + pr_debug("%s: not detect UHS2 interface in 100ms.\n", mmc_hostname(host->mmc)); + sdhci_dbg_dumpregs(host, "UHS2 interface detect timeout in 100ms"); return -EIO; } @@ -345,8 +346,8 @@ static int sdhci_uhs2_interface_detect(struct sdhci_host *host) if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_LANE_SYNC), 100, UHS2_LANE_SYNC_TIMEOUT_150MS, true, host, SDHCI_PRESENT_STATE)) { - pr_warn("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc)); - sdhci_dumpregs(host); + pr_debug("%s: UHS2 Lane sync fail in 150ms.\n", mmc_hostname(host->mmc)); + sdhci_dbg_dumpregs(host, "UHS2 Lane sync fail in 150ms"); return -EIO; } @@ -417,12 +418,12 @@ static int sdhci_uhs2_do_detect_init(struct mmc_host *mmc) host->ops->uhs2_pre_detect_init(host); if (sdhci_uhs2_interface_detect(host)) { - pr_warn("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc)); + pr_debug("%s: cannot detect UHS2 interface.\n", mmc_hostname(host->mmc)); return -EIO; } if (sdhci_uhs2_init(host)) { - pr_warn("%s: UHS2 init fail.\n", mmc_hostname(host->mmc)); + pr_debug("%s: UHS2 init fail.\n", mmc_hostname(host->mmc)); return -EIO; } @@ -504,8 +505,8 @@ static int sdhci_uhs2_check_dormant(struct sdhci_host *host) if (read_poll_timeout(sdhci_readl, val, (val & SDHCI_UHS2_IN_DORMANT_STATE), 100, UHS2_CHECK_DORMANT_TIMEOUT_100MS, true, host, SDHCI_PRESENT_STATE)) { - pr_warn("%s: UHS2 IN_DORMANT fail in 100ms.\n", mmc_hostname(host->mmc)); - sdhci_dumpregs(host); + pr_debug("%s: UHS2 IN_DORMANT fail in 100ms.\n", mmc_hostname(host->mmc)); + sdhci_dbg_dumpregs(host, "UHS2 IN_DORMANT fail in 100ms"); return -EIO; } return 0; @@ -1125,7 +1126,7 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id) /*****************************************************************************\ * * - * Driver init/exit * + * Driver init * * * \*****************************************************************************/ @@ -1137,17 +1138,6 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host) return 0; } -static int __init sdhci_uhs2_mod_init(void) -{ - return 0; -} -module_init(sdhci_uhs2_mod_init); - -static void __exit sdhci_uhs2_mod_exit(void) -{ -} -module_exit(sdhci_uhs2_mod_exit); - /*****************************************************************************\ * * Device allocation/registration * diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c index 098f0ea45cbe..046e8100dd08 100644 --- a/drivers/mmc/host/sdhci-xenon.c +++ b/drivers/mmc/host/sdhci-xenon.c @@ -532,14 +532,13 @@ static int xenon_probe(struct platform_device *pdev) if (dev->of_node) { pltfm_host->clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(pltfm_host->clk)) { - err = PTR_ERR(pltfm_host->clk); - dev_err(&pdev->dev, "Failed to setup input clk: %d\n", err); - goto free_pltfm; - } + if (IS_ERR(pltfm_host->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pltfm_host->clk), + "Failed to setup input clk.\n"); + err = clk_prepare_enable(pltfm_host->clk); if (err) - goto free_pltfm; + return err; priv->axi_clk = devm_clk_get(&pdev->dev, "axi"); if (IS_ERR(priv->axi_clk)) { @@ -603,8 +602,6 @@ err_clk_axi: clk_disable_unprepare(priv->axi_clk); err_clk: clk_disable_unprepare(pltfm_host->clk); -free_pltfm: - sdhci_pltfm_free(pdev); return err; } @@ -623,11 +620,8 @@ static void xenon_remove(struct platform_device *pdev) xenon_sdhc_unprepare(host); clk_disable_unprepare(priv->axi_clk); clk_disable_unprepare(pltfm_host->clk); - - sdhci_pltfm_free(pdev); } -#ifdef CONFIG_PM_SLEEP static int xenon_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -640,19 +634,14 @@ static int xenon_suspend(struct device *dev) priv->restore_needed = true; return ret; } -#endif -#ifdef CONFIG_PM static int xenon_runtime_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); - int ret; - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); if (host->tuning_mode != SDHCI_TUNING_MODE_3) mmc_retune_needed(host->mmc); @@ -687,22 +676,16 @@ static int xenon_runtime_resume(struct device *dev) priv->restore_needed = false; } - ret = sdhci_runtime_resume_host(host, 0); - if (ret) - goto out; + sdhci_runtime_resume_host(host, 0); return 0; out: clk_disable_unprepare(pltfm_host->clk); return ret; } -#endif /* CONFIG_PM */ static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(xenon_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(xenon_runtime_suspend, - xenon_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(xenon_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(xenon_runtime_suspend, xenon_runtime_resume, NULL) }; static const struct of_device_id sdhci_xenon_dt_ids[] = { @@ -731,7 +714,7 @@ static struct platform_driver sdhci_xenon_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sdhci_xenon_dt_ids, .acpi_match_table = ACPI_PTR(sdhci_xenon_acpi_ids), - .pm = &sdhci_xenon_dev_pm_ops, + .pm = pm_ptr(&sdhci_xenon_dev_pm_ops), }, .probe = xenon_probe, .remove = xenon_remove, diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f4a7733a8ad2..605be55f8d2d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -158,7 +158,7 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) u32 present; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || - !mmc_card_is_removable(host->mmc) || mmc_can_gpio_cd(host->mmc)) + !mmc_card_is_removable(host->mmc) || mmc_host_can_gpio_cd(host->mmc)) return; if (enable) { @@ -517,9 +517,9 @@ EXPORT_SYMBOL_GPL(sdhci_mod_timer); static void sdhci_del_timer(struct sdhci_host *host, struct mmc_request *mrq) { if (sdhci_data_line_cmd(mrq->cmd)) - del_timer(&host->data_timer); + timer_delete(&host->data_timer); else - del_timer(&host->timer); + timer_delete(&host->timer); } static inline bool sdhci_has_requests(struct sdhci_host *host) @@ -2367,23 +2367,6 @@ void sdhci_set_ios_common(struct mmc_host *mmc, struct mmc_ios *ios) (ios->power_mode == MMC_POWER_UP) && !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) sdhci_enable_preset_value(host, false); - - if (!ios->clock || ios->clock != host->clock) { - host->ops->set_clock(host, ios->clock); - host->clock = ios->clock; - - if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK && - host->clock) { - host->timeout_clk = mmc->actual_clock ? - mmc->actual_clock / 1000 : - host->clock / 1000; - mmc->max_busy_timeout = - host->ops->get_max_timeout_count ? - host->ops->get_max_timeout_count(host) : - 1 << 27; - mmc->max_busy_timeout /= host->timeout_clk; - } - } } EXPORT_SYMBOL_GPL(sdhci_set_ios_common); @@ -2410,6 +2393,23 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_set_ios_common(mmc, ios); + if (!ios->clock || ios->clock != host->clock) { + host->ops->set_clock(host, ios->clock); + host->clock = ios->clock; + + if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK && + host->clock) { + host->timeout_clk = mmc->actual_clock ? + mmc->actual_clock / 1000 : + host->clock / 1000; + mmc->max_busy_timeout = + host->ops->get_max_timeout_count ? + host->ops->get_max_timeout_count(host) : + 1 << 27; + mmc->max_busy_timeout /= host->timeout_clk; + } + } + if (host->ops->set_power) host->ops->set_power(host, ios->power_mode, ios->vdd); else @@ -2566,7 +2566,7 @@ int sdhci_get_ro(struct mmc_host *mmc) is_readonly = 0; } else if (host->ops->get_ro) { is_readonly = host->ops->get_ro(host); - } else if (mmc_can_gpio_ro(mmc)) { + } else if (mmc_host_can_gpio_ro(mmc)) { is_readonly = mmc_gpio_get_ro(mmc); /* Do not invert twice */ allow_invert = !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); @@ -3240,7 +3240,7 @@ static void sdhci_timeout_timer(struct timer_list *t) struct sdhci_host *host; unsigned long flags; - host = from_timer(host, t, timer); + host = timer_container_of(host, t, timer); spin_lock_irqsave(&host->lock, flags); @@ -3262,7 +3262,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t) struct sdhci_host *host; unsigned long flags; - host = from_timer(host, t, data_timer); + host = timer_container_of(host, t, data_timer); spin_lock_irqsave(&host->lock, flags); @@ -3739,7 +3739,7 @@ static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host) { return mmc_card_is_removable(host->mmc) && !(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && - !mmc_can_gpio_cd(host->mmc); + !mmc_host_can_gpio_cd(host->mmc); } /* @@ -3750,7 +3750,7 @@ static bool sdhci_cd_irq_can_wakeup(struct sdhci_host *host) * sdhci_disable_irq_wakeups() since it will be set by * sdhci_enable_card_detection() or sdhci_init(). */ -static bool sdhci_enable_irq_wakeups(struct sdhci_host *host) +bool sdhci_enable_irq_wakeups(struct sdhci_host *host) { u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE | SDHCI_WAKE_ON_INT; @@ -3782,8 +3782,9 @@ static bool sdhci_enable_irq_wakeups(struct sdhci_host *host) return host->irq_wake_enabled; } +EXPORT_SYMBOL_GPL(sdhci_enable_irq_wakeups); -static void sdhci_disable_irq_wakeups(struct sdhci_host *host) +void sdhci_disable_irq_wakeups(struct sdhci_host *host) { u8 val; u8 mask = SDHCI_WAKE_ON_INSERT | SDHCI_WAKE_ON_REMOVE @@ -3797,6 +3798,7 @@ static void sdhci_disable_irq_wakeups(struct sdhci_host *host) host->irq_wake_enabled = false; } +EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); int sdhci_suspend_host(struct sdhci_host *host) { @@ -3856,7 +3858,7 @@ int sdhci_resume_host(struct sdhci_host *host) EXPORT_SYMBOL_GPL(sdhci_resume_host); -int sdhci_runtime_suspend_host(struct sdhci_host *host) +void sdhci_runtime_suspend_host(struct sdhci_host *host) { unsigned long flags; @@ -3873,12 +3875,10 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) spin_lock_irqsave(&host->lock, flags); host->runtime_suspended = true; spin_unlock_irqrestore(&host->lock, flags); - - return 0; } EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host); -int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) +void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) { struct mmc_host *mmc = host->mmc; unsigned long flags; @@ -3924,8 +3924,6 @@ int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) sdhci_enable_card_detection(host); spin_unlock_irqrestore(&host->lock, flags); - - return 0; } EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host); @@ -4069,7 +4067,7 @@ struct sdhci_host *sdhci_alloc_host(struct device *dev, WARN_ON(dev == NULL); - mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev); + mmc = devm_mmc_alloc_host(dev, sizeof(struct sdhci_host) + priv_size); if (!mmc) return ERR_PTR(-ENOMEM); @@ -4195,6 +4193,12 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) unsigned int bounce_size; int ret; + /* Drivers may have already allocated the buffer */ + if (host->bounce_buffer) { + bounce_size = host->bounce_buffer_size; + max_blocks = bounce_size / 512; + goto out; + } /* * Cap the bounce buffer at 64KB. Using a bigger bounce buffer * has diminishing returns, this is probably because SD/MMC @@ -4243,6 +4247,7 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) host->bounce_buffer_size = bounce_size; +out: /* Lie about this since we're bouncing */ mmc->max_segs = max_blocks; mmc->max_seg_size = bounce_size; @@ -4534,8 +4539,15 @@ int sdhci_setup_host(struct sdhci_host *host) * their platform code before calling sdhci_add_host(), and we * won't assume 8-bit width for hosts without that CAP. */ - if (!(host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA)) + if (host->quirks & SDHCI_QUIRK_FORCE_1_BIT_DATA) { + host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + if (host->quirks2 & SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400) + host->caps1 &= ~SDHCI_SUPPORT_HS400; + mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); + mmc->caps &= ~(MMC_CAP_DDR | MMC_CAP_UHS); + } else { mmc->caps |= MMC_CAP_4_BIT_DATA; + } if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23) mmc->caps &= ~MMC_CAP_CMD23; @@ -4971,8 +4983,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); free_irq(host->irq, host); - del_timer_sync(&host->timer); - del_timer_sync(&host->data_timer); + timer_delete_sync(&host->timer); + timer_delete_sync(&host->data_timer); destroy_workqueue(host->complete_wq); @@ -4993,35 +5005,12 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) EXPORT_SYMBOL_GPL(sdhci_remove_host); -void sdhci_free_host(struct sdhci_host *host) -{ - mmc_free_host(host->mmc); -} - -EXPORT_SYMBOL_GPL(sdhci_free_host); - /*****************************************************************************\ * * * Driver init/exit * * * \*****************************************************************************/ -static int __init sdhci_drv_init(void) -{ - pr_info(DRIVER_NAME - ": Secure Digital Host Controller Interface driver\n"); - pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); - - return 0; -} - -static void __exit sdhci_drv_exit(void) -{ -} - -module_init(sdhci_drv_init); -module_exit(sdhci_drv_exit); - module_param(debug_quirks, uint, 0444); module_param(debug_quirks2, uint, 0444); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index cd0e35a80542..b6a571d866fa 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -811,7 +811,6 @@ static inline u8 sdhci_readb(struct sdhci_host *host, int reg) #endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */ struct sdhci_host *sdhci_alloc_host(struct device *dev, size_t priv_size); -void sdhci_free_host(struct sdhci_host *host); static inline void *sdhci_priv(struct sdhci_host *host) { @@ -875,10 +874,19 @@ void sdhci_adma_write_desc(struct sdhci_host *host, void **desc, dma_addr_t addr, int len, unsigned int cmd); #ifdef CONFIG_PM +bool sdhci_enable_irq_wakeups(struct sdhci_host *host); +void sdhci_disable_irq_wakeups(struct sdhci_host *host); int sdhci_suspend_host(struct sdhci_host *host); int sdhci_resume_host(struct sdhci_host *host); -int sdhci_runtime_suspend_host(struct sdhci_host *host); -int sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset); +void sdhci_runtime_suspend_host(struct sdhci_host *host); +void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset); +#else +static inline bool sdhci_enable_irq_wakeups(struct sdhci_host *host) { return false; } +static inline void sdhci_disable_irq_wakeups(struct sdhci_host *host) {} +static inline int sdhci_suspend_host(struct sdhci_host *host) { return -EOPNOTSUPP; } +static inline int sdhci_resume_host(struct sdhci_host *host) { return -EOPNOTSUPP; } +static inline void sdhci_runtime_suspend_host(struct sdhci_host *host) {} +static inline void sdhci_runtime_resume_host(struct sdhci_host *host, int soft_reset) {} #endif void sdhci_cqe_enable(struct mmc_host *mmc); @@ -898,4 +906,20 @@ void sdhci_switch_external_dma(struct sdhci_host *host, bool en); void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable); void __sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd); +#if defined(CONFIG_DYNAMIC_DEBUG) || \ + (defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE)) +#define SDHCI_DBG_ANYWAY 0 +#elif defined(DEBUG) +#define SDHCI_DBG_ANYWAY 1 +#else +#define SDHCI_DBG_ANYWAY 0 +#endif + +#define sdhci_dbg_dumpregs(host, fmt) \ +do { \ + DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \ + if (DYNAMIC_DEBUG_BRANCH(descriptor) || SDHCI_DBG_ANYWAY) \ + sdhci_dumpregs(host); \ +} while (0) + #endif /* __SDHCI_HW_H */ diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index f75c31815ab0..d235b0aecfdb 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -95,7 +95,6 @@ static const struct regmap_config sdhci_am654_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .fast_io = true, }; struct timing_data { @@ -155,6 +154,8 @@ struct sdhci_am654_data { u32 tuning_loop; #define SDHCI_AM654_QUIRK_FORCE_CDTEST BIT(0) +#define SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA BIT(1) +#define SDHCI_AM654_QUIRK_DISABLE_HS400 BIT(2) }; struct window { @@ -166,6 +167,7 @@ struct window { struct sdhci_am654_driver_data { const struct sdhci_pltfm_data *pdata; u32 flags; + u32 quirks; #define IOMUX_PRESENT (1 << 0) #define FREQSEL_2_BIT (1 << 1) #define STRBSEL_4_BIT (1 << 2) @@ -356,6 +358,29 @@ static void sdhci_j721e_4bit_set_clock(struct sdhci_host *host, sdhci_set_clock(host, clock); } +static int sdhci_am654_start_signal_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); + int ret; + + if ((sdhci_am654->quirks & SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA) && + ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = mmc_regulator_set_vqmmc(mmc, ios); + if (ret < 0) { + pr_err("%s: Switching to 1.8V signalling voltage failed,\n", + mmc_hostname(mmc)); + return -EIO; + } + } + return 0; + } + + return sdhci_start_signal_voltage_switch(mmc, ios); +} + static u8 sdhci_am654_write_power_on(struct sdhci_host *host, u8 val, int reg) { writeb(val, host->ioaddr + reg); @@ -588,7 +613,8 @@ static const struct sdhci_ops sdhci_am654_ops = { static const struct sdhci_pltfm_data sdhci_am654_pdata = { .ops = &sdhci_am654_ops, .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_DISABLE_HW_TIMEOUT, }; static const struct sdhci_am654_driver_data sdhci_am654_sr1_drvdata = { @@ -618,7 +644,8 @@ static const struct sdhci_ops sdhci_j721e_8bit_ops = { static const struct sdhci_pltfm_data sdhci_j721e_8bit_pdata = { .ops = &sdhci_j721e_8bit_ops, .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_DISABLE_HW_TIMEOUT, }; static const struct sdhci_am654_driver_data sdhci_j721e_8bit_drvdata = { @@ -642,7 +669,8 @@ static const struct sdhci_ops sdhci_j721e_4bit_ops = { static const struct sdhci_pltfm_data sdhci_j721e_4bit_pdata = { .ops = &sdhci_j721e_4bit_ops, .quirks = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_DISABLE_HW_TIMEOUT, }; static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = { @@ -650,6 +678,12 @@ static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = { .flags = IOMUX_PRESENT, }; +static const struct sdhci_am654_driver_data sdhci_am62_4bit_drvdata = { + .pdata = &sdhci_j721e_4bit_pdata, + .flags = IOMUX_PRESENT, + .quirks = SDHCI_AM654_QUIRK_SUPPRESS_V1P8_ENA, +}; + static const struct soc_device_attribute sdhci_am654_devices[] = { { .family = "AM65X", .revision = "SR1.0", @@ -731,6 +765,7 @@ static int sdhci_am654_init(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_am654_data *sdhci_am654 = sdhci_pltfm_priv(pltfm_host); + struct device *dev = mmc_dev(host->mmc); u32 ctl_cfg_2 = 0; u32 mask; u32 val; @@ -786,6 +821,12 @@ static int sdhci_am654_init(struct sdhci_host *host) if (ret) goto err_cleanup_host; + if (sdhci_am654->quirks & SDHCI_AM654_QUIRK_DISABLE_HS400 && + host->mmc->caps2 & (MMC_CAP2_HS400 | MMC_CAP2_HS400_ES)) { + dev_info(dev, "HS400 mode not supported on this silicon revision, disabling it\n"); + host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); + } + ret = __sdhci_add_host(host); if (ret) goto err_cleanup_host; @@ -849,6 +890,12 @@ static int sdhci_am654_get_of_property(struct platform_device *pdev, return 0; } +static const struct soc_device_attribute sdhci_am654_descope_hs400[] = { + { .family = "AM62PX", .revision = "SR1.0" }, + { .family = "AM62PX", .revision = "SR1.1" }, + { /* sentinel */ } +}; + static const struct of_device_id sdhci_am654_of_match[] = { { .compatible = "ti,am654-sdhci-5.1", @@ -872,7 +919,7 @@ static const struct of_device_id sdhci_am654_of_match[] = { }, { .compatible = "ti,am62-sdhci", - .data = &sdhci_j721e_4bit_drvdata, + .data = &sdhci_am62_4bit_drvdata, }, { /* sentinel */ } }; @@ -906,40 +953,41 @@ static int sdhci_am654_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); sdhci_am654 = sdhci_pltfm_priv(pltfm_host); sdhci_am654->flags = drvdata->flags; + sdhci_am654->quirks = drvdata->quirks; clk_xin = devm_clk_get(dev, "clk_xin"); if (IS_ERR(clk_xin)) { dev_err(dev, "clk_xin clock not found.\n"); - ret = PTR_ERR(clk_xin); - goto err_pltfm_free; + return PTR_ERR(clk_xin); } pltfm_host->clk = clk_xin; base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(base)) { - ret = PTR_ERR(base); - goto err_pltfm_free; + return PTR_ERR(base); } sdhci_am654->base = devm_regmap_init_mmio(dev, base, &sdhci_am654_regmap_config); if (IS_ERR(sdhci_am654->base)) { dev_err(dev, "Failed to initialize regmap\n"); - ret = PTR_ERR(sdhci_am654->base); - goto err_pltfm_free; + return PTR_ERR(sdhci_am654->base); } ret = sdhci_am654_get_of_property(pdev, sdhci_am654); if (ret) - goto err_pltfm_free; + return ret; ret = mmc_of_parse(host->mmc); - if (ret) { - dev_err_probe(dev, ret, "parsing dt failed\n"); - goto err_pltfm_free; - } + if (ret) + return dev_err_probe(dev, ret, "parsing dt failed\n"); + + soc = soc_device_match(sdhci_am654_descope_hs400); + if (soc) + sdhci_am654->quirks |= SDHCI_AM654_QUIRK_DISABLE_HS400; + host->mmc_host_ops.start_signal_voltage_switch = sdhci_am654_start_signal_voltage_switch; host->mmc_host_ops.execute_tuning = sdhci_am654_execute_tuning; pm_runtime_get_noresume(dev); @@ -958,7 +1006,6 @@ static int sdhci_am654_probe(struct platform_device *pdev) /* Setting up autosuspend */ pm_runtime_set_autosuspend_delay(dev, SDHCI_AM654_AUTOSUSPEND_DELAY); pm_runtime_use_autosuspend(dev); - pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); return 0; @@ -968,8 +1015,6 @@ pm_disable: pm_runtime_disable(dev); pm_put: pm_runtime_put_noidle(dev); -err_pltfm_free: - sdhci_pltfm_free(pdev); return ret; } @@ -988,10 +1033,8 @@ static void sdhci_am654_remove(struct platform_device *pdev) clk_disable_unprepare(pltfm_host->clk); pm_runtime_disable(dev); pm_runtime_put_noidle(dev); - sdhci_pltfm_free(pdev); } -#ifdef CONFIG_PM static int sdhci_am654_restore(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1049,9 +1092,7 @@ static int sdhci_am654_runtime_suspend(struct device *dev) if (ret) return ret; - ret = sdhci_runtime_suspend_host(host); - if (ret) - return ret; + sdhci_runtime_suspend_host(host); /* disable the clock */ clk_disable_unprepare(pltfm_host->clk); @@ -1073,9 +1114,7 @@ static int sdhci_am654_runtime_resume(struct device *dev) if (ret) return ret; - ret = sdhci_runtime_resume_host(host, 0); - if (ret) - return ret; + sdhci_runtime_resume_host(host, 0); ret = cqhci_resume(host->mmc); if (ret) @@ -1083,20 +1122,17 @@ static int sdhci_am654_runtime_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops sdhci_am654_dev_pm_ops = { - SET_RUNTIME_PM_OPS(sdhci_am654_runtime_suspend, - sdhci_am654_runtime_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + RUNTIME_PM_OPS(sdhci_am654_runtime_suspend, sdhci_am654_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; static struct platform_driver sdhci_am654_driver = { .driver = { .name = "sdhci-am654", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &sdhci_am654_dev_pm_ops, + .pm = pm_ptr(&sdhci_am654_dev_pm_ops), .of_match_table = sdhci_am654_of_match, }, .probe = sdhci_am654_probe, diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c index ee66e4f3683d..e9b934e159ad 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -133,20 +133,18 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) - goto err; + return ret; if (dev_of_node(dev)) { sdhci_get_of_property(pdev); priv->clk_iface = devm_clk_get(&pdev->dev, "iface"); - if (IS_ERR(priv->clk_iface)) { - ret = PTR_ERR(priv->clk_iface); - goto err; - } + if (IS_ERR(priv->clk_iface)) + return PTR_ERR(priv->clk_iface); ret = clk_prepare_enable(priv->clk_iface); if (ret) - goto err; + return ret; priv->clk = devm_clk_get(&pdev->dev, "core"); if (IS_ERR(priv->clk)) { @@ -200,9 +198,6 @@ err_rst: clk_disable_unprepare(priv->clk); err_clk: clk_disable_unprepare(priv->clk_iface); -err: - sdhci_pltfm_free(pdev); - return ret; } diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 57b8c1a96756..481cb552c2b4 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -403,9 +403,9 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev, } /* allocate privdata */ mmc = pcmcia_dev->priv = - mmc_alloc_host(sizeof(struct sdricoh_host), &pcmcia_dev->dev); + devm_mmc_alloc_host(&pcmcia_dev->dev, sizeof(*host)); if (!mmc) { - dev_err(dev, "mmc_alloc_host failed\n"); + dev_err(dev, "devm_mmc_alloc_host failed\n"); result = -ENOMEM; goto unmap_io; } @@ -431,7 +431,7 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev, if (sdricoh_reset(host)) { dev_dbg(dev, "could not reset\n"); result = -EIO; - goto free_host; + goto unmap_io; } result = mmc_add_host(mmc); @@ -440,8 +440,6 @@ static int sdricoh_init_mmc(struct pci_dev *pci_dev, dev_dbg(dev, "mmc host registered\n"); return 0; } -free_host: - mmc_free_host(mmc); unmap_io: pci_iounmap(pci_dev, iobase); return result; @@ -483,10 +481,8 @@ static void sdricoh_pcmcia_detach(struct pcmcia_device *link) mmc_remove_host(mmc); pci_iounmap(host->pci_dev, host->iobase); pci_dev_put(host->pci_dev); - mmc_free_host(mmc); } pcmcia_disable_device(link); - } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index ce60cec26b98..bf899c8e38f5 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1444,13 +1444,13 @@ static int sh_mmcif_probe(struct platform_device *pdev) if (IS_ERR(reg)) return PTR_ERR(reg); - mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; ret = mmc_of_parse(mmc); if (ret < 0) - goto err_host; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -1481,15 +1481,13 @@ static int sh_mmcif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); host->clk = devm_clk_get(dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - dev_err(dev, "cannot get clock: %d\n", ret); - goto err_host; - } + if (IS_ERR(host->clk)) + return dev_err_probe(dev, PTR_ERR(host->clk), + "cannot get clock\n"); ret = clk_prepare_enable(host->clk); if (ret < 0) - goto err_host; + return ret; sh_mmcif_clk_setup(host); @@ -1542,8 +1540,6 @@ err_clk: clk_disable_unprepare(host->clk); pm_runtime_put_sync(dev); pm_runtime_disable(dev); -err_host: - mmc_free_host(mmc); return ret; } @@ -1568,12 +1564,10 @@ static void sh_mmcif_remove(struct platform_device *pdev) cancel_delayed_work_sync(&host->timeout_work); clk_disable_unprepare(host->clk); - mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); } -#ifdef CONFIG_PM_SLEEP static int sh_mmcif_suspend(struct device *dev) { struct sh_mmcif_host *host = dev_get_drvdata(dev); @@ -1585,15 +1579,7 @@ static int sh_mmcif_suspend(struct device *dev) return 0; } -static int sh_mmcif_resume(struct device *dev) -{ - return 0; -} -#endif - -static const struct dev_pm_ops sh_mmcif_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(sh_mmcif_dev_pm_ops, sh_mmcif_suspend, NULL); static struct platform_driver sh_mmcif_driver = { .probe = sh_mmcif_probe, @@ -1601,7 +1587,7 @@ static struct platform_driver sh_mmcif_driver = { .driver = { .name = DRIVER_NAME, .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &sh_mmcif_dev_pm_ops, + .pm = pm_sleep_ptr(&sh_mmcif_dev_pm_ops), .of_match_table = sh_mmcif_of_match, }, }; diff --git a/drivers/mmc/host/sunplus-mmc.c b/drivers/mmc/host/sunplus-mmc.c index 1cddea615a27..63279760239c 100644 --- a/drivers/mmc/host/sunplus-mmc.c +++ b/drivers/mmc/host/sunplus-mmc.c @@ -791,7 +791,7 @@ static int spmmc_get_cd(struct mmc_host *mmc) { int ret = 0; - if (mmc_can_gpio_cd(mmc)) + if (mmc_host_can_gpio_cd(mmc)) ret = mmc_gpio_get_cd(mmc); if (ret < 0) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 1508eead5d01..8dbcff53a631 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1369,11 +1369,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev) struct mmc_host *mmc; int ret; - mmc = mmc_alloc_host(sizeof(struct sunxi_mmc_host), &pdev->dev); - if (!mmc) { - dev_err(&pdev->dev, "mmc alloc host failed\n"); - return -ENOMEM; - } + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); + if (!mmc) + return dev_err_probe(&pdev->dev, -ENOMEM, + "mmc alloc host failed\n"); platform_set_drvdata(pdev, mmc); host = mmc_priv(mmc); @@ -1383,15 +1382,13 @@ static int sunxi_mmc_probe(struct platform_device *pdev) ret = sunxi_mmc_resource_request(host, pdev); if (ret) - goto error_free_host; + return ret; host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); - if (!host->sg_cpu) { - dev_err(&pdev->dev, "Failed to allocate DMA descriptor mem\n"); - ret = -ENOMEM; - goto error_free_host; - } + if (!host->sg_cpu) + return dev_err_probe(&pdev->dev, -ENOMEM, + "Failed to allocate DMA descriptor mem\n"); if (host->cfg->ccu_has_timings_switch) { /* @@ -1481,8 +1478,6 @@ static int sunxi_mmc_probe(struct platform_device *pdev) error_free_dma: dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); -error_free_host: - mmc_free_host(mmc); return ret; } @@ -1498,10 +1493,8 @@ static void sunxi_mmc_remove(struct platform_device *pdev) sunxi_mmc_disable(host); } dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); - mmc_free_host(mmc); } -#ifdef CONFIG_PM static int sunxi_mmc_runtime_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); @@ -1536,14 +1529,10 @@ static int sunxi_mmc_runtime_suspend(struct device *dev) return 0; } -#endif static const struct dev_pm_ops sunxi_mmc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, - sunxi_mmc_runtime_resume, - NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, sunxi_mmc_runtime_resume, NULL) }; static struct platform_driver sunxi_mmc_driver = { @@ -1551,7 +1540,7 @@ static struct platform_driver sunxi_mmc_driver = { .name = "sunxi-mmc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sunxi_mmc_of_match, - .pm = &sunxi_mmc_pm_ops, + .pm = pm_ptr(&sunxi_mmc_pm_ops), }, .probe = sunxi_mmc_probe, .remove = sunxi_mmc_remove, diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index aea14bf3e2e8..c1f7d5b37911 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -191,11 +191,9 @@ static void tifm_sd_transfer_data(struct tifm_sd *host) } off = sg[host->sg_pos].offset + host->block_pos; - pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT); + pg = sg_page(&sg[host->sg_pos]) + (off >> PAGE_SHIFT); p_off = offset_in_page(off); - p_cnt = PAGE_SIZE - p_off; - p_cnt = min(p_cnt, cnt); - p_cnt = min(p_cnt, t_size); + p_cnt = min3(PAGE_SIZE - p_off, cnt, t_size); if (r_data->flags & MMC_DATA_READ) tifm_sd_read_fifo(host, pg, p_off, p_cnt); @@ -240,7 +238,7 @@ static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data) } off = sg[host->sg_pos].offset + host->block_pos; - pg = nth_page(sg_page(&sg[host->sg_pos]), off >> PAGE_SHIFT); + pg = sg_page(&sg[host->sg_pos]) + (off >> PAGE_SHIFT); p_off = offset_in_page(off); p_cnt = PAGE_SIZE - p_off; p_cnt = min(p_cnt, cnt); @@ -735,7 +733,7 @@ static void tifm_sd_end_cmd(struct work_struct *t) spin_lock_irqsave(&sock->lock, flags); - del_timer(&host->timer); + timer_delete(&host->timer); mrq = host->req; host->req = NULL; @@ -777,7 +775,7 @@ static void tifm_sd_end_cmd(struct work_struct *t) static void tifm_sd_abort(struct timer_list *t) { - struct tifm_sd *host = from_timer(host, t, timer); + struct tifm_sd *host = timer_container_of(host, t, timer); pr_err("%s : card failed to respond for a long period of time " "(%x, %x)\n", @@ -947,7 +945,7 @@ static int tifm_sd_probe(struct tifm_dev *sock) return rc; } - mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); + mmc = devm_mmc_alloc_host(&sock->dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -982,10 +980,7 @@ static int tifm_sd_probe(struct tifm_dev *sock) if (!rc) rc = mmc_add_host(mmc); - if (!rc) - return 0; - mmc_free_host(mmc); return rc; } @@ -1015,8 +1010,6 @@ static void tifm_sd_remove(struct tifm_dev *sock) spin_unlock_irqrestore(&sock->lock, flags); mmc_remove_host(mmc); dev_dbg(&sock->dev, "after remove\n"); - - mmc_free_host(mmc); } #ifdef CONFIG_PM diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index a75755f31d31..b9de03325c58 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -16,6 +16,7 @@ #include <linux/dmaengine.h> #include <linux/highmem.h> +#include <linux/io.h> #include <linux/mutex.h> #include <linux/pagemap.h> #include <linux/scatterlist.h> @@ -44,6 +45,7 @@ #define CTL_RESET_SD 0xe0 #define CTL_VERSION 0xe2 #define CTL_SDIF_MODE 0xe6 /* only known on R-Car 2+ */ +#define CTL_SD_STATUS 0xf2 /* only known on RZ/{G2L,G3E,V2H} */ /* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */ #define TMIO_STOP_STP BIT(0) @@ -103,6 +105,10 @@ /* Definitions for values the CTL_SDIF_MODE register can take */ #define SDIF_MODE_HS400 BIT(0) /* only known on R-Car 2+ */ +/* Definitions for values the CTL_SD_STATUS register can take */ +#define SD_STATUS_PWEN BIT(0) /* only known on RZ/{G3E,V2H} */ +#define SD_STATUS_IOVS BIT(16) /* only known on RZ/{G3E,V2H} */ + /* Define some IRQ masks */ /* This is the mask used at reset by the chip */ #define TMIO_MASK_ALL 0x837f031d @@ -188,13 +194,13 @@ struct tmio_mmc_host { bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq); void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq); unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host); + void (*sdio_irq)(struct tmio_mmc_host *host); const struct tmio_mmc_dma_ops *dma_ops; }; struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev, struct tmio_mmc_data *pdata); -void tmio_mmc_host_free(struct tmio_mmc_host *host); int tmio_mmc_host_probe(struct tmio_mmc_host *host); void tmio_mmc_host_remove(struct tmio_mmc_host *host); void tmio_mmc_do_data_irq(struct tmio_mmc_host *host); @@ -203,10 +209,8 @@ 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); -#ifdef CONFIG_PM int tmio_mmc_host_runtime_suspend(struct device *dev); int tmio_mmc_host_runtime_resume(struct device *dev); -#endif static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr) { @@ -226,12 +230,31 @@ static inline u32 sd_ctrl_read16_and_16_as_32(struct tmio_mmc_host *host, ioread16(host->ctl + ((addr + 2) << host->bus_shift)) << 16; } +static inline u32 sd_ctrl_read32(struct tmio_mmc_host *host, int addr) +{ + return ioread32(host->ctl + (addr << host->bus_shift)); +} + static inline void sd_ctrl_read32_rep(struct tmio_mmc_host *host, int addr, u32 *buf, int count) { ioread32_rep(host->ctl + (addr << host->bus_shift), buf, count); } +#ifdef CONFIG_64BIT +static inline void sd_ctrl_read64_rep(struct tmio_mmc_host *host, int addr, + u64 *buf, int count) +{ + readsq(host->ctl + (addr << host->bus_shift), buf, count); +} + +static inline void sd_ctrl_write64_rep(struct tmio_mmc_host *host, int addr, + const u64 *buf, int count) +{ + writesq(host->ctl + (addr << host->bus_shift), buf, count); +} +#endif + static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val) { diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 04c1c54df791..775e0d9353d5 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -160,7 +160,6 @@ static void tmio_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) sd_ctrl_write16(host, CTL_SDIO_IRQ_MASK, host->sdio_irq_mask); host->sdio_irq_enabled = false; - pm_runtime_mark_last_busy(mmc_dev(mmc)); pm_runtime_put_autosuspend(mmc_dev(mmc)); } } @@ -350,6 +349,39 @@ static void tmio_mmc_transfer_data(struct tmio_mmc_host *host, /* * Transfer the data */ +#ifdef CONFIG_64BIT + if (host->pdata->flags & TMIO_MMC_64BIT_DATA_PORT) { + u64 *buf64 = (u64 *)buf; + u64 data = 0; + + if (count >= 8) { + if (is_read) + sd_ctrl_read64_rep(host, CTL_SD_DATA_PORT, + buf64, count >> 3); + else + sd_ctrl_write64_rep(host, CTL_SD_DATA_PORT, + buf64, count >> 3); + } + + /* if count was multiple of 8 */ + if (!(count & 0x7)) + return; + + buf64 += count >> 3; + count %= 8; + + if (is_read) { + sd_ctrl_read64_rep(host, CTL_SD_DATA_PORT, &data, 1); + memcpy(buf64, &data, count); + } else { + memcpy(&data, buf64, count); + sd_ctrl_write64_rep(host, CTL_SD_DATA_PORT, &data, 1); + } + + return; + } +#endif + if (host->pdata->flags & TMIO_MMC_32BIT_DATA_PORT) { u32 data = 0; u32 *buf32 = (u32 *)buf; @@ -696,8 +728,11 @@ static bool __tmio_mmc_sdio_irq(struct tmio_mmc_host *host) sd_ctrl_write16(host, CTL_SDIO_STATUS, sdio_status); - if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ) + if (mmc->caps & MMC_CAP_SDIO_IRQ && ireg & TMIO_SDIO_STAT_IOIRQ) { + if (host->sdio_irq) + host->sdio_irq(host); mmc_signal_sdio_irq(mmc); + } return ireg; } @@ -1097,7 +1132,7 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev, if (IS_ERR(ctl)) return ERR_CAST(ctl); - mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) return ERR_PTR(-ENOMEM); @@ -1110,29 +1145,17 @@ struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev, mmc->ops = &host->ops; ret = mmc_of_parse(host->mmc); - if (ret) { - host = ERR_PTR(ret); - goto free; - } + if (ret) + return ERR_PTR(ret); tmio_mmc_of_parse(pdev, mmc); platform_set_drvdata(pdev, host); return host; -free: - mmc_free_host(mmc); - - return host; } EXPORT_SYMBOL_GPL(tmio_mmc_host_alloc); -void tmio_mmc_host_free(struct tmio_mmc_host *host) -{ - mmc_free_host(host->mmc); -} -EXPORT_SYMBOL_GPL(tmio_mmc_host_free); - int tmio_mmc_host_probe(struct tmio_mmc_host *_host) { struct platform_device *pdev = _host->pdev; @@ -1176,14 +1199,14 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) dma_max_mapping_size(&pdev->dev)); mmc->max_seg_size = mmc->max_req_size; - if (mmc_can_gpio_ro(mmc)) + if (mmc_host_can_gpio_ro(mmc)) _host->ops.get_ro = mmc_gpio_get_ro; - if (mmc_can_gpio_cd(mmc)) + if (mmc_host_can_gpio_cd(mmc)) _host->ops.get_cd = mmc_gpio_get_cd; /* must be set before tmio_mmc_reset() */ - _host->native_hotplug = !(mmc_can_gpio_cd(mmc) || + _host->native_hotplug = !(mmc_host_can_gpio_cd(mmc) || mmc->caps & MMC_CAP_NEEDS_POLL || !mmc_card_is_removable(mmc)); diff --git a/drivers/mmc/host/toshsd.c b/drivers/mmc/host/toshsd.c index 497791ffada6..aa5d2511a62b 100644 --- a/drivers/mmc/host/toshsd.c +++ b/drivers/mmc/host/toshsd.c @@ -567,7 +567,6 @@ static void toshsd_powerdown(struct toshsd_host *host) pci_write_config_byte(host->pdev, SD_PCICFG_CLKSTOP, 0); } -#ifdef CONFIG_PM_SLEEP static int toshsd_pm_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -599,7 +598,6 @@ static int toshsd_pm_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -612,7 +610,7 @@ static int toshsd_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) return ret; - mmc = mmc_alloc_host(sizeof(struct toshsd_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*host)); if (!mmc) { ret = -ENOMEM; goto err; @@ -669,7 +667,6 @@ unmap: release: pci_release_regions(pdev); free: - mmc_free_host(mmc); pci_set_drvdata(pdev, NULL); err: pci_disable_device(pdev); @@ -685,21 +682,18 @@ static void toshsd_remove(struct pci_dev *pdev) free_irq(pdev->irq, host); pci_iounmap(pdev, host->ioaddr); pci_release_regions(pdev); - mmc_free_host(host->mmc); pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); } -static const struct dev_pm_ops toshsd_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(toshsd_pm_suspend, toshsd_pm_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(toshsd_pm_ops, toshsd_pm_suspend, toshsd_pm_resume); static struct pci_driver toshsd_driver = { .name = DRIVER_NAME, .id_table = pci_ids, .probe = toshsd_probe, .remove = toshsd_remove, - .driver.pm = &toshsd_pm_ops, + .driver.pm = pm_sleep_ptr(&toshsd_pm_ops), }; module_pci_driver(toshsd_driver); diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c index 4ad02cfdc238..1eae2f4b6c1f 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -663,8 +663,7 @@ static int uniphier_sd_probe(struct platform_device *pdev) priv->rst_hw = devm_reset_control_get_exclusive(dev, "hw"); if (IS_ERR(priv->rst_hw)) { dev_err(dev, "failed to get hw reset\n"); - ret = PTR_ERR(priv->rst_hw); - goto free_host; + return PTR_ERR(priv->rst_hw); } host->ops.card_hw_reset = uniphier_sd_hw_reset; } @@ -694,7 +693,7 @@ static int uniphier_sd_probe(struct platform_device *pdev) ret = uniphier_sd_clk_enable(host); if (ret) - goto free_host; + return ret; uniphier_sd_host_init(host); @@ -720,8 +719,6 @@ static int uniphier_sd_probe(struct platform_device *pdev) disable_clk: uniphier_sd_clk_disable(host); -free_host: - tmio_mmc_host_free(host); return ret; } @@ -732,7 +729,6 @@ static void uniphier_sd_remove(struct platform_device *pdev) tmio_mmc_host_remove(host); uniphier_sd_clk_disable(host); - tmio_mmc_host_free(host); } static const struct of_device_id uniphier_sd_match[] = { diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index 49efb960a052..3bccf800339b 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -323,7 +323,7 @@ static void usdhi6_blk_bounce(struct usdhi6_host *host, host->head_pg.page = host->pg.page; host->head_pg.mapped = host->pg.mapped; - host->pg.page = nth_page(host->pg.page, 1); + host->pg.page = host->pg.page + 1; host->pg.mapped = kmap(host->pg.page); host->blk_page = host->bounce_buf; @@ -503,7 +503,7 @@ static void usdhi6_sg_advance(struct usdhi6_host *host) /* We cannot get here after crossing a page border */ /* Next page in the same SG */ - host->pg.page = nth_page(sg_page(host->sg), host->page_idx); + host->pg.page = sg_page(host->sg) + host->page_idx; host->pg.mapped = kmap(host->pg.page); host->blk_page = host->pg.mapped; @@ -1762,17 +1762,17 @@ static int usdhi6_probe(struct platform_device *pdev) if (irq_sdio < 0) return irq_sdio; - mmc = mmc_alloc_host(sizeof(struct usdhi6_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; ret = mmc_regulator_get_supply(mmc); if (ret) - goto e_free_mmc; + return ret; ret = mmc_of_parse(mmc); if (ret < 0) - goto e_free_mmc; + return ret; host = mmc_priv(mmc); host->mmc = mmc; @@ -1785,30 +1785,24 @@ static int usdhi6_probe(struct platform_device *pdev) mmc->max_busy_timeout = USDHI6_REQ_TIMEOUT_MS; host->pinctrl = devm_pinctrl_get(&pdev->dev); - if (IS_ERR(host->pinctrl)) { - ret = PTR_ERR(host->pinctrl); - goto e_free_mmc; - } + if (IS_ERR(host->pinctrl)) + return PTR_ERR(host->pinctrl); host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs"); host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(host->base)) { - ret = PTR_ERR(host->base); - goto e_free_mmc; - } + if (IS_ERR(host->base)) + return PTR_ERR(host->base); host->clk = devm_clk_get(dev, NULL); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - goto e_free_mmc; - } + if (IS_ERR(host->clk)) + return PTR_ERR(host->clk); host->imclk = clk_get_rate(host->clk); ret = clk_prepare_enable(host->clk); if (ret < 0) - goto e_free_mmc; + return ret; version = usdhi6_read(host, USDHI6_VERSION); if ((version & 0xfff) != 0xa0d) { @@ -1878,9 +1872,6 @@ e_release_dma: usdhi6_dma_release(host); e_clk_off: clk_disable_unprepare(host->clk); -e_free_mmc: - mmc_free_host(mmc); - return ret; } @@ -1894,7 +1885,6 @@ static void usdhi6_remove(struct platform_device *pdev) cancel_delayed_work_sync(&host->timeout_work); usdhi6_dma_release(host); clk_disable_unprepare(host->clk); - mmc_free_host(host->mmc); } static struct platform_driver usdhi6_driver = { diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c index 9a6358fd9512..7e8af5a06ac9 100644 --- a/drivers/mmc/host/ushc.c +++ b/drivers/mmc/host/ushc.c @@ -404,8 +404,6 @@ static void ushc_clean_up(struct ushc_data *ushc) kfree(ushc->int_data); kfree(ushc->cbw); kfree(ushc->csw); - - mmc_free_host(ushc->mmc); } static const struct mmc_host_ops ushc_ops = { @@ -425,7 +423,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id if (intf->cur_altsetting->desc.bNumEndpoints < 1) return -ENODEV; - mmc = mmc_alloc_host(sizeof(struct ushc_data), &intf->dev); + mmc = devm_mmc_alloc_host(&intf->dev, sizeof(*ushc)); if (mmc == NULL) return -ENOMEM; ushc = mmc_priv(mmc); @@ -464,7 +462,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id ret = -ENOMEM; goto err; } - ushc->int_data = kzalloc(sizeof(struct ushc_int_data), GFP_KERNEL); + ushc->int_data = kzalloc_obj(struct ushc_int_data); if (ushc->int_data == NULL) { ret = -ENOMEM; goto err; @@ -481,7 +479,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id ret = -ENOMEM; goto err; } - ushc->cbw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL); + ushc->cbw = kzalloc_obj(struct ushc_cbw); if (ushc->cbw == NULL) { ret = -ENOMEM; goto err; @@ -503,7 +501,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id ret = -ENOMEM; goto err; } - ushc->csw = kzalloc(sizeof(struct ushc_csw), GFP_KERNEL); + ushc->csw = kzalloc_obj(struct ushc_csw); if (ushc->csw == NULL) { ret = -ENOMEM; goto err; diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index f77457105ec3..c628b3bbfd7a 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -937,7 +937,7 @@ static void via_sdc_timeout(struct timer_list *t) struct via_crdr_mmc_host *sdhost; unsigned long flags; - sdhost = from_timer(sdhost, t, timer); + sdhost = timer_container_of(sdhost, t, timer); spin_lock_irqsave(&sdhost->lock, flags); @@ -971,7 +971,7 @@ static void via_sdc_finish_bh_work(struct work_struct *t) spin_lock_irqsave(&host->lock, flags); - del_timer(&host->timer); + timer_delete(&host->timer); mrq = host->mrq; host->mrq = NULL; host->cmd = NULL; @@ -1100,7 +1100,7 @@ static int via_sd_probe(struct pci_dev *pcidev, pci_write_config_byte(pcidev, VIA_CRDR_PCI_WORK_MODE, 0); pci_write_config_byte(pcidev, VIA_CRDR_PCI_DBG_MODE, 0); - mmc = mmc_alloc_host(sizeof(struct via_crdr_mmc_host), &pcidev->dev); + mmc = devm_mmc_alloc_host(&pcidev->dev, sizeof(*sdhost)); if (!mmc) { ret = -ENOMEM; goto release; @@ -1115,7 +1115,7 @@ static int via_sd_probe(struct pci_dev *pcidev, sdhost->mmiobase = ioremap(base, len); if (!sdhost->mmiobase) { ret = -ENOMEM; - goto free_mmc_host; + goto release; } sdhost->sdhc_mmiobase = @@ -1160,8 +1160,6 @@ static int via_sd_probe(struct pci_dev *pcidev, unmap: iounmap(sdhost->mmiobase); -free_mmc_host: - mmc_free_host(mmc); release: pci_release_regions(pcidev); disable: @@ -1202,7 +1200,7 @@ static void via_sd_remove(struct pci_dev *pcidev) free_irq(pcidev->irq, sdhost); - del_timer_sync(&sdhost->timer); + timer_delete_sync(&sdhost->timer); cancel_work_sync(&sdhost->finish_bh_work); @@ -1212,7 +1210,6 @@ static void via_sd_remove(struct pci_dev *pcidev) writeb(gatt, sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); iounmap(sdhost->mmiobase); - mmc_free_host(sdhost->mmc); pci_release_regions(pcidev); pci_disable_device(pcidev); @@ -1221,7 +1218,7 @@ static void via_sd_remove(struct pci_dev *pcidev) pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device); } -static void __maybe_unused via_init_sdc_pm(struct via_crdr_mmc_host *host) +static void via_init_sdc_pm(struct via_crdr_mmc_host *host) { struct sdhcreg *pm_sdhcreg; void __iomem *addrbase; @@ -1255,7 +1252,7 @@ static void __maybe_unused via_init_sdc_pm(struct via_crdr_mmc_host *host) via_print_sdchc(host); } -static int __maybe_unused via_sd_suspend(struct device *dev) +static int via_sd_suspend(struct device *dev) { struct via_crdr_mmc_host *host; unsigned long flags; @@ -1272,7 +1269,7 @@ static int __maybe_unused via_sd_suspend(struct device *dev) return 0; } -static int __maybe_unused via_sd_resume(struct device *dev) +static int via_sd_resume(struct device *dev) { struct via_crdr_mmc_host *sdhost; u8 gatt; @@ -1298,14 +1295,14 @@ static int __maybe_unused via_sd_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(via_sd_pm_ops, via_sd_suspend, via_sd_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(via_sd_pm_ops, via_sd_suspend, via_sd_resume); static struct pci_driver via_sd_driver = { .name = DRV_NAME, .id_table = via_ids, .probe = via_sd_probe, .remove = via_sd_remove, - .driver.pm = &via_sd_pm_ops, + .driver.pm = pm_sleep_ptr(&via_sd_pm_ops), }; module_pci_driver(via_sd_driver); diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index fd67c0682b38..6c3cb2f1c9d3 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -370,6 +370,7 @@ static void vub300_delete(struct kref *kref) { /* kref callback - softirq */ struct vub300_mmc_host *vub300 = kref_to_vub300_mmc_host(kref); struct mmc_host *mmc = vub300->mmc; + usb_free_urb(vub300->command_out_urb); vub300->command_out_urb = NULL; usb_free_urb(vub300->command_res_urb); @@ -740,8 +741,8 @@ static void vub300_deadwork_thread(struct work_struct *work) static void vub300_inactivity_timer_expired(struct timer_list *t) { /* softirq */ - struct vub300_mmc_host *vub300 = from_timer(vub300, t, - inactivity_timer); + struct vub300_mmc_host *vub300 = timer_container_of(vub300, t, + inactivity_timer); if (!vub300->interface) { kref_put(&vub300->kref, vub300_delete); } else if (vub300->cmd) { @@ -1180,8 +1181,8 @@ static void send_command(struct vub300_mmc_host *vub300) */ static void vub300_sg_timed_out(struct timer_list *t) { - struct vub300_mmc_host *vub300 = from_timer(vub300, t, - sg_transfer_timer); + struct vub300_mmc_host *vub300 = timer_container_of(vub300, t, + sg_transfer_timer); vub300->usb_timed_out = 1; usb_sg_cancel(&vub300->sg_request); usb_unlink_urb(vub300->command_out_urb); @@ -1452,7 +1453,7 @@ static int __command_read_data(struct vub300_mmc_host *vub300, (linear_length / 16384)); add_timer(&vub300->sg_transfer_timer); usb_sg_wait(&vub300->sg_request); - del_timer(&vub300->sg_transfer_timer); + timer_delete(&vub300->sg_transfer_timer); if (vub300->sg_request.status < 0) { cmd->error = vub300->sg_request.status; data->bytes_xfered = 0; @@ -1572,7 +1573,7 @@ static int __command_write_data(struct vub300_mmc_host *vub300, if (cmd->error) { data->bytes_xfered = 0; } else { - del_timer(&vub300->sg_transfer_timer); + timer_delete(&vub300->sg_transfer_timer); if (vub300->sg_request.status < 0) { cmd->error = vub300->sg_request.status; data->bytes_xfered = 0; @@ -2106,19 +2107,19 @@ static int vub300_probe(struct usb_interface *interface, command_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_out_urb) { retval = -ENOMEM; - goto error0; + goto err_put_udev; } command_res_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_res_urb) { retval = -ENOMEM; - goto error1; + goto err_free_out_urb; } /* this also allocates memory for our VUB300 mmc host device */ - mmc = mmc_alloc_host(sizeof(struct vub300_mmc_host), &udev->dev); + mmc = mmc_alloc_host(sizeof(*vub300), &udev->dev); if (!mmc) { retval = -ENOMEM; dev_err(&udev->dev, "not enough memory for the mmc_host\n"); - goto error4; + goto err_free_res_urb; } /* MMC core transfer sizes tunable parameters */ mmc->caps = 0; @@ -2271,7 +2272,7 @@ static int vub300_probe(struct usb_interface *interface, dev_err(&vub300->udev->dev, "Could not find two sets of bulk-in/out endpoint pairs\n"); retval = -EINVAL; - goto error5; + goto err_free_host; } retval = usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0), @@ -2280,14 +2281,14 @@ static int vub300_probe(struct usb_interface *interface, 0x0000, 0x0000, &vub300->hc_info, sizeof(vub300->hc_info), 1000); if (retval < 0) - goto error5; + goto err_free_host; retval = usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), SET_ROM_WAIT_STATES, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, firmware_rom_wait_states, 0x0000, NULL, 0, 1000); if (retval < 0) - goto error5; + goto err_free_host; dev_info(&vub300->udev->dev, "operating_mode = %s %s %d MHz %s %d byte USB packets\n", (mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL", @@ -2302,7 +2303,7 @@ static int vub300_probe(struct usb_interface *interface, 0x0000, 0x0000, &vub300->system_port_status, sizeof(vub300->system_port_status), 1000); if (retval < 0) { - goto error5; + goto err_free_host; } else if (sizeof(vub300->system_port_status) == retval) { vub300->card_present = (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0; @@ -2310,7 +2311,7 @@ static int vub300_probe(struct usb_interface *interface, (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0; } else { retval = -EINVAL; - goto error5; + goto err_free_host; } usb_set_intfdata(interface, vub300); INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread); @@ -2335,23 +2336,25 @@ static int vub300_probe(struct usb_interface *interface, interface_to_InterfaceNumber(interface)); retval = mmc_add_host(mmc); if (retval) - goto error6; + goto err_delete_timer; return 0; -error6: - del_timer_sync(&vub300->inactivity_timer); -error5: + +err_delete_timer: + timer_delete_sync(&vub300->inactivity_timer); +err_free_host: mmc_free_host(mmc); /* * and hence also frees vub300 * which is contained at the end of struct mmc */ -error4: +err_free_res_urb: usb_free_urb(command_res_urb); -error1: +err_free_out_urb: usb_free_urb(command_out_urb); -error0: +err_put_udev: usb_put_dev(udev); + return retval; } @@ -2369,8 +2372,8 @@ static void vub300_disconnect(struct usb_interface *interface) usb_set_intfdata(interface, NULL); /* prevent more I/O from starting */ vub300->interface = NULL; - kref_put(&vub300->kref, vub300_delete); mmc_remove_host(mmc); + kref_put(&vub300->kref, vub300_delete); pr_info("USB vub300 remote SDIO host controller[%d]" " now disconnected", ifnum); return; @@ -2426,37 +2429,36 @@ static int __init vub300_init(void) pr_info("VUB300 Driver rom wait states = %02X irqpoll timeout = %04X", firmware_rom_wait_states, 0x0FFFF & firmware_irqpoll_timeout); + cmndworkqueue = create_singlethread_workqueue("kvub300c"); - if (!cmndworkqueue) { - pr_err("not enough memory for the REQUEST workqueue"); - result = -ENOMEM; - goto out1; - } + if (!cmndworkqueue) + return -ENOMEM; + pollworkqueue = create_singlethread_workqueue("kvub300p"); if (!pollworkqueue) { - pr_err("not enough memory for the IRQPOLL workqueue"); result = -ENOMEM; - goto out2; + goto err_destroy_cmdwq; } + deadworkqueue = create_singlethread_workqueue("kvub300d"); if (!deadworkqueue) { - pr_err("not enough memory for the EXPIRED workqueue"); result = -ENOMEM; - goto out3; + goto err_destroy_pollwq; } + result = usb_register(&vub300_driver); - if (result) { - pr_err("usb_register failed. Error number %d", result); - goto out4; - } + if (result) + goto err_destroy_deadwq; + return 0; -out4: + +err_destroy_deadwq: destroy_workqueue(deadworkqueue); -out3: +err_destroy_pollwq: destroy_workqueue(pollworkqueue); -out2: +err_destroy_cmdwq: destroy_workqueue(cmndworkqueue); -out1: + return result; } diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 8b268e8a0ec9..c33a0223ce7f 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -947,7 +947,7 @@ static const struct mmc_host_ops wbsd_ops = { static void wbsd_reset_ignore(struct timer_list *t) { - struct wbsd_host *host = from_timer(host, t, ignore_timer); + struct wbsd_host *host = timer_container_of(host, t, ignore_timer); BUG_ON(host == NULL); @@ -1190,7 +1190,7 @@ static int wbsd_alloc_mmc(struct device *dev) /* * Allocate MMC structure. */ - mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); + mmc = devm_mmc_alloc_host(dev, sizeof(*host)); if (!mmc) return -ENOMEM; @@ -1261,9 +1261,7 @@ static void wbsd_free_mmc(struct device *dev) host = mmc_priv(mmc); BUG_ON(host == NULL); - del_timer_sync(&host->ignore_timer); - - mmc_free_host(mmc); + timer_delete_sync(&host->ignore_timer); } /* diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index cdb36a9f9e38..1b1d691e19fc 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -774,7 +774,7 @@ static int wmt_mci_probe(struct platform_device *pdev) goto fail1; } - mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(*priv)); if (!mmc) { dev_err(&pdev->dev, "Failed to allocate mmc_host\n"); ret = -ENOMEM; @@ -808,7 +808,7 @@ static int wmt_mci_probe(struct platform_device *pdev) if (!priv->sdmmc_base) { dev_err(&pdev->dev, "Failed to map IO space\n"); ret = -ENOMEM; - goto fail2; + goto fail1; } priv->irq_regular = regular_irq; @@ -873,8 +873,6 @@ fail4: free_irq(regular_irq, priv); fail3: iounmap(priv->sdmmc_base); -fail2: - mmc_free_host(mmc); fail1: return ret; } @@ -910,12 +908,9 @@ static void wmt_mci_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clk_sdmmc); clk_put(priv->clk_sdmmc); - mmc_free_host(mmc); - dev_info(&pdev->dev, "WMT MCI device removed\n"); } -#ifdef CONFIG_PM static int wmt_mci_suspend(struct device *dev) { u32 reg_tmp; @@ -967,18 +962,7 @@ static int wmt_mci_resume(struct device *dev) return 0; } -static const struct dev_pm_ops wmt_mci_pm = { - .suspend = wmt_mci_suspend, - .resume = wmt_mci_resume, -}; - -#define wmt_mci_pm_ops (&wmt_mci_pm) - -#else /* !CONFIG_PM */ - -#define wmt_mci_pm_ops NULL - -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(wmt_mci_pm_ops, wmt_mci_suspend, wmt_mci_resume); static struct platform_driver wmt_mci_driver = { .probe = wmt_mci_probe, @@ -986,7 +970,7 @@ static struct platform_driver wmt_mci_driver = { .driver = { .name = DRIVER_NAME, .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = wmt_mci_pm_ops, + .pm = pm_sleep_ptr(&wmt_mci_pm_ops), .of_match_table = wmt_mci_dt_ids, }, }; |
