diff options
Diffstat (limited to 'drivers/mmc/host')
49 files changed, 1831 insertions, 1182 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 8a4c066787d7..7fc5099e44b2 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -284,11 +284,11 @@ config MMC_OMAP config MMC_OMAP_HS tristate "TI OMAP High Speed Multimedia Card Interface support" - depends on SOC_OMAP2430 || ARCH_OMAP3 || ARCH_OMAP4 + depends on ARCH_OMAP2PLUS || COMPILE_TEST help This selects the TI OMAP High Speed Multimedia card Interface. - If you have an OMAP2430 or OMAP3 board or OMAP4 board with a - Multimedia Card slot, say Y or M here. + If you have an omap2plus board with a Multimedia Card slot, + say Y or M here. If unsure, say N. @@ -487,7 +487,7 @@ config MMC_SDHI config MMC_CB710 tristate "ENE CB710 MMC/SD Interface support" - depends on PCI && GENERIC_HARDIRQS + depends on PCI select CB710_CORE help This option enables support for MMC/SD part of ENE CB710/720 Flash @@ -530,7 +530,7 @@ config SDH_BFIN_MISSING_CMD_PULLUP_WORKAROUND config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" - depends on ARM + depends on ARC || ARM help This selects support for the Synopsys DesignWare Mobile Storage IP block, this provides host support for SD and MMC interfaces, in both @@ -569,7 +569,7 @@ config MMC_DW_EXYNOS config MMC_DW_SOCFPGA tristate "SOCFPGA specific extensions for Synopsys DW Memory Card Interface" - depends on MMC_DW + depends on MMC_DW && MFD_SYSCON select MMC_DW_PLTFM help This selects support for Altera SoCFPGA specific extensions to the diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index d422e2167e19..c41d0c364509 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -52,8 +52,6 @@ obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o -obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o - obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index bdb84da74952..2cbb4516d353 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -255,7 +255,6 @@ struct atmel_mci_slot { #define ATMCI_CARD_PRESENT 0 #define ATMCI_CARD_NEED_INIT 1 #define ATMCI_SHUTDOWN 2 -#define ATMCI_SUSPENDED 3 int detect_pin; int wp_pin; @@ -378,6 +377,8 @@ static int atmci_regs_show(struct seq_file *s, void *v) { struct atmel_mci *host = s->private; u32 *buf; + int ret = 0; + buf = kmalloc(ATMCI_REGS_SIZE, GFP_KERNEL); if (!buf) @@ -388,12 +389,16 @@ static int atmci_regs_show(struct seq_file *s, void *v) * not disabling interrupts, so IMR and SR may not be * consistent. */ + ret = clk_prepare_enable(host->mck); + if (ret) + goto out; + spin_lock_bh(&host->lock); - clk_enable(host->mck); memcpy_fromio(buf, host->regs, ATMCI_REGS_SIZE); - clk_disable(host->mck); spin_unlock_bh(&host->lock); + clk_disable_unprepare(host->mck); + seq_printf(s, "MR:\t0x%08x%s%s ", buf[ATMCI_MR / 4], buf[ATMCI_MR / 4] & ATMCI_MR_RDPROOF ? " RDPROOF" : "", @@ -442,9 +447,10 @@ static int atmci_regs_show(struct seq_file *s, void *v) val & ATMCI_CFG_LSYNC ? " LSYNC" : ""); } +out: kfree(buf); - return 0; + return ret; } static int atmci_regs_open(struct inode *inode, struct file *file) @@ -582,6 +588,13 @@ static void atmci_timeout_timer(unsigned long data) if (host->mrq->cmd->data) { host->mrq->cmd->data->error = -ETIMEDOUT; host->data = NULL; + /* + * With some SDIO modules, sometimes DMA transfer hangs. If + * stop_transfer() is not called then the DMA request is not + * removed, following ones are queued and never computed. + */ + if (host->state == STATE_DATA_XFER) + host->stop_transfer(host); } else { host->mrq->cmd->error = -ETIMEDOUT; host->cmd = NULL; @@ -1262,6 +1275,7 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) struct atmel_mci_slot *slot = mmc_priv(mmc); struct atmel_mci *host = slot->host; unsigned int i; + bool unprepare_clk; slot->sdc_reg &= ~ATMCI_SDCBUS_MASK; switch (ios->bus_width) { @@ -1277,9 +1291,13 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) unsigned int clock_min = ~0U; u32 clkdiv; + clk_prepare(host->mck); + unprepare_clk = true; + spin_lock_bh(&host->lock); if (!host->mode_reg) { clk_enable(host->mck); + unprepare_clk = false; atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN); if (host->caps.has_cfg_reg) @@ -1347,6 +1365,8 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } else { bool any_slot_active = false; + unprepare_clk = false; + spin_lock_bh(&host->lock); slot->clock = 0; for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { @@ -1360,12 +1380,16 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->mode_reg) { atmci_readl(host, ATMCI_MR); clk_disable(host->mck); + unprepare_clk = true; } host->mode_reg = 0; } spin_unlock_bh(&host->lock); } + if (unprepare_clk) + clk_unprepare(host->mck); + switch (ios->power_mode) { case MMC_POWER_UP: set_bit(ATMCI_CARD_NEED_INIT, &slot->flags); @@ -1785,12 +1809,14 @@ static void atmci_tasklet_func(unsigned long priv) if (unlikely(status)) { host->stop_transfer(host); host->data = NULL; - if (status & ATMCI_DTOE) { - data->error = -ETIMEDOUT; - } else if (status & ATMCI_DCRCE) { - data->error = -EILSEQ; - } else { - data->error = -EIO; + if (data) { + if (status & ATMCI_DTOE) { + data->error = -ETIMEDOUT; + } else if (status & ATMCI_DCRCE) { + data->error = -EILSEQ; + } else { + data->error = -EIO; + } } } @@ -2376,10 +2402,12 @@ static int __init atmci_probe(struct platform_device *pdev) if (!host->regs) goto err_ioremap; - clk_enable(host->mck); + ret = clk_prepare_enable(host->mck); + if (ret) + goto err_request_irq; atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST); host->bus_hz = clk_get_rate(host->mck); - clk_disable(host->mck); + clk_disable_unprepare(host->mck); host->mapbase = regs->start; @@ -2482,11 +2510,11 @@ static int __exit atmci_remove(struct platform_device *pdev) atmci_cleanup_slot(host->slot[i], i); } - clk_enable(host->mck); + clk_prepare_enable(host->mck); atmci_writel(host, ATMCI_IDR, ~0UL); atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS); atmci_readl(host, ATMCI_SR); - clk_disable(host->mck); + clk_disable_unprepare(host->mck); if (host->dma.chan) dma_release_channel(host->dma.chan); @@ -2500,70 +2528,10 @@ static int __exit atmci_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int atmci_suspend(struct device *dev) -{ - struct atmel_mci *host = dev_get_drvdata(dev); - int i; - - for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { - struct atmel_mci_slot *slot = host->slot[i]; - int ret; - - if (!slot) - continue; - ret = mmc_suspend_host(slot->mmc); - if (ret < 0) { - while (--i >= 0) { - slot = host->slot[i]; - if (slot - && test_bit(ATMCI_SUSPENDED, &slot->flags)) { - mmc_resume_host(host->slot[i]->mmc); - clear_bit(ATMCI_SUSPENDED, &slot->flags); - } - } - return ret; - } else { - set_bit(ATMCI_SUSPENDED, &slot->flags); - } - } - - return 0; -} - -static int atmci_resume(struct device *dev) -{ - struct atmel_mci *host = dev_get_drvdata(dev); - int i; - int ret = 0; - - for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) { - struct atmel_mci_slot *slot = host->slot[i]; - int err; - - slot = host->slot[i]; - if (!slot) - continue; - if (!test_bit(ATMCI_SUSPENDED, &slot->flags)) - continue; - err = mmc_resume_host(slot->mmc); - if (err < 0) - ret = err; - else - clear_bit(ATMCI_SUSPENDED, &slot->flags); - } - - return ret; -} -#endif - -static SIMPLE_DEV_PM_OPS(atmci_pm, atmci_suspend, atmci_resume); - static struct platform_driver atmci_driver = { .remove = __exit_p(atmci_remove), .driver = { .name = "atmel_mci", - .pm = &atmci_pm, .of_match_table = of_match_ptr(atmci_dt_ids), }, }; diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index df9becdd2e99..f5443a6c4915 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -1157,11 +1157,6 @@ static int au1xmmc_remove(struct platform_device *pdev) static int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state) { struct au1xmmc_host *host = platform_get_drvdata(pdev); - int ret; - - ret = mmc_suspend_host(host->mmc); - if (ret) - return ret; au_writel(0, HOST_CONFIG2(host)); au_writel(0, HOST_CONFIG(host)); @@ -1178,7 +1173,7 @@ static int au1xmmc_resume(struct platform_device *pdev) au1xmmc_reset_controller(host); - return mmc_resume_host(host->mmc); + return 0; } #else #define au1xmmc_suspend NULL diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 94fae2f1baaf..2b7f37e82ca9 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -391,6 +391,7 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* Disable 4 bit SDIO */ cfg &= ~SD4E; } + bfin_write_SDH_CFG(cfg); host->power_mode = ios->power_mode; #ifndef RSI_BLKSZ @@ -415,7 +416,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) cfg &= ~SD_CMD_OD; # endif - if (ios->power_mode != MMC_POWER_OFF) cfg |= PWR_ON; else @@ -433,7 +433,6 @@ static void sdh_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) clk_ctl |= CLK_E; host->clk_div = clk_div; bfin_write_SDH_CLK_CTL(clk_ctl); - } else sdh_stop_clock(host); @@ -640,21 +639,15 @@ static int sdh_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int sdh_suspend(struct platform_device *dev, pm_message_t state) { - struct mmc_host *mmc = platform_get_drvdata(dev); struct bfin_sd_host *drv_data = get_sdh_data(dev); - int ret = 0; - - if (mmc) - ret = mmc_suspend_host(mmc); peripheral_free_list(drv_data->pin_req); - return ret; + return 0; } static int sdh_resume(struct platform_device *dev) { - struct mmc_host *mmc = platform_get_drvdata(dev); struct bfin_sd_host *drv_data = get_sdh_data(dev); int ret = 0; @@ -665,10 +658,6 @@ static int sdh_resume(struct platform_device *dev) } sdh_reset(); - - if (mmc) - ret = mmc_resume_host(mmc); - return ret; } #else diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 9d6e2b844404..1087b4c79cd6 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -667,12 +667,6 @@ static const struct mmc_host_ops cb710_mmc_host = { static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); - struct mmc_host *mmc = cb710_slot_to_mmc(slot); - int err; - - err = mmc_suspend_host(mmc); - if (err) - return err; cb710_mmc_enable_irq(slot, 0, ~0); return 0; @@ -681,11 +675,9 @@ static int cb710_mmc_suspend(struct platform_device *pdev, pm_message_t state) static int cb710_mmc_resume(struct platform_device *pdev) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); - struct mmc_host *mmc = cb710_slot_to_mmc(slot); cb710_mmc_enable_irq(slot, 0, ~0); - - return mmc_resume_host(mmc); + return 0; } #endif /* CONFIG_PM */ diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index e9fa87df909c..d6153740b77f 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -193,7 +193,6 @@ struct mmc_davinci_host { #define DAVINCI_MMC_DATADIR_READ 1 #define DAVINCI_MMC_DATADIR_WRITE 2 unsigned char data_dir; - unsigned char suspended; /* buffer is used during PIO of one scatterlist segment, and * is updated along with buffer_bytes_left. bytes_left applies @@ -1435,38 +1434,23 @@ static int davinci_mmcsd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct mmc_davinci_host *host = platform_get_drvdata(pdev); - int ret; - ret = mmc_suspend_host(host->mmc); - if (!ret) { - writel(0, host->base + DAVINCI_MMCIM); - mmc_davinci_reset_ctrl(host, 1); - clk_disable(host->clk); - host->suspended = 1; - } else { - host->suspended = 0; - } + writel(0, host->base + DAVINCI_MMCIM); + mmc_davinci_reset_ctrl(host, 1); + clk_disable(host->clk); - return ret; + return 0; } static int davinci_mmcsd_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct mmc_davinci_host *host = platform_get_drvdata(pdev); - int ret; - - if (!host->suspended) - return 0; clk_enable(host->clk); - mmc_davinci_reset_ctrl(host, 0); - ret = mmc_resume_host(host->mmc); - if (!ret) - host->suspended = 0; - return ret; + return 0; } static const struct dev_pm_ops davinci_mmcsd_pm = { diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 866edef2e820..3423c5ed50c7 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -14,8 +14,10 @@ #include <linux/clk.h> #include <linux/mmc/host.h> #include <linux/mmc/dw_mmc.h> +#include <linux/mmc/mmc.h> #include <linux/of.h> #include <linux/of_gpio.h> +#include <linux/slab.h> #include "dw_mmc.h" #include "dw_mmc-pltfm.h" @@ -30,15 +32,39 @@ #define SDMMC_CLKSEL_TIMING(x, y, z) (SDMMC_CLKSEL_CCLK_SAMPLE(x) | \ SDMMC_CLKSEL_CCLK_DRIVE(y) | \ SDMMC_CLKSEL_CCLK_DIVIDER(z)) +#define SDMMC_CLKSEL_WAKEUP_INT BIT(11) #define EXYNOS4210_FIXED_CIU_CLK_DIV 2 #define EXYNOS4412_FIXED_CIU_CLK_DIV 4 +/* Block number in eMMC */ +#define DWMCI_BLOCK_NUM 0xFFFFFFFF + +#define SDMMC_EMMCP_BASE 0x1000 +#define SDMMC_MPSECURITY (SDMMC_EMMCP_BASE + 0x0010) +#define SDMMC_MPSBEGIN0 (SDMMC_EMMCP_BASE + 0x0200) +#define SDMMC_MPSEND0 (SDMMC_EMMCP_BASE + 0x0204) +#define SDMMC_MPSCTRL0 (SDMMC_EMMCP_BASE + 0x020C) + +/* SMU control bits */ +#define DWMCI_MPSCTRL_SECURE_READ_BIT BIT(7) +#define DWMCI_MPSCTRL_SECURE_WRITE_BIT BIT(6) +#define DWMCI_MPSCTRL_NON_SECURE_READ_BIT BIT(5) +#define DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT BIT(4) +#define DWMCI_MPSCTRL_USE_FUSE_KEY BIT(3) +#define DWMCI_MPSCTRL_ECB_MODE BIT(2) +#define DWMCI_MPSCTRL_ENCRYPTION BIT(1) +#define DWMCI_MPSCTRL_VALID BIT(0) + +#define EXYNOS_CCLKIN_MIN 50000000 /* unit: HZ */ + /* Variations in Exynos specific dw-mshc controller */ enum dw_mci_exynos_type { DW_MCI_TYPE_EXYNOS4210, DW_MCI_TYPE_EXYNOS4412, DW_MCI_TYPE_EXYNOS5250, + DW_MCI_TYPE_EXYNOS5420, + DW_MCI_TYPE_EXYNOS5420_SMU, }; /* Exynos implementation specific driver private data */ @@ -47,6 +73,7 @@ struct dw_mci_exynos_priv_data { u8 ciu_div; u32 sdr_timing; u32 ddr_timing; + u32 cur_speed; }; static struct dw_mci_exynos_compatible { @@ -62,43 +89,83 @@ static struct dw_mci_exynos_compatible { }, { .compatible = "samsung,exynos5250-dw-mshc", .ctrl_type = DW_MCI_TYPE_EXYNOS5250, + }, { + .compatible = "samsung,exynos5420-dw-mshc", + .ctrl_type = DW_MCI_TYPE_EXYNOS5420, + }, { + .compatible = "samsung,exynos5420-dw-mshc-smu", + .ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU, }, }; static int dw_mci_exynos_priv_init(struct dw_mci *host) { - struct dw_mci_exynos_priv_data *priv; - int idx; - - priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(host->dev, "mem alloc failed for private data\n"); - return -ENOMEM; - } + struct dw_mci_exynos_priv_data *priv = host->priv; - for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) { - if (of_device_is_compatible(host->dev->of_node, - exynos_compat[idx].compatible)) - priv->ctrl_type = exynos_compat[idx].ctrl_type; + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) { + mci_writel(host, MPSBEGIN0, 0); + mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM); + mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT | + DWMCI_MPSCTRL_NON_SECURE_READ_BIT | + DWMCI_MPSCTRL_VALID | + DWMCI_MPSCTRL_NON_SECURE_WRITE_BIT); } - host->priv = priv; return 0; } static int dw_mci_exynos_setup_clock(struct dw_mci *host) { struct dw_mci_exynos_priv_data *priv = host->priv; + unsigned long rate = clk_get_rate(host->ciu_clk); - if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250) - host->bus_hz /= (priv->ciu_div + 1); - else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412) - host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV; - else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210) - host->bus_hz /= EXYNOS4210_FIXED_CIU_CLK_DIV; + host->bus_hz = rate / (priv->ciu_div + 1); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int dw_mci_exynos_suspend(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + return dw_mci_suspend(host); +} + +static int dw_mci_exynos_resume(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + + dw_mci_exynos_priv_init(host); + return dw_mci_resume(host); +} + +/** + * dw_mci_exynos_resume_noirq - Exynos-specific resume code + * + * On exynos5420 there is a silicon errata that will sometimes leave the + * WAKEUP_INT bit in the CLKSEL register asserted. This bit is 1 to indicate + * that it fired and we can clear it by writing a 1 back. Clear it to prevent + * interrupts from going off constantly. + * + * We run this code on all exynos variants because it doesn't hurt. + */ + +static int dw_mci_exynos_resume_noirq(struct device *dev) +{ + struct dw_mci *host = dev_get_drvdata(dev); + u32 clksel; + + clksel = mci_readl(host, CLKSEL); + if (clksel & SDMMC_CLKSEL_WAKEUP_INT) + mci_writel(host, CLKSEL, clksel); return 0; } +#else +#define dw_mci_exynos_suspend NULL +#define dw_mci_exynos_resume NULL +#define dw_mci_exynos_resume_noirq NULL +#endif /* CONFIG_PM_SLEEP */ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) { @@ -116,23 +183,68 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) { struct dw_mci_exynos_priv_data *priv = host->priv; + unsigned int wanted = ios->clock; + unsigned long actual; + u8 div = priv->ciu_div + 1; - if (ios->timing == MMC_TIMING_UHS_DDR50) + if (ios->timing == MMC_TIMING_UHS_DDR50) { mci_writel(host, CLKSEL, priv->ddr_timing); - else + /* Should be double rate for DDR mode */ + if (ios->bus_width == MMC_BUS_WIDTH_8) + wanted <<= 1; + } else { mci_writel(host, CLKSEL, priv->sdr_timing); + } + + /* Don't care if wanted clock is zero */ + if (!wanted) + return; + + /* Guaranteed minimum frequency for cclkin */ + if (wanted < EXYNOS_CCLKIN_MIN) + wanted = EXYNOS_CCLKIN_MIN; + + if (wanted != priv->cur_speed) { + int ret = clk_set_rate(host->ciu_clk, wanted * div); + if (ret) + dev_warn(host->dev, + "failed to set clk-rate %u error: %d\n", + wanted * div, ret); + actual = clk_get_rate(host->ciu_clk); + host->bus_hz = actual / div; + priv->cur_speed = wanted; + host->current_speed = 0; + } } static int dw_mci_exynos_parse_dt(struct dw_mci *host) { - struct dw_mci_exynos_priv_data *priv = host->priv; + struct dw_mci_exynos_priv_data *priv; struct device_node *np = host->dev->of_node; u32 timing[2]; u32 div = 0; + int idx; int ret; - of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); - priv->ciu_div = div; + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(host->dev, "mem alloc failed for private data\n"); + return -ENOMEM; + } + + for (idx = 0; idx < ARRAY_SIZE(exynos_compat); idx++) { + if (of_device_is_compatible(np, exynos_compat[idx].compatible)) + priv->ctrl_type = exynos_compat[idx].ctrl_type; + } + + if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412) + priv->ciu_div = EXYNOS4412_FIXED_CIU_CLK_DIV - 1; + else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4210) + priv->ciu_div = EXYNOS4210_FIXED_CIU_CLK_DIV - 1; + else { + of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); + priv->ciu_div = div; + } ret = of_property_read_u32_array(np, "samsung,dw-mshc-sdr-timing", timing, 2); @@ -147,9 +259,131 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) return ret; priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); + host->priv = priv; return 0; } +static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host) +{ + return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); +} + +static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) +{ + u32 clksel; + clksel = mci_readl(host, CLKSEL); + clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample); + mci_writel(host, CLKSEL, clksel); +} + +static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) +{ + u32 clksel; + u8 sample; + + clksel = mci_readl(host, CLKSEL); + sample = (clksel + 1) & 0x7; + clksel = (clksel & ~0x7) | sample; + mci_writel(host, CLKSEL, clksel); + return sample; +} + +static s8 dw_mci_exynos_get_best_clksmpl(u8 candiates) +{ + const u8 iter = 8; + u8 __c; + s8 i, loc = -1; + + for (i = 0; i < iter; i++) { + __c = ror8(candiates, i); + if ((__c & 0xc7) == 0xc7) { + loc = i; + goto out; + } + } + + for (i = 0; i < iter; i++) { + __c = ror8(candiates, i); + if ((__c & 0x83) == 0x83) { + loc = i; + goto out; + } + } + +out: + return loc; +} + +static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode, + struct dw_mci_tuning_data *tuning_data) +{ + struct dw_mci *host = slot->host; + struct mmc_host *mmc = slot->mmc; + const u8 *blk_pattern = tuning_data->blk_pattern; + u8 *blk_test; + unsigned int blksz = tuning_data->blksz; + u8 start_smpl, smpl, candiates = 0; + s8 found = -1; + int ret = 0; + + blk_test = kmalloc(blksz, GFP_KERNEL); + if (!blk_test) + return -ENOMEM; + + start_smpl = dw_mci_exynos_get_clksmpl(host); + + do { + struct mmc_request mrq = {NULL}; + struct mmc_command cmd = {0}; + struct mmc_command stop = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + + cmd.opcode = opcode; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + + data.blksz = blksz; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, blk_test, blksz); + mrq.cmd = &cmd; + mrq.stop = &stop; + mrq.data = &data; + host->mrq = &mrq; + + mci_writel(host, TMOUT, ~0); + smpl = dw_mci_exynos_move_next_clksmpl(host); + + mmc_wait_for_req(mmc, &mrq); + + if (!cmd.error && !data.error) { + if (!memcmp(blk_pattern, blk_test, blksz)) + candiates |= (1 << smpl); + } else { + dev_dbg(host->dev, + "Tuning error: cmd.error:%d, data.error:%d\n", + cmd.error, data.error); + } + } while (start_smpl != smpl); + + found = dw_mci_exynos_get_best_clksmpl(candiates); + if (found >= 0) + dw_mci_exynos_set_clksmpl(host, found); + else + ret = -EIO; + + kfree(blk_test); + return ret; +} + /* Common capabilities of Exynos4/Exynos5 SoC */ static unsigned long exynos_dwmmc_caps[4] = { MMC_CAP_UHS_DDR50 | MMC_CAP_1_8V_DDR | @@ -166,6 +400,7 @@ static const struct dw_mci_drv_data exynos_drv_data = { .prepare_command = dw_mci_exynos_prepare_command, .set_ios = dw_mci_exynos_set_ios, .parse_dt = dw_mci_exynos_parse_dt, + .execute_tuning = dw_mci_exynos_execute_tuning, }; static const struct of_device_id dw_mci_exynos_match[] = { @@ -173,6 +408,10 @@ static const struct of_device_id dw_mci_exynos_match[] = { .data = &exynos_drv_data, }, { .compatible = "samsung,exynos5250-dw-mshc", .data = &exynos_drv_data, }, + { .compatible = "samsung,exynos5420-dw-mshc", + .data = &exynos_drv_data, }, + { .compatible = "samsung,exynos5420-dw-mshc-smu", + .data = &exynos_drv_data, }, {}, }; MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); @@ -187,13 +426,20 @@ static int dw_mci_exynos_probe(struct platform_device *pdev) return dw_mci_pltfm_register(pdev, drv_data); } +const struct dev_pm_ops dw_mci_exynos_pmops = { + SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume) + .resume_noirq = dw_mci_exynos_resume_noirq, + .thaw_noirq = dw_mci_exynos_resume_noirq, + .restore_noirq = dw_mci_exynos_resume_noirq, +}; + static struct platform_driver dw_mci_exynos_pltfm_driver = { .probe = dw_mci_exynos_probe, .remove = __exit_p(dw_mci_pltfm_remove), .driver = { .name = "dwmmc_exynos", .of_match_table = dw_mci_exynos_match, - .pm = &dw_mci_pltfm_pmops, + .pm = &dw_mci_exynos_pmops, }, }; diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index b456b0c35231..f70546a3a7cc 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -59,7 +59,9 @@ static int dw_mci_pci_probe(struct pci_dev *pdev, if (ret) return ret; - host->regs = pcim_iomap_table(pdev)[0]; + host->regs = pcim_iomap_table(pdev)[PCI_BAR_NO]; + + pci_set_master(pdev); ret = dw_mci_probe(host); if (ret) diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index ee525565aa77..5c4965655297 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -23,6 +23,7 @@ #include <linux/of.h> #include "dw_mmc.h" +#include "dw_mmc-pltfm.h" static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr) { @@ -38,7 +39,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev, { struct dw_mci *host; struct resource *regs; - int ret; host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); if (!host) @@ -58,12 +58,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev, if (IS_ERR(host->regs)) return PTR_ERR(host->regs); - if (drv_data && drv_data->init) { - ret = drv_data->init(host); - if (ret) - return ret; - } - platform_set_drvdata(pdev, host); return dw_mci_probe(host); } diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c index 14b5961a851c..3e8e53ae3302 100644 --- a/drivers/mmc/host/dw_mmc-socfpga.c +++ b/drivers/mmc/host/dw_mmc-socfpga.c @@ -38,21 +38,6 @@ struct dw_mci_socfpga_priv_data { static int dw_mci_socfpga_priv_init(struct dw_mci *host) { - struct dw_mci_socfpga_priv_data *priv; - - priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(host->dev, "mem alloc failed for private data\n"); - return -ENOMEM; - } - - priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); - if (IS_ERR(priv->sysreg)) { - dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n"); - return PTR_ERR(priv->sysreg); - } - host->priv = priv; - return 0; } @@ -79,12 +64,24 @@ static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr) static int dw_mci_socfpga_parse_dt(struct dw_mci *host) { - struct dw_mci_socfpga_priv_data *priv = host->priv; + struct dw_mci_socfpga_priv_data *priv; struct device_node *np = host->dev->of_node; u32 timing[2]; u32 div = 0; int ret; + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(host->dev, "mem alloc failed for private data\n"); + return -ENOMEM; + } + + priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr"); + if (IS_ERR(priv->sysreg)) { + dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n"); + return PTR_ERR(priv->sysreg); + } + ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div); if (ret) dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1"); @@ -96,6 +93,7 @@ static int dw_mci_socfpga_parse_dt(struct dw_mci *host) return ret; priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]); + host->priv = priv; return 0; } @@ -113,7 +111,7 @@ static const struct of_device_id dw_mci_socfpga_match[] = { }; MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match); -int dw_mci_socfpga_probe(struct platform_device *pdev) +static int dw_mci_socfpga_probe(struct platform_device *pdev) { const struct dw_mci_drv_data *drv_data; const struct of_device_id *match; @@ -128,7 +126,7 @@ static struct platform_driver dw_mci_socfpga_pltfm_driver = { .remove = __exit_p(dw_mci_pltfm_remove), .driver = { .name = "dwmmc_socfpga", - .of_match_table = of_match_ptr(dw_mci_socfpga_match), + .of_match_table = dw_mci_socfpga_match, .pm = &dw_mci_pltfm_pmops, }, }; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 542407363dd2..4bce0deec362 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -29,6 +29,7 @@ #include <linux/irq.h> #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> +#include <linux/mmc/sdio.h> #include <linux/mmc/dw_mmc.h> #include <linux/bitops.h> #include <linux/regulator/consumer.h> @@ -50,6 +51,9 @@ #define DW_MCI_RECV_STATUS 2 #define DW_MCI_DMA_THRESHOLD 16 +#define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ +#define DW_MCI_FREQ_MIN 400000 /* unit: HZ */ + #ifdef CONFIG_MMC_DW_IDMAC #define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ @@ -76,42 +80,39 @@ struct idmac_desc { }; #endif /* CONFIG_MMC_DW_IDMAC */ -/** - * struct dw_mci_slot - MMC slot state - * @mmc: The mmc_host representing this slot. - * @host: The MMC controller this slot is using. - * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX) - * @wp_gpio: If gpio_is_valid() we'll use this to read write protect. - * @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. - * @flags: Random state bits associated with the slot. - * @id: Number of this slot. - * @last_detect_state: Most recently observed card detect state. - */ -struct dw_mci_slot { - struct mmc_host *mmc; - struct dw_mci *host; - - int quirks; - int wp_gpio; - - u32 ctype; - - struct mmc_request *mrq; - struct list_head queue_node; +static const u8 tuning_blk_pattern_4bit[] = { + 0xff, 0x0f, 0xff, 0x00, 0xff, 0xcc, 0xc3, 0xcc, + 0xc3, 0x3c, 0xcc, 0xff, 0xfe, 0xff, 0xfe, 0xef, + 0xff, 0xdf, 0xff, 0xdd, 0xff, 0xfb, 0xff, 0xfb, + 0xbf, 0xff, 0x7f, 0xff, 0x77, 0xf7, 0xbd, 0xef, + 0xff, 0xf0, 0xff, 0xf0, 0x0f, 0xfc, 0xcc, 0x3c, + 0xcc, 0x33, 0xcc, 0xcf, 0xff, 0xef, 0xff, 0xee, + 0xff, 0xfd, 0xff, 0xfd, 0xdf, 0xff, 0xbf, 0xff, + 0xbb, 0xff, 0xf7, 0xff, 0xf7, 0x7f, 0x7b, 0xde, +}; - unsigned int clock; - unsigned long flags; -#define DW_MMC_CARD_PRESENT 0 -#define DW_MMC_CARD_NEED_INIT 1 - int id; - int last_detect_state; +static const u8 tuning_blk_pattern_8bit[] = { + 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, 0xcc, + 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, 0xff, + 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, 0xff, + 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, 0xdd, + 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, + 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, 0xff, + 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, 0xff, + 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xff, 0xff, 0xcc, 0xcc, 0xcc, 0x33, 0xcc, + 0xcc, 0xcc, 0x33, 0x33, 0xcc, 0xcc, 0xcc, 0xff, + 0xff, 0xff, 0xee, 0xff, 0xff, 0xff, 0xee, 0xee, + 0xff, 0xff, 0xff, 0xdd, 0xff, 0xff, 0xff, 0xdd, + 0xdd, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff, 0xff, + 0xbb, 0xbb, 0xff, 0xff, 0xff, 0x77, 0xff, 0xff, + 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, }; +static inline bool dw_mci_fifo_reset(struct dw_mci *host); +static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host); + #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) { @@ -249,10 +250,15 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr = cmd->opcode; - if (cmdr == MMC_STOP_TRANSMISSION) + if (cmd->opcode == MMC_STOP_TRANSMISSION || + cmd->opcode == MMC_GO_IDLE_STATE || + cmd->opcode == MMC_GO_INACTIVE_STATE || + (cmd->opcode == SD_IO_RW_DIRECT && + ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT)) cmdr |= SDMMC_CMD_STOP; else - cmdr |= SDMMC_CMD_PRV_DAT_WAIT; + if (cmd->opcode != MMC_SEND_STATUS && cmd->data) + cmdr |= SDMMC_CMD_PRV_DAT_WAIT; if (cmd->flags & MMC_RSP_PRESENT) { /* We expect a response, so set this bit */ @@ -279,6 +285,40 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) return cmdr; } +static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) +{ + struct mmc_command *stop; + u32 cmdr; + + if (!cmd->data) + return 0; + + stop = &host->stop_abort; + cmdr = cmd->opcode; + memset(stop, 0, sizeof(struct mmc_command)); + + if (cmdr == MMC_READ_SINGLE_BLOCK || + cmdr == MMC_READ_MULTIPLE_BLOCK || + cmdr == MMC_WRITE_BLOCK || + cmdr == MMC_WRITE_MULTIPLE_BLOCK) { + stop->opcode = MMC_STOP_TRANSMISSION; + stop->arg = 0; + stop->flags = MMC_RSP_R1B | MMC_CMD_AC; + } else if (cmdr == SD_IO_RW_EXTENDED) { + stop->opcode = SD_IO_RW_DIRECT; + stop->arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) | + ((cmd->arg >> 28) & 0x7); + stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; + } else { + return 0; + } + + cmdr = stop->opcode | SDMMC_CMD_STOP | + SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; + + return cmdr; +} + static void dw_mci_start_command(struct dw_mci *host, struct mmc_command *cmd, u32 cmd_flags) { @@ -293,9 +333,10 @@ static void dw_mci_start_command(struct dw_mci *host, mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); } -static void send_stop_cmd(struct dw_mci *host, struct mmc_data *data) +static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) { - dw_mci_start_command(host, data->stop, host->stop_cmdr); + struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort; + dw_mci_start_command(host, stop, host->stop_cmdr); } /* DMA interface functions */ @@ -304,10 +345,10 @@ static void dw_mci_stop_dma(struct dw_mci *host) if (host->using_dma) { host->dma_ops->stop(host); host->dma_ops->cleanup(host); - } else { - /* Data transfer was stopped by the interrupt handler */ - set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } + + /* Data transfer was stopped by the interrupt handler */ + set_bit(EVENT_XFER_COMPLETE, &host->pending_events); } static int dw_mci_get_dma_dir(struct mmc_data *data) @@ -331,6 +372,14 @@ static void dw_mci_dma_cleanup(struct dw_mci *host) dw_mci_get_dma_dir(data)); } +static void dw_mci_idmac_reset(struct dw_mci *host) +{ + u32 bmod = mci_readl(host, BMOD); + /* Software reset of DMA */ + bmod |= SDMMC_IDMAC_SWRESET; + mci_writel(host, BMOD, bmod); +} + static void dw_mci_idmac_stop_dma(struct dw_mci *host) { u32 temp; @@ -344,6 +393,7 @@ static void dw_mci_idmac_stop_dma(struct dw_mci *host) /* Stop the IDMAC running */ temp = mci_readl(host, BMOD); temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB); + temp |= SDMMC_IDMAC_SWRESET; mci_writel(host, BMOD, temp); } @@ -435,7 +485,7 @@ static int dw_mci_idmac_init(struct dw_mci *host) p->des3 = host->sg_dma; p->des0 = IDMAC_DES0_ER; - mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET); + dw_mci_idmac_reset(host); /* Mask out interrupts - get Tx & Rx complete only */ mci_writel(host, IDSTS, IDMAC_INT_CLR); @@ -532,6 +582,78 @@ static void dw_mci_post_req(struct mmc_host *mmc, data->host_cookie = 0; } +static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) +{ +#ifdef CONFIG_MMC_DW_IDMAC + unsigned int blksz = data->blksz; + const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256}; + u32 fifo_width = 1 << host->data_shift; + u32 blksz_depth = blksz / fifo_width, fifoth_val; + u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers; + int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1; + + tx_wmark = (host->fifo_depth) / 2; + tx_wmark_invers = host->fifo_depth - tx_wmark; + + /* + * MSIZE is '1', + * if blksz is not a multiple of the FIFO width + */ + if (blksz % fifo_width) { + msize = 0; + rx_wmark = 1; + goto done; + } + + do { + if (!((blksz_depth % mszs[idx]) || + (tx_wmark_invers % mszs[idx]))) { + msize = idx; + rx_wmark = mszs[idx] - 1; + break; + } + } while (--idx > 0); + /* + * If idx is '0', it won't be tried + * Thus, initial values are uesed + */ +done: + fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark); + mci_writel(host, FIFOTH, fifoth_val); +#endif +} + +static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) +{ + unsigned int blksz = data->blksz; + u32 blksz_depth, fifo_depth; + u16 thld_size; + + WARN_ON(!(data->flags & MMC_DATA_READ)); + + if (host->timing != MMC_TIMING_MMC_HS200 && + host->timing != MMC_TIMING_UHS_SDR104) + goto disable; + + blksz_depth = blksz / (1 << host->data_shift); + fifo_depth = host->fifo_depth; + + if (blksz_depth > fifo_depth) + goto disable; + + /* + * If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz' + * If (blksz_depth) < (fifo_depth >> 1), should be thld_size = blksz + * Currently just choose blksz. + */ + thld_size = blksz; + mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1)); + return; + +disable: + mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0)); +} + static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) { int sg_len; @@ -556,6 +678,14 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma, sg_len); + /* + * Decide the MSIZE and RX/TX Watermark. + * If current block size is same with previous size, + * no need to update fifoth. + */ + if (host->prev_blksz != data->blksz) + dw_mci_adjust_fifoth(host, data); + /* Enable the DMA interface */ temp = mci_readl(host, CTRL); temp |= SDMMC_CTRL_DMA_ENABLE; @@ -581,10 +711,12 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->sg = NULL; host->data = data; - if (data->flags & MMC_DATA_READ) + if (data->flags & MMC_DATA_READ) { host->dir_status = DW_MCI_RECV_STATUS; - else + dw_mci_ctrl_rd_thld(host, data); + } else { host->dir_status = DW_MCI_SEND_STATUS; + } if (dw_mci_submit_data_dma(host, data)) { int flags = SG_MITER_ATOMIC; @@ -606,6 +738,21 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) temp = mci_readl(host, CTRL); temp &= ~SDMMC_CTRL_DMA_ENABLE; mci_writel(host, CTRL, temp); + + /* + * Use the initial fifoth_val for PIO mode. + * If next issued data may be transfered by DMA mode, + * prev_blksz should be invalidated. + */ + mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; + } else { + /* + * Keep the current block size. + * It will be used to decide whether to update + * fifoth register next time. + */ + host->prev_blksz = data->blksz; } } @@ -632,24 +779,31 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot->host; + unsigned int clock = slot->clock; u32 div; u32 clk_en_a; - if (slot->clock != host->current_speed || force_clkinit) { - div = host->bus_hz / slot->clock; - if (host->bus_hz % slot->clock && host->bus_hz > slot->clock) + if (!clock) { + mci_writel(host, CLKENA, 0); + mci_send_cmd(slot, + SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); + } else if (clock != host->current_speed || force_clkinit) { + div = host->bus_hz / clock; + if (host->bus_hz % clock && host->bus_hz > clock) /* * move the + 1 after the divide to prevent * over-clocking the card. */ div += 1; - div = (host->bus_hz != slot->clock) ? DIV_ROUND_UP(div, 2) : 0; + div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; - dev_info(&slot->mmc->class_dev, - "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ" - " div = %d)\n", slot->id, host->bus_hz, slot->clock, - div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); + if ((clock << div) != slot->__clk_old || 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, + div ? ((host->bus_hz / div) >> 1) : + host->bus_hz, div); /* disable clock */ mci_writel(host, CLKENA, 0); @@ -676,9 +830,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); - host->current_speed = slot->clock; + /* keep the clock with reflecting clock dividor */ + slot->__clk_old = clock << div; } + host->current_speed = clock; + /* Set the current slot bus width */ mci_writel(host, CTYPE, (slot->ctype << slot->id)); } @@ -700,7 +857,9 @@ static void __dw_mci_start_request(struct dw_mci *host, host->pending_events = 0; host->completed_events = 0; + host->cmd_status = 0; host->data_status = 0; + host->dir_status = 0; data = cmd->data; if (data) { @@ -724,6 +883,8 @@ static void __dw_mci_start_request(struct dw_mci *host, if (mrq->stop) host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); + else + host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); } static void dw_mci_start_request(struct dw_mci *host, @@ -806,14 +967,13 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) regs &= ~((0x1 << slot->id) << 16); mci_writel(slot->host, UHS_REG, regs); + slot->host->timing = ios->timing; - if (ios->clock) { - /* - * Use mirror of ios->clock to prevent race with mmc - * core ios update when finding the minimum. - */ - slot->clock = ios->clock; - } + /* + * Use mirror of ios->clock to prevent race with mmc + * core ios update when finding the minimum. + */ + slot->clock = ios->clock; if (drv_data && drv_data->set_ios) drv_data->set_ios(slot->host, ios); @@ -939,6 +1099,38 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) } } +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; + const struct dw_mci_drv_data *drv_data = host->drv_data; + struct dw_mci_tuning_data tuning_data; + int err = -ENOSYS; + + if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { + tuning_data.blk_pattern = tuning_blk_pattern_8bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_8bit); + } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) { + tuning_data.blk_pattern = tuning_blk_pattern_4bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); + } else { + return -EINVAL; + } + } else if (opcode == MMC_SEND_TUNING_BLOCK) { + tuning_data.blk_pattern = tuning_blk_pattern_4bit; + tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); + } else { + dev_err(host->dev, + "Undefined command(%d) for tuning\n", opcode); + return -EINVAL; + } + + if (drv_data && drv_data->execute_tuning) + err = drv_data->execute_tuning(slot, opcode, &tuning_data); + return err; +} + static const struct mmc_host_ops dw_mci_ops = { .request = dw_mci_request, .pre_req = dw_mci_pre_req, @@ -947,6 +1139,7 @@ static const struct mmc_host_ops dw_mci_ops = { .get_ro = dw_mci_get_ro, .get_cd = dw_mci_get_cd, .enable_sdio_irq = dw_mci_enable_sdio_irq, + .execute_tuning = dw_mci_execute_tuning, }; static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) @@ -978,7 +1171,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) spin_lock(&host->lock); } -static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) +static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) { u32 status = host->cmd_status; @@ -1012,12 +1205,52 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd /* newer ip versions need a delay between retries */ if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY) mdelay(20); + } - if (cmd->data) { - dw_mci_stop_dma(host); - host->data = NULL; + return cmd->error; +} + +static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) +{ + u32 status = host->data_status; + + if (status & DW_MCI_DATA_ERROR_FLAGS) { + if (status & SDMMC_INT_DRTO) { + data->error = -ETIMEDOUT; + } else if (status & SDMMC_INT_DCRC) { + data->error = -EILSEQ; + } else if (status & SDMMC_INT_EBE) { + if (host->dir_status == + DW_MCI_SEND_STATUS) { + /* + * No data CRC status was returned. + * The number of bytes transferred + * will be exaggerated in PIO mode. + */ + data->bytes_xfered = 0; + data->error = -ETIMEDOUT; + } else if (host->dir_status == + DW_MCI_RECV_STATUS) { + data->error = -EIO; + } + } else { + /* SDMMC_INT_SBE is included */ + data->error = -EIO; } + + dev_err(host->dev, "data error, status 0x%08x\n", status); + + /* + * After an error, there may be data lingering + * in the FIFO + */ + dw_mci_fifo_reset(host); + } else { + data->bytes_xfered = data->blocks * data->blksz; + data->error = 0; } + + return data->error; } static void dw_mci_tasklet_func(unsigned long priv) @@ -1025,14 +1258,16 @@ static void dw_mci_tasklet_func(unsigned long priv) struct dw_mci *host = (struct dw_mci *)priv; struct mmc_data *data; struct mmc_command *cmd; + struct mmc_request *mrq; enum dw_mci_state state; enum dw_mci_state prev_state; - u32 status, ctrl; + unsigned int err; spin_lock(&host->lock); state = host->state; data = host->data; + mrq = host->mrq; do { prev_state = state; @@ -1049,16 +1284,23 @@ static void dw_mci_tasklet_func(unsigned long priv) cmd = host->cmd; host->cmd = NULL; set_bit(EVENT_CMD_COMPLETE, &host->completed_events); - dw_mci_command_complete(host, cmd); - if (cmd == host->mrq->sbc && !cmd->error) { + err = dw_mci_command_complete(host, cmd); + if (cmd == mrq->sbc && !err) { prev_state = state = STATE_SENDING_CMD; __dw_mci_start_request(host, host->cur_slot, - host->mrq->cmd); + mrq->cmd); goto unlock; } - if (!host->mrq->data || cmd->error) { - dw_mci_request_end(host, host->mrq); + if (cmd->data && err) { + dw_mci_stop_dma(host); + send_stop_abort(host, data); + state = STATE_SENDING_STOP; + break; + } + + if (!cmd->data || err) { + dw_mci_request_end(host, mrq); goto unlock; } @@ -1069,8 +1311,7 @@ static void dw_mci_tasklet_func(unsigned long priv) if (test_and_clear_bit(EVENT_DATA_ERROR, &host->pending_events)) { dw_mci_stop_dma(host); - if (data->stop) - send_stop_cmd(host, data); + send_stop_abort(host, data); state = STATE_DATA_ERROR; break; } @@ -1090,60 +1331,27 @@ static void dw_mci_tasklet_func(unsigned long priv) host->data = NULL; set_bit(EVENT_DATA_COMPLETE, &host->completed_events); - status = host->data_status; - - if (status & DW_MCI_DATA_ERROR_FLAGS) { - if (status & SDMMC_INT_DRTO) { - data->error = -ETIMEDOUT; - } else if (status & SDMMC_INT_DCRC) { - data->error = -EILSEQ; - } else if (status & SDMMC_INT_EBE && - host->dir_status == - DW_MCI_SEND_STATUS) { - /* - * No data CRC status was returned. - * The number of bytes transferred will - * be exaggerated in PIO mode. - */ - data->bytes_xfered = 0; - data->error = -ETIMEDOUT; - } else { - dev_err(host->dev, - "data FIFO error " - "(status=%08x)\n", - status); - data->error = -EIO; - } - /* - * After an error, there may be data lingering - * in the FIFO, so reset it - doing so - * generates a block interrupt, hence setting - * the scatter-gather pointer to NULL. - */ - sg_miter_stop(&host->sg_miter); - host->sg = NULL; - ctrl = mci_readl(host, CTRL); - ctrl |= SDMMC_CTRL_FIFO_RESET; - mci_writel(host, CTRL, ctrl); - } else { - data->bytes_xfered = data->blocks * data->blksz; - data->error = 0; - } + err = dw_mci_data_complete(host, data); - if (!data->stop) { - dw_mci_request_end(host, host->mrq); - goto unlock; - } + if (!err) { + if (!data->stop || mrq->sbc) { + if (mrq->sbc) + data->stop->error = 0; + dw_mci_request_end(host, mrq); + goto unlock; + } - if (host->mrq->sbc && !data->error) { - data->stop->error = 0; - dw_mci_request_end(host, host->mrq); - goto unlock; + /* stop command for open-ended transfer*/ + if (data->stop) + send_stop_abort(host, data); } + /* + * If err has non-zero, + * stop-abort command has been already issued. + */ prev_state = state = STATE_SENDING_STOP; - if (!data->error) - send_stop_cmd(host, data); + /* fall through */ case STATE_SENDING_STOP: @@ -1151,9 +1359,19 @@ static void dw_mci_tasklet_func(unsigned long priv) &host->pending_events)) break; + /* CMD error in data command */ + if (mrq->cmd->error && mrq->data) + dw_mci_fifo_reset(host); + host->cmd = NULL; - dw_mci_command_complete(host, host->mrq->stop); - dw_mci_request_end(host, host->mrq); + host->data = NULL; + + if (mrq->stop) + dw_mci_command_complete(host, mrq->stop); + else + host->cmd_status = 0; + + dw_mci_request_end(host, mrq); goto unlock; case STATE_DATA_ERROR: @@ -1601,18 +1819,17 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) pending = mci_readl(host, MINTSTS); /* read-only mask reg */ - if (pending) { - - /* - * DTO fix - version 2.10a and below, and only if internal DMA - * is configured. - */ - if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { - if (!pending && - ((mci_readl(host, STATUS) >> 17) & 0x1fff)) - pending |= SDMMC_INT_DATA_OVER; - } + /* + * DTO fix - version 2.10a and below, and only if internal DMA + * is configured. + */ + if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { + if (!pending && + ((mci_readl(host, STATUS) >> 17) & 0x1fff)) + pending |= SDMMC_INT_DATA_OVER; + } + if (pending) { if (pending & DW_MCI_CMD_ERROR_FLAGS) { mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); host->cmd_status = pending; @@ -1698,7 +1915,6 @@ static void dw_mci_work_routine_card(struct work_struct *work) struct mmc_host *mmc = slot->mmc; struct mmc_request *mrq; int present; - u32 ctrl; present = dw_mci_get_cd(mmc); while (present != slot->last_detect_state) { @@ -1737,11 +1953,10 @@ static void dw_mci_work_routine_card(struct work_struct *work) case STATE_DATA_ERROR: if (mrq->data->error == -EINPROGRESS) mrq->data->error = -ENOMEDIUM; - if (!mrq->stop) - break; /* fall through */ case STATE_SENDING_STOP: - mrq->stop->error = -ENOMEDIUM; + if (mrq->stop) + mrq->stop->error = -ENOMEDIUM; break; } @@ -1764,23 +1979,10 @@ static void dw_mci_work_routine_card(struct work_struct *work) if (present == 0) { clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - /* - * Clear down the FIFO - doing so generates a - * block interrupt, hence setting the - * scatter-gather pointer to NULL. - */ - sg_miter_stop(&host->sg_miter); - host->sg = NULL; - - ctrl = mci_readl(host, CTRL); - ctrl |= SDMMC_CTRL_FIFO_RESET; - mci_writel(host, CTRL, ctrl); - + /* Clear down the FIFO */ + dw_mci_fifo_reset(host); #ifdef CONFIG_MMC_DW_IDMAC - ctrl = mci_readl(host, BMOD); - /* Software reset of DMA */ - ctrl |= SDMMC_IDMAC_SWRESET; - mci_writel(host, BMOD, ctrl); + dw_mci_idmac_reset(host); #endif } @@ -1902,6 +2104,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) struct dw_mci_slot *slot; const struct dw_mci_drv_data *drv_data = host->drv_data; int ctrl_id, ret; + u32 freq[2]; u8 bus_width; mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); @@ -1917,8 +2120,14 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id); mmc->ops = &dw_mci_ops; - mmc->f_min = DIV_ROUND_UP(host->bus_hz, 510); - mmc->f_max = host->bus_hz; + if (of_property_read_u32_array(host->dev->of_node, + "clock-freq-min-max", freq, 2)) { + mmc->f_min = DW_MCI_FREQ_MIN; + mmc->f_max = DW_MCI_FREQ_MAX; + } else { + mmc->f_min = freq[0]; + mmc->f_max = freq[1]; + } if (host->pdata->get_ocr) mmc->ocr_avail = host->pdata->get_ocr(id); @@ -1965,9 +2174,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) mmc->caps |= MMC_CAP_4_BIT_DATA; } - if (host->pdata->quirks & DW_MCI_QUIRK_HIGHSPEED) - mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; - if (host->pdata->blk_settings) { mmc->max_segs = host->pdata->blk_settings->max_segs; mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; @@ -2009,12 +2215,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) /* Card initially undetected */ slot->last_detect_state = 0; - /* - * Card may have been plugged in prior to boot so we - * need to run the detect tasklet - */ - queue_work(host->card_workqueue, &host->card_work); - return 0; err_setup_bus: @@ -2075,36 +2275,57 @@ no_dma: return; } -static bool mci_wait_reset(struct device *dev, struct dw_mci *host) +static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) { unsigned long timeout = jiffies + msecs_to_jiffies(500); - unsigned int ctrl; + u32 ctrl; - mci_writel(host, CTRL, (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | - SDMMC_CTRL_DMA_RESET)); + ctrl = mci_readl(host, CTRL); + ctrl |= reset; + mci_writel(host, CTRL, ctrl); /* wait till resets clear */ do { ctrl = mci_readl(host, CTRL); - if (!(ctrl & (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | - SDMMC_CTRL_DMA_RESET))) + if (!(ctrl & reset)) return true; } while (time_before(jiffies, timeout)); - dev_err(dev, "Timeout resetting block (ctrl %#x)\n", ctrl); + dev_err(host->dev, + "Timeout resetting block (ctrl reset %#x)\n", + ctrl & reset); return false; } +static inline bool dw_mci_fifo_reset(struct dw_mci *host) +{ + /* + * Reseting generates a block interrupt, hence setting + * the scatter-gather pointer to NULL. + */ + if (host->sg) { + sg_miter_stop(&host->sg_miter); + host->sg = NULL; + } + + return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET); +} + +static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host) +{ + return dw_mci_ctrl_reset(host, + SDMMC_CTRL_FIFO_RESET | + SDMMC_CTRL_RESET | + SDMMC_CTRL_DMA_RESET); +} + #ifdef CONFIG_OF static struct dw_mci_of_quirks { char *quirk; int id; } of_quirks[] = { { - .quirk = "supports-highspeed", - .id = DW_MCI_QUIRK_HIGHSPEED, - }, { .quirk = "broken-cd", .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, }, @@ -2159,6 +2380,15 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) if (of_find_property(np, "enable-sdio-wakeup", NULL)) pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; + if (of_find_property(np, "supports-highspeed", NULL)) + pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + + if (of_find_property(np, "caps2-mmc-hs200-1_8v", NULL)) + pdata->caps2 |= MMC_CAP2_HS200_1_8V_SDR; + + if (of_find_property(np, "caps2-mmc-hs200-1_2v", NULL)) + pdata->caps2 |= MMC_CAP2_HS200_1_2V_SDR; + return pdata; } @@ -2222,6 +2452,15 @@ int dw_mci_probe(struct dw_mci *host) host->bus_hz = clk_get_rate(host->ciu_clk); } + if (drv_data && drv_data->init) { + ret = drv_data->init(host); + if (ret) { + dev_err(host->dev, + "implementation specific init failed\n"); + goto err_clk_ciu; + } + } + if (drv_data && drv_data->setup_clock) { ret = drv_data->setup_clock(host); if (ret) { @@ -2288,7 +2527,7 @@ int dw_mci_probe(struct dw_mci *host) } /* Reset all blocks */ - if (!mci_wait_reset(host->dev, host)) + if (!dw_mci_ctrl_all_reset(host)) return -ENODEV; host->dma_ops = host->pdata->dma_ops; @@ -2318,8 +2557,8 @@ int dw_mci_probe(struct dw_mci *host) fifo_size = host->pdata->fifo_depth; } host->fifo_depth = fifo_size; - host->fifoth_val = ((0x2 << 28) | ((fifo_size/2 - 1) << 16) | - ((fifo_size/2) << 0)); + host->fifoth_val = + SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2); mci_writel(host, FIFOTH, host->fifoth_val); /* disable clock to CIU */ @@ -2457,23 +2696,6 @@ EXPORT_SYMBOL(dw_mci_remove); */ int dw_mci_suspend(struct dw_mci *host) { - int i, ret = 0; - - for (i = 0; i < host->num_slots; i++) { - struct dw_mci_slot *slot = host->slot[i]; - if (!slot) - continue; - ret = mmc_suspend_host(slot->mmc); - if (ret < 0) { - while (--i >= 0) { - slot = host->slot[i]; - if (slot) - mmc_resume_host(host->slot[i]->mmc); - } - return ret; - } - } - if (host->vmmc) regulator_disable(host->vmmc); @@ -2494,7 +2716,7 @@ int dw_mci_resume(struct dw_mci *host) } } - if (!mci_wait_reset(host->dev, host)) { + if (!dw_mci_ctrl_all_reset(host)) { ret = -ENODEV; return ret; } @@ -2502,8 +2724,15 @@ int dw_mci_resume(struct dw_mci *host) if (host->use_dma && host->dma_ops->init) host->dma_ops->init(host); - /* Restore the old value at FIFOTH register */ + /* + * Restore the initial value at FIFOTH register + * And Invalidate the prev_blksz with zero + */ mci_writel(host, FIFOTH, host->fifoth_val); + host->prev_blksz = 0; + + /* Put in max timeout */ + mci_writel(host, TMOUT, 0xFFFFFFFF); mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | @@ -2519,10 +2748,6 @@ int dw_mci_resume(struct dw_mci *host) dw_mci_set_ios(slot->mmc, &slot->mmc->ios); dw_mci_setup_bus(slot, true); } - - ret = mmc_resume_host(host->slot[i]->mmc); - if (ret < 0) - return ret; } return 0; } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 81b29941c5b9..6bf24ab917e6 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -53,6 +53,7 @@ #define SDMMC_IDINTEN 0x090 #define SDMMC_DSCADDR 0x094 #define SDMMC_BUFADDR 0x098 +#define SDMMC_CDTHRCTL 0x100 #define SDMMC_DATA(x) (x) /* @@ -128,6 +129,10 @@ #define SDMMC_CMD_INDX(n) ((n) & 0x1F) /* Status register defines */ #define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF) +/* FIFOTH register defines */ +#define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \ + ((r) & 0xFFF) << 16 | \ + ((t) & 0xFFF)) /* Internal DMAC interrupt defines */ #define SDMMC_IDMAC_INT_AI BIT(9) #define SDMMC_IDMAC_INT_NI BIT(8) @@ -142,6 +147,8 @@ #define SDMMC_IDMAC_SWRESET BIT(0) /* Version ID register define */ #define SDMMC_GET_VERID(x) ((x) & 0xFFFF) +/* Card read threshold */ +#define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x)) /* Register access macros */ #define mci_readl(dev, reg) \ @@ -184,6 +191,52 @@ extern int dw_mci_resume(struct dw_mci *host); #endif /** + * struct dw_mci_slot - MMC slot state + * @mmc: The mmc_host representing this slot. + * @host: The MMC controller this slot is using. + * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX) + * @wp_gpio: If gpio_is_valid() we'll use this to read write protect. + * @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 updated clock with reflecting clock divider. + * Keeping track of this helps us to avoid spamming the console + * with CONFIG_MMC_CLKGATE. + * @flags: Random state bits associated with the slot. + * @id: Number of this slot. + * @last_detect_state: Most recently observed card detect state. + */ +struct dw_mci_slot { + struct mmc_host *mmc; + struct dw_mci *host; + + int quirks; + int wp_gpio; + + 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 + int id; + int last_detect_state; +}; + +struct dw_mci_tuning_data { + const u8 *blk_pattern; + unsigned int blksz; +}; + +/** * dw_mci driver data - dw-mshc implementation specific driver data. * @caps: mmc subsystem specified capabilities of the controller(s). * @init: early implementation specific initialization. @@ -203,5 +256,7 @@ struct dw_mci_drv_data { void (*prepare_command)(struct dw_mci *host, u32 *cmdr); 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, + struct dw_mci_tuning_data *tuning_data); }; #endif /* _DW_MMC_H_ */ diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 0308c9f1cf52..de2139cf3444 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -713,7 +713,7 @@ static int jz4740_mmc_request_gpios(struct mmc_host *mmc, mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; if (gpio_is_valid(pdata->gpio_card_detect)) { - ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect); + ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect, 0); if (ret) return ret; } @@ -783,9 +783,8 @@ static int jz4740_mmc_probe(struct platform_device* pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->base = devm_ioremap_resource(&pdev->dev, res); - if (!host->base) { - ret = -EBUSY; - dev_err(&pdev->dev, "Failed to ioremap base memory\n"); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); goto err_free_host; } @@ -881,8 +880,6 @@ static int jz4740_mmc_suspend(struct device *dev) { struct jz4740_mmc_host *host = dev_get_drvdata(dev); - mmc_suspend_host(host->mmc); - jz_gpio_bulk_suspend(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); return 0; @@ -894,8 +891,6 @@ static int jz4740_mmc_resume(struct device *dev) jz_gpio_bulk_resume(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); - mmc_resume_host(host->mmc); - return 0; } diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 74145d1d51f5..0a87e5691341 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -36,6 +36,7 @@ #include <linux/mmc/host.h> #include <linux/mmc/mmc.h> /* for R1_SPI_* bit values */ +#include <linux/mmc/slot-gpio.h> #include <linux/spi/spi.h> #include <linux/spi/mmc_spi.h> @@ -1272,33 +1273,11 @@ static void mmc_spi_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } -static int mmc_spi_get_ro(struct mmc_host *mmc) -{ - struct mmc_spi_host *host = mmc_priv(mmc); - - if (host->pdata && host->pdata->get_ro) - return !!host->pdata->get_ro(mmc->parent); - /* - * Board doesn't support read only detection; let the mmc core - * decide what to do. - */ - return -ENOSYS; -} - -static int mmc_spi_get_cd(struct mmc_host *mmc) -{ - struct mmc_spi_host *host = mmc_priv(mmc); - - if (host->pdata && host->pdata->get_cd) - return !!host->pdata->get_cd(mmc->parent); - return -ENOSYS; -} - static const struct mmc_host_ops mmc_spi_ops = { .request = mmc_spi_request, .set_ios = mmc_spi_set_ios, - .get_ro = mmc_spi_get_ro, - .get_cd = mmc_spi_get_cd, + .get_ro = mmc_gpio_get_ro, + .get_cd = mmc_gpio_get_cd, }; @@ -1324,6 +1303,7 @@ static int mmc_spi_probe(struct spi_device *spi) struct mmc_host *mmc; struct mmc_spi_host *host; int status; + bool has_ro = false; /* We rely on full duplex transfers, mostly to reduce * per-transfer overheads (by making fewer transfers). @@ -1448,18 +1428,33 @@ static int mmc_spi_probe(struct spi_device *spi) } /* pass platform capabilities, if any */ - if (host->pdata) + if (host->pdata) { mmc->caps |= host->pdata->caps; + mmc->caps2 |= host->pdata->caps2; + } status = mmc_add_host(mmc); if (status != 0) goto fail_add_host; + if (host->pdata && host->pdata->flags & MMC_SPI_USE_CD_GPIO) { + status = mmc_gpio_request_cd(mmc, host->pdata->cd_gpio, + host->pdata->cd_debounce); + if (status != 0) + goto fail_add_host; + } + + if (host->pdata && host->pdata->flags & MMC_SPI_USE_RO_GPIO) { + has_ro = true; + status = mmc_gpio_request_ro(mmc, host->pdata->ro_gpio); + if (status != 0) + goto fail_add_host; + } + dev_info(&spi->dev, "SD/MMC host %s%s%s%s%s\n", dev_name(&mmc->class_dev), host->dma_dev ? "" : ", no DMA", - (host->pdata && host->pdata->get_ro) - ? "" : ", no WP", + has_ro ? "" : ", no WP", (host->pdata && host->pdata->setpower) ? "" : ", no poweroff", (mmc->caps & MMC_CAP_NEEDS_POLL) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c3785edc0e92..f32057972dd7 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -62,6 +62,7 @@ static unsigned int fmax = 515633; * @signal_direction: input/out direction of bus signals can be indicated * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock * @busy_detect: true if busy detection on dat0 is supported + * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply */ struct variant_data { unsigned int clkreg; @@ -76,6 +77,7 @@ struct variant_data { bool signal_direction; bool pwrreg_clkgate; bool busy_detect; + bool pwrreg_nopower; }; static struct variant_data variant_arm = { @@ -109,6 +111,7 @@ static struct variant_data variant_u300 = { .pwrreg_powerup = MCI_PWR_ON, .signal_direction = true, .pwrreg_clkgate = true, + .pwrreg_nopower = true, }; static struct variant_data variant_nomadik = { @@ -121,6 +124,7 @@ static struct variant_data variant_nomadik = { .pwrreg_powerup = MCI_PWR_ON, .signal_direction = true, .pwrreg_clkgate = true, + .pwrreg_nopower = true, }; static struct variant_data variant_ux500 = { @@ -135,6 +139,7 @@ static struct variant_data variant_ux500 = { .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, + .pwrreg_nopower = true, }; static struct variant_data variant_ux500v2 = { @@ -150,6 +155,7 @@ static struct variant_data variant_ux500v2 = { .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, + .pwrreg_nopower = true, }; static int mmci_card_busy(struct mmc_host *mmc) @@ -189,6 +195,21 @@ static int mmci_validate_data(struct mmci_host *host, return 0; } +static void mmci_reg_delay(struct mmci_host *host) +{ + /* + * According to the spec, at least three feedback clock cycles + * of max 52 MHz must pass between two writes to the MMCICLOCK reg. + * Three MCLK clock cycles must pass between two MMCIPOWER reg writes. + * Worst delay time during card init is at 100 kHz => 30 us. + * Worst delay time when up and running is at 25 MHz => 120 ns. + */ + if (host->cclk < 25000000) + udelay(30); + else + ndelay(120); +} + /* * This must be called with host->lock held */ @@ -1264,6 +1285,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) mmci_set_clkreg(host, ios->clock); mmci_write_pwrreg(host, pwr); + mmci_reg_delay(host); spin_unlock_irqrestore(&host->lock, flags); @@ -1510,23 +1532,6 @@ static int mmci_probe(struct amba_device *dev, mmc->f_max = min(host->mclk, fmax); dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); - host->pinctrl = devm_pinctrl_get(&dev->dev); - if (IS_ERR(host->pinctrl)) { - ret = PTR_ERR(host->pinctrl); - goto clk_disable; - } - - host->pins_default = pinctrl_lookup_state(host->pinctrl, - PINCTRL_STATE_DEFAULT); - - /* enable pins to be muxed in and configured */ - if (!IS_ERR(host->pins_default)) { - ret = pinctrl_select_state(host->pinctrl, host->pins_default); - if (ret) - dev_warn(&dev->dev, "could not set default pins\n"); - } else - dev_warn(&dev->dev, "could not get default pinstate\n"); - /* Get regulators and the supported OCR mask */ mmc_regulator_get_supply(mmc); if (!mmc->ocr_avail) @@ -1725,41 +1730,67 @@ static int mmci_suspend(struct device *dev) { struct amba_device *adev = to_amba_device(dev); struct mmc_host *mmc = amba_get_drvdata(adev); - int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); - - ret = mmc_suspend_host(mmc); - if (ret == 0) { - pm_runtime_get_sync(dev); - writel(0, host->base + MMCIMASK0); - } + pm_runtime_get_sync(dev); + writel(0, host->base + MMCIMASK0); } - return ret; + return 0; } static int mmci_resume(struct device *dev) { struct amba_device *adev = to_amba_device(dev); struct mmc_host *mmc = amba_get_drvdata(adev); - int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); - writel(MCI_IRQENABLE, host->base + MMCIMASK0); pm_runtime_put(dev); - - ret = mmc_resume_host(mmc); } - return ret; + return 0; } #endif #ifdef CONFIG_PM_RUNTIME +static void mmci_save(struct mmci_host *host) +{ + unsigned long flags; + + if (host->variant->pwrreg_nopower) { + spin_lock_irqsave(&host->lock, flags); + + writel(0, host->base + MMCIMASK0); + writel(0, host->base + MMCIDATACTRL); + writel(0, host->base + MMCIPOWER); + writel(0, host->base + MMCICLOCK); + mmci_reg_delay(host); + + spin_unlock_irqrestore(&host->lock, flags); + } + +} + +static void mmci_restore(struct mmci_host *host) +{ + unsigned long flags; + + if (host->variant->pwrreg_nopower) { + spin_lock_irqsave(&host->lock, flags); + + writel(host->clk_reg, host->base + MMCICLOCK); + writel(host->datactrl_reg, host->base + MMCIDATACTRL); + writel(host->pwr_reg, host->base + MMCIPOWER); + writel(MCI_IRQENABLE, host->base + MMCIMASK0); + mmci_reg_delay(host); + + spin_unlock_irqrestore(&host->lock, flags); + } +} + static int mmci_runtime_suspend(struct device *dev) { struct amba_device *adev = to_amba_device(dev); @@ -1767,6 +1798,8 @@ static int mmci_runtime_suspend(struct device *dev) if (mmc) { struct mmci_host *host = mmc_priv(mmc); + pinctrl_pm_select_sleep_state(dev); + mmci_save(host); clk_disable_unprepare(host->clk); } @@ -1781,6 +1814,8 @@ static int mmci_runtime_resume(struct device *dev) if (mmc) { struct mmci_host *host = mmc_priv(mmc); clk_prepare_enable(host->clk); + mmci_restore(host); + pinctrl_pm_select_default_state(dev); } return 0; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 69080fab6375..168bc72f7a94 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -200,10 +200,6 @@ struct mmci_host { struct sg_mapping_iter sg_miter; unsigned int size; - /* pinctrl handles */ - struct pinctrl *pinctrl; - struct pinctrl_state *pins_default; - #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ struct dma_chan *dma_current; diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index b900de4e7e94..9405ecdaf6cf 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1416,28 +1416,10 @@ ioremap_free: } #ifdef CONFIG_PM -#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ -static void -do_resume_work(struct work_struct *work) -{ - struct msmsdcc_host *host = - container_of(work, struct msmsdcc_host, resume_task); - struct mmc_host *mmc = host->mmc; - - if (mmc) { - mmc_resume_host(mmc); - if (host->stat_irq) - enable_irq(host->stat_irq); - } -} -#endif - - static int msmsdcc_suspend(struct platform_device *dev, pm_message_t state) { struct mmc_host *mmc = mmc_get_drvdata(dev); - int rc = 0; if (mmc) { struct msmsdcc_host *host = mmc_priv(mmc); @@ -1445,14 +1427,11 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state) if (host->stat_irq) disable_irq(host->stat_irq); - if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) - rc = mmc_suspend_host(mmc); - if (!rc) - msmsdcc_writel(host, 0, MMCIMASK0); + msmsdcc_writel(host, 0, MMCIMASK0); if (host->clks_on) msmsdcc_disable_clocks(host, 0); } - return rc; + return 0; } static int @@ -1467,8 +1446,6 @@ msmsdcc_resume(struct platform_device *dev) msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0); - if (mmc->card && mmc->card->type != MMC_TYPE_SDIO) - mmc_resume_host(mmc); if (host->stat_irq) enable_irq(host->stat_irq); #if BUSCLK_PWRSAVE diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 4ddd83f98658..45aa2206741d 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -655,7 +655,7 @@ static const struct mmc_host_ops mvsd_ops = { .enable_sdio_irq = mvsd_enable_sdio_irq, }; -static void __init +static void mv_conf_mbus_windows(struct mvsd_host *host, const struct mbus_dram_target_info *dram) { @@ -677,7 +677,7 @@ mv_conf_mbus_windows(struct mvsd_host *host, } } -static int __init mvsd_probe(struct platform_device *pdev) +static int mvsd_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct mmc_host *mmc = NULL; @@ -757,7 +757,8 @@ static int __init mvsd_probe(struct platform_device *pdev) if (mvsd_data->gpio_card_detect && gpio_is_valid(mvsd_data->gpio_card_detect)) { ret = mmc_gpio_request_cd(mmc, - mvsd_data->gpio_card_detect); + mvsd_data->gpio_card_detect, + 0); if (ret) goto out; } else { @@ -774,9 +775,9 @@ static int __init mvsd_probe(struct platform_device *pdev) spin_lock_init(&host->lock); - host->base = devm_request_and_ioremap(&pdev->dev, r); - if (!host->base) { - ret = -ENOMEM; + host->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); goto out; } @@ -818,7 +819,7 @@ out: return ret; } -static int __exit mvsd_remove(struct platform_device *pdev) +static int mvsd_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); @@ -837,33 +838,6 @@ static int __exit mvsd_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int mvsd_suspend(struct platform_device *dev, pm_message_t state) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_suspend_host(mmc); - - return ret; -} - -static int mvsd_resume(struct platform_device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_resume_host(mmc); - - return ret; -} -#else -#define mvsd_suspend NULL -#define mvsd_resume NULL -#endif - static const struct of_device_id mvsdio_dt_ids[] = { { .compatible = "marvell,orion-sdio" }, { /* sentinel */ } @@ -871,16 +845,15 @@ static const struct of_device_id mvsdio_dt_ids[] = { MODULE_DEVICE_TABLE(of, mvsdio_dt_ids); static struct platform_driver mvsd_driver = { - .remove = __exit_p(mvsd_remove), - .suspend = mvsd_suspend, - .resume = mvsd_resume, + .probe = mvsd_probe, + .remove = mvsd_remove, .driver = { .name = DRIVER_NAME, .of_match_table = mvsdio_dt_ids, }, }; -module_platform_driver_probe(mvsd_driver, mvsd_probe); +module_platform_driver(mvsd_driver); /* maximum card clock frequency (default 50MHz) */ module_param(maxfreq, int, 0); diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index c174c6a0d224..f7199c83f5cf 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1250,28 +1250,20 @@ static int mxcmci_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); - int ret = 0; - if (mmc) - ret = mmc_suspend_host(mmc); clk_disable_unprepare(host->clk_per); clk_disable_unprepare(host->clk_ipg); - - return ret; + return 0; } static int mxcmci_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); - int ret = 0; clk_prepare_enable(host->clk_per); clk_prepare_enable(host->clk_ipg); - if (mmc) - ret = mmc_resume_host(mmc); - - return ret; + return 0; } static const struct dev_pm_ops mxcmci_pm_ops = { diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index f38d75f46f78..50fc9df791b2 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -102,12 +102,15 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc) BM_SSP_STATUS_CARD_DETECT) ^ host->cd_inverted; } -static void mxs_mmc_reset(struct mxs_mmc_host *host) +static int mxs_mmc_reset(struct mxs_mmc_host *host) { struct mxs_ssp *ssp = &host->ssp; u32 ctrl0, ctrl1; + int ret; - stmp_reset_block(ssp->base); + ret = stmp_reset_block(ssp->base); + if (ret) + return ret; ctrl0 = BM_SSP_CTRL0_IGNORE_CRC; ctrl1 = BF_SSP(0x3, CTRL1_SSP_MODE) | @@ -132,6 +135,7 @@ static void mxs_mmc_reset(struct mxs_mmc_host *host) writel(ctrl0, ssp->base + HW_SSP_CTRL0); writel(ctrl1, ssp->base + HW_SSP_CTRL1(ssp)); + return 0; } static void mxs_mmc_start_cmd(struct mxs_mmc_host *host, @@ -618,21 +622,25 @@ static int mxs_mmc_probe(struct platform_device *pdev) } } - ssp->clk = clk_get(&pdev->dev, NULL); + ssp->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(ssp->clk)) { ret = PTR_ERR(ssp->clk); goto out_mmc_free; } clk_prepare_enable(ssp->clk); - mxs_mmc_reset(host); + ret = mxs_mmc_reset(host); + if (ret) { + dev_err(&pdev->dev, "Failed to reset mmc: %d\n", ret); + goto out_clk_disable; + } ssp->dmach = dma_request_slave_channel(&pdev->dev, "rx-tx"); if (!ssp->dmach) { dev_err(mmc_dev(host->mmc), "%s: failed to request dma\n", __func__); ret = -ENODEV; - goto out_clk_put; + goto out_clk_disable; } /* set mmc core parameters */ @@ -685,9 +693,8 @@ static int mxs_mmc_probe(struct platform_device *pdev) out_free_dma: if (ssp->dmach) dma_release_channel(ssp->dmach); -out_clk_put: +out_clk_disable: clk_disable_unprepare(ssp->clk); - clk_put(ssp->clk); out_mmc_free: mmc_free_host(mmc); return ret; @@ -705,7 +712,6 @@ static int mxs_mmc_remove(struct platform_device *pdev) dma_release_channel(ssp->dmach); clk_disable_unprepare(ssp->clk); - clk_put(ssp->clk); mmc_free_host(mmc); @@ -718,13 +724,9 @@ static int mxs_mmc_suspend(struct device *dev) struct mmc_host *mmc = dev_get_drvdata(dev); struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; - int ret = 0; - - ret = mmc_suspend_host(mmc); clk_disable_unprepare(ssp->clk); - - return ret; + return 0; } static int mxs_mmc_resume(struct device *dev) @@ -732,13 +734,9 @@ static int mxs_mmc_resume(struct device *dev) struct mmc_host *mmc = dev_get_drvdata(dev); struct mxs_mmc_host *host = mmc_priv(mmc); struct mxs_ssp *ssp = &host->ssp; - int ret = 0; clk_prepare_enable(ssp->clk); - - ret = mmc_resume_host(mmc); - - return ret; + return 0; } static const struct dev_pm_ops mxs_mmc_pm_ops = { diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c index d720b5e05b9c..6e218fb1a669 100644 --- a/drivers/mmc/host/of_mmc_spi.c +++ b/drivers/mmc/host/of_mmc_spi.c @@ -50,25 +50,6 @@ static struct of_mmc_spi *to_of_mmc_spi(struct device *dev) return container_of(dev->platform_data, struct of_mmc_spi, pdata); } -static int of_mmc_spi_read_gpio(struct device *dev, int gpio_num) -{ - struct of_mmc_spi *oms = to_of_mmc_spi(dev); - bool active_low = oms->alow_gpios[gpio_num]; - bool value = gpio_get_value(oms->gpios[gpio_num]); - - return active_low ^ value; -} - -static int of_mmc_spi_get_cd(struct device *dev) -{ - return of_mmc_spi_read_gpio(dev, CD_GPIO); -} - -static int of_mmc_spi_get_ro(struct device *dev) -{ - return of_mmc_spi_read_gpio(dev, WP_GPIO); -} - static int of_mmc_spi_init(struct device *dev, irqreturn_t (*irqhandler)(int, void *), void *mmc) { @@ -130,20 +111,22 @@ struct mmc_spi_platform_data *mmc_spi_get_pdata(struct spi_device *spi) if (!gpio_is_valid(oms->gpios[i])) continue; - ret = gpio_request(oms->gpios[i], dev_name(dev)); - if (ret < 0) { - oms->gpios[i] = -EINVAL; - continue; - } - if (gpio_flags & OF_GPIO_ACTIVE_LOW) oms->alow_gpios[i] = true; } - if (gpio_is_valid(oms->gpios[CD_GPIO])) - oms->pdata.get_cd = of_mmc_spi_get_cd; - if (gpio_is_valid(oms->gpios[WP_GPIO])) - oms->pdata.get_ro = of_mmc_spi_get_ro; + if (gpio_is_valid(oms->gpios[CD_GPIO])) { + oms->pdata.cd_gpio = oms->gpios[CD_GPIO]; + oms->pdata.flags |= MMC_SPI_USE_CD_GPIO; + if (!oms->alow_gpios[CD_GPIO]) + oms->pdata.caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; + } + if (gpio_is_valid(oms->gpios[WP_GPIO])) { + oms->pdata.ro_gpio = oms->gpios[WP_GPIO]; + oms->pdata.flags |= MMC_SPI_USE_RO_GPIO; + if (!oms->alow_gpios[WP_GPIO]) + oms->pdata.caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; + } oms->detect_irq = irq_of_parse_and_map(np, 0); if (oms->detect_irq != 0) { @@ -166,15 +149,10 @@ void mmc_spi_put_pdata(struct spi_device *spi) struct device *dev = &spi->dev; struct device_node *np = dev->of_node; struct of_mmc_spi *oms = to_of_mmc_spi(dev); - int i; if (!dev->platform_data || !np) return; - for (i = 0; i < ARRAY_SIZE(oms->gpios); i++) { - if (gpio_is_valid(oms->gpios[i])) - gpio_free(oms->gpios[i]); - } kfree(oms); dev->platform_data = NULL; } diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index b94f38ec2a83..98b6b6ef7e5c 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -22,6 +22,7 @@ #include <linux/delay.h> #include <linux/spinlock.h> #include <linux/timer.h> +#include <linux/of.h> #include <linux/omap-dma.h> #include <linux/mmc/host.h> #include <linux/mmc/card.h> @@ -90,17 +91,6 @@ #define OMAP_MMC_CMDTYPE_AC 2 #define OMAP_MMC_CMDTYPE_ADTC 3 -#define OMAP_DMA_MMC_TX 21 -#define OMAP_DMA_MMC_RX 22 -#define OMAP_DMA_MMC2_TX 54 -#define OMAP_DMA_MMC2_RX 55 - -#define OMAP24XX_DMA_MMC2_TX 47 -#define OMAP24XX_DMA_MMC2_RX 48 -#define OMAP24XX_DMA_MMC1_TX 61 -#define OMAP24XX_DMA_MMC1_RX 62 - - #define DRIVER_NAME "mmci-omap" /* Specifies how often in millisecs to poll for card status changes @@ -128,7 +118,6 @@ struct mmc_omap_slot { struct mmc_omap_host { int initialized; - int suspended; struct mmc_request * mrq; struct mmc_command * cmd; struct mmc_data * data; @@ -1331,7 +1320,7 @@ static int mmc_omap_probe(struct platform_device *pdev) struct mmc_omap_host *host = NULL; struct resource *res; dma_cap_mask_t mask; - unsigned sig; + unsigned sig = 0; int i, ret = 0; int irq; @@ -1341,7 +1330,7 @@ static int mmc_omap_probe(struct platform_device *pdev) } if (pdata->nr_slots == 0) { dev_err(&pdev->dev, "no slots\n"); - return -ENXIO; + return -EPROBE_DEFER; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1408,19 +1397,20 @@ static int mmc_omap_probe(struct platform_device *pdev) host->dma_tx_burst = -1; host->dma_rx_burst = -1; - if (mmc_omap2()) - sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX; - else - sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX; - host->dma_tx = dma_request_channel(mask, omap_dma_filter_fn, &sig); + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (res) + sig = res->start; + host->dma_tx = dma_request_slave_channel_compat(mask, + omap_dma_filter_fn, &sig, &pdev->dev, "tx"); if (!host->dma_tx) dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n", sig); - if (mmc_omap2()) - sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX; - else - sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX; - host->dma_rx = dma_request_channel(mask, omap_dma_filter_fn, &sig); + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (res) + sig = res->start; + host->dma_rx = dma_request_slave_channel_compat(mask, + omap_dma_filter_fn, &sig, &pdev->dev, "rx"); if (!host->dma_rx) dev_warn(host->dev, "unable to obtain RX DMA engine channel %u\n", sig); @@ -1513,64 +1503,20 @@ static int mmc_omap_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - int i, ret = 0; - struct mmc_omap_host *host = platform_get_drvdata(pdev); - - if (host == NULL || host->suspended) - return 0; - - for (i = 0; i < host->nr_slots; i++) { - struct mmc_omap_slot *slot; - - slot = host->slots[i]; - ret = mmc_suspend_host(slot->mmc); - if (ret < 0) { - while (--i >= 0) { - slot = host->slots[i]; - mmc_resume_host(slot->mmc); - } - return ret; - } - } - host->suspended = 1; - return 0; -} - -static int mmc_omap_resume(struct platform_device *pdev) -{ - int i, ret = 0; - struct mmc_omap_host *host = platform_get_drvdata(pdev); - - if (host == NULL || !host->suspended) - return 0; - - for (i = 0; i < host->nr_slots; i++) { - struct mmc_omap_slot *slot; - slot = host->slots[i]; - ret = mmc_resume_host(slot->mmc); - if (ret < 0) - return ret; - - host->suspended = 0; - } - return 0; -} -#else -#define mmc_omap_suspend NULL -#define mmc_omap_resume NULL +#if IS_BUILTIN(CONFIG_OF) +static const struct of_device_id mmc_omap_match[] = { + { .compatible = "ti,omap2420-mmc", }, + { }, +}; #endif static struct platform_driver mmc_omap_driver = { .probe = mmc_omap_probe, .remove = mmc_omap_remove, - .suspend = mmc_omap_suspend, - .resume = mmc_omap_resume, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = of_match_ptr(mmc_omap_match), }, }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 1865321465c4..dbd32ad3b749 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -21,6 +21,7 @@ #include <linux/debugfs.h> #include <linux/dmaengine.h> #include <linux/seq_file.h> +#include <linux/sizes.h> #include <linux/interrupt.h> #include <linux/delay.h> #include <linux/dma-mapping.h> @@ -74,6 +75,7 @@ #define ICE 0x1 #define ICS 0x2 #define CEN (1 << 2) +#define CLKD_MAX 0x3FF /* max clock divisor: 1023 */ #define CLKD_MASK 0x0000FFC0 #define CLKD_SHIFT 6 #define DTO_MASK 0x000F0000 @@ -118,7 +120,8 @@ BRR_EN | BWR_EN | TC_EN | CC_EN) #define MMC_AUTOSUSPEND_DELAY 100 -#define MMC_TIMEOUT_MS 20 +#define MMC_TIMEOUT_MS 20 /* 20 mSec */ +#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */ #define OMAP_MMC_MIN_CLOCK 400000 #define OMAP_MMC_MAX_CLOCK 52000000 #define DRIVER_NAME "omap_hsmmc" @@ -170,6 +173,10 @@ struct omap_hsmmc_host { unsigned char bus_mode; unsigned char power_mode; int suspended; + u32 con; + u32 hctl; + u32 sysctl; + u32 capa; int irq; int use_dma, dma_ch; struct dma_chan *tx_chan; @@ -182,7 +189,6 @@ struct omap_hsmmc_host { int use_reg; int req_in_progress; struct omap_hsmmc_next next_data; - struct omap_mmc_platform_data *pdata; }; @@ -492,8 +498,8 @@ static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios) if (ios->clock) { dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock); - if (dsor > 250) - dsor = 250; + if (dsor > CLKD_MAX) + dsor = CLKD_MAX; } return dsor; @@ -596,25 +602,20 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host) static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) { struct mmc_ios *ios = &host->mmc->ios; - struct omap_mmc_platform_data *pdata = host->pdata; - int context_loss = 0; u32 hctl, capa; unsigned long timeout; - if (pdata->get_context_loss_count) { - context_loss = pdata->get_context_loss_count(host->dev); - if (context_loss < 0) - return 1; - } - - dev_dbg(mmc_dev(host->mmc), "context was %slost\n", - context_loss == host->context_loss ? "not " : ""); - if (host->context_loss == context_loss) - return 1; - if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) return 1; + if (host->con == OMAP_HSMMC_READ(host->base, CON) && + host->hctl == OMAP_HSMMC_READ(host->base, HCTL) && + host->sysctl == OMAP_HSMMC_READ(host->base, SYSCTL) && + host->capa == OMAP_HSMMC_READ(host->base, CAPA)) + return 0; + + host->context_loss++; + if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) { if (host->power_mode != MMC_POWER_OFF && (1 << ios->vdd) <= MMC_VDD_23_24) @@ -654,9 +655,8 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) omap_hsmmc_set_bus_mode(host); out: - host->context_loss = context_loss; - - dev_dbg(mmc_dev(host->mmc), "context is restored\n"); + dev_dbg(mmc_dev(host->mmc), "context is restored: restore count %d\n", + host->context_loss); return 0; } @@ -665,15 +665,10 @@ out: */ static void omap_hsmmc_context_save(struct omap_hsmmc_host *host) { - struct omap_mmc_platform_data *pdata = host->pdata; - int context_loss; - - if (pdata->get_context_loss_count) { - context_loss = pdata->get_context_loss_count(host->dev); - if (context_loss < 0) - return; - host->context_loss = context_loss; - } + host->con = OMAP_HSMMC_READ(host->base, CON); + host->hctl = OMAP_HSMMC_READ(host->base, HCTL); + host->sysctl = OMAP_HSMMC_READ(host->base, SYSCTL); + host->capa = OMAP_HSMMC_READ(host->base, CAPA); } #else @@ -974,8 +969,7 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, unsigned long bit) { unsigned long i = 0; - unsigned long limit = (loops_per_jiffy * - msecs_to_jiffies(MMC_TIMEOUT_MS)); + unsigned long limit = MMC_TIMEOUT_US; OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | bit); @@ -987,13 +981,13 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, if (mmc_slot(host).features & HSMMC_HAS_UPDATED_RESET) { while ((!(OMAP_HSMMC_READ(host->base, SYSCTL) & bit)) && (i++ < limit)) - cpu_relax(); + udelay(1); } i = 0; while ((OMAP_HSMMC_READ(host->base, SYSCTL) & bit) && (i++ < limit)) - cpu_relax(); + udelay(1); if (OMAP_HSMMC_READ(host->base, SYSCTL) & bit) dev_err(mmc_dev(host->mmc), @@ -1041,6 +1035,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status) } } + OMAP_HSMMC_WRITE(host->base, STAT, status); if (end_cmd || ((status & CC_EN) && host->cmd)) omap_hsmmc_cmd_done(host, host->cmd); if ((end_trans || (status & TC_EN)) && host->mrq) @@ -1060,7 +1055,6 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) omap_hsmmc_do_irq(host, status); /* Flush posted write */ - OMAP_HSMMC_WRITE(host->base, STAT, status); status = OMAP_HSMMC_READ(host->base, STAT); } @@ -1177,9 +1171,6 @@ static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id) struct omap_mmc_slot_data *slot = &mmc_slot(host); int carddetect; - if (host->suspended) - return IRQ_HANDLED; - sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); if (slot->card_detect) @@ -1634,18 +1625,9 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) { struct mmc_host *mmc = s->private; struct omap_hsmmc_host *host = mmc_priv(mmc); - int context_loss = 0; - - if (host->pdata->get_context_loss_count) - context_loss = host->pdata->get_context_loss_count(host->dev); - seq_printf(s, "mmc%d:\n ctx_loss:\t%d:%d\n\nregs:\n", - mmc->index, host->context_loss, context_loss); - - if (host->suspended) { - seq_printf(s, "host suspended, can't read registers\n"); - return 0; - } + seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n", + mmc->index, host->context_loss); pm_runtime_get_sync(host->dev); @@ -1837,13 +1819,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) mmc->ops = &omap_hsmmc_ops; - /* - * If regulator_disable can only put vcc_aux to sleep then there is - * no off state. - */ - if (mmc_slot(host).vcc_aux_disable_is_sleep) - mmc_slot(host).no_off = 1; - mmc->f_min = OMAP_MMC_MIN_CLOCK; if (pdata->max_freq > 0) @@ -1873,7 +1848,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev) omap_hsmmc_context_save(host); /* This can be removed once we support PBIAS with DT */ - if (host->dev->of_node && host->mapbase == 0x4809c000) + if (host->dev->of_node && res->start == 0x4809c000) host->pbias_disable = 1; host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck"); @@ -2118,23 +2093,12 @@ static void omap_hsmmc_complete(struct device *dev) static int omap_hsmmc_suspend(struct device *dev) { - int ret = 0; struct omap_hsmmc_host *host = dev_get_drvdata(dev); if (!host) return 0; - if (host && host->suspended) - return 0; - pm_runtime_get_sync(host->dev); - host->suspended = 1; - ret = mmc_suspend_host(host->mmc); - - if (ret) { - host->suspended = 0; - goto err; - } if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) { omap_hsmmc_disable_irq(host); @@ -2144,23 +2108,19 @@ static int omap_hsmmc_suspend(struct device *dev) if (host->dbclk) clk_disable_unprepare(host->dbclk); -err: + pm_runtime_put_sync(host->dev); - return ret; + return 0; } /* Routine to resume the MMC device */ static int omap_hsmmc_resume(struct device *dev) { - int ret = 0; struct omap_hsmmc_host *host = dev_get_drvdata(dev); if (!host) return 0; - if (host && !host->suspended) - return 0; - pm_runtime_get_sync(host->dev); if (host->dbclk) @@ -2171,16 +2131,9 @@ static int omap_hsmmc_resume(struct device *dev) omap_hsmmc_protect_card(host); - /* Notify the core to resume the host */ - ret = mmc_resume_host(host->mmc); - if (ret == 0) - host->suspended = 0; - pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); - - return ret; - + return 0; } #else diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 1956a3df7cf3..32fe11323f39 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -880,35 +880,6 @@ static int pxamci_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int pxamci_suspend(struct device *dev) -{ - struct mmc_host *mmc = dev_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_suspend_host(mmc); - - return ret; -} - -static int pxamci_resume(struct device *dev) -{ - struct mmc_host *mmc = dev_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_resume_host(mmc); - - return ret; -} - -static const struct dev_pm_ops pxamci_pm_ops = { - .suspend = pxamci_suspend, - .resume = pxamci_resume, -}; -#endif - static struct platform_driver pxamci_driver = { .probe = pxamci_probe, .remove = pxamci_remove, @@ -916,9 +887,6 @@ static struct platform_driver pxamci_driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(pxa_mmc_dt_ids), -#ifdef CONFIG_PM - .pm = &pxamci_pm_ops, -#endif }, }; diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 82a35b91cdbc..c46feda07d56 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1,6 +1,6 @@ /* Realtek PCI-Express SD/MMC Card Interface driver * - * Copyright(c) 2009 Realtek Semiconductor Corp. All rights reserved. + * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -17,7 +17,6 @@ * * Author: * Wei WANG <wei_wang@realsil.com.cn> - * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China */ #include <linux/module.h> @@ -56,7 +55,6 @@ struct realtek_pci_sdmmc { bool double_clk; bool eject; bool initial_mode; - bool ddr_mode; int power_state; #define SDMMC_POWER_ON 1 #define SDMMC_POWER_OFF 0 @@ -228,6 +226,7 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, int stat_idx = 0; u8 rsp_type; int rsp_len = 5; + bool clock_toggled = false; dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", __func__, cmd_idx, arg); @@ -271,6 +270,8 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, 0xFF, SD_CLK_TOGGLE_EN); if (err < 0) goto out; + + clock_toggled = true; } rtsx_pci_init_cmd(pcr); @@ -351,6 +352,10 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, out: cmd->error = err; + + if (err && clock_toggled) + rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); } static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) @@ -359,7 +364,7 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) struct mmc_host *mmc = host->mmc; struct mmc_card *card = mmc->card; struct mmc_data *data = mrq->data; - int uhs = mmc_sd_card_uhs(card); + int uhs = mmc_card_uhs(card); int read = (data->flags & MMC_DATA_READ) ? 1 : 0; u8 cfg2, trans_mode; int err; @@ -475,18 +480,24 @@ static void sd_normal_rw(struct realtek_pci_sdmmc *host, kfree(buf); } -static int sd_change_phase(struct realtek_pci_sdmmc *host, u8 sample_point) +static int sd_change_phase(struct realtek_pci_sdmmc *host, + u8 sample_point, bool rx) { struct rtsx_pcr *pcr = host->pcr; int err; - dev_dbg(sdmmc_dev(host), "%s: sample_point = %d\n", - __func__, sample_point); + dev_dbg(sdmmc_dev(host), "%s(%s): sample_point = %d\n", + __func__, rx ? "RX" : "TX", sample_point); rtsx_pci_init_cmd(pcr); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, CHANGE_CLK); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPRX_CTL, 0x1F, sample_point); + if (rx) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD_VPRX_CTL, 0x1F, sample_point); + else + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD_VPTX_CTL, 0x1F, sample_point); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, PHASE_NOT_RESET); @@ -602,7 +613,7 @@ static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host, int err; u8 cmd[5] = {0}; - err = sd_change_phase(host, sample_point); + err = sd_change_phase(host, sample_point, true); if (err < 0) return err; @@ -664,7 +675,7 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode) if (final_phase == 0xFF) return -EINVAL; - err = sd_change_phase(host, final_phase); + err = sd_change_phase(host, final_phase, true); if (err < 0) return err; } else { @@ -833,14 +844,11 @@ static int sd_set_power_mode(struct realtek_pci_sdmmc *host, return err; } -static int sd_set_timing(struct realtek_pci_sdmmc *host, - unsigned char timing, bool *ddr_mode) +static int sd_set_timing(struct realtek_pci_sdmmc *host, unsigned char timing) { struct rtsx_pcr *pcr = host->pcr; int err = 0; - *ddr_mode = false; - rtsx_pci_init_cmd(pcr); switch (timing) { @@ -857,8 +865,6 @@ static int sd_set_timing(struct realtek_pci_sdmmc *host, break; case MMC_TIMING_UHS_DDR50: - *ddr_mode = true; - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, 0x0C | SD_ASYNC_FIFO_NOT_RST, SD_DDR_MODE | SD_ASYNC_FIFO_NOT_RST); @@ -926,7 +932,7 @@ static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sd_set_bus_width(host, ios->bus_width); sd_set_power_mode(host, ios->power_mode); - sd_set_timing(host, ios->timing, &host->ddr_mode); + sd_set_timing(host, ios->timing); host->vpclk = false; host->double_clk = true; @@ -1121,11 +1127,11 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) 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); -out: mutex_unlock(&pcr->pcr_mutex); return err; @@ -1148,9 +1154,35 @@ static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) rtsx_pci_start_run(pcr); - if (!host->ddr_mode) - err = sd_tuning_rx(host, MMC_SEND_TUNING_BLOCK); + /* Set initial TX phase */ + switch (mmc->ios.timing) { + case MMC_TIMING_UHS_SDR104: + err = sd_change_phase(host, SDR104_TX_PHASE(pcr), false); + break; + + case MMC_TIMING_UHS_SDR50: + err = sd_change_phase(host, SDR50_TX_PHASE(pcr), false); + break; + + case MMC_TIMING_UHS_DDR50: + err = sd_change_phase(host, DDR50_TX_PHASE(pcr), false); + break; + + default: + err = 0; + } + if (err) + goto out; + + /* Tuning RX phase */ + if ((mmc->ios.timing == MMC_TIMING_UHS_SDR104) || + (mmc->ios.timing == MMC_TIMING_UHS_SDR50)) + err = sd_tuning_rx(host, opcode); + else if (mmc->ios.timing == MMC_TIMING_UHS_DDR50) + err = sd_change_phase(host, DDR50_RX_PHASE(pcr), true); + +out: mutex_unlock(&pcr->pcr_mutex); return err; @@ -1165,37 +1197,6 @@ static const struct mmc_host_ops realtek_pci_sdmmc_ops = { .execute_tuning = sdmmc_execute_tuning, }; -#ifdef CONFIG_PM -static int rtsx_pci_sdmmc_suspend(struct platform_device *pdev, - pm_message_t state) -{ - struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); - struct mmc_host *mmc = host->mmc; - int err; - - dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); - - err = mmc_suspend_host(mmc); - if (err) - return err; - - return 0; -} - -static int rtsx_pci_sdmmc_resume(struct platform_device *pdev) -{ - struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); - struct mmc_host *mmc = host->mmc; - - dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); - - return mmc_resume_host(mmc); -} -#else /* CONFIG_PM */ -#define rtsx_pci_sdmmc_suspend NULL -#define rtsx_pci_sdmmc_resume NULL -#endif /* CONFIG_PM */ - static void init_extra_caps(struct realtek_pci_sdmmc *host) { struct mmc_host *mmc = host->mmc; @@ -1335,8 +1336,6 @@ static struct platform_driver rtsx_pci_sdmmc_driver = { .probe = rtsx_pci_sdmmc_drv_probe, .remove = rtsx_pci_sdmmc_drv_remove, .id_table = rtsx_pci_sdmmc_ids, - .suspend = rtsx_pci_sdmmc_suspend, - .resume = rtsx_pci_sdmmc_resume, .driver = { .owner = THIS_MODULE, .name = DRV_NAME_RTSX_PCI_SDMMC, diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 8d6794cdf899..2fce5ea5eb39 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1949,39 +1949,10 @@ static struct platform_device_id s3cmci_driver_ids[] = { MODULE_DEVICE_TABLE(platform, s3cmci_driver_ids); - -#ifdef CONFIG_PM - -static int s3cmci_suspend(struct device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); - - return mmc_suspend_host(mmc); -} - -static int s3cmci_resume(struct device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(to_platform_device(dev)); - - return mmc_resume_host(mmc); -} - -static const struct dev_pm_ops s3cmci_pm = { - .suspend = s3cmci_suspend, - .resume = s3cmci_resume, -}; - -#define s3cmci_pm_ops &s3cmci_pm -#else /* CONFIG_PM */ -#define s3cmci_pm_ops NULL -#endif /* CONFIG_PM */ - - static struct platform_driver s3cmci_driver = { .driver = { .name = "s3c-sdi", .owner = THIS_MODULE, - .pm = s3cmci_pm_ops, }, .id_table = s3cmci_driver_ids, .probe = s3cmci_probe, diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index cdd4ce0d7c90..ef19874fcd1f 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -310,8 +310,9 @@ static int sdhci_acpi_probe(struct platform_device *pdev) dma_mask = DMA_BIT_MASK(32); } - dev->dma_mask = &dev->coherent_dma_mask; - dev->coherent_dma_mask = dma_mask; + err = dma_coerce_mask_and_coherent(dev, dma_mask); + if (err) + goto err_free; } if (c->slot) { diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 87175f9817c2..7a190fe4dff1 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -24,7 +24,6 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_gpio.h> -#include <linux/version.h> #include <linux/mmc/slot-gpio.h> #include "sdhci-pltfm.h" @@ -162,7 +161,7 @@ static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert) /* * SD card interrupt event callback */ -void sdhci_bcm_kona_card_event(struct sdhci_host *host) +static void sdhci_bcm_kona_card_event(struct sdhci_host *host) { if (mmc_gpio_get_cd(host->mmc) > 0) { dev_dbg(mmc_dev(host->mmc), @@ -221,13 +220,14 @@ static struct sdhci_pltfm_data sdhci_pltfm_data_kona = { SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, }; -static const struct of_device_id sdhci_bcm_kona_of_match[] __initdata = { - { .compatible = "bcm,kona-sdhci"}, +static struct __initconst of_device_id sdhci_bcm_kona_of_match[] = { + { .compatible = "brcm,kona-sdhci"}, + { .compatible = "bcm,kona-sdhci"}, /* deprecated name */ {} }; MODULE_DEVICE_TABLE(of, sdhci_bcm_kona_of_match); -static int __init sdhci_bcm_kona_probe(struct platform_device *pdev) +static int sdhci_bcm_kona_probe(struct platform_device *pdev) { struct sdhci_bcm_kona_dev *kona_dev = NULL; struct sdhci_pltfm_host *pltfm_priv; @@ -263,7 +263,7 @@ static int __init sdhci_bcm_kona_probe(struct platform_device *pdev) (mmc_gpio_get_cd(host->mmc) != -ENOSYS) ? 'Y' : 'N', (mmc_gpio_get_ro(host->mmc) != -ENOSYS) ? 'Y' : 'N'); - if (host->mmc->caps | MMC_CAP_NONREMOVABLE) + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; dev_dbg(dev, "is_8bit=%c\n", @@ -282,7 +282,7 @@ static int __init sdhci_bcm_kona_probe(struct platform_device *pdev) } /* if device is eMMC, emulate card insert right here */ - if (host->mmc->caps | MMC_CAP_NONREMOVABLE) { + if (host->mmc->caps & MMC_CAP_NONREMOVABLE) { ret = sdhci_bcm_kona_sd_card_emulate(host, 1); if (ret) { dev_err(dev, @@ -316,19 +316,7 @@ err_pltfm_free: static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev) { - struct sdhci_host *host = platform_get_drvdata(pdev); - int dead; - u32 scratch; - - dead = 0; - scratch = readl(host->ioaddr + SDHCI_INT_STATUS); - if (scratch == (u32)-1) - dead = 1; - sdhci_remove_host(host, dead); - - sdhci_free_host(host); - - return 0; + return sdhci_pltfm_unregister(pdev); } static struct platform_driver sdhci_bcm_kona_driver = { @@ -336,10 +324,10 @@ static struct platform_driver sdhci_bcm_kona_driver = { .name = "sdhci-kona", .owner = THIS_MODULE, .pm = SDHCI_PLTFM_PMOPS, - .of_match_table = of_match_ptr(sdhci_bcm_kona_of_match), + .of_match_table = sdhci_bcm_kona_of_match, }, .probe = sdhci_bcm_kona_probe, - .remove = __exit_p(sdhci_bcm_kona_remove), + .remove = sdhci_bcm_kona_remove, }; module_platform_driver(sdhci_bcm_kona_driver); diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 0584a1c788b8..f6d8d67c545f 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -119,7 +119,7 @@ static u8 bcm2835_sdhci_readb(struct sdhci_host *host, int reg) return byte; } -unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host) +static unsigned int bcm2835_sdhci_get_min_clock(struct sdhci_host *host) { return MIN_FREQ; } @@ -178,13 +178,7 @@ err: static int bcm2835_sdhci_remove(struct platform_device *pdev) { - struct sdhci_host *host = platform_get_drvdata(pdev); - int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); - - sdhci_remove_host(host, dead); - sdhci_pltfm_free(pdev); - - return 0; + return sdhci_pltfm_unregister(pdev); } static const struct of_device_id bcm2835_sdhci_of_match[] = { diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 1dd5ba858754..461a4c3f4ef7 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -34,12 +34,40 @@ /* VENDOR SPEC register */ #define ESDHC_VENDOR_SPEC 0xc0 #define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1) +#define ESDHC_VENDOR_SPEC_VSELECT (1 << 1) +#define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8) #define ESDHC_WTMK_LVL 0x44 #define ESDHC_MIX_CTRL 0x48 +#define ESDHC_MIX_CTRL_DDREN (1 << 3) #define ESDHC_MIX_CTRL_AC23EN (1 << 7) +#define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22) +#define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23) +#define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25) /* Bits 3 and 6 are not SDHCI standard definitions */ #define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7 +/* dll control register */ +#define ESDHC_DLL_CTRL 0x60 +#define ESDHC_DLL_OVERRIDE_VAL_SHIFT 9 +#define ESDHC_DLL_OVERRIDE_EN_SHIFT 8 + +/* tune control register */ +#define ESDHC_TUNE_CTRL_STATUS 0x68 +#define ESDHC_TUNE_CTRL_STEP 1 +#define ESDHC_TUNE_CTRL_MIN 0 +#define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1) + +#define ESDHC_TUNING_CTRL 0xcc +#define ESDHC_STD_TUNING_EN (1 << 24) +/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */ +#define ESDHC_TUNING_START_TAP 0x1 + +#define ESDHC_TUNING_BLOCK_PATTERN_LEN 64 + +/* pinctrl state */ +#define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz" +#define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz" + /* * Our interpretation of the SDHCI_HOST_CONTROL register */ @@ -66,21 +94,60 @@ * As a result, the TC flag is not asserted and SW received timeout * exeception. Bit1 of Vendor Spec registor is used to fix it. */ -#define ESDHC_FLAG_MULTIBLK_NO_INT (1 << 1) - -enum imx_esdhc_type { - IMX25_ESDHC, - IMX35_ESDHC, - IMX51_ESDHC, - IMX53_ESDHC, - IMX6Q_USDHC, +#define ESDHC_FLAG_MULTIBLK_NO_INT BIT(1) +/* + * The flag enables the workaround for ESDHC errata ENGcm07207 which + * affects i.MX25 and i.MX35. + */ +#define ESDHC_FLAG_ENGCM07207 BIT(2) +/* + * The flag tells that the ESDHC controller is an USDHC block that is + * integrated on the i.MX6 series. + */ +#define ESDHC_FLAG_USDHC BIT(3) +/* The IP supports manual tuning process */ +#define ESDHC_FLAG_MAN_TUNING BIT(4) +/* The IP supports standard tuning process */ +#define ESDHC_FLAG_STD_TUNING BIT(5) +/* The IP has SDHCI_CAPABILITIES_1 register */ +#define ESDHC_FLAG_HAVE_CAP1 BIT(6) + +struct esdhc_soc_data { + u32 flags; +}; + +static struct esdhc_soc_data esdhc_imx25_data = { + .flags = ESDHC_FLAG_ENGCM07207, +}; + +static struct esdhc_soc_data esdhc_imx35_data = { + .flags = ESDHC_FLAG_ENGCM07207, +}; + +static struct esdhc_soc_data esdhc_imx51_data = { + .flags = 0, +}; + +static struct esdhc_soc_data esdhc_imx53_data = { + .flags = ESDHC_FLAG_MULTIBLK_NO_INT, +}; + +static struct esdhc_soc_data usdhc_imx6q_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING, +}; + +static struct esdhc_soc_data usdhc_imx6sl_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1, }; struct pltfm_imx_data { - int flags; u32 scratchpad; - enum imx_esdhc_type devtype; struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_100mhz; + struct pinctrl_state *pins_200mhz; + const struct esdhc_soc_data *socdata; struct esdhc_platform_data boarddata; struct clk *clk_ipg; struct clk *clk_ahb; @@ -90,25 +157,20 @@ struct pltfm_imx_data { MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */ WAIT_FOR_INT, /* sent CMD12, waiting for response INT */ } multiblock_status; - + u32 uhs_mode; + u32 is_ddr; }; static struct platform_device_id imx_esdhc_devtype[] = { { .name = "sdhci-esdhc-imx25", - .driver_data = IMX25_ESDHC, + .driver_data = (kernel_ulong_t) &esdhc_imx25_data, }, { .name = "sdhci-esdhc-imx35", - .driver_data = IMX35_ESDHC, + .driver_data = (kernel_ulong_t) &esdhc_imx35_data, }, { .name = "sdhci-esdhc-imx51", - .driver_data = IMX51_ESDHC, - }, { - .name = "sdhci-esdhc-imx53", - .driver_data = IMX53_ESDHC, - }, { - .name = "sdhci-usdhc-imx6q", - .driver_data = IMX6Q_USDHC, + .driver_data = (kernel_ulong_t) &esdhc_imx51_data, }, { /* sentinel */ } @@ -116,38 +178,34 @@ static struct platform_device_id imx_esdhc_devtype[] = { MODULE_DEVICE_TABLE(platform, imx_esdhc_devtype); static const struct of_device_id imx_esdhc_dt_ids[] = { - { .compatible = "fsl,imx25-esdhc", .data = &imx_esdhc_devtype[IMX25_ESDHC], }, - { .compatible = "fsl,imx35-esdhc", .data = &imx_esdhc_devtype[IMX35_ESDHC], }, - { .compatible = "fsl,imx51-esdhc", .data = &imx_esdhc_devtype[IMX51_ESDHC], }, - { .compatible = "fsl,imx53-esdhc", .data = &imx_esdhc_devtype[IMX53_ESDHC], }, - { .compatible = "fsl,imx6q-usdhc", .data = &imx_esdhc_devtype[IMX6Q_USDHC], }, + { .compatible = "fsl,imx25-esdhc", .data = &esdhc_imx25_data, }, + { .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, }, + { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, }, + { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, }, + { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, }, + { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); static inline int is_imx25_esdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX25_ESDHC; -} - -static inline int is_imx35_esdhc(struct pltfm_imx_data *data) -{ - return data->devtype == IMX35_ESDHC; + return data->socdata == &esdhc_imx25_data; } -static inline int is_imx51_esdhc(struct pltfm_imx_data *data) +static inline int is_imx53_esdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX51_ESDHC; + return data->socdata == &esdhc_imx53_data; } -static inline int is_imx53_esdhc(struct pltfm_imx_data *data) +static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX53_ESDHC; + return data->socdata == &usdhc_imx6q_data; } -static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) +static inline int esdhc_is_usdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX6Q_USDHC; + return !!(data->socdata->flags & ESDHC_FLAG_USDHC); } static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) @@ -164,7 +222,21 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) struct pltfm_imx_data *imx_data = pltfm_host->priv; u32 val = readl(host->ioaddr + reg); + if (unlikely(reg == SDHCI_PRESENT_STATE)) { + u32 fsl_prss = val; + /* save the least 20 bits */ + val = fsl_prss & 0x000FFFFF; + /* move dat[0-3] bits */ + val |= (fsl_prss & 0x0F000000) >> 4; + /* move cmd line bit */ + val |= (fsl_prss & 0x00800000) << 1; + } + if (unlikely(reg == SDHCI_CAPABILITIES)) { + /* ignore bit[0-15] as it stores cap_1 register val for mx6sl */ + if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1) + val &= 0xffff0000; + /* In FSL esdhc IC module, only bit20 is used to indicate the * ADMA2 capability of esdhc, but this bit is messed up on * some SOCs (e.g. on MX25, MX35 this bit is set, but they @@ -178,6 +250,25 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) } } + if (unlikely(reg == SDHCI_CAPABILITIES_1)) { + if (esdhc_is_usdhc(imx_data)) { + if (imx_data->socdata->flags & ESDHC_FLAG_HAVE_CAP1) + val = readl(host->ioaddr + SDHCI_CAPABILITIES) & 0xFFFF; + else + /* imx6q/dl does not have cap_1 register, fake one */ + val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 + | SDHCI_SUPPORT_SDR50 + | SDHCI_USE_SDR50_TUNING; + } + } + + if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) { + val = 0; + val |= 0xFF << SDHCI_MAX_CURRENT_330_SHIFT; + val |= 0xFF << SDHCI_MAX_CURRENT_300_SHIFT; + val |= 0xFF << SDHCI_MAX_CURRENT_180_SHIFT; + } + if (unlikely(reg == SDHCI_INT_STATUS)) { if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) { val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR; @@ -224,7 +315,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) } } - if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) + if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) && (reg == SDHCI_INT_STATUS) && (val & SDHCI_INT_DATA_END))) { u32 v; @@ -256,10 +347,12 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; + u16 ret = 0; + u32 val; if (unlikely(reg == SDHCI_HOST_VERSION)) { reg ^= 2; - if (is_imx6q_usdhc(imx_data)) { + if (esdhc_is_usdhc(imx_data)) { /* * The usdhc register returns a wrong host version. * Correct it here. @@ -268,6 +361,30 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) } } + if (unlikely(reg == SDHCI_HOST_CONTROL2)) { + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + if (val & ESDHC_VENDOR_SPEC_VSELECT) + ret |= SDHCI_CTRL_VDD_180; + + if (esdhc_is_usdhc(imx_data)) { + if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) + val = readl(host->ioaddr + ESDHC_MIX_CTRL); + else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) + /* the std tuning bits is in ACMD12_ERR for imx6sl */ + val = readl(host->ioaddr + SDHCI_ACMD12_ERR); + } + + if (val & ESDHC_MIX_CTRL_EXE_TUNE) + ret |= SDHCI_CTRL_EXEC_TUNING; + if (val & ESDHC_MIX_CTRL_SMPCLK_SEL) + ret |= SDHCI_CTRL_TUNED_CLK; + + ret |= (imx_data->uhs_mode & SDHCI_CTRL_UHS_MASK); + ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE; + + return ret; + } + return readw(host->ioaddr + reg); } @@ -275,10 +392,59 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; + u32 new_val = 0; switch (reg) { + case SDHCI_CLOCK_CONTROL: + new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + if (val & SDHCI_CLOCK_CARD_EN) + new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON; + else + new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON; + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); + return; + case SDHCI_HOST_CONTROL2: + new_val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + if (val & SDHCI_CTRL_VDD_180) + new_val |= ESDHC_VENDOR_SPEC_VSELECT; + else + new_val &= ~ESDHC_VENDOR_SPEC_VSELECT; + writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); + imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK; + if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { + new_val = readl(host->ioaddr + ESDHC_MIX_CTRL); + if (val & SDHCI_CTRL_TUNED_CLK) + new_val |= ESDHC_MIX_CTRL_SMPCLK_SEL; + else + new_val &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; + writel(new_val , host->ioaddr + ESDHC_MIX_CTRL); + } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { + u32 v = readl(host->ioaddr + SDHCI_ACMD12_ERR); + u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); + new_val = readl(host->ioaddr + ESDHC_TUNING_CTRL); + if (val & SDHCI_CTRL_EXEC_TUNING) { + new_val |= ESDHC_STD_TUNING_EN | + ESDHC_TUNING_START_TAP; + v |= ESDHC_MIX_CTRL_EXE_TUNE; + m |= ESDHC_MIX_CTRL_FBCLK_SEL; + } else { + new_val &= ~ESDHC_STD_TUNING_EN; + v &= ~ESDHC_MIX_CTRL_EXE_TUNE; + m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; + } + + if (val & SDHCI_CTRL_TUNED_CLK) + v |= ESDHC_MIX_CTRL_SMPCLK_SEL; + else + v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; + + writel(new_val, host->ioaddr + ESDHC_TUNING_CTRL); + writel(v, host->ioaddr + SDHCI_ACMD12_ERR); + writel(m, host->ioaddr + ESDHC_MIX_CTRL); + } + return; case SDHCI_TRANSFER_MODE: - if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) + if ((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) && (host->cmd->opcode == SD_IO_RW_EXTENDED) && (host->cmd->data->blocks > 1) && (host->cmd->data->flags & MMC_DATA_READ)) { @@ -288,7 +454,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) writel(v, host->ioaddr + ESDHC_VENDOR_SPEC); } - if (is_imx6q_usdhc(imx_data)) { + if (esdhc_is_usdhc(imx_data)) { u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); /* Swap AC23 bit */ if (val & SDHCI_TRNS_AUTO_CMD23) { @@ -310,10 +476,10 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) val |= SDHCI_CMD_ABORTCMD; if ((host->cmd->opcode == MMC_SET_BLOCK_COUNT) && - (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) + (imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT)) imx_data->multiblock_status = MULTIBLK_IN_PROCESS; - if (is_imx6q_usdhc(imx_data)) + if (esdhc_is_usdhc(imx_data)) writel(val << 16, host->ioaddr + SDHCI_TRANSFER_MODE); else @@ -379,8 +545,10 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg) * The reset on usdhc fails to clear MIX_CTRL register. * Do it manually here. */ - if (is_imx6q_usdhc(imx_data)) + if (esdhc_is_usdhc(imx_data)) { writel(0, host->ioaddr + ESDHC_MIX_CTRL); + imx_data->is_ddr = 0; + } } } @@ -409,8 +577,60 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + unsigned int host_clock = clk_get_rate(pltfm_host->clk); + int pre_div = 2; + int div = 1; + u32 temp, val; + + if (clock == 0) { + if (esdhc_is_usdhc(imx_data)) { + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + writel(val & ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, + host->ioaddr + ESDHC_VENDOR_SPEC); + } + goto out; + } + + if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr) + pre_div = 1; + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | ESDHC_CLOCK_MASK); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + while (host_clock / pre_div / 16 > clock && pre_div < 256) + pre_div *= 2; + + while (host_clock / pre_div / div > clock && div < 16) + div++; - esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk)); + host->mmc->actual_clock = host_clock / pre_div / div; + dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", + clock, host->mmc->actual_clock); + + if (imx_data->is_ddr) + pre_div >>= 2; + else + pre_div >>= 1; + div--; + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | (div << ESDHC_DIVIDER_SHIFT) + | (pre_div << ESDHC_PREDIV_SHIFT)); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + if (esdhc_is_usdhc(imx_data)) { + val = readl(host->ioaddr + ESDHC_VENDOR_SPEC); + writel(val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON, + host->ioaddr + ESDHC_VENDOR_SPEC); + } + + mdelay(1); +out: + host->clock = clock; } static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) @@ -454,7 +674,192 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) return 0; } -static const struct sdhci_ops sdhci_esdhc_ops = { +static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) +{ + u32 reg; + + /* FIXME: delay a bit for card to be ready for next tuning due to errors */ + mdelay(1); + + reg = readl(host->ioaddr + ESDHC_MIX_CTRL); + reg |= ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL | + ESDHC_MIX_CTRL_FBCLK_SEL; + writel(reg, host->ioaddr + ESDHC_MIX_CTRL); + writel(val << 8, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); + dev_dbg(mmc_dev(host->mmc), + "tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x\n", + val, readl(host->ioaddr + ESDHC_TUNE_CTRL_STATUS)); +} + +static void esdhc_request_done(struct mmc_request *mrq) +{ + complete(&mrq->completion); +} + +static int esdhc_send_tuning_cmd(struct sdhci_host *host, u32 opcode) +{ + struct mmc_command cmd = {0}; + struct mmc_request mrq = {0}; + struct mmc_data data = {0}; + struct scatterlist sg; + char tuning_pattern[ESDHC_TUNING_BLOCK_PATTERN_LEN]; + + cmd.opcode = opcode; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, tuning_pattern, sizeof(tuning_pattern)); + + mrq.cmd = &cmd; + mrq.cmd->mrq = &mrq; + mrq.data = &data; + mrq.data->mrq = &mrq; + mrq.cmd->data = mrq.data; + + mrq.done = esdhc_request_done; + init_completion(&(mrq.completion)); + + disable_irq(host->irq); + spin_lock(&host->lock); + host->mrq = &mrq; + + sdhci_send_command(host, mrq.cmd); + + spin_unlock(&host->lock); + enable_irq(host->irq); + + wait_for_completion(&mrq.completion); + + if (cmd.error) + return cmd.error; + if (data.error) + return data.error; + + return 0; +} + +static void esdhc_post_tuning(struct sdhci_host *host) +{ + u32 reg; + + reg = readl(host->ioaddr + ESDHC_MIX_CTRL); + reg &= ~ESDHC_MIX_CTRL_EXE_TUNE; + writel(reg, host->ioaddr + ESDHC_MIX_CTRL); +} + +static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) +{ + int min, max, avg, ret; + + /* find the mininum delay first which can pass tuning */ + min = ESDHC_TUNE_CTRL_MIN; + while (min < ESDHC_TUNE_CTRL_MAX) { + esdhc_prepare_tuning(host, min); + if (!esdhc_send_tuning_cmd(host, opcode)) + break; + min += ESDHC_TUNE_CTRL_STEP; + } + + /* find the maxinum delay which can not pass tuning */ + max = min + ESDHC_TUNE_CTRL_STEP; + while (max < ESDHC_TUNE_CTRL_MAX) { + esdhc_prepare_tuning(host, max); + if (esdhc_send_tuning_cmd(host, opcode)) { + max -= ESDHC_TUNE_CTRL_STEP; + break; + } + max += ESDHC_TUNE_CTRL_STEP; + } + + /* use average delay to get the best timing */ + avg = (min + max) / 2; + esdhc_prepare_tuning(host, avg); + ret = esdhc_send_tuning_cmd(host, opcode); + esdhc_post_tuning(host); + + dev_dbg(mmc_dev(host->mmc), "tunning %s at 0x%x ret %d\n", + ret ? "failed" : "passed", avg, ret); + + return ret; +} + +static int esdhc_change_pinstate(struct sdhci_host *host, + unsigned int uhs) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + struct pinctrl_state *pinctrl; + + dev_dbg(mmc_dev(host->mmc), "change pinctrl state for uhs %d\n", uhs); + + if (IS_ERR(imx_data->pinctrl) || + IS_ERR(imx_data->pins_default) || + IS_ERR(imx_data->pins_100mhz) || + IS_ERR(imx_data->pins_200mhz)) + return -EINVAL; + + switch (uhs) { + case MMC_TIMING_UHS_SDR50: + pinctrl = imx_data->pins_100mhz; + break; + case MMC_TIMING_UHS_SDR104: + pinctrl = imx_data->pins_200mhz; + break; + default: + /* back to default state for other legacy timing */ + pinctrl = imx_data->pins_default; + } + + return pinctrl_select_state(imx_data->pinctrl, pinctrl); +} + +static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = pltfm_host->priv; + struct esdhc_platform_data *boarddata = &imx_data->boarddata; + + switch (uhs) { + case MMC_TIMING_UHS_SDR12: + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR12; + break; + case MMC_TIMING_UHS_SDR25: + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR25; + break; + case MMC_TIMING_UHS_SDR50: + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR50; + break; + case MMC_TIMING_UHS_SDR104: + imx_data->uhs_mode = SDHCI_CTRL_UHS_SDR104; + break; + case MMC_TIMING_UHS_DDR50: + imx_data->uhs_mode = SDHCI_CTRL_UHS_DDR50; + writel(readl(host->ioaddr + ESDHC_MIX_CTRL) | + ESDHC_MIX_CTRL_DDREN, + host->ioaddr + ESDHC_MIX_CTRL); + imx_data->is_ddr = 1; + if (boarddata->delay_line) { + u32 v; + v = boarddata->delay_line << + ESDHC_DLL_OVERRIDE_VAL_SHIFT | + (1 << ESDHC_DLL_OVERRIDE_EN_SHIFT); + if (is_imx53_esdhc(imx_data)) + v <<= 1; + writel(v, host->ioaddr + ESDHC_DLL_CTRL); + } + break; + } + + return esdhc_change_pinstate(host, uhs); +} + +static struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, .write_l = esdhc_writel_le, @@ -465,6 +870,7 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .get_min_clock = esdhc_pltfm_get_min_clock, .get_ro = esdhc_pltfm_get_ro, .platform_bus_width = esdhc_pltfm_bus_width, + .set_uhs_signaling = esdhc_set_uhs_signaling, }; static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { @@ -506,6 +912,14 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, of_property_read_u32(np, "max-frequency", &boarddata->f_max); + if (of_find_property(np, "no-1-8-v", NULL)) + boarddata->support_vsel = false; + else + boarddata->support_vsel = true; + + if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) + boarddata->delay_line = 0; + return 0; } #else @@ -539,9 +953,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) goto free_sdhci; } - if (of_id) - pdev->id_entry = of_id->data; - imx_data->devtype = pdev->id_entry->driver_data; + imx_data->socdata = of_id ? of_id->data : (struct esdhc_soc_data *) + pdev->id_entry->driver_data; pltfm_host->priv = imx_data; imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); @@ -568,29 +981,39 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) clk_prepare_enable(imx_data->clk_ipg); clk_prepare_enable(imx_data->clk_ahb); - imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + imx_data->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(imx_data->pinctrl)) { err = PTR_ERR(imx_data->pinctrl); goto disable_clk; } + imx_data->pins_default = pinctrl_lookup_state(imx_data->pinctrl, + PINCTRL_STATE_DEFAULT); + if (IS_ERR(imx_data->pins_default)) { + err = PTR_ERR(imx_data->pins_default); + dev_err(mmc_dev(host->mmc), "could not get default state\n"); + goto disable_clk; + } + host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; - if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) + if (imx_data->socdata->flags & ESDHC_FLAG_ENGCM07207) /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK | SDHCI_QUIRK_BROKEN_ADMA; - if (is_imx53_esdhc(imx_data)) - imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT; - /* * The imx6q ROM code will change the default watermark level setting * to something insane. Change it back here. */ - if (is_imx6q_usdhc(imx_data)) + if (esdhc_is_usdhc(imx_data)) { writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL); + host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; + } + if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) + sdhci_esdhc_ops.platform_execute_tuning = + esdhc_executing_tuning; boarddata = &imx_data->boarddata; if (sdhci_esdhc_imx_probe_dt(pdev, boarddata) < 0) { if (!host->mmc->parent->platform_data) { @@ -616,7 +1039,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) /* card_detect */ switch (boarddata->cd_type) { case ESDHC_CD_GPIO: - err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio); + err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0); if (err) { dev_err(mmc_dev(host->mmc), "failed to request card-detect gpio!\n"); @@ -650,6 +1073,23 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) break; } + /* sdr50 and sdr104 needs work on 1.8v signal voltage */ + if ((boarddata->support_vsel) && esdhc_is_usdhc(imx_data)) { + imx_data->pins_100mhz = pinctrl_lookup_state(imx_data->pinctrl, + ESDHC_PINCTRL_STATE_100MHZ); + imx_data->pins_200mhz = pinctrl_lookup_state(imx_data->pinctrl, + ESDHC_PINCTRL_STATE_200MHZ); + if (IS_ERR(imx_data->pins_100mhz) || + IS_ERR(imx_data->pins_200mhz)) { + dev_warn(mmc_dev(host->mmc), + "could not get ultra high speed state, work on normal mode\n"); + /* fall back to not support uhs by specify no 1.8v quirk */ + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + } + } else { + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + } + err = sdhci_add_host(host); if (err) goto disable_clk; diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a2a06420e463..a7d9f95a7b03 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -49,41 +49,4 @@ #define ESDHC_HOST_CONTROL_RES 0x05 -static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock, - unsigned int host_clock) -{ - int pre_div = 2; - int div = 1; - u32 temp; - - if (clock == 0) - goto out; - - temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); - temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN - | ESDHC_CLOCK_MASK); - sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - - while (host_clock / pre_div / 16 > clock && pre_div < 256) - pre_div *= 2; - - while (host_clock / pre_div / div > clock && div < 16) - div++; - - dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", - clock, host_clock / pre_div / div); - - pre_div >>= 1; - div--; - - temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); - temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN - | (div << ESDHC_DIVIDER_SHIFT) - | (pre_div << ESDHC_PREDIV_SHIFT)); - sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - mdelay(1); -out: - host->clock = clock; -} - #endif /* _DRIVERS_MMC_SDHCI_ESDHC_H */ diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 15039e2d1c12..0b249970b119 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -199,6 +199,14 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) { + + int pre_div = 2; + int div = 1; + u32 temp; + + if (clock == 0) + goto out; + /* Workaround to reduce the clock frequency for p1010 esdhc */ if (of_find_compatible_node(NULL, NULL, "fsl,p1010-esdhc")) { if (clock > 20000000) @@ -207,8 +215,31 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) clock -= 5000000; } - /* Set the clock */ - esdhc_set_clock(host, clock, host->max_clk); + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | ESDHC_CLOCK_MASK); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + while (host->max_clk / pre_div / 16 > clock && pre_div < 256) + pre_div *= 2; + + while (host->max_clk / pre_div / div > clock && div < 16) + div++; + + dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", + clock, host->max_clk / pre_div / div); + + pre_div >>= 1; + div--; + + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); + temp |= (ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN + | (div << ESDHC_DIVIDER_SHIFT) + | (pre_div << ESDHC_PREDIV_SHIFT)); + sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + mdelay(1); +out: + host->clock = clock; } #ifdef CONFIG_PM @@ -316,6 +347,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) /* call to generic mmc_of_parse to support additional capabilities */ mmc_of_parse(host->mmc); + mmc_of_parse_voltage(np, &host->ocr_mask); ret = sdhci_add_host(host); if (ret) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index d7d6bc8968d2..8f753811fc7a 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -37,6 +37,12 @@ #define PCI_DEVICE_ID_INTEL_BYT_SDIO 0x0f15 #define PCI_DEVICE_ID_INTEL_BYT_SD 0x0f16 #define PCI_DEVICE_ID_INTEL_BYT_EMMC2 0x0f50 +#define PCI_DEVICE_ID_INTEL_MRFL_MMC 0x1190 +#define PCI_DEVICE_ID_INTEL_CLV_SDIO0 0x08f9 +#define PCI_DEVICE_ID_INTEL_CLV_SDIO1 0x08fa +#define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb +#define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5 +#define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6 /* * PCI registers @@ -356,6 +362,28 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { .allow_runtime_pm = true, }; +/* Define Host controllers for Intel Merrifield platform */ +#define INTEL_MRFL_EMMC_0 0 +#define INTEL_MRFL_EMMC_1 1 + +static int intel_mrfl_mmc_probe_slot(struct sdhci_pci_slot *slot) +{ + if ((PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_0) && + (PCI_FUNC(slot->chip->pdev->devfn) != INTEL_MRFL_EMMC_1)) + /* SD support is not ready yet */ + return -ENODEV; + + slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | + MMC_CAP_1_8V_DDR; + + return 0; +} + +static const struct sdhci_pci_fixes sdhci_intel_mrfl_mmc = { + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, + .probe_slot = intel_mrfl_mmc_probe_slot, +}; + /* O2Micro extra registers */ #define O2_SD_LOCK_WP 0xD3 #define O2_SD_MULTI_VCC3V 0xEE @@ -939,6 +967,54 @@ static const struct pci_device_id pci_ids[] = { .driver_data = (kernel_ulong_t)&sdhci_intel_byt_emmc, }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_SDIO0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sd, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_SDIO1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_SDIO2, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_sdio, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_EMMC0, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_CLV_EMMC1, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mfd_emmc, + }, + + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_MRFL_MMC, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_mrfl_mmc, + }, { .vendor = PCI_VENDOR_ID_O2, .device = PCI_DEVICE_ID_O2_8120, diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index bf99359a3a90..793dacd3b841 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -278,7 +278,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) host->mmc->pm_caps |= pdata->pm_caps; if (gpio_is_valid(pdata->ext_cd_gpio)) { - ret = mmc_gpio_request_cd(host->mmc, pdata->ext_cd_gpio); + ret = mmc_gpio_request_cd(host->mmc, pdata->ext_cd_gpio, + 0); if (ret) { dev_err(mmc_dev(host->mmc), "failed to allocate card detect gpio\n"); diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 926aaf6acc67..6debda952155 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -296,9 +296,12 @@ static void sdhci_cmu_set_clock(struct sdhci_host *host, unsigned int clock) unsigned long timeout; u16 clk = 0; - /* don't bother if the clock is going off */ - if (clock == 0) + /* If the clock is going off, set to 0 at clock control register */ + if (clock == 0) { + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + host->clock = clock; return; + } sdhci_s3c_set_clock(host, clock); @@ -608,6 +611,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev) host->hw_name = "samsung-hsmmc"; host->ops = &sdhci_s3c_ops; host->quirks = 0; + host->quirks2 = 0; host->irq = irq; /* Setup quirks for the controller */ diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 62a4a835acc6..696122c1b468 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -84,7 +84,7 @@ static int sdhci_sirf_probe(struct platform_device *pdev) * gets setup in sdhci_add_host() and we oops. */ if (gpio_is_valid(priv->gpio_cd)) { - ret = mmc_gpio_request_cd(host->mmc, priv->gpio_cd); + ret = mmc_gpio_request_cd(host->mmc, priv->gpio_cd, 0); if (ret) { dev_err(&pdev->dev, "card detect irq request failed: %d\n", ret); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index dd2c083c434d..bd8a0982aec3 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -49,7 +49,6 @@ static unsigned int debug_quirks2; static void sdhci_finish_data(struct sdhci_host *); -static void sdhci_send_command(struct sdhci_host *, struct mmc_command *); static void sdhci_finish_command(struct sdhci_host *); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_tuning_timer(unsigned long data); @@ -981,7 +980,7 @@ static void sdhci_finish_data(struct sdhci_host *host) tasklet_schedule(&host->finish_tasklet); } -static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) +void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) { int flags; u32 mask; @@ -1053,6 +1052,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); } +EXPORT_SYMBOL_GPL(sdhci_send_command); static void sdhci_finish_command(struct sdhci_host *host) { @@ -1435,7 +1435,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) } if (host->version >= SDHCI_SPEC_300 && - (ios->power_mode == MMC_POWER_UP)) + (ios->power_mode == MMC_POWER_UP) && + !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) sdhci_enable_preset_value(host, false); sdhci_set_clock(host, ios->clock); @@ -1875,6 +1876,14 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) return 0; } + if (host->ops->platform_execute_tuning) { + spin_unlock(&host->lock); + enable_irq(host->irq); + err = host->ops->platform_execute_tuning(host, opcode); + sdhci_runtime_pm_put(host); + return err; + } + sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); /* @@ -1981,6 +1990,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) if (!tuning_loop_counter || !timeout) { ctrl &= ~SDHCI_CTRL_TUNED_CLK; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); + err = -EIO; } else { if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) { pr_info(DRIVER_NAME ": Tuning procedure" @@ -2491,6 +2501,14 @@ again: result = IRQ_HANDLED; intmask = sdhci_readl(host, SDHCI_INT_STATUS); + + /* + * If we know we'll call the driver to signal SDIO IRQ, disregard + * further indications of Card Interrupt in the status to avoid a + * needless loop. + */ + if (cardint) + intmask &= ~SDHCI_INT_CARD_INT; if (intmask && --max_loops) goto again; out: @@ -2546,8 +2564,6 @@ EXPORT_SYMBOL_GPL(sdhci_disable_irq_wakeups); int sdhci_suspend_host(struct sdhci_host *host) { - int ret; - if (host->ops->platform_suspend) host->ops->platform_suspend(host); @@ -2559,19 +2575,6 @@ int sdhci_suspend_host(struct sdhci_host *host) host->flags &= ~SDHCI_NEEDS_RETUNING; } - ret = mmc_suspend_host(host->mmc); - if (ret) { - if (host->flags & SDHCI_USING_RETUNING_TIMER) { - host->flags |= SDHCI_NEEDS_RETUNING; - mod_timer(&host->tuning_timer, jiffies + - host->tuning_count * HZ); - } - - sdhci_enable_card_detection(host); - - return ret; - } - if (!device_may_wakeup(mmc_dev(host->mmc))) { sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); free_irq(host->irq, host); @@ -2579,14 +2582,14 @@ int sdhci_suspend_host(struct sdhci_host *host) sdhci_enable_irq_wakeups(host); enable_irq_wake(host->irq); } - return ret; + return 0; } EXPORT_SYMBOL_GPL(sdhci_suspend_host); int sdhci_resume_host(struct sdhci_host *host) { - int ret; + int ret = 0; if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) @@ -2615,7 +2618,6 @@ int sdhci_resume_host(struct sdhci_host *host) mmiowb(); } - ret = mmc_resume_host(host->mmc); sdhci_enable_card_detection(host); if (host->ops->platform_resume) @@ -3119,6 +3121,9 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_MAX_CURRENT_MULTIPLIER; } + if (host->ocr_mask) + ocr_avail = host->ocr_mask; + mmc->ocr_avail = ocr_avail; mmc->ocr_avail_sdio = ocr_avail; if (host->ocr_avail_sdio) @@ -3213,6 +3218,8 @@ int sdhci_add_host(struct sdhci_host *host) host->tuning_timer.function = sdhci_tuning_timer; } + sdhci_init(host, 0); + ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, mmc_hostname(mmc), host); if (ret) { @@ -3221,8 +3228,6 @@ int sdhci_add_host(struct sdhci_host *host) goto untasklet; } - sdhci_init(host, 0); - #ifdef CONFIG_MMC_DEBUG sdhci_dumpregs(host); #endif diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b037f188fe44..0a3ed01887db 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -288,6 +288,7 @@ struct sdhci_ops { unsigned int (*get_ro)(struct sdhci_host *host); void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); + int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); void (*hw_reset)(struct sdhci_host *host); void (*platform_suspend)(struct sdhci_host *host); @@ -393,6 +394,8 @@ static inline void *sdhci_priv(struct sdhci_host *host) extern void sdhci_card_detect(struct sdhci_host *host); extern int sdhci_add_host(struct sdhci_host *host); extern void sdhci_remove_host(struct sdhci_host *host, int dead); +extern void sdhci_send_command(struct sdhci_host *host, + struct mmc_command *cmd); #ifdef CONFIG_PM extern int sdhci_suspend_host(struct sdhci_host *host); diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 50adbd155f35..b7e305775314 100644 --- a/drivers/mmc/host/sdricoh_cs.c +++ b/drivers/mmc/host/sdricoh_cs.c @@ -516,9 +516,7 @@ static void sdricoh_pcmcia_detach(struct pcmcia_device *link) #ifdef CONFIG_PM static int sdricoh_pcmcia_suspend(struct pcmcia_device *link) { - struct mmc_host *mmc = link->priv; dev_dbg(&link->dev, "suspend\n"); - mmc_suspend_host(mmc); return 0; } @@ -527,7 +525,6 @@ static int sdricoh_pcmcia_resume(struct pcmcia_device *link) struct mmc_host *mmc = link->priv; dev_dbg(&link->dev, "resume\n"); sdricoh_reset(mmc_priv(mmc)); - mmc_resume_host(mmc); return 0; } #else diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6706b5e3b974..d032b080ac4d 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -61,6 +61,7 @@ #include <linux/platform_device.h> #include <linux/pm_qos.h> #include <linux/pm_runtime.h> +#include <linux/sh_dma.h> #include <linux/spinlock.h> #include <linux/module.h> @@ -133,6 +134,8 @@ INT_BUFWEN | INT_CMD12DRE | INT_BUFRE | \ INT_DTRANE | INT_CMD12RBE | INT_CMD12CRE) +#define INT_CCS (INT_CCSTO | INT_CCSRCV | INT_CCSDE) + /* CE_INT_MASK */ #define MASK_ALL 0x00000000 #define MASK_MCCSDE (1 << 29) @@ -161,7 +164,7 @@ #define MASK_START_CMD (MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | \ MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | \ - MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | \ + MASK_MCRCSTO | MASK_MWDATTO | \ MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO) #define MASK_CLEAN (INT_ERR_STS | MASK_MRBSYE | MASK_MCRSPE | \ @@ -243,6 +246,8 @@ struct sh_mmcif_host { int sg_blkidx; bool power; bool card_present; + bool ccs_enable; /* Command Completion Signal support */ + bool clk_ctrl2_enable; struct mutex thread_lock; /* DMA support */ @@ -386,25 +391,29 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host, host->dma_active = false; - if (!pdata) - return; - - if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0) + if (pdata) { + if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0) + return; + } else if (!host->pd->dev.of_node) { return; + } /* We can only either use DMA for both Tx and Rx or not use it at all */ dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); - host->chan_tx = dma_request_channel(mask, shdma_chan_filter, - (void *)pdata->slave_id_tx); + host->chan_tx = dma_request_slave_channel_compat(mask, shdma_chan_filter, + pdata ? (void *)pdata->slave_id_tx : NULL, + &host->pd->dev, "tx"); dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__, host->chan_tx); if (!host->chan_tx) return; - cfg.slave_id = pdata->slave_id_tx; + /* In the OF case the driver will get the slave ID from the DT */ + if (pdata) + cfg.slave_id = pdata->slave_id_tx; cfg.direction = DMA_MEM_TO_DEV; cfg.dst_addr = res->start + MMCIF_CE_DATA; cfg.src_addr = 0; @@ -412,15 +421,17 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host, if (ret < 0) goto ecfgtx; - host->chan_rx = dma_request_channel(mask, shdma_chan_filter, - (void *)pdata->slave_id_rx); + host->chan_rx = dma_request_slave_channel_compat(mask, shdma_chan_filter, + pdata ? (void *)pdata->slave_id_rx : NULL, + &host->pd->dev, "rx"); dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__, host->chan_rx); if (!host->chan_rx) goto erqrx; - cfg.slave_id = pdata->slave_id_rx; + if (pdata) + cfg.slave_id = pdata->slave_id_rx; cfg.direction = DMA_DEV_TO_MEM; cfg.dst_addr = 0; cfg.src_addr = res->start + MMCIF_CE_DATA; @@ -485,8 +496,12 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) sh_mmcif_writel(host->addr, MMCIF_CE_VERSION, SOFT_RST_ON); sh_mmcif_writel(host->addr, MMCIF_CE_VERSION, SOFT_RST_OFF); + if (host->ccs_enable) + tmp |= SCCSTO_29; + if (host->clk_ctrl2_enable) + sh_mmcif_writel(host->addr, MMCIF_CE_CLK_CTRL2, 0x0F0F0000); sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, tmp | - SRSPTO_256 | SRBSYTO_29 | SRWDTO_29 | SCCSTO_29); + SRSPTO_256 | SRBSYTO_29 | SRWDTO_29); /* byte swap on */ sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_ATYP); } @@ -866,6 +881,9 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, break; } + if (host->ccs_enable) + mask |= MASK_MCCSTO; + if (mrq->data) { sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, 0); sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, @@ -873,7 +891,10 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, } opc = sh_mmcif_set_cmd(host, mrq); - sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0); + if (host->ccs_enable) + sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0); + else + sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0 | INT_CCS); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, mask); /* set arg */ sh_mmcif_writel(host->addr, MMCIF_CE_ARG, cmd->arg); @@ -943,7 +964,7 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) static int sh_mmcif_clk_update(struct sh_mmcif_host *host) { - int ret = clk_enable(host->hclk); + int ret = clk_prepare_enable(host->hclk); if (!ret) { host->clk = clk_get_rate(host->hclk); @@ -956,11 +977,8 @@ static int sh_mmcif_clk_update(struct sh_mmcif_host *host) static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios) { - struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data; struct mmc_host *mmc = host->mmc; - if (pd && pd->set_pwr) - pd->set_pwr(host->pd, ios->power_mode != MMC_POWER_OFF); if (!IS_ERR(mmc->supply.vmmc)) /* Errors ignored... */ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, @@ -1000,7 +1018,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (host->power) { pm_runtime_put_sync(&host->pd->dev); - clk_disable(host->hclk); + clk_disable_unprepare(host->hclk); host->power = false; if (ios->power_mode == MMC_POWER_OFF) sh_mmcif_set_power(host, ios); @@ -1241,11 +1259,14 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; - u32 state; + u32 state, mask; state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); - sh_mmcif_writel(host->addr, MMCIF_CE_INT, - ~(state & sh_mmcif_readl(host->addr, MMCIF_CE_INT_MASK))); + mask = sh_mmcif_readl(host->addr, MMCIF_CE_INT_MASK); + if (host->ccs_enable) + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(state & mask)); + else + sh_mmcif_writel(host->addr, MMCIF_CE_INT, INT_CCS | ~(state & mask)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN); if (state & ~MASK_CLEAN) @@ -1379,6 +1400,8 @@ static int sh_mmcif_probe(struct platform_device *pdev) host->mmc = mmc; host->addr = reg; host->timeout = msecs_to_jiffies(1000); + host->ccs_enable = !pd || !pd->ccs_unsupported; + host->clk_ctrl2_enable = pd && pd->clk_ctrl2_present; host->pd = pdev; @@ -1436,14 +1459,14 @@ static int sh_mmcif_probe(struct platform_device *pdev) } if (pd && pd->use_cd_gpio) { - ret = mmc_gpio_request_cd(mmc, pd->cd_gpio); + ret = mmc_gpio_request_cd(mmc, pd->cd_gpio, 0); if (ret < 0) goto erqcd; } mutex_init(&host->thread_lock); - clk_disable(host->hclk); + clk_disable_unprepare(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) goto emmcaddh; @@ -1464,7 +1487,7 @@ ereqirq1: ereqirq0: pm_runtime_suspend(&pdev->dev); eresume: - clk_disable(host->hclk); + clk_disable_unprepare(host->hclk); eclkupdate: clk_put(host->hclk); eclkget: @@ -1482,7 +1505,7 @@ static int sh_mmcif_remove(struct platform_device *pdev) int irq[2]; host->dying = true; - clk_enable(host->hclk); + clk_prepare_enable(host->hclk); pm_runtime_get_sync(&pdev->dev); dev_pm_qos_hide_latency_limit(&pdev->dev); @@ -1507,7 +1530,7 @@ static int sh_mmcif_remove(struct platform_device *pdev) if (irq[1] >= 0) free_irq(irq[1], host); - clk_disable(host->hclk); + clk_disable_unprepare(host->hclk); mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1515,28 +1538,21 @@ static int sh_mmcif_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int sh_mmcif_suspend(struct device *dev) { struct sh_mmcif_host *host = dev_get_drvdata(dev); - int ret = mmc_suspend_host(host->mmc); - if (!ret) - sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); + sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - return ret; + return 0; } static int sh_mmcif_resume(struct device *dev) { - struct sh_mmcif_host *host = dev_get_drvdata(dev); - - return mmc_resume_host(host->mmc); + return 0; } -#else -#define sh_mmcif_suspend NULL -#define sh_mmcif_resume NULL -#endif /* CONFIG_PM */ +#endif static const struct of_device_id mmcif_of_match[] = { { .compatible = "renesas,sh-mmcif" }, @@ -1545,8 +1561,7 @@ static const struct of_device_id mmcif_of_match[] = { MODULE_DEVICE_TABLE(of, mmcif_of_match); static const struct dev_pm_ops sh_mmcif_dev_pm_ops = { - .suspend = sh_mmcif_suspend, - .resume = sh_mmcif_resume, + SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume) }; static struct platform_driver sh_mmcif_driver = { diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index ebea749297c2..f344659dceac 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -70,20 +70,6 @@ static void sh_mobile_sdhi_clk_disable(struct platform_device *pdev) clk_disable(priv->clk); } -static void sh_mobile_sdhi_set_pwr(struct platform_device *pdev, int state) -{ - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - - p->set_pwr(pdev, state); -} - -static int sh_mobile_sdhi_get_cd(struct platform_device *pdev) -{ - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - - return p->get_cd(pdev); -} - static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host) { int timeout = 1000; @@ -127,9 +113,14 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = { }; static const struct of_device_id sh_mobile_sdhi_of_match[] = { - { .compatible = "renesas,shmobile-sdhi" }, - { .compatible = "renesas,sh7372-sdhi" }, - { .compatible = "renesas,r8a7740-sdhi", .data = &sh_mobile_sdhi_of_cfg[0], }, + { .compatible = "renesas,sdhi-shmobile" }, + { .compatible = "renesas,sdhi-sh7372" }, + { .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], }, + { .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], }, + { .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], }, + { .compatible = "renesas,sdhi-r8a7778", .data = &sh_mobile_sdhi_of_cfg[0], }, + { .compatible = "renesas,sdhi-r8a7779", .data = &sh_mobile_sdhi_of_cfg[0], }, + { .compatible = "renesas,sdhi-r8a7790", .data = &sh_mobile_sdhi_of_cfg[0], }, {}, }; MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match); @@ -180,10 +171,6 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev) mmc_data->capabilities |= p->tmio_caps; mmc_data->capabilities2 |= p->tmio_caps2; mmc_data->cd_gpio = p->cd_gpio; - if (p->set_pwr) - mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; - if (p->get_cd) - mmc_data->get_cd = sh_mobile_sdhi_get_cd; if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) { /* diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 43d962829f8e..d1760ebcac03 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -1030,7 +1030,7 @@ static void tifm_sd_remove(struct tifm_dev *sock) static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state) { - return mmc_suspend_host(tifm_get_drvdata(sock)); + return 0; } static int tifm_sd_resume(struct tifm_dev *sock) @@ -1044,8 +1044,6 @@ static int tifm_sd_resume(struct tifm_dev *sock) if (rc) host->eject = 1; - else - rc = mmc_resume_host(mmc); return rc; } diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 47bdb8fa341b..65edb4a62452 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -104,6 +104,7 @@ static void tmio_mmc_start_dma_rx(struct tmio_mmc_host *host) pio: if (!desc) { /* DMA failed, fall back to PIO */ + tmio_mmc_enable_dma(host, false); if (ret >= 0) ret = -EIO; host->chan_rx = NULL; @@ -116,7 +117,6 @@ pio: } dev_warn(&host->pdev->dev, "DMA failed: %d, falling back to PIO\n", ret); - tmio_mmc_enable_dma(host, false); } dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__, @@ -185,6 +185,7 @@ static void tmio_mmc_start_dma_tx(struct tmio_mmc_host *host) pio: if (!desc) { /* DMA failed, fall back to PIO */ + tmio_mmc_enable_dma(host, false); if (ret >= 0) ret = -EIO; host->chan_tx = NULL; @@ -197,7 +198,6 @@ pio: } dev_warn(&host->pdev->dev, "DMA failed: %d, falling back to PIO\n", ret); - tmio_mmc_enable_dma(host, false); } dev_dbg(&host->pdev->dev, "%s(): desc %p, cookie %d\n", __func__, diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b72edb72f7d2..f3b2d8ca1eca 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -795,9 +795,13 @@ static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd) * omap_hsmmc.c driver does. */ if (!IS_ERR(mmc->supply.vqmmc) && !ret) { - regulator_enable(mmc->supply.vqmmc); + ret = regulator_enable(mmc->supply.vqmmc); udelay(200); } + + if (ret < 0) + dev_dbg(&host->pdev->dev, "Regulators failed to power up: %d\n", + ret); } static void tmio_mmc_power_off(struct tmio_mmc_host *host) @@ -932,25 +936,11 @@ static int tmio_mmc_get_ro(struct mmc_host *mmc) (sd_ctrl_read32(host, CTL_STATUS) & TMIO_STAT_WRPROTECT)); } -static int tmio_mmc_get_cd(struct mmc_host *mmc) -{ - struct tmio_mmc_host *host = mmc_priv(mmc); - struct tmio_mmc_data *pdata = host->pdata; - int ret = mmc_gpio_get_cd(mmc); - if (ret >= 0) - return ret; - - if (!pdata->get_cd) - return -ENOSYS; - else - return pdata->get_cd(host->pdev); -} - static const struct mmc_host_ops tmio_mmc_ops = { .request = tmio_mmc_request, .set_ios = tmio_mmc_set_ios, .get_ro = tmio_mmc_get_ro, - .get_cd = tmio_mmc_get_cd, + .get_cd = mmc_gpio_get_cd, .enable_sdio_irq = tmio_mmc_enable_sdio_irq, }; @@ -1106,7 +1096,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host **host, dev_pm_qos_expose_latency_limit(&pdev->dev, 100); if (pdata->flags & TMIO_MMC_USE_GPIO_CD) { - ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio); + ret = mmc_gpio_request_cd(mmc, pdata->cd_gpio, 0); if (ret < 0) { tmio_mmc_host_remove(_host); return ret; @@ -1155,12 +1145,9 @@ int tmio_mmc_host_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct tmio_mmc_host *host = mmc_priv(mmc); - int ret = mmc_suspend_host(mmc); - - if (!ret) - tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); - return ret; + tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL); + return 0; } EXPORT_SYMBOL(tmio_mmc_host_suspend); @@ -1173,7 +1160,7 @@ int tmio_mmc_host_resume(struct device *dev) /* The MMC core will perform the complete set up */ host->resuming = true; - return mmc_resume_host(mmc); + return 0; } EXPORT_SYMBOL(tmio_mmc_host_resume); diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 4f84586c6e9e..63fac78b3d46 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1269,21 +1269,18 @@ static void via_init_sdc_pm(struct via_crdr_mmc_host *host) static int via_sd_suspend(struct pci_dev *pcidev, pm_message_t state) { struct via_crdr_mmc_host *host; - int ret = 0; host = pci_get_drvdata(pcidev); via_save_pcictrlreg(host); via_save_sdcreg(host); - ret = mmc_suspend_host(host->mmc); - pci_save_state(pcidev); pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); pci_disable_device(pcidev); pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); - return ret; + return 0; } static int via_sd_resume(struct pci_dev *pcidev) @@ -1316,8 +1313,6 @@ static int via_sd_resume(struct pci_dev *pcidev) via_restore_pcictrlreg(sdhost); via_init_sdc_pm(sdhost); - ret = mmc_resume_host(sdhost->mmc); - return ret; } diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index cb9f361c03ab..4262296c12fa 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2079,7 +2079,7 @@ static void vub300_enable_sdio_irq(struct mmc_host *mmc, int enable) kref_put(&vub300->kref, vub300_delete); } -void vub300_init_card(struct mmc_host *mmc, struct mmc_card *card) +static void vub300_init_card(struct mmc_host *mmc, struct mmc_card *card) { /* NOT irq */ struct vub300_mmc_host *vub300 = mmc_priv(mmc); dev_info(&vub300->udev->dev, "NO host QUIRKS for this card\n"); @@ -2392,26 +2392,12 @@ static void vub300_disconnect(struct usb_interface *interface) #ifdef CONFIG_PM static int vub300_suspend(struct usb_interface *intf, pm_message_t message) { - struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); - if (!vub300 || !vub300->mmc) { - return 0; - } else { - struct mmc_host *mmc = vub300->mmc; - mmc_suspend_host(mmc); - return 0; - } + return 0; } static int vub300_resume(struct usb_interface *intf) { - struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); - if (!vub300 || !vub300->mmc) { - return 0; - } else { - struct mmc_host *mmc = vub300->mmc; - mmc_resume_host(mmc); - return 0; - } + return 0; } #else #define vub300_suspend NULL diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index e954b7758876..1defd5ed3236 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1814,28 +1814,11 @@ static void wbsd_pnp_remove(struct pnp_dev *dev) #ifdef CONFIG_PM -static int wbsd_suspend(struct wbsd_host *host, pm_message_t state) -{ - BUG_ON(host == NULL); - - return mmc_suspend_host(host->mmc); -} - -static int wbsd_resume(struct wbsd_host *host) -{ - BUG_ON(host == NULL); - - wbsd_init_device(host); - - return mmc_resume_host(host->mmc); -} - static int wbsd_platform_suspend(struct platform_device *dev, pm_message_t state) { struct mmc_host *mmc = platform_get_drvdata(dev); struct wbsd_host *host; - int ret; if (mmc == NULL) return 0; @@ -1844,12 +1827,7 @@ static int wbsd_platform_suspend(struct platform_device *dev, host = mmc_priv(mmc); - ret = wbsd_suspend(host, state); - if (ret) - return ret; - wbsd_chip_poweroff(host); - return 0; } @@ -1872,7 +1850,8 @@ static int wbsd_platform_resume(struct platform_device *dev) */ mdelay(5); - return wbsd_resume(host); + wbsd_init_device(host); + return 0; } #ifdef CONFIG_PNP @@ -1880,16 +1859,12 @@ static int wbsd_platform_resume(struct platform_device *dev) static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state) { struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); - struct wbsd_host *host; if (mmc == NULL) return 0; DBGF("Suspending...\n"); - - host = mmc_priv(mmc); - - return wbsd_suspend(host, state); + return 0; } static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) @@ -1922,7 +1897,8 @@ static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) */ mdelay(5); - return wbsd_resume(host); + wbsd_init_device(host); + return 0; } #endif /* CONFIG_PNP */ diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 34231d5168fc..e902ed7846b0 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -212,28 +212,14 @@ struct wmt_mci_priv { static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable) { - u32 reg_tmp; - if (enable) { - if (priv->power_inverted) { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp | BM_SD_OFF, - priv->sdmmc_base + SDMMC_BUSMODE); - } else { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp & (~BM_SD_OFF), - priv->sdmmc_base + SDMMC_BUSMODE); - } - } else { - if (priv->power_inverted) { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp & (~BM_SD_OFF), - priv->sdmmc_base + SDMMC_BUSMODE); - } else { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp | BM_SD_OFF, - priv->sdmmc_base + SDMMC_BUSMODE); - } - } + u32 reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + + if (enable ^ priv->power_inverted) + reg_tmp &= ~BM_SD_OFF; + else + reg_tmp |= BM_SD_OFF; + + writeb(reg_tmp, priv->sdmmc_base + SDMMC_BUSMODE); } static void wmt_mci_read_response(struct mmc_host *mmc) @@ -939,28 +925,23 @@ static int wmt_mci_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct mmc_host *mmc = platform_get_drvdata(pdev); struct wmt_mci_priv *priv; - int ret; if (!mmc) return 0; priv = mmc_priv(mmc); - ret = mmc_suspend_host(mmc); - - if (!ret) { - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + - SDMMC_BUSMODE); + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + + SDMMC_BUSMODE); - reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); - writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); - writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); - writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); - clk_disable(priv->clk_sdmmc); - } - return ret; + clk_disable(priv->clk_sdmmc); + return 0; } static int wmt_mci_resume(struct device *dev) @@ -969,7 +950,6 @@ static int wmt_mci_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct mmc_host *mmc = platform_get_drvdata(pdev); struct wmt_mci_priv *priv; - int ret = 0; if (mmc) { priv = mmc_priv(mmc); @@ -987,10 +967,9 @@ static int wmt_mci_resume(struct device *dev) writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base + SDMMC_INTMASK0); - ret = mmc_resume_host(mmc); } - return ret; + return 0; } static const struct dev_pm_ops wmt_mci_pm = { |