From b83e867026caedd6d67fccfbe6e9a621edd79b21 Mon Sep 17 00:00:00 2001 From: Grant Grundler Date: Thu, 19 Sep 2013 18:21:36 -0700 Subject: mmc: core: remove dead function mmc_try_claim_host cscope says there are no callers for mmc_try_claim_host in the kernel. No reason to keep it. Signed-off-by: Grant Grundler Acked-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index bf18b6b..006ead2 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -918,31 +918,6 @@ int __mmc_claim_host(struct mmc_host *host, atomic_t *abort) EXPORT_SYMBOL(__mmc_claim_host); /** - * mmc_try_claim_host - try exclusively to claim a host - * @host: mmc host to claim - * - * Returns %1 if the host is claimed, %0 otherwise. - */ -int mmc_try_claim_host(struct mmc_host *host) -{ - int claimed_host = 0; - unsigned long flags; - - spin_lock_irqsave(&host->lock, flags); - if (!host->claimed || host->claimer == current) { - host->claimed = 1; - host->claimer = current; - host->claim_cnt += 1; - claimed_host = 1; - } - spin_unlock_irqrestore(&host->lock, flags); - if (host->ops->enable && claimed_host && host->claim_cnt == 1) - host->ops->enable(host); - return claimed_host; -} -EXPORT_SYMBOL(mmc_try_claim_host); - -/** * mmc_release_host - release a host * @host: mmc host to release * diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index da51bec..a00fc49 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -188,7 +188,6 @@ extern unsigned int mmc_align_data_size(struct mmc_card *, unsigned int); extern int __mmc_claim_host(struct mmc_host *host, atomic_t *abort); extern void mmc_release_host(struct mmc_host *host); -extern int mmc_try_claim_host(struct mmc_host *host); extern void mmc_get_card(struct mmc_card *card); extern void mmc_put_card(struct mmc_card *card); -- cgit v0.10.2 From 357002b9c09e5332c9fcd4fa3d3c0fa00ca6ae4f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 24 Sep 2013 18:55:22 -0700 Subject: sh: ecovec: fixup compile error on sdhi afa2c9407f8908 ("sh: ecovec24: Use MMC/SDHI CD and RO GPIO") added .tmio_flags = TMIO_MMC_USE_GPIO_CD on sh_mobile_sdhi_info, but it needs header. This patch adds it. Signed-off-by: Kuninori Morimoto Reviewed-by: Yusuke Goda Acked-by: Laurent Pinchart Signed-off-by: Chris Ball diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index 1fa8be4..122f737 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include -- cgit v0.10.2 From 369ac8614fcef040f96f6548acb43dcefebd21b9 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Sat, 31 Aug 2013 00:11:06 +0900 Subject: mmc: dw_mmc: don't queue up a card detect at slot startup The MMC subsystem handles looking for a card at probe time. Queuing up our own can race with the rest of the MMC subsystem and cause problems if we get unlucky with timing. Just remove driver own detection triggering. While progressing the request from 'mmc_rescan', if 'dw_mci_work_routine_card' routine is activated, it will cancel the current request. The problem case is that 'mmc_rescan' is prior to 'dw_mci_work_routine_card' from host own. Specifically, the following message shows the detection problem in driver's probing. It would get an err -123 (-ENOMEDIUM) during probe. [ 4.216595] dwmmc_exynos 12210000.dwmmc1: Using internal DMA controller. [ 4.395935] dwmmc_exynos 12210000.dwmmc1: Version ID is 250a [ 4.401948] dwmmc_exynos 12210000.dwmmc1: DW MMC controller at irq 108, 64 bit host data width, 64 deep fifo [ 4.424430] dwmmc_exynos 12210000.dwmmc1: sdr0 mode (irq=108, width=0) [ 4.453975] dwmmc_exynos 12210000.dwmmc1: sdr0 mode (irq=108, width=0) [ 4.459592] mmc_host mmc1: Bus speed (slot 0) = 100000000Hz (slot req 400000Hz, actual 400000HZ div = 125) [ 4.484258] dwmmc_exynos 12210000.dwmmc1: 1 slots initialized [ 4.485406] dwmmc_exynos 12210000.dwmmc1: sdr0 mode (irq=108, width=0) [ 4.487606] dwmmc_exynos 12210000.dwmmc1: sdr0 mode (irq=108, width=0) [ 4.489794] dwmmc_exynos 12210000.dwmmc1: sdr0 mode (irq=108, width=0) [ 4.509757] mmc1: error -123 whilst initialising SDIO card Signed-off-by: Doug Anderson Acked-by: Seungwon Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 018f365..7192f89 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2008,12 +2008,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: -- cgit v0.10.2 From e2c635999f44324d0c9db3330debd807be809507 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Sat, 31 Aug 2013 00:11:21 +0900 Subject: mmc: dw_mmc: Add exynos resume_noirq callback to clear WAKEUP_INT If the WAKEUP_INT is asserted at wakeup and not cleared, we'll end up looping around forever. This has been seen to happen on exynos5420 silicon despite the fact that we haven't enabled any wakeup events due to a silicon errata. It is safe to do on all exynos variants. Signed-off-by: Doug Anderson Acked-by: Seungwon Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 6a1fa21..2f28dd8 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -30,6 +30,7 @@ #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 @@ -105,6 +106,49 @@ static int dw_mci_exynos_setup_clock(struct dw_mci *host) 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); + + 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) { /* @@ -194,13 +238,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, }, }; -- cgit v0.10.2 From fdf492a1cc6db8c13f650948f43855a077600895 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Sat, 31 Aug 2013 00:11:43 +0900 Subject: mmc: dw_mmc: Honor requests to set the clock to 0 Previously the dw_mmc driver would ignore any requests to disable the card's clock. This doesn't seem like a good thing in general, but had one extra bad side effect in the following situation: * mmc core would set clk to 400kHz at boot time while scanning * mmc core would set clk to 0 since no card, but it would be ignored. * suspend to ram and resume; clocks in the dw_mmc IP block are now 0 but dw_mmc thinks that they're 400kHz (it ignored the set to 0). * insert card * mmc core would set clk to 400kHz which would be considered a no-op. Note that if there is no card in the slot and we do a suspend/resume cycle, we _do_ still end up with differences in a dw_mmc register dump, but the differences are clock related and we've got the clock disabled both before and after, so this should be OK. Signed-off-by: Doug Anderson Signed-off-by: Seungwon Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 7192f89..bc3a430 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -88,6 +88,9 @@ struct idmac_desc { * @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. @@ -105,6 +108,7 @@ struct dw_mci_slot { 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 @@ -632,24 +636,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 +687,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)); } @@ -807,13 +821,11 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) mci_writel(slot->host, UHS_REG, regs); - 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); -- cgit v0.10.2 From 2eb2944fa7d14703898063eaee34d41524fb46ad Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Sat, 31 Aug 2013 00:11:49 +0900 Subject: mmc: dw_mmc: Set timeout to max upon resume The TMOUT register is set to 0xffffffff at probe time but isn't set after suspend/resume. Add an init of this value. No problems were observed without this (it will also be set in __dw_mci_start_request if there is data to send), but it makes the register dump before and after suspend cleaner. Signed-off-by: Doug Anderson Acked-by: Seungwon Jeon Reviewed-by: Tomasz Figa Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bc3a430..19b289d 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2510,6 +2510,9 @@ int dw_mci_resume(struct dw_mci *host) /* Restore the old value at FIFOTH register */ mci_writel(host, FIFOTH, host->fifoth_val); + /* 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 | SDMMC_INT_TXDR | SDMMC_INT_RXDR | -- cgit v0.10.2 From e6c784eded7b39caeaf2e9206336fa1daeccfd09 Mon Sep 17 00:00:00 2001 From: Yuvaraj Kumar C D Date: Sat, 31 Aug 2013 00:11:57 +0900 Subject: mmc: dw_mmc: exynos: move the exynos private init Currently platform specific private data initialization is done by dw_mci_exynos_priv_init and dw_mci_exynos_parse_dt. As we already have separate platform specific device tree parser dw_mci_exynos_parse_dt, move the dw_mci_exynos_priv_init code to dw_mci_exynos_parse_dt. We can use the dw_mci_exynos_priv_init to do some actual platform specific initialization of SMU and etc. Signed-off-by: Yuvaraj Kumar C D Tested-by: Alim Akhtar Tested-by: Jaehoon Chung Acked-by: Seungwon Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 2f28dd8..37bad61 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -72,22 +72,8 @@ static struct dw_mci_exynos_compatible { 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; - } - - 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; - } + struct dw_mci_exynos_priv_data *priv = host->priv; - host->priv = priv; return 0; } @@ -174,12 +160,24 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) 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; + 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; + } + of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); priv->ciu_div = div; @@ -188,14 +186,14 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) if (ret) return ret; - priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); - ret = of_property_read_u32_array(np, "samsung,dw-mshc-ddr-timing", timing, 2); if (ret) return ret; + priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); + host->priv = priv; return 0; } -- cgit v0.10.2 From 3aa741f39587d03f38b1c8883d3cf06d242abf16 Mon Sep 17 00:00:00 2001 From: Yuvaraj Kumar C D Date: Sat, 31 Aug 2013 00:12:10 +0900 Subject: mmc: dw_mmc: socfpga: move socfpga private init Currently platform specific private data initialization is done by dw_mci_socfpga_priv_init and dw_mci_socfpga_parse_dt. As we already have separate platform specific device tree parser dw_mci_socfpga_parse_dt, move the dw_mci_socfpga_priv_init code to dw_mci_socfpga_parse_dt. We can use the dw_mci_socfpga_priv_init to do some actual platform specific initialization. Signed-off-by: Yuvaraj Kumar C D Tested-by: Alim Akhtar Tested-by: Jaehoon Chung Acked-by: Seungwon Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c index 14b5961..4d7b999 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; } -- cgit v0.10.2 From 002f0d5c63e9f37347864c1c9f5656c3cfdb6947 Mon Sep 17 00:00:00 2001 From: Yuvaraj Kumar C D Date: Sat, 31 Aug 2013 00:12:19 +0900 Subject: mmc: dw_mmc: move the platform specific init call Current platform specific private data initialization call dw_mci_exynos_priv_init() can be used to do platform specific initialization of SMU and others in future. So the drv_data->init call has moved to dw_mci_probe(). Signed-off-by: Yuvaraj Kumar C D Tested-by: Alim Akhtar Tested-by: Jaehoon Chung Acked-by: Seungwon Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 2089752..5c49656 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -39,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) @@ -59,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.c b/drivers/mmc/host/dw_mmc.c index 19b289d..9595922 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2227,6 +2227,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) { -- cgit v0.10.2 From 6bce431ca13bb72261adbd20b7bc58c447ef2ce3 Mon Sep 17 00:00:00 2001 From: Yuvaraj Kumar C D Date: Sat, 31 Aug 2013 00:12:35 +0900 Subject: mmc: dw_mmc: exynos: configure SMU in exynos5420 Exynos5420 Mobile Storage Host controller has Security Management Unit (SMU) for channel 0 and channel 1 (mainly for eMMC). This time, SMU configuration is set for non-encryption mode. Signed-off-by: Yuvaraj Kumar C D Signed-off-by: Alim Akhtar Tested-by: Jaehoon Chung Acked-by: Seungwon Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 37bad61..83405bd 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -35,12 +35,32 @@ #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) + /* 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 */ @@ -67,6 +87,9 @@ static struct dw_mci_exynos_compatible { }, { .compatible = "samsung,exynos5420-dw-mshc", .ctrl_type = DW_MCI_TYPE_EXYNOS5420, + }, { + .compatible = "samsung,exynos5420-dw-mshc-smu", + .ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU, }, }; @@ -74,6 +97,15 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) { struct dw_mci_exynos_priv_data *priv = host->priv; + 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); + } + return 0; } @@ -82,7 +114,8 @@ static int dw_mci_exynos_setup_clock(struct dw_mci *host) struct dw_mci_exynos_priv_data *priv = host->priv; if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5250 || - priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420) + priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420 || + priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) host->bus_hz /= (priv->ciu_div + 1); else if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS4412) host->bus_hz /= EXYNOS4412_FIXED_CIU_CLK_DIV; @@ -104,6 +137,7 @@ 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); } @@ -222,6 +256,8 @@ static const struct of_device_id dw_mci_exynos_match[] = { .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); -- cgit v0.10.2 From 0976f16d2d7fdd3d33738c1f64b83cde6f783db3 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:12:42 +0900 Subject: mmc: dw_mmc: add support tuning scheme For the speed modes HS200 and SDR104, tuning is needed to determine the correct sampling point. Actual tuning procedure is provided by specific host controller driver. This patch defines the tuning command and tuning data. Additionally, 'struct dw_mci_slot' is moved to header file to consider the extensive usages in driver. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 9595922..696bb46 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -76,44 +76,34 @@ 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. - * @__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; +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 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; +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, }; #if defined(CONFIG_DEBUG_FS) @@ -951,6 +941,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, @@ -959,6 +981,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) diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 81b2994..b281fdc 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -184,6 +184,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 +249,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_ */ -- cgit v0.10.2 From c537a1c5ff63d3553617a9ff80ef5ed1493028e2 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:12:50 +0900 Subject: mmc: dw_mmc: exynos: add variable delay tuning sequence Implements variable delay tuning. In this change, exynos host can determine the correct sampling point for the HS200 and SDR104 speed mode. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 83405bd..b86f518 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -14,8 +14,10 @@ #include #include #include +#include #include #include +#include #include "dw_mmc.h" #include "dw_mmc-pltfm.h" @@ -231,6 +233,127 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) 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 | @@ -247,6 +370,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[] = { -- cgit v0.10.2 From c6d9deda64d426a25aafeb179962c9cf3c834e2f Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:13:03 +0900 Subject: mmc: dw_mmc: exynos: adjust the clock rate with speed mode Exynos's host has divider logic before 'cclk_in' to controller core. It means that actual clock rate of ciu clock comes from this divider value. So, source clock should be adjusted along with 'ciu_div' which indicates the host's divider ratio. Setting clock rate basically fits the required speed. Specially, 'cclk_in' should have double rate of target speed in case of DDR 8-bit mode. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index b86f518..d42e664 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -56,6 +56,8 @@ #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, @@ -71,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 { @@ -114,16 +117,9 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) 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 || - priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420 || - priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) - 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; } @@ -187,11 +183,38 @@ 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) @@ -214,8 +237,14 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) priv->ctrl_type = exynos_compat[idx].ctrl_type; } - of_property_read_u32(np, "samsung,dw-mshc-ciu-div", &div); - priv->ciu_div = div; + 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); -- cgit v0.10.2 From 5dd63f524666d0b1d1b07da7cc993bc8fcd141a9 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:13:09 +0900 Subject: mmc: dw_mmc: add the capability to support hs200 mode As host controller can support eMMC's HS200 mode at 1.8V or 1.2V, these capability will be added. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt index cdcebea..ae5f8e8 100644 --- a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt @@ -66,6 +66,10 @@ Optional properties: * supports-highspeed: Enables support for high speed cards (up to 50MHz) +* caps2-mmc-hs200-1_8v: Supports mmc HS200 SDR 1.8V mode + +* caps2-mmc-hs200-1_2v: Supports mmc HS200 SDR 1.2V mode + * broken-cd: as documented in mmc core bindings. * vmmc-supply: The phandle to the regulator to use for vmmc. If this is @@ -95,6 +99,7 @@ board specific portions as listed below. clock-frequency = <400000000>; num-slots = <1>; supports-highspeed; + caps2-mmc-hs200-1_8v; broken-cd; fifo-depth = <0x80>; card-detect-delay = <200>; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 696bb46..d9f864f 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2187,6 +2187,12 @@ 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, "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; } -- cgit v0.10.2 From 10b498419f41ffaca7510286c0db8ea34300576d Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:13:22 +0900 Subject: mmc: dw_mmc: move supports-highspeed of quirks to caps 'supports-highspeed' is not one of the quirks but is a capability. So, it's removed from quirks. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index d9f864f..cca9aa6 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1999,9 +1999,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; @@ -2130,9 +2127,6 @@ static struct dw_mci_of_quirks { int id; } of_quirks[] = { { - .quirk = "supports-highspeed", - .id = DW_MCI_QUIRK_HIGHSPEED, - }, { .quirk = "broken-cd", .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, }, @@ -2187,6 +2181,9 @@ 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; -- cgit v0.10.2 From 1f44a2a55787faa08a50266fa5dc99f0dcd36b7c Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:13:31 +0900 Subject: mmc: dw_mmc: set the supported max/min frequency Both f_max and f_min will be informed for core layer to request valid clock rate. But current setting from 'host->bus_hz' may not represent the max/min frequency properly. Even if host can actually support high speed than bus_hz, core layer will not request clock rate over bus_hz. Basically, f_max/f_min can be set with the values according to spec. And then host will make its best effort to meet the rate. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt index ae5f8e8..fdbf686 100644 --- a/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/synopsis-dw-mshc.txt @@ -52,6 +52,9 @@ Optional properties: is specified and the ciu clock is specified then we'll try to set the ciu clock to this at probe time. +* clock-freq-min-max: Minimum and Maximum clock frequency for card output + clock(cclk_out). If it's not specified, max is 200MHZ and min is 400KHz by default. + * num-slots: specifies the number of slots supported by the controller. The number of physical slots actually used could be equal or less than the value specified by num-slots. If this property is not specified, the value @@ -97,6 +100,7 @@ board specific portions as listed below. dwmmc0@12200000 { clock-frequency = <400000000>; + clock-freq-min-max = <400000 200000000>; num-slots = <1>; supports-highspeed; caps2-mmc-hs200-1_8v; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index cca9aa6..50da6e3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -50,6 +50,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 | \ @@ -1936,6 +1939,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); @@ -1951,8 +1955,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); -- cgit v0.10.2 From 524268990e54dab9bd71a984b70c13ce0eb1c525 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:13:42 +0900 Subject: mmc: dw_mmc: adjust the fifoth with block size This change helps to choose msize, rx_watermark and tx_watermark depending on block size for IDMAC mode. For SDIO block size can be variable, so if these values are set incorrectly, card clock may stop. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 50da6e3..acefdeb 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -529,6 +529,47 @@ 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 int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) { int sg_len; @@ -553,6 +594,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; @@ -603,6 +652,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; } } @@ -2368,8 +2432,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 */ @@ -2552,8 +2616,12 @@ 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); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index b281fdc..a0f2f8d 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -128,6 +128,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) diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 198f0fa..4ec9dcc 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -129,6 +129,7 @@ struct dw_mci { struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; + unsigned int prev_blksz; struct workqueue_struct *card_workqueue; /* DMA interface members*/ -- cgit v0.10.2 From f1d2736c815629f3b43d8eebf73a81b581438e65 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:13:55 +0900 Subject: mmc: dw_mmc: control card read threshold Card Read Threshold should be ensured that the card clock does not stop in the middle of a block of data being transferred from the card to the Host. Specially, clock stop is allowed in fast transfer such as HS200 or SDR104 mode. And so, it should be enabled. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index acefdeb..8eb3798 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -570,6 +570,37 @@ done: #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; @@ -627,10 +658,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; @@ -877,6 +910,7 @@ 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; /* * Use mirror of ios->clock to prevent race with mmc diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index a0f2f8d..6bf24ab 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) /* @@ -146,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) \ diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 4ec9dcc..a829f7e 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -130,6 +130,7 @@ struct dw_mci { struct mmc_command *cmd; struct mmc_data *data; unsigned int prev_blksz; + unsigned char timing; struct workqueue_struct *card_workqueue; /* DMA interface members*/ -- cgit v0.10.2 From 71abb1339436160b6ab0ae2f816a4046c7328f28 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:13:59 +0900 Subject: mmc: dw_mmc: fix error handling on response error Even if response error is detected in case data command, data transfer is continued. It means that data can live in FIFO. Current handling just breaks out the request when seeing the command error. This causes kernel panic in dw_mci_read_data_pio() [host->data = NULL]. And also, FIFO should be guaranteed to be empty. Unable to handle kernel NULL pointer dereference at virtual address 00000018 <...> [] (dw_mci_read_data_pio+0x68/0x198) from [] (dw_mci_interrupt+0x374/0x3a0) [] (dw_mci_interrupt+0x374/0x3a0) from [] (handle_irq_event_percpu+0x50/0x194) [] (handle_irq_event_percpu+0x50/0x194) from [] (handle_irq_event+0x3c/0x5c) [] (handle_irq_event+0x3c/0x5c) from [] (handle_fasteoi_irq+0xa4/0x148) [] (handle_fasteoi_irq+0xa4/0x148) from [] (generic_handle_irq+0x20/0x30) [] (generic_handle_irq+0x20/0x30) from [] (handle_IRQ+0x38/0x90) [] (handle_IRQ+0x38/0x90) from [] (gic_handle_irq+0x34/0x68) [] (gic_handle_irq+0x34/0x68) from [] (__irq_svc+0x40/0x70) Exception stack(0xef0b1c00 to 0xef0b1c48) 1c00: 000eb0cf ffffffff 00001300 c01a7738 ef295e10 0000000a c04df298 ef0b1dc0 1c20: ef295ec0 00000000 00000000 00000006 00000000 ef0b1c48 c02b1274 c01a7764 1c40: 20000113 ffffffff [] (__irq_svc+0x40/0x70) from [] (__loop_delay+0x0/0xc) Code: e1a00005 e0891006 e0662004 e12fff33 (e59a3018) ---[ end trace a7043b9ba9aed1db ]--- Kernel panic - not syncing: Fatal exception in interrupt Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8eb3798..649127e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1148,11 +1148,6 @@ 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; - } } } @@ -1193,6 +1188,17 @@ static void dw_mci_tasklet_func(unsigned long priv) goto unlock; } + if (cmd->data && cmd->error) { + dw_mci_stop_dma(host); + if (data->stop) { + send_stop_cmd(host, data); + state = STATE_SENDING_STOP; + break; + } else { + host->data = NULL; + } + } + if (!host->mrq->data || cmd->error) { dw_mci_request_end(host, host->mrq); goto unlock; @@ -1287,7 +1293,17 @@ static void dw_mci_tasklet_func(unsigned long priv) &host->pending_events)) break; + /* CMD error in data command */ + if (host->mrq->cmd->error && host->mrq->data) { + sg_miter_stop(&host->sg_miter); + host->sg = NULL; + ctrl = mci_readl(host, CTRL); + ctrl |= SDMMC_CTRL_FIFO_RESET; + mci_writel(host, CTRL, ctrl); + } + host->cmd = NULL; + host->data = NULL; dw_mci_command_complete(host, host->mrq->stop); dw_mci_request_end(host, host->mrq); goto unlock; -- cgit v0.10.2 From 90c2143a8f6d0cd1dbae1ea32fcd1befb81e4b0d Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:14:05 +0900 Subject: mmc: dw_mmc: guarantee stop-abort cmd in data errors In error cases, DTO interrupt may or may not be generated depending on remained data. Stop/Abort command ensures DTO generation for that situation. Currently if 'stop' field of data is empty, there is no stop/abort command. So, it could hang waiting DTO. This change reinforces these cases. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 649127e..b4328ad 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -246,10 +247,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 */ @@ -276,6 +282,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) { @@ -290,9 +330,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 */ @@ -828,6 +869,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, @@ -1190,13 +1233,9 @@ static void dw_mci_tasklet_func(unsigned long priv) if (cmd->data && cmd->error) { dw_mci_stop_dma(host); - if (data->stop) { - send_stop_cmd(host, data); - state = STATE_SENDING_STOP; - break; - } else { - host->data = NULL; - } + send_stop_abort(host, data); + state = STATE_SENDING_STOP; + break; } if (!host->mrq->data || cmd->error) { @@ -1211,8 +1250,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; } @@ -1272,7 +1310,7 @@ static void dw_mci_tasklet_func(unsigned long priv) data->error = 0; } - if (!data->stop) { + if (!data->stop && !data->error) { dw_mci_request_end(host, host->mrq); goto unlock; } @@ -1284,8 +1322,10 @@ static void dw_mci_tasklet_func(unsigned long priv) } prev_state = state = STATE_SENDING_STOP; - if (!data->error) - send_stop_cmd(host, data); + if (data->stop && !data->error) { + /* stop command for open-ended transfer*/ + send_stop_abort(host, data); + } /* fall through */ case STATE_SENDING_STOP: @@ -1304,7 +1344,12 @@ static void dw_mci_tasklet_func(unsigned long priv) host->cmd = NULL; host->data = NULL; - dw_mci_command_complete(host, host->mrq->stop); + + if (host->mrq->stop) + dw_mci_command_complete(host, host->mrq->stop); + else + host->cmd_status = 0; + dw_mci_request_end(host, host->mrq); goto unlock; @@ -1888,11 +1933,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; } diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index a829f7e..6ce7d2c 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -15,6 +15,7 @@ #define LINUX_MMC_DW_MMC_H #include +#include #define MAX_MCI_SLOTS 2 @@ -129,6 +130,7 @@ struct dw_mci { struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; + struct mmc_command stop_abort; unsigned int prev_blksz; unsigned char timing; struct workqueue_struct *card_workqueue; -- cgit v0.10.2 From e352c813110004b205ad285dcf54619a4c09c47c Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:14:17 +0900 Subject: mmc: dw_mmc: rework the code related to cmd/data completion Main change corresponds to dw_mci_command_complete(). And EBE is divided into read and write. Some minor changes for code readability. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b4328ad..03ee5c6 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -845,7 +845,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) { @@ -1157,7 +1159,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; @@ -1192,6 +1194,57 @@ static void dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY) mdelay(20); } + + return cmd->error; +} + +static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) +{ + u32 status = host->data_status, ctrl; + + 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, 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; + } + + return data->error; } static void dw_mci_tasklet_func(unsigned long priv) @@ -1199,14 +1252,17 @@ 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; + u32 ctrl; + unsigned int err; spin_lock(&host->lock); state = host->state; data = host->data; + mrq = host->mrq; do { prev_state = state; @@ -1223,23 +1279,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 (cmd->data && cmd->error) { + if (cmd->data && err) { dw_mci_stop_dma(host); send_stop_abort(host, data); state = STATE_SENDING_STOP; break; } - if (!host->mrq->data || cmd->error) { - dw_mci_request_end(host, host->mrq); + if (!cmd->data || err) { + dw_mci_request_end(host, mrq); goto unlock; } @@ -1270,62 +1326,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 && !data->error) { - 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->stop && !data->error) { - /* stop command for open-ended transfer*/ - send_stop_abort(host, data); - } + /* fall through */ case STATE_SENDING_STOP: @@ -1334,7 +1355,7 @@ static void dw_mci_tasklet_func(unsigned long priv) break; /* CMD error in data command */ - if (host->mrq->cmd->error && host->mrq->data) { + if (mrq->cmd->error && mrq->data) { sg_miter_stop(&host->sg_miter); host->sg = NULL; ctrl = mci_readl(host, CTRL); @@ -1345,12 +1366,12 @@ static void dw_mci_tasklet_func(unsigned long priv) host->cmd = NULL; host->data = NULL; - if (host->mrq->stop) - dw_mci_command_complete(host, host->mrq->stop); + if (mrq->stop) + dw_mci_command_complete(host, mrq->stop); else host->cmd_status = 0; - dw_mci_request_end(host, host->mrq); + dw_mci_request_end(host, mrq); goto unlock; case STATE_DATA_ERROR: -- cgit v0.10.2 From 31bff450d243bc6a75bdfd0a60e4bc18907c7f27 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:14:23 +0900 Subject: mmc: dw_mmc: gather each reset code into functions There are three resets in CTRL register. FIFO reset is especially used in several points with the same routine. It could be replaced with one function and the others may be applied similarly if needed. So, mci_wait_reset() is modified to allow various bit field of reset. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 03ee5c6..068f611 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -110,6 +110,9 @@ static const u8 tuning_blk_pattern_8bit[] = { 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) { @@ -1200,7 +1203,7 @@ static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) { - u32 status = host->data_status, ctrl; + u32 status = host->data_status; if (status & DW_MCI_DATA_ERROR_FLAGS) { if (status & SDMMC_INT_DRTO) { @@ -1230,15 +1233,9 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) /* * 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. + * in the FIFO */ - sg_miter_stop(&host->sg_miter); - host->sg = NULL; - ctrl = mci_readl(host, CTRL); - ctrl |= SDMMC_CTRL_FIFO_RESET; - mci_writel(host, CTRL, ctrl); + dw_mci_fifo_reset(host); } else { data->bytes_xfered = data->blocks * data->blksz; data->error = 0; @@ -1255,7 +1252,6 @@ static void dw_mci_tasklet_func(unsigned long priv) struct mmc_request *mrq; enum dw_mci_state state; enum dw_mci_state prev_state; - u32 ctrl; unsigned int err; spin_lock(&host->lock); @@ -1355,13 +1351,8 @@ static void dw_mci_tasklet_func(unsigned long priv) break; /* CMD error in data command */ - if (mrq->cmd->error && mrq->data) { - sg_miter_stop(&host->sg_miter); - host->sg = NULL; - ctrl = mci_readl(host, CTRL); - ctrl |= SDMMC_CTRL_FIFO_RESET; - mci_writel(host, CTRL, ctrl); - } + if (mrq->cmd->error && mrq->data) + dw_mci_fifo_reset(host); host->cmd = NULL; host->data = NULL; @@ -1980,18 +1971,8 @@ 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 */ @@ -2289,27 +2270,51 @@ 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; @@ -2517,7 +2522,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; @@ -2723,7 +2728,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; } -- cgit v0.10.2 From 5ce9d96177908e288bf64449a3b7b7159940050b Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:14:33 +0900 Subject: mmc: dw_mmc: amend use of idmac sw reset First, compiling warning along with previous change is removed. [drivers/mmc/host/dw_mmc.c:1890:7: warning: unused variable 'ctrl'] And with the recommendation in manual, IDMAC software reset is followed by dma-reset of the CTRL register in order to terminate the transfer. Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 068f611..05c239f 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -372,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; @@ -385,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); } @@ -476,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); @@ -1906,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) { @@ -1974,10 +1982,7 @@ static void dw_mci_work_routine_card(struct work_struct *work) /* 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 } -- cgit v0.10.2 From aa50f259d65603f40a82ef0d256a6a954a3db283 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Sat, 31 Aug 2013 00:14:38 +0900 Subject: mmc: dw_mmc: fix the transfer termination in IDMAC mode In IDMAC mode EVENT_XFER_COMPLETE is set when RI/TI of last descriptor is done. So if errors are happened in the middle of data transfers, 'dw_mci_stop_dma' during error handing can be called and eventually prevents this flag to be set. This results in permanent wait for EVENT_XFER_COMPLETE in 'dw_mci_tasklet_func'. Therefore, if dma running is stopped forcibly, EVENT_XFER_COMPLETE should be set. Reported-by: Bing Zhao Signed-off-by: Doug Anderson Signed-off-by: Seungwon Jeon Tested-by: Alim Akhtar Acked-by: Jaehoon Chung Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 05c239f..0a6a512 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -345,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) -- cgit v0.10.2 From a27fbf2f067b0cd6f172c8b696b9a44c58bfaa7a Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Wed, 4 Sep 2013 21:21:05 +0900 Subject: mmc: add ignorance case for CMD13 CRC error While speed mode is changed, CMD13 cannot be guaranteed. According to the spec., it is not recommended to use CMD13 to check the busy completion of the timing change. If CMD13 is used in this case, CRC error must be ignored. Signed-off-by: Seungwon Jeon Acked-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index ef18348..37f7d70 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -23,6 +23,40 @@ #define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ +static inline int __mmc_send_status(struct mmc_card *card, u32 *status, + bool ignore_crc) +{ + int err; + struct mmc_command cmd = {0}; + + BUG_ON(!card); + BUG_ON(!card->host); + + cmd.opcode = MMC_SEND_STATUS; + if (!mmc_host_is_spi(card->host)) + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; + if (ignore_crc) + cmd.flags &= ~MMC_RSP_CRC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); + if (err) + return err; + + /* NOTE: callers are required to understand the difference + * between "native" and SPI format status words! + */ + if (status) + *status = cmd.resp[0]; + + return 0; +} + +int mmc_send_status(struct mmc_card *card, u32 *status) +{ + return __mmc_send_status(card, status, false); +} + static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) { int err; @@ -380,6 +414,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, struct mmc_command cmd = {0}; unsigned long timeout; u32 status; + bool ignore_crc = false; BUG_ON(!card); BUG_ON(!card->host); @@ -408,10 +443,18 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, if (!use_busy_signal) return 0; - /* Must check status to be sure of no errors */ + /* + * Must check status to be sure of no errors + * If CMD13 is to check the busy completion of the timing change, + * disable the check of CRC error. + */ + if (index == EXT_CSD_HS_TIMING && + !(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)) + ignore_crc = true; + timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS); do { - err = mmc_send_status(card, &status); + err = __mmc_send_status(card, &status, ignore_crc); if (err) return err; if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) @@ -449,32 +492,6 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, } EXPORT_SYMBOL_GPL(mmc_switch); -int mmc_send_status(struct mmc_card *card, u32 *status) -{ - int err; - struct mmc_command cmd = {0}; - - BUG_ON(!card); - BUG_ON(!card->host); - - cmd.opcode = MMC_SEND_STATUS; - if (!mmc_host_is_spi(card->host)) - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_SPI_R2 | MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); - if (err) - return err; - - /* NOTE: callers are required to understand the difference - * between "native" and SPI format status words! - */ - if (status) - *status = cmd.resp[0]; - - return 0; -} - static int mmc_send_bus_test(struct mmc_card *card, struct mmc_host *host, u8 opcode, u8 len) -- cgit v0.10.2 From c1fa3426aa5c782724c97394303d52228206eda4 Mon Sep 17 00:00:00 2001 From: Ludovic Desroches Date: Mon, 9 Sep 2013 17:29:56 +0200 Subject: mmc: atmel-mci: abort transfer on timeout error When a software timeout occurs, the transfer is not stopped. In DMA case, it causes DMA channel to be stuck because the transfer is still active causing following transfers to be queued but not computed. Cc: # 3.9+ Signed-off-by: Ludovic Desroches Reported-by: Alexander Morozov Acked-by: Nicolas Ferre Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 69e438e..b8dfe0d 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -589,6 +589,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; -- cgit v0.10.2 From fbd986cd420d1deeabf1039ec4e74075a5639db5 Mon Sep 17 00:00:00 2001 From: Rodolfo Giometti Date: Mon, 9 Sep 2013 17:31:59 +0200 Subject: mmc: atmel-mci: fix oops in atmci_tasklet_func In some cases, a NULL pointer dereference happens because data is NULL when STATE_END_REQUEST case is reached in atmci_tasklet_func. Cc: # 3.9+ Signed-off-by: Rodolfo Giometti Acked-by: Ludovic Desroches Acked-by: Nicolas Ferre Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index b8dfe0d..92c1877 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1810,12 +1810,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; + } } } -- cgit v0.10.2 From 71ef1ea418ee45a4f939d53a02410627b394c336 Mon Sep 17 00:00:00 2001 From: Jackey Shen Date: Fri, 17 May 2013 17:17:43 +0800 Subject: mmc: core: clean up duplicate macros Clean up the duplicate macros: mmc_sd_card_uhs -> mmc_card_uhs mmc_sd_card_set_uhs -> mmc_card_set_uhs Signed-off-by: Jackey Shen Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 704bf66..cdca8a7 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -340,7 +340,7 @@ int mmc_add_card(struct mmc_card *card) break; } - if (mmc_sd_card_uhs(card) && + if (mmc_card_uhs(card) && (card->sd_bus_speed < ARRAY_SIZE(uhs_speeds))) uhs_bus_speed_mode = uhs_speeds[card->sd_bus_speed]; diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 375a880e..e2ab124 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -364,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; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 842de3e..f42cdbd 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -420,7 +420,6 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) #define mmc_card_ddr_mode(c) ((c)->state & MMC_STATE_HIGHSPEED_DDR) #define mmc_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) -#define mmc_sd_card_uhs(c) ((c)->state & MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) #define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS) @@ -432,7 +431,6 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) #define mmc_card_set_ddr_mode(c) ((c)->state |= MMC_STATE_HIGHSPEED_DDR) #define mmc_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) -#define mmc_sd_card_set_uhs(c) ((c)->state |= MMC_STATE_ULTRAHIGHSPEED) #define mmc_card_set_ext_capacity(c) ((c)->state |= MMC_CARD_SDXC) #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) #define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS) -- cgit v0.10.2 From e02d2930040f993922e9c4449e3d90c8040cfedc Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 12 Sep 2013 12:16:34 +0530 Subject: mmc: mvsdio: Convert to devm_ioremap_resource devm_request_and_ioremap() is deprecated. Use devm_ioremap_resource() instead. Signed-off-by: Sachin Kamat Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 06c5b0b..a592407 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -775,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; } -- cgit v0.10.2 From 21f67c2fd820b615ca7f5b1e87704b605410308e Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 12 Sep 2013 12:16:35 +0530 Subject: mmc: dw_mmc-socfpga: Remove redundant of_match_ptr 'dw_mci_socfpga_match' is always compiled in. Hence of_match_ptr is not necessary. Signed-off-by: Sachin Kamat Acked-by: Jaehoon Chung Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c index 4d7b999..8e8e205 100644 --- a/drivers/mmc/host/dw_mmc-socfpga.c +++ b/drivers/mmc/host/dw_mmc-socfpga.c @@ -126,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, }, }; -- cgit v0.10.2 From 08de89b7915afaba00164f2a972290b86aa27e6d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 12 Sep 2013 12:16:36 +0530 Subject: mmc: dw_mmc-socfpga: Staticize dw_mci_socfpga_probe 'dw_mci_socfpga_probe' is used only in this file. Make it static. Signed-off-by: Sachin Kamat Acked-by: Jaehoon Chung Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c index 8e8e205..3e8e53a 100644 --- a/drivers/mmc/host/dw_mmc-socfpga.c +++ b/drivers/mmc/host/dw_mmc-socfpga.c @@ -111,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; -- cgit v0.10.2 From ff1acfd0def7487d0554048bc613db4e7c3f3211 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 17 Sep 2013 15:59:32 +0800 Subject: mmc: sdhci-bcm-kona: Use sdhci_pltfm_unregister instead of open coded This avoid duplicated implementation and also fixes missing iounmap() and release_mem_region() calls in sdhci_bcm_kona_remove(). sdhci_pltfm_init() calls request_mem_region() and ioremap(), thus we need to call the corresponding iounmap() and release_mem_region() calls in sdhci_bcm_kona_remove(). Signed-off-by: Axel Lin Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 85472d3..7a190fe 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -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 = { -- cgit v0.10.2 From d0e04938275e63d4df17ee00714c3635a4ac925c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 17 Sep 2013 16:00:47 +0800 Subject: mmc: sdhci-bcm2835: Use sdhci_pltfm_unregister instead of open coded This avoid duplicated implementation. Signed-off-by: Axel Lin Acked-by: Stephen Warren Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 36fa2df..f6d8d67 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -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[] = { -- cgit v0.10.2 From 4525181245c0f4005788c17315aee4371662ddea Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 13 Sep 2013 19:11:30 +0800 Subject: mmc: sdhci: add hooks for platform specific tuning The tuning of some platforms may not follow the standard host control spec v3.0, e.g. Freescale uSDHC on i.MX6Q/DL. Add a hook here to allow execute platform specific tuning instead of standard host controller tuning. The hook only replaces the tuning process, so it's placed after tuning checking and before the real tuning process. Some notes for the tuning hook: 1) it needs handle lock itself if it wants to access host controller according platform specific implementation. 2) do not need to handle runtime pm since it executes with runtime pm get already. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 7a7fb4f..072d78c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1875,6 +1875,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); /* diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index b037f18..976c14b 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); -- cgit v0.10.2 From c0e55129b09f3b9fee271e5fe23332393ab4c2a9 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 13 Sep 2013 19:11:31 +0800 Subject: mmc: sdhci: allow platform access of sdhci_send_command It helps for platform code to use it send tuning commands. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 072d78c..66bce7a 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) { diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 976c14b..0a3ed01 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -394,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); -- cgit v0.10.2 From d31fc00a71b4f3a52d23c5a995ccd63e50a2c877 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 13 Sep 2013 19:11:32 +0800 Subject: mmc: sdhci-esdhc: move common esdhc_set_clock to platform driver We need a lot of imx6 specific things into common esdhc_set_clock for support SD3.0 and eMMC DDR mode which is not needed for power pc platforms, so esdhc_set_clock seems not so common anymore. Instead of keeping add platform specfics things into this common API, we choose to move that code into platform driver itself to handle. This can also exclude the dependency between imx and power pc on this headfile and is easy for maintain in the future. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index abc8cf0..67eaec9 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -409,8 +409,39 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - - esdhc_set_clock(host, clock, clk_get_rate(pltfm_host->clk)); + unsigned int host_clock = clk_get_rate(pltfm_host->clk); + 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; } static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host) diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index a2a0642..a7d9f95 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 e328252..b1605a1 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_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; } #ifdef CONFIG_PM -- cgit v0.10.2 From fed2f6e2d42e4bcdec5ea357e7a9db8602744753 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 13 Sep 2013 19:11:33 +0800 Subject: mmc: sdhci-esdhc-imx: support real clock on and off for imx6q The signal voltage switch flow requires to shutdown and output clock in a specific sequence according to standard host controller v3.0 spec. In that timing, the card must really receive clock or not. However, for i.MX6Q, the uSDHC will not output clock even the clock is enabled until there is command or data in transfer on the bus, which will then cause singal voltage switch always to fail. For i.MX6Q, we clear ESDHC_VENDOR_SPEC_FRC_SDCLK_ON bit to let controller to gate off clock automatically and set that bit to force clock output if clock is on. This is required by SD3.0 support. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 67eaec9..37fafd7 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -34,6 +34,7 @@ /* VENDOR SPEC register */ #define ESDHC_VENDOR_SPEC 0xc0 #define ESDHC_VENDOR_SPEC_SDIO_QUIRK (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_AC23EN (1 << 7) @@ -409,13 +410,20 @@ 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; + u32 temp, val; - if (clock == 0) + if (clock == 0) { + if (is_imx6q_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; + } temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN @@ -439,6 +447,13 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, | (div << ESDHC_DIVIDER_SHIFT) | (pre_div << ESDHC_PREDIV_SHIFT)); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); + + if (is_imx6q_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; -- cgit v0.10.2 From 0322191e62984b94d1b2ae5ff322112e1fa1ef1a Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 13 Sep 2013 19:11:34 +0800 Subject: mmc: sdhci-esdhc-imx: add sd3.0 SDR clock tuning support Freescale i.MX6Q/DL uSDHC clock tuning progress is a little different from the standard tuning process defined in host controller spec v3.0. Thus we use platform_execute_tuning instead of standard sdhci tuning. The main difference are: 1) not only generate Buffer Read Ready interrupt when tuning is performing. It generates all other DATA interrupts like the normal data command. 2) SDHCI_CTRL_EXEC_TUNING is not automatically cleared by HW, instead it's controlled by SW. 3) SDHCI_CTRL_TUNED_CLK is not automatically set by HW, it's controlled by SW. 4) the clock delay for every tuning is set by SW. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 37fafd7..f906c20 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -34,13 +34,25 @@ /* 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_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 +/* 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_BLOCK_PATTERN_LEN 64 + /* * Our interpretation of the SDHCI_HOST_CONTROL register */ @@ -91,7 +103,7 @@ 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; }; static struct platform_device_id imx_esdhc_devtype[] = { @@ -165,6 +177,16 @@ 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)) { /* In FSL esdhc IC module, only bit20 is used to indicate the * ADMA2 capability of esdhc, but this bit is messed up on @@ -179,6 +201,17 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) } } + if (unlikely(reg == SDHCI_CAPABILITIES_1) && is_imx6q_usdhc(imx_data)) + val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 + | SDHCI_SUPPORT_SDR50; + + if (unlikely(reg == SDHCI_MAX_CURRENT) && is_imx6q_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; @@ -257,6 +290,8 @@ 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; @@ -269,6 +304,25 @@ 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 (is_imx6q_usdhc(imx_data)) { + val = readl(host->ioaddr + ESDHC_MIX_CTRL); + 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); } @@ -276,8 +330,32 @@ 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; + 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); + return; case SDHCI_TRANSFER_MODE: if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT) && (host->cmd->opcode == SD_IO_RW_EXTENDED) @@ -500,6 +578,121 @@ static int esdhc_pltfm_bus_width(struct sdhci_host *host, int width) return 0; } +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 const struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, @@ -511,6 +704,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, + .platform_execute_tuning = esdhc_executing_tuning, }; static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { -- cgit v0.10.2 From ad93220de7da1f5bd8f9e4be1f1eeefba5eed0ad Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 13 Sep 2013 19:11:35 +0800 Subject: mmc: sdhci-esdhc-imx: change pinctrl state according to uhs mode Without proper pinctrl state, the card may not be able to work on high speed stablely. e.g. SDR104. This patch add pinctrl state switch code according to different uhs mode include 100mhz sate, 200mhz sate and normal state (50Mhz and below). Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index f906c20..ed00d6d 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -53,6 +53,10 @@ #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 */ @@ -94,6 +98,9 @@ struct pltfm_imx_data { 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; struct esdhc_platform_data boarddata; struct clk *clk_ipg; struct clk *clk_ahb; @@ -693,6 +700,62 @@ static int esdhc_executing_tuning(struct sdhci_host *host, u32 opcode) 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; + + 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; + break; + } + + return esdhc_change_pinstate(host, uhs); +} + static const struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, @@ -704,6 +767,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, .platform_execute_tuning = esdhc_executing_tuning, }; @@ -746,6 +810,11 @@ 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; + return 0; } #else @@ -808,12 +877,20 @@ 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)) @@ -890,6 +967,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) && is_imx6q_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/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h index d44912d..a0f5a8f 100644 --- a/include/linux/platform_data/mmc-esdhc-imx.h +++ b/include/linux/platform_data/mmc-esdhc-imx.h @@ -10,6 +10,8 @@ #ifndef __ASM_ARCH_IMX_ESDHC_H #define __ASM_ARCH_IMX_ESDHC_H +#include + enum wp_types { ESDHC_WP_NONE, /* no WP, neither controller nor gpio */ ESDHC_WP_CONTROLLER, /* mmc controller internal WP */ @@ -32,6 +34,7 @@ enum cd_types { * @cd_gpio: gpio for card_detect interrupt * @wp_type: type of write_protect method (see wp_types enum above) * @cd_type: type of card_detect method (see cd_types enum above) + * @support_vsel: indicate it supports 1.8v switching */ struct esdhc_platform_data { @@ -41,5 +44,6 @@ struct esdhc_platform_data { enum cd_types cd_type; int max_bus_width; unsigned int f_max; + bool support_vsel; }; #endif /* __ASM_ARCH_IMX_ESDHC_H */ -- cgit v0.10.2 From 5f7886c5ef40c79b5811f23219167096ab578a91 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 13 Sep 2013 19:11:36 +0800 Subject: mmc: sdhci-esdhc-imx: correct pre_div for imx6q According to spec, the pre_div for imx6q should be 1, or the biggest clock rate we can get is a half of host clock rate. This may cause we can not get the proper clock rate as we want. e.g. if the desired clock is 200Mhz, however, the host clock is 200Mhz too, then it causes the actual clock we get is 100Mhz due to pre_div is 2. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ed00d6d..fe48ec3 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -510,6 +510,9 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, goto out; } + if (is_imx6q_usdhc(imx_data)) + pre_div = 1; + temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); temp &= ~(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK); -- cgit v0.10.2 From e76b855956383d58232cfcf367f0ce34f149161b Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 13 Sep 2013 19:11:37 +0800 Subject: mmc: sdhci-esdhc-imx: set actual_clock in clock setting This enables access the actual_clock via sys. root@imx6qsabreauto:~# cat /sys/kernel/debug/mmc0/ios clock: 198000000 Hz actual clock: 198000000 Hz vdd: 17 (2.9 ~ 3.0 V) bus mode: 2 (push-pull) chip select: 0 (don't care) power mode: 2 (on) bus width: 2 (4 bits) timing spec: 6 (sd uhs SDR104) signal voltage: 0 (1.80 V) Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index fe48ec3..b9899e9 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -524,8 +524,9 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, while (host_clock / pre_div / div > clock && div < 16) div++; + host->mmc->actual_clock = host_clock / pre_div / div; dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", - clock, host_clock / pre_div / div); + clock, host->mmc->actual_clock); pre_div >>= 1; div--; diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index b1605a1..0b24997 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -227,7 +227,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) div++; dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", - clock, host_clock / pre_div / div); + clock, host->max_clk / pre_div / div); pre_div >>= 1; div--; -- cgit v0.10.2 From 8776a165d152d57a3a58895d55204614abe93d7f Mon Sep 17 00:00:00 2001 From: David Cohen Date: Tue, 1 Oct 2013 13:18:15 -0700 Subject: mmc: sdhci-pci: add Intel Merrifield support Implement initial SDHCI Intel Merrifield support. This patch is based on previous one from Yunpeng Gao . Signed-off-by: David Cohen Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index d7d6bc8..06f026a 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -37,6 +37,7 @@ #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 /* * PCI registers @@ -356,6 +357,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 @@ -940,6 +963,13 @@ static const struct pci_device_id pci_ids[] = { }, { + .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, .subvendor = PCI_ANY_ID, -- cgit v0.10.2 From 7b2399034af31d934ce3efc898e023e027f110bd Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 3 Oct 2013 17:04:40 +0800 Subject: mmc: wmt-sdmmc: Simplify wmt_set_sd_power implementation Simplify the code to set/clear BM_SD_OFF bit: | power_inverted: 0 | power_inverted: 1 ------------------------------------------------- enable: 0 | SET BM_SD_OFF | CLEAR BM_SD_OFF ------------------------------------------------- enable: 1 | CLEAR BM_SD_OFF | SET BM_SD_OFF Signed-off-by: Axel Lin Acked-by: Tony Prisk Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 34231d5..57858ef 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) -- cgit v0.10.2 From e69e4dff1c0228294572e27df7b2fc4c0b518632 Mon Sep 17 00:00:00 2001 From: Sonic Zhang Date: Wed, 16 Oct 2013 13:42:05 +0800 Subject: mmc: bfin_sdh: Forgot to write SDH_CFG register in function set_ios. The DMA operation in SDIO CMD53 may fail because the 4 bit SDIO mode is not set up correctly on BF548. Remove 2 blank lines as well. Signed-off-by: Sonic Zhang Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 94fae2f..e62c5bc 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); -- cgit v0.10.2 From 31fbb3013506e58320fcea2ce07543b2a22c5ee0 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 17 Oct 2013 15:19:44 +0800 Subject: mmc: sdhci-esdhc-imx: add flag ESDHC_FLAG_ENGCM07207 Just like the use of the flag ESDHC_FLAG_MULTIBLK_NO_INT, let's add another flag ESDHC_FLAG_ENGCM07207 to enable the workaround for errata ENGcm07207 and set the flag for i.MX25 and i.MX35 ESDHC. While at it, let's use BIT() macro for ESDHC_FLAG_MULTIBLK_NO_INT as well. Signed-off-by: Shawn Guo Acked-by: Dong Aisheng Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index b9899e9..d844be8 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -83,7 +83,12 @@ * 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) +#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) enum imx_esdhc_type { IMX25_ESDHC, @@ -857,6 +862,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) imx_data->devtype = pdev->id_entry->driver_data; pltfm_host->priv = imx_data; + if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) + imx_data->flags |= ESDHC_FLAG_ENGCM07207; + imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(imx_data->clk_ipg)) { err = PTR_ERR(imx_data->clk_ipg); @@ -897,7 +905,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; - if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) + if (imx_data->flags & ESDHC_FLAG_ENGCM07207) /* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */ host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK | SDHCI_QUIRK_BROKEN_ADMA; -- cgit v0.10.2 From 9d61c0092ca1abe93853d1b8dfeb5525b6cf3ddc Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 17 Oct 2013 15:19:45 +0800 Subject: mmc: sdhci-esdhc-imx: add flag ESDHC_FLAG_USDHC Add flag ESDHC_FLAG_USDHC to tell that the ESDHC is actually an USDHC block, and replace the is_imx6q_usdhc() occurrences with inline function esdhc_is_usdhc() which checks the flag. Signed-off-by: Shawn Guo Acked-by: Dong Aisheng Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index d844be8..2421ac7 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -89,6 +89,11 @@ * 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) enum imx_esdhc_type { IMX25_ESDHC, @@ -175,6 +180,11 @@ static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) return data->devtype == IMX6Q_USDHC; } +static inline int esdhc_is_usdhc(struct pltfm_imx_data *data) +{ + return !!(data->flags & ESDHC_FLAG_USDHC); +} + static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) { void __iomem *base = host->ioaddr + (reg & ~0x3); @@ -213,11 +223,11 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) } } - if (unlikely(reg == SDHCI_CAPABILITIES_1) && is_imx6q_usdhc(imx_data)) + if (unlikely(reg == SDHCI_CAPABILITIES_1) && esdhc_is_usdhc(imx_data)) val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50; - if (unlikely(reg == SDHCI_MAX_CURRENT) && is_imx6q_usdhc(imx_data)) { + 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; @@ -307,7 +317,7 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) 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. @@ -321,7 +331,7 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) if (val & ESDHC_VENDOR_SPEC_VSELECT) ret |= SDHCI_CTRL_VDD_180; - if (is_imx6q_usdhc(imx_data)) { + if (esdhc_is_usdhc(imx_data)) { val = readl(host->ioaddr + ESDHC_MIX_CTRL); if (val & ESDHC_MIX_CTRL_EXE_TUNE) ret |= SDHCI_CTRL_EXEC_TUNING; @@ -379,7 +389,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) { @@ -404,7 +414,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) (imx_data->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 @@ -470,7 +480,7 @@ 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); } } @@ -507,7 +517,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, u32 temp, val; if (clock == 0) { - if (is_imx6q_usdhc(imx_data)) { + 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); @@ -515,7 +525,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, goto out; } - if (is_imx6q_usdhc(imx_data)) + if (esdhc_is_usdhc(imx_data)) pre_div = 1; temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); @@ -542,7 +552,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, | (pre_div << ESDHC_PREDIV_SHIFT)); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - if (is_imx6q_usdhc(imx_data)) { + 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); @@ -865,6 +875,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) imx_data->flags |= ESDHC_FLAG_ENGCM07207; + if (is_imx6q_usdhc(imx_data)) + imx_data->flags |= ESDHC_FLAG_USDHC; + imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(imx_data->clk_ipg)) { err = PTR_ERR(imx_data->clk_ipg); @@ -917,7 +930,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) * 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); boarddata = &imx_data->boarddata; @@ -980,7 +993,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) } /* sdr50 and sdr104 needs work on 1.8v signal voltage */ - if ((boarddata->support_vsel) && is_imx6q_usdhc(imx_data)) { + 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, -- cgit v0.10.2 From 3770ee8f95a2aa2da66db460a10898e96eee1b13 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 17 Oct 2013 15:19:46 +0800 Subject: mmc: sdhci-esdhc-imx: pdev->id_entry should be immutable As a good practice, device driver should not modify pdev->id_entry but keep it immutable. Let's assign of_device_id.data with imx_esdhc_type constants directly, so that we do not have to manipulate pdev->id_entry in .probe(). As the result, sdhci-esdhc-imx53 and sdhci-usdhc-imx6q can be removed from platform_device_id table now, since they will only probe from device tree. Signed-off-by: Shawn Guo Acked-by: Dong Aisheng Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 2421ac7..83023fa 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -134,23 +134,17 @@ static struct platform_device_id imx_esdhc_devtype[] = { .name = "sdhci-esdhc-imx51", .driver_data = IMX51_ESDHC, }, { - .name = "sdhci-esdhc-imx53", - .driver_data = IMX53_ESDHC, - }, { - .name = "sdhci-usdhc-imx6q", - .driver_data = IMX6Q_USDHC, - }, { /* sentinel */ } }; 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 = (void *) IMX25_ESDHC, }, + { .compatible = "fsl,imx35-esdhc", .data = (void *) IMX35_ESDHC, }, + { .compatible = "fsl,imx51-esdhc", .data = (void *) IMX51_ESDHC, }, + { .compatible = "fsl,imx53-esdhc", .data = (void *) IMX53_ESDHC, }, + { .compatible = "fsl,imx6q-usdhc", .data = (void *) IMX6Q_USDHC, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); @@ -867,9 +861,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->devtype = of_id ? (enum imx_esdhc_type) of_id->data : + pdev->id_entry->driver_data; pltfm_host->priv = imx_data; if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) -- cgit v0.10.2 From f47c4bbfa283df2dfd23c422c53f96d12005bbf6 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 17 Oct 2013 15:19:47 +0800 Subject: mmc: sdhci-esdhc-imx: create struct esdhc_soc_data Create a struct esdhc_soc_data with moving 'flags' field from pltfm_imx_data into it, and pass the pointer of this SoC specific data structure through of_device_id.data directly, so that the translation from enum imx_esdhc_type to flags can be saved. With the change, enum imx_esdhc_type can be eliminated, since we can implement the is_imx*_esdhc() by checking the esdhc_soc_data pointer. The unused is_imx35_esdhc() and is_imx51_esdhc() are also removed, and the others are kept there as we will need to use them to handle some small register differences later, where use of new flags might be a little overkilled. Signed-off-by: Shawn Guo Acked-by: Dong Aisheng Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 83023fa..c84c808 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -95,22 +95,37 @@ */ #define ESDHC_FLAG_USDHC BIT(3) -enum imx_esdhc_type { - IMX25_ESDHC, - IMX35_ESDHC, - IMX51_ESDHC, - IMX53_ESDHC, - IMX6Q_USDHC, +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, }; 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; @@ -126,13 +141,13 @@ struct pltfm_imx_data { 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, + .driver_data = (kernel_ulong_t) &esdhc_imx51_data, }, { /* sentinel */ } @@ -140,43 +155,33 @@ 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 = (void *) IMX25_ESDHC, }, - { .compatible = "fsl,imx35-esdhc", .data = (void *) IMX35_ESDHC, }, - { .compatible = "fsl,imx51-esdhc", .data = (void *) IMX51_ESDHC, }, - { .compatible = "fsl,imx53-esdhc", .data = (void *) IMX53_ESDHC, }, - { .compatible = "fsl,imx6q-usdhc", .data = (void *) 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,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; -} - -static inline int is_imx51_esdhc(struct pltfm_imx_data *data) -{ - return data->devtype == IMX51_ESDHC; + return data->socdata == &esdhc_imx25_data; } static inline int is_imx53_esdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX53_ESDHC; + return data->socdata == &esdhc_imx53_data; } static inline int is_imx6q_usdhc(struct pltfm_imx_data *data) { - return data->devtype == IMX6Q_USDHC; + return data->socdata == &usdhc_imx6q_data; } static inline int esdhc_is_usdhc(struct pltfm_imx_data *data) { - return !!(data->flags & ESDHC_FLAG_USDHC); + return !!(data->socdata->flags & ESDHC_FLAG_USDHC); } static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg) @@ -274,7 +279,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; @@ -373,7 +378,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) writel(new_val , 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)) { @@ -405,7 +410,7 @@ 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 (esdhc_is_usdhc(imx_data)) @@ -861,16 +866,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) goto free_sdhci; } - imx_data->devtype = of_id ? (enum imx_esdhc_type) of_id->data : - 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; - if (is_imx25_esdhc(imx_data) || is_imx35_esdhc(imx_data)) - imx_data->flags |= ESDHC_FLAG_ENGCM07207; - - if (is_imx6q_usdhc(imx_data)) - imx_data->flags |= ESDHC_FLAG_USDHC; - imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(imx_data->clk_ipg)) { err = PTR_ERR(imx_data->clk_ipg); @@ -911,14 +910,11 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; - if (imx_data->flags & ESDHC_FLAG_ENGCM07207) + 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. -- cgit v0.10.2 From 6e9fd28e1f2422e8e5ad3c9871c952aad6a7e5ca Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 18 Oct 2013 19:48:43 +0800 Subject: mmc: sdhci-esdhc-imx: add std tuning support for mx6sl The mx6sl supports standard sdhci tuning, then esdhc_executing_tuning is only needed for mx6q/dl. We introduce is_imx6_usdhc() and is_imx6sl_usdhc() to handle the difference. The standard tuning is enabled by setting ESDHC_TUNE_CTRL_STD_TUNING_EN bit in new register ESDHC_TUNE_CTRL and operates with new tuning bits defined in SDHCI_ACMD12_ERR register. Note: mx6sl can also work on the old manually tuning mode as mx6q/dl if not enable standard tuning mode. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index c84c808..3b9c94f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -51,6 +51,11 @@ #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 */ @@ -94,6 +99,12 @@ * 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; @@ -116,7 +127,12 @@ static struct esdhc_soc_data esdhc_imx53_data = { }; static struct esdhc_soc_data usdhc_imx6q_data = { - .flags = ESDHC_FLAG_USDHC, + .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 { @@ -159,6 +175,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .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 */ } }; @@ -222,9 +239,16 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) } } - if (unlikely(reg == SDHCI_CAPABILITIES_1) && esdhc_is_usdhc(imx_data)) - val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 - | SDHCI_SUPPORT_SDR50; + 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; + } + } if (unlikely(reg == SDHCI_MAX_CURRENT) && esdhc_is_usdhc(imx_data)) { val = 0; @@ -331,13 +355,18 @@ static u16 esdhc_readw_le(struct sdhci_host *host, int reg) ret |= SDHCI_CTRL_VDD_180; if (esdhc_is_usdhc(imx_data)) { - val = readl(host->ioaddr + ESDHC_MIX_CTRL); - if (val & ESDHC_MIX_CTRL_EXE_TUNE) - ret |= SDHCI_CTRL_EXEC_TUNING; - if (val & ESDHC_MIX_CTRL_SMPCLK_SEL) - ret |= SDHCI_CTRL_TUNED_CLK; + 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; @@ -370,12 +399,37 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) new_val &= ~ESDHC_VENDOR_SPEC_VSELECT; writel(new_val, host->ioaddr + ESDHC_VENDOR_SPEC); imx_data->uhs_mode = val & SDHCI_CTRL_UHS_MASK; - 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); + 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->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) @@ -774,7 +828,7 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) return esdhc_change_pinstate(host, uhs); } -static const struct sdhci_ops sdhci_esdhc_ops = { +static struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl_le, .read_w = esdhc_readw_le, .write_l = esdhc_writel_le, @@ -786,7 +840,6 @@ static const struct sdhci_ops sdhci_esdhc_ops = { .get_ro = esdhc_pltfm_get_ro, .platform_bus_width = esdhc_pltfm_bus_width, .set_uhs_signaling = esdhc_set_uhs_signaling, - .platform_execute_tuning = esdhc_executing_tuning, }; static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = { @@ -922,6 +975,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (esdhc_is_usdhc(imx_data)) writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL); + 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) { -- cgit v0.10.2 From 6b4fb6712a4c58b205c163e9080c349d71c208d0 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 18 Oct 2013 19:48:44 +0800 Subject: mmc: sdhci-esdhc-imx: fix reading cap_1 register value for mx6sl When reading CAP_1 register for mx6sl, ignore bit[0-15] as it stores CAP_2 register value which is new introduced in mx6sl. Without this fix, the max clock for mx6sl may not be correct since it's wrongly calculated by reading CAP_1 register. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 3b9c94f..1652e18 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -226,6 +226,10 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) } 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 -- cgit v0.10.2 From 114f2bf6a158cd84c1a7280cf6233904c8bce430 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 18 Oct 2013 19:48:45 +0800 Subject: mmc: sdhci: report error once the maximum tuning loops exhausted or timeout The original code missed to report an error when the maximum tuning loops exhausted or timeout, it will cause the upper layer to wrongly think the tuning process is passed. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 66bce7a..cbde17d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1989,6 +1989,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" -- cgit v0.10.2 From de5bdbffd89dea0e3de76f99b0b036422bb93686 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 18 Oct 2013 19:48:46 +0800 Subject: mmc: sdhci-esdhc-imx: add DDR mode support for mx6 When DDR mode is enabled, the initial pre_div should be 2. And the pre_div value should be changed accordingly from ... 02h) Base clock divided by 4 01h) Base clock divided by 2 00h) Base clock divided by 1 to .. 02h) Base clock divided by 8 01h) Base clock divided by 4 00h) Base clock divided by 2 Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 1652e18..915fa68 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -38,6 +38,7 @@ #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) @@ -152,6 +153,7 @@ struct pltfm_imx_data { 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[] = { @@ -537,8 +539,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 (esdhc_is_usdhc(imx_data)) + if (esdhc_is_usdhc(imx_data)) { writel(0, host->ioaddr + ESDHC_MIX_CTRL); + imx_data->is_ddr = 0; + } } } @@ -582,7 +586,7 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, goto out; } - if (esdhc_is_usdhc(imx_data)) + if (esdhc_is_usdhc(imx_data) && !imx_data->is_ddr) pre_div = 1; temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); @@ -600,7 +604,10 @@ static inline void esdhc_pltfm_set_clock(struct sdhci_host *host, dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", clock, host->mmc->actual_clock); - pre_div >>= 1; + if (imx_data->is_ddr) + pre_div >>= 2; + else + pre_div >>= 1; div--; temp = sdhci_readl(host, ESDHC_SYSTEM_CONTROL); @@ -826,6 +833,10 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) 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; break; } -- cgit v0.10.2 From 602519b2bd63adfdf9e24b2f94aaddcfeb464e9e Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 18 Oct 2013 19:48:47 +0800 Subject: mmc: sdhci-esdhc-imx: add delay line setting support The DLL(Delay Line) is newly added to assist in sampling read data. The DLL provides the ability to programmatically select a quantized delay (in fractions of the clock period) regardless of on-chip variations such as process, voltage and temperature (PVT). This patch adds a user interface to set slave delay line via device tree. It's usually used in high speed mode like mmc DDR mode when the signal quality is not good caused by board design, e.g. the signal path is too long. User can manually set delay line to find a suitable data sampling window for card to work properly. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt index 1dd6225..9046ba06 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt @@ -12,6 +12,11 @@ Required properties: Optional properties: - fsl,cd-controller : Indicate to use controller internal card detection - fsl,wp-controller : Indicate to use controller internal write protection +- fsl,delay-line : Specify the number of delay cells for override mode. + This is used to set the clock delay for DLL(Delay Line) on override mode + to select a proper data sampling window in case the clock quality is not good + due to signal path is too long on the board. Please refer to eSDHC/uSDHC + chapter, DLL (Delay Line) section in RM for details. Examples: diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 915fa68..260a81f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -46,6 +46,11 @@ /* 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 @@ -817,6 +822,7 @@ 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: @@ -837,6 +843,15 @@ static int esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) 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; } @@ -901,6 +916,9 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, else boarddata->support_vsel = true; + if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) + boarddata->delay_line = 0; + return 0; } #else diff --git a/include/linux/platform_data/mmc-esdhc-imx.h b/include/linux/platform_data/mmc-esdhc-imx.h index a0f5a8f..75f70f6 100644 --- a/include/linux/platform_data/mmc-esdhc-imx.h +++ b/include/linux/platform_data/mmc-esdhc-imx.h @@ -45,5 +45,6 @@ struct esdhc_platform_data { int max_bus_width; unsigned int f_max; bool support_vsel; + unsigned int delay_line; }; #endif /* __ASM_ARCH_IMX_ESDHC_H */ -- cgit v0.10.2 From 888824bb63130ba70f310ebf8ffc2b44477847f5 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 18 Oct 2013 19:48:48 +0800 Subject: mmc: sdhci-esdhc-imx: enable SDR50 tuning for imx6q/dl The imx6q/dl supports SDR50 tunning, enable it for a better timing on SDR50 mode. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 260a81f..1d1fcb2 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -257,7 +257,8 @@ static u32 esdhc_readl_le(struct sdhci_host *host, int reg) else /* imx6q/dl does not have cap_1 register, fake one */ val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104 - | SDHCI_SUPPORT_SDR50; + | SDHCI_SUPPORT_SDR50 + | SDHCI_USE_SDR50_TUNING; } } -- cgit v0.10.2 From 69ed60e07bd2bb8517b4f87c6ddbb7f14c0189ab Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 18 Oct 2013 19:48:49 +0800 Subject: mmc: sdhci-esdhc-imx: add preset value quirk for mx6 The i.MX6 does not support preset value feature. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 1d1fcb2..461a4c3 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1006,8 +1006,10 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) * The imx6q ROM code will change the default watermark level setting * to something insane. Change it back here. */ - if (esdhc_is_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 = -- cgit v0.10.2 From 372c463487c6af8275d216f4b259dda6343a33a5 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 18 Oct 2013 19:48:50 +0800 Subject: mmc: sdhci: remove unneeded call when have preset value quirk Remove unneeded call of call sdhci_enable_preset_value when having SDHCI_QUIRK2_PRESET_VALUE_BROKEN. Signed-off-by: Dong Aisheng Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index cbde17d..a21a710 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -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); -- cgit v0.10.2 From e002264f7e45d7661b237045577052fd0b40f89c Mon Sep 17 00:00:00 2001 From: Balaji T K Date: Mon, 21 Oct 2013 00:25:18 +0530 Subject: mmc: omap_hsmmc: Fix pbias_disable for omap4 pbias_disable is set to protect the mmc pbias i/o cells in DT boot by preventing voltage switch. Currently pbias_disable is enabled only for omap3 and not for omap4 due to reg_offset difference of 0x100. Enable pbias_disable for omap4+ too by using res->start which does not include the reg_offset. Signed-off-by: Balaji T K Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 6ac63df..f5de0b7 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1874,7 +1874,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"); -- cgit v0.10.2 From 0a82e06e6183a252608df48cc4793b83e2d73dfd Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 21 Oct 2013 00:25:19 +0530 Subject: mmc: omap_hsmmc: context save and restore for DT We want to get rid of the omap specific platform init code callbacks as they don't play nice with device tree. Convert the context loss check to be based on a register state detection instead. Cc: Andreas Fenkart Signed-off-by: Tony Lindgren [add check for CON and HCTL in context save and restore function] Signed-off-by: Balaji T K Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index f5de0b7..53b53b5 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -171,6 +171,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; @@ -183,7 +187,6 @@ struct omap_hsmmc_host { int use_reg; int req_in_progress; struct omap_hsmmc_next next_data; - struct omap_mmc_platform_data *pdata; }; @@ -597,25 +600,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) @@ -655,9 +653,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; } @@ -666,15 +663,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 @@ -1635,13 +1627,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); + seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n", + mmc->index, host->context_loss); if (host->suspended) { seq_printf(s, "host suspended, can't read registers\n"); -- cgit v0.10.2 From 1e881786c9038cd78dfa325b012f4a00ce1bcfd4 Mon Sep 17 00:00:00 2001 From: Jianpeng Ma Date: Mon, 21 Oct 2013 00:25:20 +0530 Subject: mmc: omap_hsmmc: fix timeout for cmd and data soft reset With HSMMC_HAS_UPDATED_RESET reset of cmd/data (SRC/SRD) can be to quick and can be missed resulting in wait for software timeout. With cpu_relax timeout can be long and unpredictable. Use udelay instead for timeout implementation. Reported-by: Yuzheng Ma Tested-by: Yuzheng Ma Reviewed-by: Hein Tibosch Signed-off-by: Jianpeng Ma Signed-off-by: Balaji T K Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 53b53b5..4d1170f 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -119,7 +119,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" @@ -967,8 +968,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); @@ -980,13 +980,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), -- cgit v0.10.2 From ed164182154d11589a33710a9d04eafd6215d6e5 Mon Sep 17 00:00:00 2001 From: Balaji T K Date: Mon, 21 Oct 2013 00:25:21 +0530 Subject: mmc: omap_hsmmc: correct max value of clkd clock divisor can take a max value of 1023. Update code so that card init can be handled at f_min even at higher IP clock frequencies from which clock to the card is derived. Signed-off-by: Balaji T K Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 4d1170f..fbd2d89 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -75,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 @@ -497,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; -- cgit v0.10.2 From 6a6caac6debac9c63a3a77269db8820b81bec143 Mon Sep 17 00:00:00 2001 From: Balaji T K Date: Mon, 21 Oct 2013 00:25:22 +0530 Subject: mmc: omap_hsmmc: remove unused no_off With lazy disable gone, no_off is not used any more in omap_hsmmc Signed-off-by: Balaji T K Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index fbd2d89..540dead 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1827,13 +1827,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) -- cgit v0.10.2 From d052068a0ba43273eb9cfe32460e9445ef75fdc5 Mon Sep 17 00:00:00 2001 From: Eric Ernst Date: Mon, 21 Oct 2013 09:54:41 -0700 Subject: mmc: sdhci-pci: Add SDIO/MMC device ID support for Intel Clovertrail This patch adds intel_mid clovertrail SDIO and eMMC device IDs to the sdhci-pci driver. Signed-off-by: Eric Ernst Signed-off-by: David Cohen Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 06f026a..8f75381 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -38,6 +38,11 @@ #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 @@ -962,6 +967,47 @@ 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, -- cgit v0.10.2 From 878e200bbb1fbde9f21582decab95b178e5a3b83 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 9 Sep 2013 11:57:57 +0200 Subject: mmc: core: Do not poll for busy with status cmd for all switch cmds Some switch operations like poweroff notify, shall according to the spec not be followed by any other new commands. For these cases and when the host does'nt support MMC_CAP_WAIT_WHILE_BUSY, we must not send status commands to poll for busy detection. Instead wait for the stated timeout from the EXT_CSD before completing the request. Signed-off-by: Ulf Hansson Cc: Jaehoon Chung Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 006ead2..0292ad4 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -301,7 +301,7 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) } err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal); + EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true); if (err) { pr_warn("%s: Error %d starting bkops\n", mmc_hostname(card->host), err); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6d02012..8f0c516 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1404,9 +1404,9 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type) if (notify_type == EXT_CSD_POWER_OFF_LONG) timeout = card->ext_csd.power_off_longtime; - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_POWER_OFF_NOTIFICATION, - notify_type, timeout); + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_OFF_NOTIFICATION, + notify_type, timeout, true, false); if (err) pr_err("%s: Power Off Notification timed out, %u\n", mmc_hostname(card->host), timeout); diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 37f7d70..aae8d8b 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -404,11 +404,12 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc) * @timeout_ms: timeout (ms) for operation performed by register write, * timeout of zero implies maximum possible timeout * @use_busy_signal: use the busy signal as response type + * @send_status: send status cmd to poll for busy * * Modifies the EXT_CSD register for selected card. */ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, - unsigned int timeout_ms, bool use_busy_signal) + unsigned int timeout_ms, bool use_busy_signal, bool send_status) { int err; struct mmc_command cmd = {0}; @@ -454,14 +455,26 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS); do { - err = __mmc_send_status(card, &status, ignore_crc); - if (err) - return err; + if (send_status) { + err = __mmc_send_status(card, &status, ignore_crc); + if (err) + return err; + } if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) break; if (mmc_host_is_spi(card->host)) break; + /* + * We are not allowed to issue a status command and the host + * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only + * rely on waiting for the stated timeout to be sufficient. + */ + if (!send_status) { + mmc_delay(timeout_ms); + return 0; + } + /* Timeout if the device never leaves the program state. */ if (time_after(jiffies, timeout)) { pr_err("%s: Card stuck in programming state! %s\n", @@ -488,7 +501,7 @@ EXPORT_SYMBOL_GPL(__mmc_switch); int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, unsigned int timeout_ms) { - return __mmc_switch(card, set, index, value, timeout_ms, true); + return __mmc_switch(card, set, index, value, timeout_ms, true, true); } EXPORT_SYMBOL_GPL(mmc_switch); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index a00fc49..87079fc 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -151,7 +151,8 @@ extern int mmc_app_cmd(struct mmc_host *, struct mmc_card *); extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); extern void mmc_start_bkops(struct mmc_card *card, bool from_exception); -extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool); +extern int __mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int, bool, + bool); extern int mmc_switch(struct mmc_card *, u8, u8, u8, unsigned int); extern int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); -- cgit v0.10.2 From 4a065193c46a16f1ddf20622c7011c8f327e0478 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 12 Sep 2013 14:36:53 +0200 Subject: mmc: core: Let mmc_power_up|cycle take ocr as parameter As a step to fixup the setup of the negotiated ocr mask, we need the mmc_power_up|cycle functions to take the ocr as a parameter. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 0292ad4..b66e7ab 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1479,7 +1479,7 @@ power_cycle: if (err) { pr_debug("%s: Signal voltage switch failed, " "power cycling card\n", mmc_hostname(host)); - mmc_power_cycle(host); + mmc_power_cycle(host, host->ocr); } mmc_host_clk_release(host); @@ -1520,22 +1520,14 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) * If a host does all the power sequencing itself, ignore the * initial MMC_POWER_UP stage. */ -void mmc_power_up(struct mmc_host *host) +void mmc_power_up(struct mmc_host *host, u32 ocr) { - int bit; - if (host->ios.power_mode == MMC_POWER_ON) return; mmc_host_clk_hold(host); - /* If ocr is set, we use it */ - if (host->ocr) - bit = ffs(host->ocr) - 1; - else - bit = fls(host->ocr_avail) - 1; - - host->ios.vdd = bit; + host->ios.vdd = fls(ocr) - 1; if (mmc_host_is_spi(host)) host->ios.chip_select = MMC_CS_HIGH; else @@ -1605,12 +1597,12 @@ void mmc_power_off(struct mmc_host *host) mmc_host_clk_release(host); } -void mmc_power_cycle(struct mmc_host *host) +void mmc_power_cycle(struct mmc_host *host, u32 ocr) { mmc_power_off(host); /* Wait at least 1 ms according to SD spec */ mmc_delay(1); - mmc_power_up(host); + mmc_power_up(host, ocr); } /* @@ -2309,7 +2301,7 @@ static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) pr_info("%s: %s: trying to init card at %u Hz\n", mmc_hostname(host), __func__, host->f_init); #endif - mmc_power_up(host); + mmc_power_up(host, host->ocr_avail); /* * Some eMMCs (with VCCQ always on) may not be reset after power up, so @@ -2479,7 +2471,7 @@ void mmc_start_host(struct mmc_host *host) if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP) mmc_power_off(host); else - mmc_power_up(host); + mmc_power_up(host, host->ocr_avail); mmc_detect_change(host, 0); } @@ -2558,7 +2550,7 @@ int mmc_power_restore_host(struct mmc_host *host) return -EINVAL; } - mmc_power_up(host); + mmc_power_up(host, host->ocr); ret = host->bus_ops->power_restore(host); mmc_bus_put(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 5345d15..502769a 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -46,9 +46,9 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); -void mmc_power_up(struct mmc_host *host); +void mmc_power_up(struct mmc_host *host, u32 ocr); void mmc_power_off(struct mmc_host *host); -void mmc_power_cycle(struct mmc_host *host); +void mmc_power_cycle(struct mmc_host *host, u32 ocr); static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 8f0c516..75c66f9 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1533,7 +1533,7 @@ static int mmc_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - mmc_power_up(host); + mmc_power_up(host, host->ocr); mmc_select_voltage(host, host->ocr); err = mmc_init_card(host, host->ocr, host->card); mmc_release_host(host); @@ -1579,7 +1579,7 @@ static int mmc_runtime_resume(struct mmc_host *host) mmc_claim_host(host); - mmc_power_up(host); + mmc_power_up(host, host->ocr); err = mmc_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5e8823d..af55e9e 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1099,7 +1099,7 @@ static int mmc_sd_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - mmc_power_up(host); + mmc_power_up(host, host->ocr); mmc_select_voltage(host, host->ocr); err = mmc_sd_init_card(host, host->ocr, host->card); mmc_release_host(host); @@ -1144,7 +1144,7 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) mmc_claim_host(host); - mmc_power_up(host); + mmc_power_up(host, host->ocr); err = mmc_sd_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 80d89cff..355c952 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -981,7 +981,7 @@ static int mmc_sdio_resume(struct mmc_host *host) /* Restore power if needed */ if (!mmc_card_keep_power(host)) { - mmc_power_up(host); + mmc_power_up(host, host->ocr); mmc_select_voltage(host, host->ocr); /* * Tell runtime PM core we just powered up the card, @@ -1108,7 +1108,7 @@ static int mmc_sdio_runtime_suspend(struct mmc_host *host) static int mmc_sdio_runtime_resume(struct mmc_host *host) { /* Restore power and re-initialize. */ - mmc_power_up(host); + mmc_power_up(host, host->ocr); return mmc_sdio_power_restore(host); } -- cgit v0.10.2 From 0f791fdad82a02af013e4fc8c385dc690080c366 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 12 Sep 2013 15:36:34 +0200 Subject: mmc: core: Let mmc_set_signal_voltage take ocr as parameter This is yet another step of restructure code to be able to fixup the setup of the negotiated ocr mask. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index b66e7ab..5909ba46 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1397,7 +1397,7 @@ int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) } -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) { struct mmc_command cmd = {0}; int err = 0; @@ -1479,7 +1479,7 @@ power_cycle: if (err) { pr_debug("%s: Signal voltage switch failed, " "power cycling card\n", mmc_hostname(host)); - mmc_power_cycle(host, host->ocr); + mmc_power_cycle(host, ocr); } mmc_host_clk_release(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 502769a..443a584 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -42,7 +42,7 @@ void mmc_set_ungated(struct mmc_host *host); void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); void mmc_set_bus_width(struct mmc_host *host, unsigned int width); u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); -int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); +int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr); int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index af55e9e..5818887 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -773,7 +773,8 @@ try_again: */ if (!mmc_host_is_spi(host) && rocr && ((*rocr & 0x41000000) == 0x41000000)) { - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, + host->ocr); if (err == -EAGAIN) { retries--; goto try_again; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 355c952..15cbc418 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -664,7 +664,8 @@ try_again: * it. */ if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) { - err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180); + err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, + host->ocr); if (err == -EAGAIN) { sdio_reset(host); mmc_go_idle(host); -- cgit v0.10.2 From ec2ed7006263bd4224083aff7833bd791ac35d65 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 12 Sep 2013 16:38:56 +0200 Subject: mmc: core: Remove unnecessary retry mechanism at SDIO attach The retry and fallback mechanism when failing to switch to 1.8V signaling voltage is handled by the SDIO card init function. Thus we can remove the duplicated old code from the attach function. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 15cbc418..26b1316 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1175,17 +1175,9 @@ int mmc_attach_sdio(struct mmc_host *host) host->ocr |= R4_18V_PRESENT; err = mmc_sdio_init_card(host, host->ocr, NULL, 0); - if (err) { - if (err == -EAGAIN) { - /* - * Retry initialization with S18R set to 0. - */ - host->ocr &= ~R4_18V_PRESENT; - err = mmc_sdio_init_card(host, host->ocr, NULL, 0); - } - if (err) - goto err; - } + if (err) + goto err; + card = host->card; /* -- cgit v0.10.2 From db4a0d0512253c23a052ae650395c07d956118b3 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 13 Sep 2013 10:49:34 +0200 Subject: mmc: core: Cleanup code for setting ocr mask for SDIO At several places in mmc_sdio_init_card function the cached mask in host->ocr were being updated. To simplify code, we make use of an local ocr parameter instead. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 26b1316..9587d9f 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -593,23 +593,27 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, struct mmc_card *card; int err; int retries = 10; + u32 rocr = 0; BUG_ON(!host); WARN_ON(!host->claimed); + /* to query card if 1.8V signalling is supported */ + if (mmc_host_uhs(host)) + ocr |= R4_18V_PRESENT; + try_again: if (!retries) { pr_warning("%s: Skipping voltage switch\n", mmc_hostname(host)); ocr &= ~R4_18V_PRESENT; - host->ocr &= ~R4_18V_PRESENT; } /* * Inform the card of the voltage */ if (!powered_resume) { - err = mmc_send_io_op_cond(host, host->ocr, &ocr); + err = mmc_send_io_op_cond(host, ocr, &rocr); if (err) goto err; } @@ -632,8 +636,8 @@ try_again: goto err; } - if ((ocr & R4_MEMORY_PRESENT) && - mmc_sd_get_cid(host, host->ocr & ocr, card->raw_cid, NULL) == 0) { + if ((rocr & R4_MEMORY_PRESENT) && + mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) { card->type = MMC_TYPE_SD_COMBO; if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || @@ -663,9 +667,9 @@ try_again: * systems that claim 1.8v signalling in fact do not support * it. */ - if (!powered_resume && (ocr & R4_18V_PRESENT) && mmc_host_uhs(host)) { + if (!powered_resume && (rocr & ocr & R4_18V_PRESENT)) { err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, - host->ocr); + ocr); if (err == -EAGAIN) { sdio_reset(host); mmc_go_idle(host); @@ -675,12 +679,10 @@ try_again: goto try_again; } else if (err) { ocr &= ~R4_18V_PRESENT; - host->ocr &= ~R4_18V_PRESENT; } err = 0; } else { ocr &= ~R4_18V_PRESENT; - host->ocr &= ~R4_18V_PRESENT; } /* @@ -1084,10 +1086,6 @@ static int mmc_sdio_power_restore(struct mmc_host *host) goto out; } - if (mmc_host_uhs(host)) - /* to query card if 1.8V signalling is supported */ - host->ocr |= R4_18V_PRESENT; - ret = mmc_sdio_init_card(host, host->ocr, host->card, mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) @@ -1170,10 +1168,6 @@ int mmc_attach_sdio(struct mmc_host *host) /* * Detect and init the card. */ - if (mmc_host_uhs(host)) - /* to query card if 1.8V signalling is supported */ - host->ocr |= R4_18V_PRESENT; - err = mmc_sdio_init_card(host, host->ocr, NULL, 0); if (err) goto err; -- cgit v0.10.2 From 6904115095ad60ced638eb1e36e0e4e5e7de00b0 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 13 Sep 2013 11:31:33 +0200 Subject: mmc: core: Move cached value of the negotiated ocr mask to card struct The negotiated ocr mask is directly related to the card. Once a card gets removed, the mask shall be dropped. By moving the cache of the ocr mask from the host struct to the card struct we have accomplished this. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 5909ba46..529d2ef 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1574,9 +1574,10 @@ void mmc_power_off(struct mmc_host *host) /* * Reset ocr mask to be the highest possible voltage supported for - * this mmc host. This value will be used at next power up. + * this card. This value will be used at next power up. */ - host->ocr = 1 << (fls(host->ocr_avail) - 1); + if (host->card) + host->card->ocr = 1 << (fls(host->ocr_avail) - 1); if (!mmc_host_is_spi(host)) { host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; @@ -2550,7 +2551,7 @@ int mmc_power_restore_host(struct mmc_host *host) return -EINVAL; } - mmc_power_up(host, host->ocr); + mmc_power_up(host, host->card->ocr); ret = host->bus_ops->power_restore(host); mmc_bus_put(host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 75c66f9..eb7161c 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -934,6 +934,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, goto err; } + card->ocr = ocr; card->type = MMC_TYPE_MMC; card->rca = 1; memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); @@ -1533,9 +1534,9 @@ static int mmc_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - mmc_power_up(host, host->ocr); - mmc_select_voltage(host, host->ocr); - err = mmc_init_card(host, host->ocr, host->card); + mmc_power_up(host, host->card->ocr); + mmc_select_voltage(host, host->card->ocr); + err = mmc_init_card(host, host->card->ocr, host->card); mmc_release_host(host); return err; @@ -1579,7 +1580,7 @@ static int mmc_runtime_resume(struct mmc_host *host) mmc_claim_host(host); - mmc_power_up(host, host->ocr); + mmc_power_up(host, host->card->ocr); err = mmc_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", @@ -1595,7 +1596,7 @@ static int mmc_power_restore(struct mmc_host *host) host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); mmc_claim_host(host); - ret = mmc_init_card(host, host->ocr, host->card); + ret = mmc_init_card(host, host->card->ocr, host->card); mmc_release_host(host); return ret; @@ -1640,7 +1641,7 @@ static void mmc_attach_bus_ops(struct mmc_host *host) int mmc_attach_mmc(struct mmc_host *host) { int err; - u32 ocr; + u32 ocr, rocr; BUG_ON(!host); WARN_ON(!host->claimed); @@ -1677,12 +1678,12 @@ int mmc_attach_mmc(struct mmc_host *host) ocr &= ~0x7F; } - host->ocr = mmc_select_voltage(host, ocr); + rocr = mmc_select_voltage(host, ocr); /* * Can we support the voltage of the card? */ - if (!host->ocr) { + if (!rocr) { err = -EINVAL; goto err; } @@ -1690,7 +1691,7 @@ int mmc_attach_mmc(struct mmc_host *host) /* * Detect and init the card. */ - err = mmc_init_card(host, host->ocr, NULL); + err = mmc_init_card(host, rocr, NULL); if (err) goto err; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5818887..398065c 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -721,6 +721,7 @@ int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr) int err; u32 max_current; int retries = 10; + u32 pocr = ocr; try_again: if (!retries) { @@ -774,7 +775,7 @@ try_again: if (!mmc_host_is_spi(host) && rocr && ((*rocr & 0x41000000) == 0x41000000)) { err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, - host->ocr); + pocr); if (err == -EAGAIN) { retries--; goto try_again; @@ -936,6 +937,7 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, if (IS_ERR(card)) return PTR_ERR(card); + card->ocr = ocr; card->type = MMC_TYPE_SD; memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); } @@ -1100,9 +1102,9 @@ static int mmc_sd_resume(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); - mmc_power_up(host, host->ocr); - mmc_select_voltage(host, host->ocr); - err = mmc_sd_init_card(host, host->ocr, host->card); + mmc_power_up(host, host->card->ocr); + mmc_select_voltage(host, host->card->ocr); + err = mmc_sd_init_card(host, host->card->ocr, host->card); mmc_release_host(host); return err; @@ -1145,7 +1147,7 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) mmc_claim_host(host); - mmc_power_up(host, host->ocr); + mmc_power_up(host, host->card->ocr); err = mmc_sd_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", @@ -1161,7 +1163,7 @@ static int mmc_sd_power_restore(struct mmc_host *host) host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_claim_host(host); - ret = mmc_sd_init_card(host, host->ocr, host->card); + ret = mmc_sd_init_card(host, host->card->ocr, host->card); mmc_release_host(host); return ret; @@ -1206,7 +1208,7 @@ static void mmc_sd_attach_bus_ops(struct mmc_host *host) int mmc_attach_sd(struct mmc_host *host) { int err; - u32 ocr; + u32 ocr, rocr; BUG_ON(!host); WARN_ON(!host->claimed); @@ -1249,12 +1251,12 @@ int mmc_attach_sd(struct mmc_host *host) ocr &= ~MMC_VDD_165_195; } - host->ocr = mmc_select_voltage(host, ocr); + rocr = mmc_select_voltage(host, ocr); /* * Can we support the voltage(s) of the card(s)? */ - if (!host->ocr) { + if (!rocr) { err = -EINVAL; goto err; } @@ -1262,7 +1264,7 @@ int mmc_attach_sd(struct mmc_host *host) /* * Detect and init the card. */ - err = mmc_sd_init_card(host, host->ocr, NULL); + err = mmc_sd_init_card(host, rocr, NULL); if (err) goto err; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 9587d9f..e0a135a 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -594,6 +594,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr, int err; int retries = 10; u32 rocr = 0; + u32 ocr_card = ocr; BUG_ON(!host); WARN_ON(!host->claimed); @@ -762,6 +763,7 @@ try_again: card = oldcard; } + card->ocr = ocr_card; mmc_fixup_device(card, NULL); if (card->type == MMC_TYPE_SD_COMBO) { @@ -984,8 +986,8 @@ static int mmc_sdio_resume(struct mmc_host *host) /* Restore power if needed */ if (!mmc_card_keep_power(host)) { - mmc_power_up(host, host->ocr); - mmc_select_voltage(host, host->ocr); + mmc_power_up(host, host->card->ocr); + mmc_select_voltage(host, host->card->ocr); /* * Tell runtime PM core we just powered up the card, * since it still believes the card is powered off. @@ -1003,7 +1005,7 @@ static int mmc_sdio_resume(struct mmc_host *host) if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { sdio_reset(host); mmc_go_idle(host); - err = mmc_sdio_init_card(host, host->ocr, host->card, + err = mmc_sdio_init_card(host, host->card->ocr, host->card, mmc_card_keep_power(host)); } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ @@ -1043,7 +1045,7 @@ static int mmc_sdio_resume(struct mmc_host *host) static int mmc_sdio_power_restore(struct mmc_host *host) { int ret; - u32 ocr; + u32 ocr, rocr; BUG_ON(!host); BUG_ON(!host->card); @@ -1080,13 +1082,13 @@ static int mmc_sdio_power_restore(struct mmc_host *host) if (host->ocr_avail_sdio) host->ocr_avail = host->ocr_avail_sdio; - host->ocr = mmc_select_voltage(host, ocr & ~0x7F); - if (!host->ocr) { + rocr = mmc_select_voltage(host, ocr & ~0x7F); + if (!rocr) { ret = -EINVAL; goto out; } - ret = mmc_sdio_init_card(host, host->ocr, host->card, + ret = mmc_sdio_init_card(host, rocr, host->card, mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) mmc_signal_sdio_irq(host); @@ -1107,7 +1109,7 @@ static int mmc_sdio_runtime_suspend(struct mmc_host *host) static int mmc_sdio_runtime_resume(struct mmc_host *host) { /* Restore power and re-initialize. */ - mmc_power_up(host, host->ocr); + mmc_power_up(host, host->card->ocr); return mmc_sdio_power_restore(host); } @@ -1130,7 +1132,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = { int mmc_attach_sdio(struct mmc_host *host) { int err, i, funcs; - u32 ocr; + u32 ocr, rocr; struct mmc_card *card; BUG_ON(!host); @@ -1155,12 +1157,12 @@ int mmc_attach_sdio(struct mmc_host *host) ocr &= ~0x7F; } - host->ocr = mmc_select_voltage(host, ocr); + rocr = mmc_select_voltage(host, ocr); /* * Can we support the voltage(s) of the card(s)? */ - if (!host->ocr) { + if (!rocr) { err = -EINVAL; goto err; } @@ -1168,7 +1170,7 @@ int mmc_attach_sdio(struct mmc_host *host) /* * Detect and init the card. */ - err = mmc_sdio_init_card(host, host->ocr, NULL, 0); + err = mmc_sdio_init_card(host, rocr, NULL, 0); if (err) goto err; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index f42cdbd..33d9a74 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -240,6 +240,7 @@ struct mmc_part { struct mmc_card { struct mmc_host *host; /* the host this device belongs to */ struct device dev; /* the device */ + u32 ocr; /* the current OCR setting */ unsigned int rca; /* relative card address of device */ unsigned int type; /* card type */ #define MMC_TYPE_MMC 0 /* MMC card */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 3b0c33a..1c91bbb 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -309,7 +309,6 @@ struct mmc_host { spinlock_t lock; /* lock for claim and bus ops */ struct mmc_ios ios; /* current io bus settings */ - u32 ocr; /* the current OCR setting */ /* group bitfields together to minimize padding */ unsigned int use_spi_crc:1; -- cgit v0.10.2 From ce69d37b7d8fa692c45d71d94aa0c921859b82ce Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 16 Sep 2013 11:28:42 +0200 Subject: mmc: core: Prevent violation of specs while initializing cards According to eMMC/SD/SDIO specs, the VDD (VCC) voltage level must be maintained during the initialization sequence. If we want/need to tune the voltage level, a complete power cycle of the card must be executed. Most host drivers conforms to the specifications by only allowing to change VDD voltage level at the MMC_POWER_UP state, but some also cares about MMC_POWER_ON state, which they should'nt. This patch will not break those drivers, but they could clean up code to better reflect what is expected from the protocol layer. A big re-work of the mmc_select_voltage function is done to only change VDD voltage level if the host supports MMC_CAP2_FULL_PWR_CYCLE. Otherwise only validation of the host and card ocr mask will be done. A very nice side-effect of this patch is that we now don't need to reset the negotiated ocr mask at the mmc_power_off function, since now it will actually reflect the present voltage level, which safely can be used at the next power up and re-initialization. Moreover, we then only need to execute mmc_select_voltage from the attach sequence. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 529d2ef..63672ab 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1358,21 +1358,20 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) int bit; ocr &= host->ocr_avail; + if (!ocr) { + dev_warn(mmc_dev(host), "no support for card's volts\n"); + return 0; + } - bit = ffs(ocr); - if (bit) { - bit -= 1; - + if (host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) { + bit = ffs(ocr) - 1; ocr &= 3 << bit; - - mmc_host_clk_hold(host); - host->ios.vdd = bit; - mmc_set_ios(host); - mmc_host_clk_release(host); + mmc_power_cycle(host, ocr); } else { - pr_warning("%s: host doesn't support card's voltages\n", - mmc_hostname(host)); - ocr = 0; + bit = fls(ocr) - 1; + ocr &= 3 << bit; + if (bit != host->ios.vdd) + dev_warn(mmc_dev(host), "exceeding card's volts\n"); } return ocr; @@ -1571,14 +1570,6 @@ void mmc_power_off(struct mmc_host *host) host->ios.clock = 0; host->ios.vdd = 0; - - /* - * Reset ocr mask to be the highest possible voltage supported for - * this card. This value will be used at next power up. - */ - if (host->card) - host->card->ocr = 1 << (fls(host->ocr_avail) - 1); - if (!mmc_host_is_spi(host)) { host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index eb7161c..18ce81a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1535,7 +1535,6 @@ static int mmc_resume(struct mmc_host *host) mmc_claim_host(host); mmc_power_up(host, host->card->ocr); - mmc_select_voltage(host, host->card->ocr); err = mmc_init_card(host, host->card->ocr, host->card); mmc_release_host(host); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 398065c..53db60a 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1103,7 +1103,6 @@ static int mmc_sd_resume(struct mmc_host *host) mmc_claim_host(host); mmc_power_up(host, host->card->ocr); - mmc_select_voltage(host, host->card->ocr); err = mmc_sd_init_card(host, host->card->ocr, host->card); mmc_release_host(host); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index e0a135a..b7c19e8 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -987,7 +987,6 @@ static int mmc_sdio_resume(struct mmc_host *host) /* Restore power if needed */ if (!mmc_card_keep_power(host)) { mmc_power_up(host, host->card->ocr); - mmc_select_voltage(host, host->card->ocr); /* * Tell runtime PM core we just powered up the card, * since it still believes the card is powered off. @@ -1045,7 +1044,6 @@ static int mmc_sdio_resume(struct mmc_host *host) static int mmc_sdio_power_restore(struct mmc_host *host) { int ret; - u32 ocr, rocr; BUG_ON(!host); BUG_ON(!host->card); @@ -1067,28 +1065,17 @@ static int mmc_sdio_power_restore(struct mmc_host *host) * for OLPC SD8686 (which expects a [CMD5,5,3,7] init sequence), and * harmless in other situations. * - * With these steps taken, mmc_select_voltage() is also required to - * restore the correct voltage setting of the card. */ sdio_reset(host); mmc_go_idle(host); mmc_send_if_cond(host, host->ocr_avail); - ret = mmc_send_io_op_cond(host, 0, &ocr); + ret = mmc_send_io_op_cond(host, 0, NULL); if (ret) goto out; - if (host->ocr_avail_sdio) - host->ocr_avail = host->ocr_avail_sdio; - - rocr = mmc_select_voltage(host, ocr & ~0x7F); - if (!rocr) { - ret = -EINVAL; - goto out; - } - - ret = mmc_sdio_init_card(host, rocr, host->card, + ret = mmc_sdio_init_card(host, host->card->ocr, host->card, mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) mmc_signal_sdio_irq(host); -- cgit v0.10.2 From 726d6f2374410d980341bfeb49399614e689e702 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 16 Sep 2013 12:06:15 +0200 Subject: mmc: core: Collect common code for card ocr validation Since mmc_select_voltage now only gets called from the attach sequence, it makes sense to move the out of spec validations of the card ocr into this function. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 63672ab..bcb5077 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1357,6 +1357,16 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) { int bit; + /* + * Sanity check the voltages that the card claims to + * support. + */ + if (ocr & 0x7F) { + dev_warn(mmc_dev(host), + "card claims to support voltages below defined range\n"); + ocr &= ~0x7F; + } + ocr &= host->ocr_avail; if (!ocr) { dev_warn(mmc_dev(host), "no support for card's volts\n"); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 18ce81a..0d06022 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1666,17 +1666,6 @@ int mmc_attach_mmc(struct mmc_host *host) goto err; } - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - pr_warning("%s: card claims to support voltages " - "below the defined range. These will be ignored.\n", - mmc_hostname(host)); - ocr &= ~0x7F; - } - rocr = mmc_select_voltage(host, ocr); /* diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 53db60a..6ef84d0 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1231,25 +1231,6 @@ int mmc_attach_sd(struct mmc_host *host) goto err; } - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - pr_warning("%s: card claims to support voltages " - "below the defined range. These will be ignored.\n", - mmc_hostname(host)); - ocr &= ~0x7F; - } - - if ((ocr & MMC_VDD_165_195) && - !(host->ocr_avail_sd & MMC_VDD_165_195)) { - pr_warning("%s: SD card claims to support the " - "incompletely defined 'low voltage range'. This " - "will be ignored.\n", mmc_hostname(host)); - ocr &= ~MMC_VDD_165_195; - } - rocr = mmc_select_voltage(host, ocr); /* diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index b7c19e8..4d721c6 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1133,16 +1133,6 @@ int mmc_attach_sdio(struct mmc_host *host) if (host->ocr_avail_sdio) host->ocr_avail = host->ocr_avail_sdio; - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - pr_warning("%s: card claims to support voltages " - "below the defined range. These will be ignored.\n", - mmc_hostname(host)); - ocr &= ~0x7F; - } rocr = mmc_select_voltage(host, ocr); -- cgit v0.10.2 From bbd43682eb12dab2ff4addfca04c32043dfde026 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 20 Sep 2013 11:02:35 +0200 Subject: mmc: core: Signal wakeup event at card insert/removal We want to give user space provision to fully consume a card insert/remove event, when the event was caused by a wakeup irq. By signaling the wakeup event for a time of 5 s for devices configured as wakeup capable, we likely will be prevent a sleep long enough to let user space consume the event. To enable this feature, host drivers must thus configure their devices as wakeup capable. This is a reworked implementation of the old wakelocks for the mmc subsystem, originally authored by Colin Cross and San Mehat for the Android kernel. Zoran Markovic shall also be given cred for recently re-trying to upstream this feature. Cc: San Mehat Cc: Colin Cross Cc: John Stultz Cc: Zoran Markovic Signed-off-by: Ulf Hansson Reviewed-by: Zoran Markovic Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index bcb5077..49dfafa 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1692,6 +1693,28 @@ void mmc_detach_bus(struct mmc_host *host) mmc_bus_put(host); } +static void _mmc_detect_change(struct mmc_host *host, unsigned long delay, + bool cd_irq) +{ +#ifdef CONFIG_MMC_DEBUG + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); + WARN_ON(host->removed); + spin_unlock_irqrestore(&host->lock, flags); +#endif + + /* + * If the device is configured as wakeup, we prevent a new sleep for + * 5 s to give provision for user space to consume the event. + */ + if (cd_irq && !(host->caps & MMC_CAP_NEEDS_POLL) && + device_can_wakeup(mmc_dev(host))) + pm_wakeup_event(mmc_dev(host), 5000); + + host->detect_change = 1; + mmc_schedule_delayed_work(&host->detect, delay); +} + /** * mmc_detect_change - process change of state on a MMC socket * @host: host which changed state. @@ -1704,16 +1727,8 @@ void mmc_detach_bus(struct mmc_host *host) */ void mmc_detect_change(struct mmc_host *host, unsigned long delay) { -#ifdef CONFIG_MMC_DEBUG - unsigned long flags; - spin_lock_irqsave(&host->lock, flags); - WARN_ON(host->removed); - spin_unlock_irqrestore(&host->lock, flags); -#endif - host->detect_change = 1; - mmc_schedule_delayed_work(&host->detect, delay); + _mmc_detect_change(host, delay, true); } - EXPORT_SYMBOL(mmc_detect_change); void mmc_init_erase(struct mmc_card *card) @@ -2392,7 +2407,7 @@ int mmc_detect_card_removed(struct mmc_host *host) * rescan handle the card removal. */ cancel_delayed_work(&host->detect); - mmc_detect_change(host, 0); + _mmc_detect_change(host, 0, false); } } @@ -2474,7 +2489,7 @@ void mmc_start_host(struct mmc_host *host) mmc_power_off(host); else mmc_power_up(host, host->ocr_avail); - mmc_detect_change(host, 0); + _mmc_detect_change(host, 0, false); } void mmc_stop_host(struct mmc_host *host) @@ -2693,7 +2708,7 @@ int mmc_pm_notify(struct notifier_block *notify_block, spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 0; spin_unlock_irqrestore(&host->lock, flags); - mmc_detect_change(host, 0); + _mmc_detect_change(host, 0, false); } -- cgit v0.10.2 From 914af7c2e58f49c3f5940030cfc30c6693069934 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 09:54:16 +0200 Subject: mmc: atmel-mci: Remove redundant suspend and resume callbacks Suspend and resume of cards are handled by the protocol layer and consequently the mmc_suspend|resume_host APIs are marked as deprecated. While moving away from using the deprecated APIs, there are nothing left to be done for the suspend and resume callbacks, so remove them. Cc: Ludovic Desroches Signed-off-by: Ulf Hansson Acked-by: Ludovic Desroches Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 92c1877..2cbb451 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; @@ -2529,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), }, }; -- cgit v0.10.2 From 1e63d48542029799b4399411ec010afc8bd747a4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 10:55:23 +0200 Subject: mmc: au1xmmc: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Manuel Lauss Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index df9becd..f5443a6 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 -- cgit v0.10.2 From 15a6af4dc0d667593f3b952e96671f15b0edd9dc Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 11:45:32 +0200 Subject: mmc: bfin_sdh: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Sonic Zhang Cc: uclinux-dist-devel@blackfin.uclinux.org Signed-off-by: Ulf Hansson Acked-by: Sonic Zhang Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index e62c5bc..2b7f37e 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -639,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; @@ -664,10 +658,6 @@ static int sdh_resume(struct platform_device *dev) } sdh_reset(); - - if (mmc) - ret = mmc_resume_host(mmc); - return ret; } #else -- cgit v0.10.2 From d978a2b631506bd0501ceb185a21d23cd63b8790 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 12:16:19 +0200 Subject: mmc: cb710: Move away from using deprecated APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Michał Mirosław Signed-off-by: Ulf Hansson Acked-by: Michał Mirosław Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 9d6e2b8..1087b4c 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 */ -- cgit v0.10.2 From 5ffdeea522c8349ac96a11ec8e19486354fda808 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 12:29:19 +0200 Subject: mmc: davinci_mmc: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Sekhar Nori Cc: Manjunathappa, Prakash Cc: davinci-linux-open-source@linux.davincidsp.com Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index e9fa87d..d615374 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 = { -- cgit v0.10.2 From 1eeaadf3fe23f2f7271c9746d3cb65db56816360 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 13:10:41 +0200 Subject: mmc: dw_mmc: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Seungwon Jeon Cc: Jaehoon Chung Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 0a6a512..4bce0de 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2696,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); @@ -2765,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; } -- cgit v0.10.2 From c91c413e471974c69fc8ae1c3e0c86d71469fa05 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 13:19:58 +0200 Subject: mmc: jz4740: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Lars-Peter Clausen Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 6651633..de2139c 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -880,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; @@ -893,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; } -- cgit v0.10.2 From 29685f6c6a6579bd580ca4d2b9dcb74214525dd1 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 13:29:28 +0200 Subject: mmc: msm_sdcc: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Additionally, remove dead code which also used the deprecated APIs. Cc: David Brown Cc: Daniel Walker Cc: Bryan Huntsman Cc: linux-arm-msm@vger.kernel.org Signed-off-by: Ulf Hansson Acked-by: David Brown Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index b900de4..9405ecd 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 -- cgit v0.10.2 From 4d6c5d83395778192a9c14c0f88dabac5d7a9254 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 13:47:24 +0200 Subject: mmc: mvsdio: Remove redundant suspend and resume callbacks Suspend and resume of cards are handled by the protocol layer and consequently the mmc_suspend|resume_host APIs are marked as deprecated. While moving away from using the deprecated APIs, there are nothing left to be done for the suspend and resume callbacks, so remove them. Cc: Nicolas Pitre Cc: Thomas Petazzoni Cc: Jason Cooper Cc: Andrew Lunn Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index a592407..22ba4d3 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -838,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 */ } @@ -873,8 +846,6 @@ MODULE_DEVICE_TABLE(of, mvsdio_dt_ids); static struct platform_driver mvsd_driver = { .remove = __exit_p(mvsd_remove), - .suspend = mvsd_suspend, - .resume = mvsd_resume, .driver = { .name = DRIVER_NAME, .of_match_table = mvsdio_dt_ids, -- cgit v0.10.2 From 990b2993d11489cb6ee19f948d203afb35871071 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 14:07:06 +0200 Subject: mmc: vub300: Move away from using deprecated APIs Suspend and resume of cards are handled by the protocol layer and consequently the mmc_suspend|resume_host APIs are marked as deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Tony Olech Cc: linux-usb@vger.kernel.org Signed-off-by: Ulf Hansson Cc: Alan Stern Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index e9028ad..4262296 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -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 -- cgit v0.10.2 From 4ee14ec65810702a95d195757d307c41ecfd1c71 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 14:15:24 +0200 Subject: mmc: sdhci: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a21a710..6785fb1 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2556,8 +2556,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); @@ -2569,19 +2567,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); @@ -2589,14 +2574,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) @@ -2625,7 +2610,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) -- cgit v0.10.2 From ccbd483027d8b1cb1c85e2477cd9247d43515046 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 14:30:03 +0200 Subject: mmc: tifm_sd: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Alex Dubov Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 43d9628..d1760eb 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; } -- cgit v0.10.2 From 3932afd514af5855741116159627a90fd1ac918e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 14:47:06 +0200 Subject: mmc: omap: Remove redundant suspend and resume callbacks Suspend and resume of cards are handled by the protocol layer and consequently the mmc_suspend|resume_host APIs are marked as deprecated. While moving away from using the deprecated APIs, there are nothing left to be done for the suspend and resume callbacks, so remove them. Cc: Jarkko Lavinen Cc: linux-omap@vger.kernel.org Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index b94f38e..0b10a90 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -128,7 +128,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; @@ -1513,61 +1512,9 @@ 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 -#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, diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 540dead..dbd32ad 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1171,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) @@ -1632,11 +1629,6 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n", mmc->index, host->context_loss); - if (host->suspended) { - seq_printf(s, "host suspended, can't read registers\n"); - return 0; - } - pm_runtime_get_sync(host->dev); seq_printf(s, "CON:\t\t0x%08x\n", @@ -2101,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); @@ -2127,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) @@ -2154,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 -- cgit v0.10.2 From 683590791dedcae4170cb0fd4b05241098d27b54 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 15:21:53 +0200 Subject: mmc: mxs-mmc: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Shawn Guo Cc: Fabio Estevam Signed-off-by: Ulf Hansson Acked-by: Shawn Guo Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index e1fa3ef..50fc9df 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -724,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) @@ -738,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 = { -- cgit v0.10.2 From bd190f90b5fb75a8421ccfecd2ee6c82605f7620 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 15:33:01 +0200 Subject: mmc: mxcmmc: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Sascha Hauer Cc: Anatolij Gustschin Signed-off-by: Ulf Hansson Acked-by: Sascha Hauer Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index c174c6a..f7199c8 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 = { -- cgit v0.10.2 From 7af37663502ff842846dba1afa2f792afeb41805 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 15:43:45 +0200 Subject: mmc: wmt-sdmmc: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Tony Prisk Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 57858ef..e902ed7 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -925,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) @@ -955,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); @@ -973,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 = { -- cgit v0.10.2 From bfc55a8e86be704903b93122444f05872db20973 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 16:10:11 +0200 Subject: mmc: s3cmci: Remove redundant suspend and resume callbacks Suspend and resume of cards are handled by the protocol layer and consequently the mmc_suspend|resume_host APIs are marked as deprecated. While moving away from using the deprecated APIs, there are nothing left to be done for the suspend and resume callbacks, so remove them. Cc: Ben Dooks Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 8d6794c..2fce5ea 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, -- cgit v0.10.2 From 359bfcb42321e97b677539803f883c50205105e8 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 16:17:10 +0200 Subject: mmc: pxamci: Remove redundant suspend and resume callbacks Suspend and resume of cards are handled by the protocol layer and consequently the mmc_suspend|resume_host APIs are marked as deprecated. While moving away from using the deprecated APIs, there are nothing left to be done for the suspend and resume callbacks, so remove them. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 1956a3d..32fe113 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 }, }; -- cgit v0.10.2 From 83234ac895b769241771e1c23f889e0615780ffc Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 25 Sep 2013 16:48:25 +0200 Subject: mmc: wbsd: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Pierre Ossman Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index e954b77..024c824 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 @@ -1886,10 +1865,7 @@ static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state) 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 +1898,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 */ -- cgit v0.10.2 From ff71c4bcb0af2730d047989e485303ae4e1ce794 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 26 Sep 2013 09:43:24 +0200 Subject: mmc: rtsx: Remove redundant suspend and resume callbacks Suspend and resume of cards are handled by the protocol layer and consequently the mmc_suspend|resume_host APIs are marked as deprecated. While moving away from using the deprecated APIs, there are nothing left to be done for the suspend and resume callbacks, so remove them. Cc: Wei WANG Cc: Samuel Ortiz Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index e2ab124..c46feda 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1197,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; @@ -1367,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, -- cgit v0.10.2 From b2822d372560dfffd730b974b73d77b210a71ffa Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 26 Sep 2013 10:02:56 +0200 Subject: mmc: sdricoh_cs: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Sascha Sommer Signed-off-by: Ulf Hansson Acked-by: Sascha Sommer Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdricoh_cs.c b/drivers/mmc/host/sdricoh_cs.c index 50adbd1..b7e3057 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 -- cgit v0.10.2 From cb3ca1aed991cd67ae76aec682e63633a7bead5b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 26 Sep 2013 10:19:09 +0200 Subject: mmc: sh_mmcif: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Guennadi Liakhovetski Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 36629a0..6bffebe 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1542,19 +1542,15 @@ static int sh_mmcif_remove(struct platform_device *pdev) 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 -- cgit v0.10.2 From d62c9577f950b1ce9b53200542c251ba8dfff344 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 26 Sep 2013 10:36:06 +0200 Subject: mmc: tmio: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Guennadi Liakhovetski Cc: Ian Molton Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index b380225..f3b2d8c 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1145,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); @@ -1163,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); -- cgit v0.10.2 From ccad9b973f50144dddd05e9704b0eb4060c316a3 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 26 Sep 2013 10:47:11 +0200 Subject: mmc: via-sdmmc: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Bruce Chang Cc: Harald Welte Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 4f84586..63fac78 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; } -- cgit v0.10.2 From 578aebc7d6eca1de7a641b7ab0e70db73a42bcfa Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 26 Sep 2013 10:54:30 +0200 Subject: mmc: mmci: Move away from using deprecated APIs Suspend and resume of cards are being handled from the protocol layer and consequently the mmc_suspend|resume_host APIs are deprecated. This means we can simplify the suspend|resume callbacks by removing the use of the deprecated APIs. Cc: Russell King Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c3785ed..9e8a482 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1725,37 +1725,28 @@ 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 -- cgit v0.10.2 From 3c0d22e8180b98eea412b84aa0f0c42c16159679 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 26 Sep 2013 11:01:18 +0200 Subject: mmc: core: Remove deprecated mmc_suspend|resume_host APIs The are no more users of the deprecated mmc_suspend|resume_host API, so let's remove it. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 49dfafa..57a2b40 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2641,28 +2641,6 @@ EXPORT_SYMBOL(mmc_cache_ctrl); #ifdef CONFIG_PM -/** - * mmc_suspend_host - suspend a host - * @host: mmc host - */ -int mmc_suspend_host(struct mmc_host *host) -{ - /* This function is deprecated */ - return 0; -} -EXPORT_SYMBOL(mmc_suspend_host); - -/** - * mmc_resume_host - resume a previously suspended host - * @host: mmc host - */ -int mmc_resume_host(struct mmc_host *host) -{ - /* This function is deprecated */ - return 0; -} -EXPORT_SYMBOL(mmc_resume_host); - /* Do the card removal on suspend if card is assumed removeable * Do that in pm notifier while userspace isn't yet frozen, so we will be able to sync the card. diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 1c91bbb..f18669e 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -381,9 +381,6 @@ static inline void *mmc_priv(struct mmc_host *host) #define mmc_classdev(x) (&(x)->class_dev) #define mmc_hostname(x) (dev_name(&(x)->class_dev)) -int mmc_suspend_host(struct mmc_host *); -int mmc_resume_host(struct mmc_host *); - int mmc_power_save_host(struct mmc_host *host); int mmc_power_restore_host(struct mmc_host *host); -- cgit v0.10.2 From 0c04c6274fbc56c40649472e0076e949ecbc75ce Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 30 Sep 2013 11:50:31 +0200 Subject: MIPS: db1235: Don't use MMC_CLKGATE As a first step in removing code for MMC_CLKGATE, MIPS db1235 defconfig which is the only current user, shall move away from this option. The mmc host drivers au1xmmc and jz4740_mmc, which are used on MIPS don't support clock gating through MMC_CLKGATE, thus removing the config option will have no effect on power save - clock gating wise. Cc: Ralf Baechle Cc: linux-mips@linux-mips.org Signed-off-by: Ulf Hansson Acked-by: Ralf Baechle Signed-off-by: Chris Ball diff --git a/arch/mips/configs/db1235_defconfig b/arch/mips/configs/db1235_defconfig index e2b4ad5..28e49f2 100644 --- a/arch/mips/configs/db1235_defconfig +++ b/arch/mips/configs/db1235_defconfig @@ -351,7 +351,6 @@ CONFIG_USB_OHCI_HCD=y CONFIG_USB_OHCI_HCD_PLATFORM=y CONFIG_USB_STORAGE=y CONFIG_MMC=y -CONFIG_MMC_CLKGATE=y CONFIG_MMC_AU1X=y CONFIG_NEW_LEDS=y CONFIG_LEDS_CLASS=y -- cgit v0.10.2 From 9ec775f7efd6d17084b4f361804d2030d50fca0e Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 Oct 2013 17:37:09 +0200 Subject: mmc: Don't force card to active state when entering suspend/shutdown By adding a card state that records if it is suspended or resumed, we can accept asyncronus suspend/resume requests for the mmc and sd bus_ops. MMC_CAP_AGGRESSIVE_PM, will at request inactivity through the runtime bus_ops callbacks, execute a suspend of the the card. In the state were this has been done, we can receive a suspend request for the mmc bus, which for sd and mmc forced the card to active state by a pm_runtime_get_sync. In other words, the card was resumed and then immediately suspended again, completely unnecessary. Since the suspend/resume bus_ops callbacks for sd and mmc are now capable of handling asynchronous requests, we no longer need to force the card to active state before executing suspend. Evidently preventing the above sequence for MMC_CAP_AGGRESSIVE_PM. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 1a3163f..29d5d98 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2448,7 +2448,6 @@ static int _mmc_blk_suspend(struct mmc_card *card) struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { - pm_runtime_get_sync(&card->dev); mmc_queue_suspend(&md->queue); list_for_each_entry(part_md, &md->part, part) { mmc_queue_suspend(&part_md->queue); @@ -2483,7 +2482,6 @@ static int mmc_blk_resume(struct mmc_card *card) list_for_each_entry(part_md, &md->part, part) { mmc_queue_resume(&part_md->queue); } - pm_runtime_put(&card->dev); } return 0; } diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 0d06022..84d8694 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1478,6 +1478,9 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) mmc_claim_host(host); + if (mmc_card_suspended(host->card)) + goto out; + if (mmc_card_doing_bkops(host->card)) { err = mmc_stop_bkops(host->card); if (err) @@ -1497,8 +1500,10 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend) err = mmc_deselect_cards(host); host->card->state &= ~(MMC_STATE_HIGHSPEED | MMC_STATE_HIGHSPEED_200); - if (!err) + if (!err) { mmc_power_off(host); + mmc_card_set_suspended(host->card); + } out: mmc_release_host(host); return err; @@ -1513,14 +1518,6 @@ static int mmc_suspend(struct mmc_host *host) } /* - * Shutdown callback - */ -static int mmc_shutdown(struct mmc_host *host) -{ - return _mmc_suspend(host, false); -} - -/* * Resume callback from host. * * This function tries to determine if the same card is still present @@ -1528,19 +1525,45 @@ static int mmc_shutdown(struct mmc_host *host) */ static int mmc_resume(struct mmc_host *host) { - int err; + int err = 0; BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); + + if (!mmc_card_suspended(host->card)) + goto out; + mmc_power_up(host, host->card->ocr); err = mmc_init_card(host, host->card->ocr, host->card); - mmc_release_host(host); + mmc_card_clr_suspended(host->card); +out: + mmc_release_host(host); return err; } +/* + * Shutdown callback + */ +static int mmc_shutdown(struct mmc_host *host) +{ + int err = 0; + + /* + * In a specific case for poweroff notify, we need to resume the card + * before we can shutdown it properly. + */ + if (mmc_can_poweroff_notify(host->card) && + !(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)) + err = mmc_resume(host); + + if (!err) + err = _mmc_suspend(host, false); + + return err; +} /* * Callback for runtime_suspend. diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 6ef84d0..6857965 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1078,13 +1078,20 @@ static int mmc_sd_suspend(struct mmc_host *host) BUG_ON(!host->card); mmc_claim_host(host); + + if (mmc_card_suspended(host->card)) + goto out; + if (!mmc_host_is_spi(host)) err = mmc_deselect_cards(host); host->card->state &= ~MMC_STATE_HIGHSPEED; - if (!err) + if (!err) { mmc_power_off(host); - mmc_release_host(host); + mmc_card_set_suspended(host->card); + } +out: + mmc_release_host(host); return err; } @@ -1096,16 +1103,22 @@ static int mmc_sd_suspend(struct mmc_host *host) */ static int mmc_sd_resume(struct mmc_host *host) { - int err; + int err = 0; BUG_ON(!host); BUG_ON(!host->card); mmc_claim_host(host); + + if (!mmc_card_suspended(host->card)) + goto out; + mmc_power_up(host, host->card->ocr); err = mmc_sd_init_card(host, host->card->ocr, host->card); - mmc_release_host(host); + mmc_card_clr_suspended(host->card); +out: + mmc_release_host(host); return err; } diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 33d9a74..176fdf8 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -258,6 +258,7 @@ struct mmc_card { #define MMC_CARD_REMOVED (1<<7) /* card has been removed */ #define MMC_STATE_HIGHSPEED_200 (1<<8) /* card is in HS200 mode */ #define MMC_STATE_DOING_BKOPS (1<<10) /* card is doing BKOPS */ +#define MMC_STATE_SUSPENDED (1<<11) /* card is suspended */ unsigned int quirks; /* card quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ @@ -424,6 +425,7 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_ext_capacity(c) ((c)->state & MMC_CARD_SDXC) #define mmc_card_removed(c) ((c) && ((c)->state & MMC_CARD_REMOVED)) #define mmc_card_doing_bkops(c) ((c)->state & MMC_STATE_DOING_BKOPS) +#define mmc_card_suspended(c) ((c)->state & MMC_STATE_SUSPENDED) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) @@ -436,6 +438,8 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) #define mmc_card_set_removed(c) ((c)->state |= MMC_CARD_REMOVED) #define mmc_card_set_doing_bkops(c) ((c)->state |= MMC_STATE_DOING_BKOPS) #define mmc_card_clr_doing_bkops(c) ((c)->state &= ~MMC_STATE_DOING_BKOPS) +#define mmc_card_set_suspended(c) ((c)->state |= MMC_STATE_SUSPENDED) +#define mmc_card_clr_suspended(c) ((c)->state &= ~MMC_STATE_SUSPENDED) /* * Quirk add/remove for MMC products. -- cgit v0.10.2 From 0cc81a8c6442e43618ff245948bfaa14fdf7938b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 3 Oct 2013 11:24:44 +0200 Subject: mmc: core: Remove redundant mmc_power_up|off at runtime callbacks Commit "mmc: core: Push common suspend|resume code into each bus_ops" moved the responsibility for doing mmc_power_up|off into each suspend/resume bus_ops. When using MMC_CAP_AGGRESSIVE_PM, through the runtime callbacks, calls to mmc_power_up|off became redundant. When removing them, we are also able to remove the calls to mmc_claim|release_host, thus simplifing code a bit more. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 84d8694..b22eff8 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1575,18 +1575,11 @@ static int mmc_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - mmc_claim_host(host); - err = mmc_suspend(host); - if (err) { + if (err) pr_err("%s: error %d doing aggessive suspend\n", mmc_hostname(host), err); - goto out; - } - mmc_power_off(host); -out: - mmc_release_host(host); return err; } @@ -1600,15 +1593,11 @@ static int mmc_runtime_resume(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - mmc_claim_host(host); - - mmc_power_up(host, host->card->ocr); err = mmc_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", mmc_hostname(host), err); - mmc_release_host(host); return 0; } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 6857965..7b28540 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1132,18 +1132,11 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - mmc_claim_host(host); - err = mmc_sd_suspend(host); - if (err) { + if (err) pr_err("%s: error %d doing aggessive suspend\n", mmc_hostname(host), err); - goto out; - } - mmc_power_off(host); -out: - mmc_release_host(host); return err; } @@ -1157,15 +1150,11 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - mmc_claim_host(host); - - mmc_power_up(host, host->card->ocr); err = mmc_sd_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", mmc_hostname(host), err); - mmc_release_host(host); return 0; } -- cgit v0.10.2 From 0cb403a227774f60f6f52137ca3618805498c4e6 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 10 Oct 2013 14:20:05 +0200 Subject: mmc: core: Improve runtime PM support during suspend/resume for sd/mmc The card device is considered as in-active after it has been suspended. To prevent any further runtime PM requests in suspend state, we then disable runtime PM. After the card device has been resumed, we shall consider it as active, like we also do after a probe sequence. When resumed, we can safely enable runtime PM again. This will make sure the PM core can request the card device to go to in-active state after a resume has been completed. Previously we had to wait for new pm_runtime_get->pm_runtime_put cycle to be executed. Additionally, once a resume has been carried out, update the last busy mark. At the moment this will have no effect but if the PM core will respect autosuspend enabled devices, when it directly triggers a runtime_suspend from a runtime_idle, it will mean the card device will be scheduled for a delayed runtime_suspend instead of done immediately. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index b22eff8..7f5dc26 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -1510,20 +1511,26 @@ out: } /* - * Suspend callback from host. + * Suspend callback */ static int mmc_suspend(struct mmc_host *host) { - return _mmc_suspend(host, true); + int err; + + err = _mmc_suspend(host, true); + if (!err) { + pm_runtime_disable(&host->card->dev); + pm_runtime_set_suspended(&host->card->dev); + } + + return err; } /* - * Resume callback from host. - * * This function tries to determine if the same card is still present * and, if so, restore all state to it. */ -static int mmc_resume(struct mmc_host *host) +static int _mmc_resume(struct mmc_host *host) { int err = 0; @@ -1557,7 +1564,7 @@ static int mmc_shutdown(struct mmc_host *host) */ if (mmc_can_poweroff_notify(host->card) && !(host->caps2 & MMC_CAP2_FULL_PWR_CYCLE)) - err = mmc_resume(host); + err = _mmc_resume(host); if (!err) err = _mmc_suspend(host, false); @@ -1566,6 +1573,21 @@ static int mmc_shutdown(struct mmc_host *host) } /* + * Callback for resume. + */ +static int mmc_resume(struct mmc_host *host) +{ + int err; + + err = _mmc_resume(host); + pm_runtime_set_active(&host->card->dev); + pm_runtime_mark_last_busy(&host->card->dev); + pm_runtime_enable(&host->card->dev); + + return err; +} + +/* * Callback for runtime_suspend. */ static int mmc_runtime_suspend(struct mmc_host *host) @@ -1575,7 +1597,7 @@ static int mmc_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - err = mmc_suspend(host); + err = _mmc_suspend(host, true); if (err) pr_err("%s: error %d doing aggessive suspend\n", mmc_hostname(host), err); @@ -1593,7 +1615,7 @@ static int mmc_runtime_resume(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - err = mmc_resume(host); + err = _mmc_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", mmc_hostname(host), err); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 7b28540..447fa8e 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -1067,10 +1068,7 @@ static void mmc_sd_detect(struct mmc_host *host) } } -/* - * Suspend callback from host. - */ -static int mmc_sd_suspend(struct mmc_host *host) +static int _mmc_sd_suspend(struct mmc_host *host) { int err = 0; @@ -1096,12 +1094,26 @@ out: } /* - * Resume callback from host. - * + * Callback for suspend + */ +static int mmc_sd_suspend(struct mmc_host *host) +{ + int err; + + err = _mmc_sd_suspend(host); + if (!err) { + pm_runtime_disable(&host->card->dev); + pm_runtime_set_suspended(&host->card->dev); + } + + return err; +} + +/* * This function tries to determine if the same card is still present * and, if so, restore all state to it. */ -static int mmc_sd_resume(struct mmc_host *host) +static int _mmc_sd_resume(struct mmc_host *host) { int err = 0; @@ -1123,6 +1135,21 @@ out: } /* + * Callback for resume + */ +static int mmc_sd_resume(struct mmc_host *host) +{ + int err; + + err = _mmc_sd_resume(host); + pm_runtime_set_active(&host->card->dev); + pm_runtime_mark_last_busy(&host->card->dev); + pm_runtime_enable(&host->card->dev); + + return err; +} + +/* * Callback for runtime_suspend. */ static int mmc_sd_runtime_suspend(struct mmc_host *host) @@ -1132,7 +1159,7 @@ static int mmc_sd_runtime_suspend(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - err = mmc_sd_suspend(host); + err = _mmc_sd_suspend(host); if (err) pr_err("%s: error %d doing aggessive suspend\n", mmc_hostname(host), err); @@ -1150,7 +1177,7 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) return 0; - err = mmc_sd_resume(host); + err = _mmc_sd_resume(host); if (err) pr_err("%s: error %d doing aggessive resume\n", mmc_hostname(host), err); -- cgit v0.10.2 From 4d22378221bd0ed69c2e99408d31c108d72aeb80 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 10 Oct 2013 17:22:23 +0200 Subject: mmc: core: Add MMC_CAP_RUNTIME_RESUME to resume at runtime_resume In some environments it is to prefer to postpone the resume of the card device until runtime_resume is being carried out, since it will mean a signficant decrease of the total system resume time. The reason of the decreased resume time is simply because of the actual re-initalization of the card, which typically takes hundreds of milliseconds, is performed outside the resume sequence and wont thus affect it. For removable card, the detect work tries to re-detect the card to make sure it is still present, as a part of that sequence the card will also be runtime_resumed and thus also fully resumed. For a non-removable card, typically a mmc blk request will trigger a runtime_resume and thus fully resume the card. This also means the first request will likely suffer from an inital latency since the re-initialization of the card needs to be performed. Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7f5dc26..f631f5a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1577,11 +1577,13 @@ static int mmc_shutdown(struct mmc_host *host) */ static int mmc_resume(struct mmc_host *host) { - int err; + int err = 0; - err = _mmc_resume(host); - pm_runtime_set_active(&host->card->dev); - pm_runtime_mark_last_busy(&host->card->dev); + if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) { + err = _mmc_resume(host); + pm_runtime_set_active(&host->card->dev); + pm_runtime_mark_last_busy(&host->card->dev); + } pm_runtime_enable(&host->card->dev); return err; @@ -1612,7 +1614,7 @@ static int mmc_runtime_resume(struct mmc_host *host) { int err; - if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME))) return 0; err = _mmc_resume(host); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 447fa8e..6f42050 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1139,11 +1139,13 @@ out: */ static int mmc_sd_resume(struct mmc_host *host) { - int err; + int err = 0; - err = _mmc_sd_resume(host); - pm_runtime_set_active(&host->card->dev); - pm_runtime_mark_last_busy(&host->card->dev); + if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) { + err = _mmc_sd_resume(host); + pm_runtime_set_active(&host->card->dev); + pm_runtime_mark_last_busy(&host->card->dev); + } pm_runtime_enable(&host->card->dev); return err; @@ -1174,7 +1176,7 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) { int err; - if (!(host->caps & MMC_CAP_AGGRESSIVE_PM)) + if (!(host->caps & (MMC_CAP_AGGRESSIVE_PM | MMC_CAP_RUNTIME_RESUME))) return 0; err = _mmc_sd_resume(host); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index f18669e..99f5709 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -254,6 +254,7 @@ struct mmc_host { #define MMC_CAP_UHS_SDR50 (1 << 17) /* Host supports UHS SDR50 mode */ #define MMC_CAP_UHS_SDR104 (1 << 18) /* Host supports UHS SDR104 mode */ #define MMC_CAP_UHS_DDR50 (1 << 19) /* Host supports UHS DDR50 mode */ +#define MMC_CAP_RUNTIME_RESUME (1 << 20) /* Resume at runtime_resume. */ #define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */ #define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */ #define MMC_CAP_DRIVER_TYPE_D (1 << 25) /* Host supports Driver Type D */ -- cgit v0.10.2 From 0a8fd09ca9d4564a1f906b530ea08415a61cff58 Mon Sep 17 00:00:00 2001 From: Alexey Neyman Date: Tue, 5 Nov 2013 19:40:36 -0800 Subject: mmc: sdhci: Avoid needless loop while handling SDIO interrupts in sdhci_irq Ignore Card Interrupt bit in the interrupt status if we already know that mmc_signal_sdio_irq() is going to be called at the end of sdhci_irq(). This avoids a needless loop in sdhci_irq() repeatedly reading interrupt status and doing nothing. Signed-off-by: Alexey Neyman Acked-by: Dong Aisheng Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6785fb1..bd8a098 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2501,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: -- cgit v0.10.2 From 2d9f0bd1d4a66ab5019194c5e8ec5a55c77dd7e3 Mon Sep 17 00:00:00 2001 From: Yuvaraj Kumar C D Date: Tue, 22 Oct 2013 14:41:56 +0530 Subject: mmc: dw_mmc: exynos: Revert the sdr_timing assignment e6c784eded7b3 ("mmc: dw_mmc: exynos: move the exynos private init") was wrongly assigning ddr_timing value to sdr_timing. This patch fixes this by reverting the sdr_timing assignment statement to the earlier location. Signed-off-by: Yuvaraj Kumar C D Acked-by: Seungwon Jeon Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index d42e664..3423c5e 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -251,12 +251,13 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) if (ret) return ret; + priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); + ret = of_property_read_u32_array(np, "samsung,dw-mshc-ddr-timing", timing, 2); if (ret) return ret; - priv->sdr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); priv->ddr_timing = SDMMC_CLKSEL_TIMING(timing[0], timing[1], div); host->priv = priv; return 0; -- cgit v0.10.2 From 51129f31d2e7abcfb4cf35f703a42d1524c45dfa Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 1 Oct 2013 14:01:46 +0200 Subject: mmc: sh_mmcif: Convert to PM macros when defining dev_pm_ops Use SET_SYSTEM_SLEEP_PM_OPS to simplify code. Cc: Guennadi Liakhovetski Signed-off-by: Ulf Hansson Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 6bffebe..32bc412 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1538,7 +1538,7 @@ 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); @@ -1552,10 +1552,7 @@ static int sh_mmcif_resume(struct device *dev) { 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" }, @@ -1564,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 = { -- cgit v0.10.2 From ac0a2e98927e4797ae716b7327c3a9c85ba5431c Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 1 Oct 2013 14:56:57 +0200 Subject: mmc: sh_mmcif: Convert to clk_prepare|unprepare Previously only clk_enable|disable were being used. Adapt properly to the clock API, by also using clk_prepare|unprepare. Cc: Guennadi Liakhovetski Signed-off-by: Ulf Hansson Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 32bc412..d032b08 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -964,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); @@ -1018,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); @@ -1466,7 +1466,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) mutex_init(&host->thread_lock); - clk_disable(host->hclk); + clk_disable_unprepare(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) goto emmcaddh; @@ -1487,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: @@ -1505,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); @@ -1530,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); -- cgit v0.10.2 From ecd3a7dad982aeba6409ef6d933fd7e372a70127 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 28 Oct 2013 02:51:41 +0100 Subject: mmc: core: Silence compiler warning in __mmc_switch Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index aae8d8b..e5b5eeb 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -414,7 +414,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, int err; struct mmc_command cmd = {0}; unsigned long timeout; - u32 status; + u32 status = 0; bool ignore_crc = false; BUG_ON(!card); -- cgit v0.10.2 From e395c4387c746b4cc7aace4c44baecd7e69a3249 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 28 Oct 2013 07:45:53 +0100 Subject: mmc: wbsd: Silence compiler warning The commit "mmc: wbsd: Move away from using deprecated APIs" introduced a compiler warning, let's silence it. Cc: Pierre Ossman Signed-off-by: Ulf Hansson Signed-off-by: Chris Ball diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 024c824..1defd5e 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1859,7 +1859,6 @@ 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; -- cgit v0.10.2