From df16219f365f7f5a2d88a6e123251d57255cca3f Mon Sep 17 00:00:00 2001 From: Giuseppe CAVALLARO Date: Fri, 4 Nov 2011 13:53:19 +0100 Subject: mmc: debugfs: expose the SDCLK frq in sys ios This patch is to expose the actual SDCLK frequency in /sys/kernel/debug/mmcX/ios entry. For example, if the max clk for a normal speed card is 20MHz this is reported in /sys/kernel/debug/mmcX/ios. Unfortunately the actual SDCLK frequency (i.e. Baseclock / divisor) is not reported at all: for example, in that case, on Arasan HC, it should be 48/4=12 (MHz). Signed-off-by: Giuseppe Cavallaro Acked-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 19ed580f2cab..a7c23118dab2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1066,12 +1066,15 @@ static void sdhci_finish_command(struct sdhci_host *host) static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div = 0; /* Initialized for compiler warning */ + int real_div = div, clk_mul = 1; u16 clk = 0; unsigned long timeout; if (clock == host->clock) return; + host->mmc->actual_clock = 0; + if (host->ops->set_clock) { host->ops->set_clock(host, clock); if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) @@ -1109,6 +1112,8 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) * Control register. */ clk = SDHCI_PROG_CLOCK_MODE; + real_div = div; + clk_mul = host->clk_mul; div--; } } else { @@ -1122,6 +1127,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) break; } } + real_div = div; div >>= 1; } } else { @@ -1130,9 +1136,13 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if ((host->max_clk / div) <= clock) break; } + real_div = div; div >>= 1; } + if (real_div) + host->mmc->actual_clock = (host->max_clk * clk_mul) / real_div; + clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) << SDHCI_DIVIDER_HI_SHIFT; -- cgit v1.2.3 From b70a7fab26db65f7daaf04f49a3bd673250f48c7 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 15 Nov 2011 16:24:40 +0530 Subject: mmc: sdhci-spear: Implement suspend/resume Suspend/Resume is missing from sdhci-spear driver. This patch adds support for suspend/resume for this driver. Signed-off-by: Viresh Kumar Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-spear.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 63cc8b6a1c9e..dee70b66305a 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -271,10 +272,49 @@ static int __devexit sdhci_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int sdhci_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct spear_sdhci *sdhci = dev_get_platdata(dev); + pm_message_t state = {.event = 0}; + int ret; + + ret = sdhci_suspend_host(host, state); + if (!ret) + clk_disable(sdhci->clk); + + return ret; +} + +static int sdhci_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct spear_sdhci *sdhci = dev_get_platdata(dev); + int ret; + + ret = clk_enable(sdhci->clk); + if (ret) { + dev_dbg(dev, "Resume: Error enabling clock\n"); + return ret; + } + + return sdhci_resume_host(host); +} + +const struct dev_pm_ops sdhci_pm_ops = { + .suspend = sdhci_suspend, + .resume = sdhci_resume, +}; +#endif + static struct platform_driver sdhci_driver = { .driver = { .name = "sdhci", .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &sdhci_pm_ops, +#endif }, .probe = sdhci_probe, .remove = __devexit_p(sdhci_remove), -- cgit v1.2.3 From 5a09262744a0b84719b933ac66801de058776755 Mon Sep 17 00:00:00 2001 From: Per Forlin Date: Mon, 14 Nov 2011 12:02:28 +0100 Subject: mmc: mmci: add capabilities2 for MMC_CAP2 Signed-off-by: Per Forlin Signed-off-by: Chris Ball --- drivers/mmc/host/mmci.c | 1 + include/linux/amba/mmci.h | 2 ++ 2 files changed, 3 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 50b5f9926f64..8eabf999a858 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1239,6 +1239,7 @@ static int __devinit mmci_probe(struct amba_device *dev, if (host->vcc == NULL) mmc->ocr_avail = plat->ocr_mask; mmc->caps = plat->capabilities; + mmc->caps2 = plat->capabilities2; /* * We can do SGIO diff --git a/include/linux/amba/mmci.h b/include/linux/amba/mmci.h index 21114810c7c0..0101e9c17fa1 100644 --- a/include/linux/amba/mmci.h +++ b/include/linux/amba/mmci.h @@ -30,6 +30,7 @@ struct dma_chan; * @cd_invert: true if the gpio_cd pin value is active low * @capabilities: the capabilities of the block as implemented in * this platform, signify anything MMC_CAP_* from mmc/host.h + * @capabilities2: more capabilities, MMC_CAP2_* from mmc/host.h * @dma_filter: function used to select an appropriate RX and TX * DMA channel to be used for DMA, if and only if you're deploying the * generic DMA engine @@ -52,6 +53,7 @@ struct mmci_platform_data { int gpio_cd; bool cd_invert; unsigned long capabilities; + unsigned long capabilities2; bool (*dma_filter)(struct dma_chan *chan, void *filter_param); void *dma_rx_param; void *dma_tx_param; -- cgit v1.2.3 From 4ee5ebaf74ba1b3e2e3f9aad442bf38db96c20d2 Mon Sep 17 00:00:00 2001 From: Tony Lin Date: Tue, 22 Nov 2011 14:42:30 +0800 Subject: mmc: sdhci-esdhc: Change delay after setting clock from 100ms to 1ms 1ms is enough for hardware to change the clock to stable. 100ms is too long in the tasklet. Signed-off-by: Tony Lin CC: Xiaobo Xie CC: Anton Vorontsov Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-esdhc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-esdhc.h b/drivers/mmc/host/sdhci-esdhc.h index c3b08f111942..b97b2f5dafdb 100644 --- a/drivers/mmc/host/sdhci-esdhc.h +++ b/drivers/mmc/host/sdhci-esdhc.h @@ -73,7 +73,7 @@ static inline void esdhc_set_clock(struct sdhci_host *host, unsigned int clock) | (div << ESDHC_DIVIDER_SHIFT) | (pre_div << ESDHC_PREDIV_SHIFT)); sdhci_writel(host, temp, ESDHC_SYSTEM_CONTROL); - mdelay(100); + mdelay(1); out: host->clock = clock; } -- cgit v1.2.3 From 86df174585de79a460e3515ec413ea1f5a0bcf68 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 23 Nov 2011 15:52:30 +0100 Subject: mmc: sh_mmcif: simplify clock divisor calculation Replace ilog2(__rounddown_pow_of_two(x)) with the equivalent but much simpler fls(x) - 1. Reported-by: Andrew Morton Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index d5505f3fe2a1..2ec23b0053b6 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -16,6 +16,7 @@ * */ +#include #include #include #include @@ -399,7 +400,7 @@ static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); else sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & - (ilog2(__rounddown_pow_of_two(host->clk / clk)) << 16)); + ((fls(host->clk / clk) - 1) << 16)); sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); } -- cgit v1.2.3 From 6601056a96034fa08120b10b8dc728f213f91ec4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 24 Nov 2011 16:29:17 +0100 Subject: mmc: Remove redundant spi driver bus initialization In ancient times it was necessary to manually initialize the bus field of an spi_driver to spi_bus_type. These days this is done in spi_driver_register(), so we can drop the manual assignment. The patch was generated using the following coccinelle semantic patch: // @@ identifier _driver; @@ struct spi_driver _driver = { .driver = { - .bus = &spi_bus_type, }, }; // Signed-off-by: Lars-Peter Clausen Signed-off-by: Chris Ball --- drivers/mmc/host/mmc_spi.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 92946b84e9fa..273306c68d58 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1525,7 +1525,6 @@ static struct of_device_id mmc_spi_of_match_table[] __devinitdata = { static struct spi_driver mmc_spi_driver = { .driver = { .name = "mmc_spi", - .bus = &spi_bus_type, .owner = THIS_MODULE, .of_match_table = mmc_spi_of_match_table, }, -- cgit v1.2.3 From d1f81a64a4250bdd776978be06ae2b8e13ec7471 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 26 Nov 2011 12:55:43 +0800 Subject: mmc: convert drivers/mmc/host/* to use module_platform_driver() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch converts the drivers in drivers/mmc/host/* to use the module_platform_driver() macro which makes the code smaller and a bit simpler. Signed-off-by: Axel Lin Acked-by: "Michał Mirosław" Acked-by: David Brown Acked-by: Viresh Kumar Acked-by: Guennadi Liakhovetski Acked-by: Sascha Hauer Acked-by: Wolfram Sang Acked-by: Anton Vorontsov Signed-off-by: Chris Ball --- drivers/mmc/host/bfin_sdh.c | 12 +----------- drivers/mmc/host/cb710-mmc.c | 13 +------------ drivers/mmc/host/jz4740_mmc.c | 12 +----------- drivers/mmc/host/msm_sdcc.c | 13 +------------ drivers/mmc/host/mxcmmc.c | 13 +------------ drivers/mmc/host/mxs-mmc.c | 13 +------------ drivers/mmc/host/pxamci.c | 13 +------------ drivers/mmc/host/s3cmci.c | 13 +------------ drivers/mmc/host/sdhci-cns3xxx.c | 12 +----------- drivers/mmc/host/sdhci-dove.c | 12 +----------- drivers/mmc/host/sdhci-esdhc-imx.c | 12 +----------- drivers/mmc/host/sdhci-of-esdhc.c | 12 +----------- drivers/mmc/host/sdhci-of-hlwd.c | 12 +----------- drivers/mmc/host/sdhci-pxav2.c | 12 +----------- drivers/mmc/host/sdhci-pxav3.c | 12 +----------- drivers/mmc/host/sdhci-s3c.c | 13 +------------ drivers/mmc/host/sdhci-spear.c | 12 +----------- drivers/mmc/host/sdhci-tegra.c | 12 +----------- drivers/mmc/host/sh_mmcif.c | 14 +------------- drivers/mmc/host/sh_mobile_sdhi.c | 13 +------------ drivers/mmc/host/tmio_mmc.c | 14 +------------- 21 files changed, 21 insertions(+), 243 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index 0371bf502249..03666174ca48 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -627,17 +627,7 @@ static struct platform_driver sdh_driver = { }, }; -static int __init sdh_init(void) -{ - return platform_driver_register(&sdh_driver); -} -module_init(sdh_init); - -static void __exit sdh_exit(void) -{ - platform_driver_unregister(&sdh_driver); -} -module_exit(sdh_exit); +module_platform_driver(sdh_driver); MODULE_DESCRIPTION("Blackfin Secure Digital Host Driver"); MODULE_AUTHOR("Cliff Cai, Roy Huang"); diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index ce2a47b71dd6..83693fd7c6b3 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -780,18 +780,7 @@ static struct platform_driver cb710_mmc_driver = { #endif }; -static int __init cb710_mmc_init_module(void) -{ - return platform_driver_register(&cb710_mmc_driver); -} - -static void __exit cb710_mmc_cleanup_module(void) -{ - platform_driver_unregister(&cb710_mmc_driver); -} - -module_init(cb710_mmc_init_module); -module_exit(cb710_mmc_cleanup_module); +module_platform_driver(cb710_mmc_driver); MODULE_AUTHOR("Michał Mirosław "); MODULE_DESCRIPTION("ENE CB710 memory card reader driver - MMC/SD part"); diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 74218ad677e4..c8852a8128a9 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1012,17 +1012,7 @@ static struct platform_driver jz4740_mmc_driver = { }, }; -static int __init jz4740_mmc_init(void) -{ - return platform_driver_register(&jz4740_mmc_driver); -} -module_init(jz4740_mmc_init); - -static void __exit jz4740_mmc_exit(void) -{ - platform_driver_unregister(&jz4740_mmc_driver); -} -module_exit(jz4740_mmc_exit); +module_platform_driver(jz4740_mmc_driver); MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 80d8eb143b48..475596653c19 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -1480,18 +1480,7 @@ static struct platform_driver msmsdcc_driver = { }, }; -static int __init msmsdcc_init(void) -{ - return platform_driver_register(&msmsdcc_driver); -} - -static void __exit msmsdcc_exit(void) -{ - platform_driver_unregister(&msmsdcc_driver); -} - -module_init(msmsdcc_init); -module_exit(msmsdcc_exit); +module_platform_driver(msmsdcc_driver); MODULE_DESCRIPTION("Qualcomm MSM 7X00A Multimedia Card Interface driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 8e0fbe994047..7088b40f9579 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1047,18 +1047,7 @@ static struct platform_driver mxcmci_driver = { } }; -static int __init mxcmci_init(void) -{ - return platform_driver_register(&mxcmci_driver); -} - -static void __exit mxcmci_exit(void) -{ - platform_driver_unregister(&mxcmci_driver); -} - -module_init(mxcmci_init); -module_exit(mxcmci_exit); +module_platform_driver(mxcmci_driver); MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver"); MODULE_AUTHOR("Sascha Hauer, Pengutronix"); diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 99b449d26a4d..cf18fc2b24cd 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -855,18 +855,7 @@ static struct platform_driver mxs_mmc_driver = { }, }; -static int __init mxs_mmc_init(void) -{ - return platform_driver_register(&mxs_mmc_driver); -} - -static void __exit mxs_mmc_exit(void) -{ - platform_driver_unregister(&mxs_mmc_driver); -} - -module_init(mxs_mmc_init); -module_exit(mxs_mmc_exit); +module_platform_driver(mxs_mmc_driver); MODULE_DESCRIPTION("FREESCALE MXS MMC peripheral"); MODULE_AUTHOR("Freescale Semiconductor"); diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index fc4356e00d46..cb2dc0e75ba7 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -872,18 +872,7 @@ static struct platform_driver pxamci_driver = { }, }; -static int __init pxamci_init(void) -{ - return platform_driver_register(&pxamci_driver); -} - -static void __exit pxamci_exit(void) -{ - platform_driver_unregister(&pxamci_driver); -} - -module_init(pxamci_init); -module_exit(pxamci_exit); +module_platform_driver(pxamci_driver); MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 720f99334a7f..1bcfd6dbb5cc 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1914,18 +1914,7 @@ static struct platform_driver s3cmci_driver = { .shutdown = s3cmci_shutdown, }; -static int __init s3cmci_init(void) -{ - return platform_driver_register(&s3cmci_driver); -} - -static void __exit s3cmci_exit(void) -{ - platform_driver_unregister(&s3cmci_driver); -} - -module_init(s3cmci_init); -module_exit(s3cmci_exit); +module_platform_driver(s3cmci_driver); MODULE_DESCRIPTION("Samsung S3C MMC/SD Card Interface driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index b4257e700617..28a870804f60 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -115,17 +115,7 @@ static struct platform_driver sdhci_cns3xxx_driver = { .remove = __devexit_p(sdhci_cns3xxx_remove), }; -static int __init sdhci_cns3xxx_init(void) -{ - return platform_driver_register(&sdhci_cns3xxx_driver); -} -module_init(sdhci_cns3xxx_init); - -static void __exit sdhci_cns3xxx_exit(void) -{ - platform_driver_unregister(&sdhci_cns3xxx_driver); -} -module_exit(sdhci_cns3xxx_exit); +module_platform_driver(sdhci_cns3xxx_driver); MODULE_DESCRIPTION("SDHCI driver for CNS3xxx"); MODULE_AUTHOR("Scott Shu, " diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index a81312c91f70..46fd1fd1b605 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -88,17 +88,7 @@ static struct platform_driver sdhci_dove_driver = { .remove = __devexit_p(sdhci_dove_remove), }; -static int __init sdhci_dove_init(void) -{ - return platform_driver_register(&sdhci_dove_driver); -} -module_init(sdhci_dove_init); - -static void __exit sdhci_dove_exit(void) -{ - platform_driver_unregister(&sdhci_dove_driver); -} -module_exit(sdhci_dove_exit); +module_platform_driver(sdhci_dove_driver); MODULE_DESCRIPTION("SDHCI driver for Dove"); MODULE_AUTHOR("Saeed Bishara , " diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 38ebc4ea259f..d601e41af282 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -606,17 +606,7 @@ static struct platform_driver sdhci_esdhc_imx_driver = { .remove = __devexit_p(sdhci_esdhc_imx_remove), }; -static int __init sdhci_esdhc_imx_init(void) -{ - return platform_driver_register(&sdhci_esdhc_imx_driver); -} -module_init(sdhci_esdhc_imx_init); - -static void __exit sdhci_esdhc_imx_exit(void) -{ - platform_driver_unregister(&sdhci_esdhc_imx_driver); -} -module_exit(sdhci_esdhc_imx_exit); +module_platform_driver(sdhci_esdhc_imx_driver); MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC"); MODULE_AUTHOR("Wolfram Sang "); diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 01e5f627e0f0..ff4adc018041 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -131,17 +131,7 @@ static struct platform_driver sdhci_esdhc_driver = { .remove = __devexit_p(sdhci_esdhc_remove), }; -static int __init sdhci_esdhc_init(void) -{ - return platform_driver_register(&sdhci_esdhc_driver); -} -module_init(sdhci_esdhc_init); - -static void __exit sdhci_esdhc_exit(void) -{ - platform_driver_unregister(&sdhci_esdhc_driver); -} -module_exit(sdhci_esdhc_exit); +module_platform_driver(sdhci_esdhc_driver); MODULE_DESCRIPTION("SDHCI OF driver for Freescale MPC eSDHC"); MODULE_AUTHOR("Xiaobo Xie , " diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 3619adc7d9fc..0ce088ae0228 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -93,17 +93,7 @@ static struct platform_driver sdhci_hlwd_driver = { .remove = __devexit_p(sdhci_hlwd_remove), }; -static int __init sdhci_hlwd_init(void) -{ - return platform_driver_register(&sdhci_hlwd_driver); -} -module_init(sdhci_hlwd_init); - -static void __exit sdhci_hlwd_exit(void) -{ - platform_driver_unregister(&sdhci_hlwd_driver); -} -module_exit(sdhci_hlwd_exit); +module_platform_driver(sdhci_hlwd_driver); MODULE_DESCRIPTION("Nintendo Wii SDHCI OF driver"); MODULE_AUTHOR("The GameCube Linux Team, Albert Herranz"); diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 7a039c3cb1f1..dbb75bfbcffb 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -223,18 +223,8 @@ static struct platform_driver sdhci_pxav2_driver = { .probe = sdhci_pxav2_probe, .remove = __devexit_p(sdhci_pxav2_remove), }; -static int __init sdhci_pxav2_init(void) -{ - return platform_driver_register(&sdhci_pxav2_driver); -} - -static void __exit sdhci_pxav2_exit(void) -{ - platform_driver_unregister(&sdhci_pxav2_driver); -} -module_init(sdhci_pxav2_init); -module_exit(sdhci_pxav2_exit); +module_platform_driver(sdhci_pxav2_driver); MODULE_DESCRIPTION("SDHCI driver for pxav2"); MODULE_AUTHOR("Marvell International Ltd."); diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 15673a7ee6a5..f29695683556 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -269,18 +269,8 @@ static struct platform_driver sdhci_pxav3_driver = { .probe = sdhci_pxav3_probe, .remove = __devexit_p(sdhci_pxav3_remove), }; -static int __init sdhci_pxav3_init(void) -{ - return platform_driver_register(&sdhci_pxav3_driver); -} - -static void __exit sdhci_pxav3_exit(void) -{ - platform_driver_unregister(&sdhci_pxav3_driver); -} -module_init(sdhci_pxav3_init); -module_exit(sdhci_pxav3_exit); +module_platform_driver(sdhci_pxav3_driver); MODULE_DESCRIPTION("SDHCI driver for pxav3"); MODULE_AUTHOR("Marvell International Ltd."); diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 0d33ff0d67fb..55c1e870da83 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -657,18 +657,7 @@ static struct platform_driver sdhci_s3c_driver = { }, }; -static int __init sdhci_s3c_init(void) -{ - return platform_driver_register(&sdhci_s3c_driver); -} - -static void __exit sdhci_s3c_exit(void) -{ - platform_driver_unregister(&sdhci_s3c_driver); -} - -module_init(sdhci_s3c_init); -module_exit(sdhci_s3c_exit); +module_platform_driver(sdhci_s3c_driver); MODULE_DESCRIPTION("Samsung SDHCI (HSMMC) glue"); MODULE_AUTHOR("Ben Dooks, "); diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index dee70b66305a..883548eaa09e 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -320,17 +320,7 @@ static struct platform_driver sdhci_driver = { .remove = __devexit_p(sdhci_remove), }; -static int __init sdhci_init(void) -{ - return platform_driver_register(&sdhci_driver); -} -module_init(sdhci_init); - -static void __exit sdhci_exit(void) -{ - platform_driver_unregister(&sdhci_driver); -} -module_exit(sdhci_exit); +module_platform_driver(sdhci_driver); MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); MODULE_AUTHOR("Viresh Kumar "); diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index e2e18d3f949c..78a36eba4df0 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -324,17 +324,7 @@ static struct platform_driver sdhci_tegra_driver = { .remove = __devexit_p(sdhci_tegra_remove), }; -static int __init sdhci_tegra_init(void) -{ - return platform_driver_register(&sdhci_tegra_driver); -} -module_init(sdhci_tegra_init); - -static void __exit sdhci_tegra_exit(void) -{ - platform_driver_unregister(&sdhci_tegra_driver); -} -module_exit(sdhci_tegra_exit); +module_platform_driver(sdhci_tegra_driver); MODULE_DESCRIPTION("SDHCI driver for Tegra"); MODULE_AUTHOR(" Google, Inc."); diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 2ec23b0053b6..7bec007bcefa 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1207,19 +1207,7 @@ static struct platform_driver sh_mmcif_driver = { }, }; -static int __init sh_mmcif_init(void) -{ - return platform_driver_register(&sh_mmcif_driver); -} - -static void __exit sh_mmcif_exit(void) -{ - platform_driver_unregister(&sh_mmcif_driver); -} - -module_init(sh_mmcif_init); -module_exit(sh_mmcif_exit); - +module_platform_driver(sh_mmcif_driver); MODULE_DESCRIPTION("SuperH on-chip MMC/eMMC interface driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 41ae6466bd83..58da3c44acc5 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -282,18 +282,7 @@ static struct platform_driver sh_mobile_sdhi_driver = { .remove = __devexit_p(sh_mobile_sdhi_remove), }; -static int __init sh_mobile_sdhi_init(void) -{ - return platform_driver_register(&sh_mobile_sdhi_driver); -} - -static void __exit sh_mobile_sdhi_exit(void) -{ - platform_driver_unregister(&sh_mobile_sdhi_driver); -} - -module_init(sh_mobile_sdhi_init); -module_exit(sh_mobile_sdhi_exit); +module_platform_driver(sh_mobile_sdhi_driver); MODULE_DESCRIPTION("SuperH Mobile SDHI driver"); MODULE_AUTHOR("Magnus Damm"); diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index a4ea10242787..113ce6c9cf32 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -138,19 +138,7 @@ static struct platform_driver tmio_mmc_driver = { .resume = tmio_mmc_resume, }; - -static int __init tmio_mmc_init(void) -{ - return platform_driver_register(&tmio_mmc_driver); -} - -static void __exit tmio_mmc_exit(void) -{ - platform_driver_unregister(&tmio_mmc_driver); -} - -module_init(tmio_mmc_init); -module_exit(tmio_mmc_exit); +module_platform_driver(tmio_mmc_driver); MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver"); MODULE_AUTHOR("Ian Molton "); -- cgit v1.2.3 From 482fce997e143a8d5429406fe066d31aa76ef70a Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Sun, 27 Nov 2011 13:27:00 +0800 Subject: mmc: remove the second argument of k[un]map_atomic() Signed-off-by: Cong Wang Acked-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/at91_mci.c | 8 ++++---- drivers/mmc/host/msm_sdcc.c | 6 +++--- drivers/mmc/host/sdhci.c | 4 ++-- drivers/mmc/host/tifm_sd.c | 16 ++++++++-------- drivers/mmc/host/tmio_mmc.h | 4 ++-- 5 files changed, 19 insertions(+), 19 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index a8b4d2aa18e5..9a32922265e5 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -236,7 +236,7 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data sg = &data->sg[i]; - sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; + sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset; amount = min(size, sg->length); size -= amount; @@ -252,7 +252,7 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data dmabuf = (unsigned *)tmpv; } - kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); + kunmap_atomic(sgbuffer); if (size == 0) break; @@ -302,7 +302,7 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) sg = &data->sg[i]; - sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; + sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset; amount = min(size, sg->length); size -= amount; @@ -318,7 +318,7 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) } flush_kernel_dcache_page(sg_page(sg)); - kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); + kunmap_atomic(sgbuffer); data->bytes_xfered += amount; if (size == 0) break; diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 475596653c19..1d14cda95e56 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -689,8 +689,8 @@ msmsdcc_pio_irq(int irq, void *dev_id) /* Map the current scatter buffer */ local_irq_save(flags); - buffer = kmap_atomic(sg_page(host->pio.sg), - KM_BIO_SRC_IRQ) + host->pio.sg->offset; + buffer = kmap_atomic(sg_page(host->pio.sg)) + + host->pio.sg->offset; buffer += host->pio.sg_off; remain = host->pio.sg->length - host->pio.sg_off; len = 0; @@ -700,7 +700,7 @@ msmsdcc_pio_irq(int irq, void *dev_id) len = msmsdcc_pio_write(host, buffer, remain, status); /* Unmap the buffer */ - kunmap_atomic(buffer, KM_BIO_SRC_IRQ); + kunmap_atomic(buffer); local_irq_restore(flags); host->pio.sg_off += len; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a7c23118dab2..ab6018fc2338 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -423,12 +423,12 @@ static void sdhci_transfer_pio(struct sdhci_host *host) static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags) { local_irq_save(*flags); - return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; + return kmap_atomic(sg_page(sg)) + sg->offset; } static void sdhci_kunmap_atomic(void *buffer, unsigned long *flags) { - kunmap_atomic(buffer, KM_BIO_SRC_IRQ); + kunmap_atomic(buffer); local_irq_restore(*flags); } diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index f70d04664cac..fc00081687b9 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -118,7 +118,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg, unsigned char *buf; unsigned int pos = 0, val; - buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off; + buf = kmap_atomic(pg) + off; if (host->cmd_flags & DATA_CARRY) { buf[pos++] = host->bounce_buf_data[0]; host->cmd_flags &= ~DATA_CARRY; @@ -134,7 +134,7 @@ static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg, } buf[pos++] = (val >> 8) & 0xff; } - kunmap_atomic(buf - off, KM_BIO_DST_IRQ); + kunmap_atomic(buf - off); } static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, @@ -144,7 +144,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, unsigned char *buf; unsigned int pos = 0, val; - buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + off; + buf = kmap_atomic(pg) + off; if (host->cmd_flags & DATA_CARRY) { val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00); writel(val, sock->addr + SOCK_MMCSD_DATA); @@ -161,7 +161,7 @@ static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, val |= (buf[pos++] << 8) & 0xff00; writel(val, sock->addr + SOCK_MMCSD_DATA); } - kunmap_atomic(buf - off, KM_BIO_SRC_IRQ); + kunmap_atomic(buf - off); } static void tifm_sd_transfer_data(struct tifm_sd *host) @@ -212,13 +212,13 @@ static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off, struct page *src, unsigned int src_off, unsigned int count) { - unsigned char *src_buf = kmap_atomic(src, KM_BIO_SRC_IRQ) + src_off; - unsigned char *dst_buf = kmap_atomic(dst, KM_BIO_DST_IRQ) + dst_off; + unsigned char *src_buf = kmap_atomic(src) + src_off; + unsigned char *dst_buf = kmap_atomic(dst) + dst_off; memcpy(dst_buf, src_buf, count); - kunmap_atomic(dst_buf - dst_off, KM_BIO_DST_IRQ); - kunmap_atomic(src_buf - src_off, KM_BIO_SRC_IRQ); + kunmap_atomic(dst_buf - dst_off); + kunmap_atomic(src_buf - src_off); } static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 3020f98218f0..a95e6d901726 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -105,13 +105,13 @@ static inline char *tmio_mmc_kmap_atomic(struct scatterlist *sg, unsigned long *flags) { local_irq_save(*flags); - return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; + return kmap_atomic(sg_page(sg)) + sg->offset; } static inline void tmio_mmc_kunmap_atomic(struct scatterlist *sg, unsigned long *flags, void *virt) { - kunmap_atomic(virt - sg->offset, KM_BIO_SRC_IRQ); + kunmap_atomic(virt - sg->offset); local_irq_restore(*flags); } -- cgit v1.2.3 From fa1773cc5b78faa1ef0ab1ab0f48bce7bc72cec5 Mon Sep 17 00:00:00 2001 From: Sangwook Lee Date: Mon, 7 Nov 2011 17:05:22 +0000 Subject: mmc: sdhci-s3c: Add pm_caps into SD/MMC host sdhci-s3c updates pm_caps from platform data for SDIO PM. Signed-off-by: Sangwook Lee Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-s3c.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index 55c1e870da83..b6558b829a9c 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -524,6 +524,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; + if (pdata->pm_caps) + host->mmc->pm_caps |= pdata->pm_caps; + host->quirks |= (SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_32BIT_DMA_SIZE); -- cgit v1.2.3 From b678b91f22debd5f8efa04895652989004761d06 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 3 Dec 2011 15:28:05 +0800 Subject: mmc: sdhci-pci: simplify error handling Signed-off-by: Axel Lin Acked-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 6878a94626bc..d2e77fb21b26 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1012,11 +1012,8 @@ static int sdhci_pci_suspend(struct device *dev) ret = sdhci_suspend_host(slot->host); - if (ret) { - for (i--; i >= 0; i--) - sdhci_resume_host(chip->slots[i]->host); - return ret; - } + if (ret) + goto err_pci_suspend; slot_pm_flags = slot->host->mmc->pm_flags; if (slot_pm_flags & MMC_PM_WAKE_SDIO_IRQ) @@ -1027,11 +1024,8 @@ static int sdhci_pci_suspend(struct device *dev) if (chip->fixes && chip->fixes->suspend) { ret = chip->fixes->suspend(chip); - if (ret) { - for (i = chip->num_slots - 1; i >= 0; i--) - sdhci_resume_host(chip->slots[i]->host); - return ret; - } + if (ret) + goto err_pci_suspend; } pci_save_state(pdev); @@ -1048,6 +1042,11 @@ static int sdhci_pci_suspend(struct device *dev) } return 0; + +err_pci_suspend: + while (--i >= 0) + sdhci_resume_host(chip->slots[i]->host); + return ret; } static int sdhci_pci_resume(struct device *dev) @@ -1113,23 +1112,22 @@ static int sdhci_pci_runtime_suspend(struct device *dev) ret = sdhci_runtime_suspend_host(slot->host); - if (ret) { - for (i--; i >= 0; i--) - sdhci_runtime_resume_host(chip->slots[i]->host); - return ret; - } + if (ret) + goto err_pci_runtime_suspend; } if (chip->fixes && chip->fixes->suspend) { ret = chip->fixes->suspend(chip); - if (ret) { - for (i = chip->num_slots - 1; i >= 0; i--) - sdhci_runtime_resume_host(chip->slots[i]->host); - return ret; - } + if (ret) + goto err_pci_runtime_suspend; } return 0; + +err_pci_runtime_suspend: + while (--i >= 0) + sdhci_runtime_resume_host(chip->slots[i]->host); + return ret; } static int sdhci_pci_runtime_resume(struct device *dev) -- cgit v1.2.3 From 6fe8890d0200ea0c2b7d83936d58f97d7ba7c1ff Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 8 Dec 2011 19:23:03 +0900 Subject: mmc: dw_mmc: use dev_pm_ops for dw_mmc controllers This patch modifies dw_mmc to use dev_pm_ops. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: James Hogan Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 3aaeb0841914..44bc11e8761e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2062,14 +2062,14 @@ static int __exit dw_mci_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP /* * TODO: we should probably disable the clock to the card in the suspend path. */ -static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg) +static int dw_mci_suspend(struct device *dev) { int i, ret; - struct dw_mci *host = platform_get_drvdata(pdev); + struct dw_mci *host = dev_get_drvdata(dev); for (i = 0; i < host->num_slots; i++) { struct dw_mci_slot *slot = host->slot[i]; @@ -2092,10 +2092,10 @@ static int dw_mci_suspend(struct platform_device *pdev, pm_message_t mesg) return 0; } -static int dw_mci_resume(struct platform_device *pdev) +static int dw_mci_resume(struct device *dev) { int i, ret; - struct dw_mci *host = platform_get_drvdata(pdev); + struct dw_mci *host = dev_get_drvdata(dev); if (host->vmmc) regulator_enable(host->vmmc); @@ -2103,7 +2103,7 @@ static int dw_mci_resume(struct platform_device *pdev) if (host->dma_ops->init) host->dma_ops->init(host); - if (!mci_wait_reset(&pdev->dev, host)) { + if (!mci_wait_reset(dev, host)) { ret = -ENODEV; return ret; } @@ -2131,14 +2131,15 @@ static int dw_mci_resume(struct platform_device *pdev) #else #define dw_mci_suspend NULL #define dw_mci_resume NULL -#endif /* CONFIG_PM */ +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(dw_mci_pmops, dw_mci_suspend, dw_mci_resume); static struct platform_driver dw_mci_driver = { .remove = __exit_p(dw_mci_remove), - .suspend = dw_mci_suspend, - .resume = dw_mci_resume, .driver = { .name = "dw_mmc", + .pm = &dw_mci_pmops, }, }; -- cgit v1.2.3 From 4f408cc67a0613f969d1e02fff6de74d31a29fb3 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Fri, 9 Dec 2011 14:55:52 +0900 Subject: mmc: dw_mmc: Add more capabilities field This patch adds another capabilities field for MMC_CAPS2_XXX. Signed-off-by: Seungwon Jeon Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 6 ++++++ include/linux/mmc/dw_mmc.h | 1 + 2 files changed, 7 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 44bc11e8761e..69e588960e79 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1681,6 +1681,12 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) else mmc->caps = 0; + if (host->pdata->caps2) + mmc->caps2 = host->pdata->caps2; + else + mmc->caps2 = 0; + + if (host->pdata->get_bus_wd) if (host->pdata->get_bus_wd(slot->id) >= 4) mmc->caps |= MMC_CAP_4_BIT_DATA; diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 6dc9b80568a0..e8779c6d1759 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -214,6 +214,7 @@ struct dw_mci_board { unsigned int bus_hz; /* Bus speed */ unsigned int caps; /* Capabilities */ + unsigned int caps2; /* More capabilities */ /* * Override fifo depth. If 0, autodetect it from the FIFOTH register, * but note that this may not be reliable after a bootloader has used -- cgit v1.2.3 From 5b93a4595b6c39f78b69cc6eb9a3fa0ae6efe4a6 Mon Sep 17 00:00:00 2001 From: Chris Ball Date: Sat, 24 Dec 2011 21:06:06 -0500 Subject: mmc: dw_mmc: Remove unnecessary else clauses Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 69e588960e79..9b839fabfb3c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1678,14 +1678,9 @@ static int __init dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (host->pdata->caps) mmc->caps = host->pdata->caps; - else - mmc->caps = 0; if (host->pdata->caps2) mmc->caps2 = host->pdata->caps2; - else - mmc->caps2 = 0; - if (host->pdata->get_bus_wd) if (host->pdata->get_bus_wd(slot->id) >= 4) -- cgit v1.2.3 From 053b3ce6c15f0199c20b3fb06c3ae479de0324e2 Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Thu, 22 Dec 2011 18:01:29 +0900 Subject: mmc: dw_mmc: Support predefined mutiple block transfers This patch adds the support for predefined multiple block r/w. dw_mmc can support MMC_CAP_CMD23 capability. Signed-off-by: Seungwon Jeon Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 9b839fabfb3c..c583b943f615 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -588,11 +588,11 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot) mci_writel(host, CTYPE, (slot->ctype << slot->id)); } -static void dw_mci_start_request(struct dw_mci *host, - struct dw_mci_slot *slot) +static void __dw_mci_start_request(struct dw_mci *host, + struct dw_mci_slot *slot, + struct mmc_command *cmd) { struct mmc_request *mrq; - struct mmc_command *cmd; struct mmc_data *data; u32 cmdflags; @@ -610,14 +610,13 @@ static void dw_mci_start_request(struct dw_mci *host, host->completed_events = 0; host->data_status = 0; - data = mrq->data; + data = cmd->data; if (data) { dw_mci_set_timeout(host); mci_writel(host, BYTCNT, data->blksz*data->blocks); mci_writel(host, BLKSIZ, data->blksz); } - cmd = mrq->cmd; cmdflags = dw_mci_prepare_command(slot->mmc, cmd); /* this is the first command, send the initialization clock */ @@ -635,6 +634,16 @@ static void dw_mci_start_request(struct dw_mci *host, host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); } +static void dw_mci_start_request(struct dw_mci *host, + struct dw_mci_slot *slot) +{ + struct mmc_request *mrq = slot->mrq; + struct mmc_command *cmd; + + cmd = mrq->sbc ? mrq->sbc : mrq->cmd; + __dw_mci_start_request(host, slot, cmd); +} + /* must be called with host->lock held */ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, struct mmc_request *mrq) @@ -889,7 +898,14 @@ 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, host->mrq->cmd); + dw_mci_command_complete(host, cmd); + if (cmd == host->mrq->sbc && !cmd->error) { + prev_state = state = STATE_SENDING_CMD; + __dw_mci_start_request(host, host->cur_slot, + host->mrq->cmd); + goto unlock; + } + if (!host->mrq->data || cmd->error) { dw_mci_request_end(host, host->mrq); goto unlock; @@ -967,6 +983,12 @@ static void dw_mci_tasklet_func(unsigned long priv) goto unlock; } + if (host->mrq->sbc && !data->error) { + data->stop->error = 0; + dw_mci_request_end(host, host->mrq); + goto unlock; + } + prev_state = state = STATE_SENDING_STOP; if (!data->error) send_stop_cmd(host, data); -- cgit v1.2.3 From 7003fecbf0c9f7dde75d93ba75e0cb7ddb662749 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Wed, 14 Dec 2011 13:25:46 +0900 Subject: mmc: sdhci-s3c: use S3C_SDHCI_CONTROL2 instead of hardcoded offset value Signed-off-by: Jingoo Han Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-s3c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index b6558b829a9c..d065e3749e56 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -80,7 +80,7 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host) tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK; tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT; - writel(tmp, host->ioaddr + 0x80); + writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2); } } -- cgit v1.2.3 From cbb18b309d3d6b6661f931279697eac77b6591c9 Mon Sep 17 00:00:00 2001 From: Bastian Hecht Date: Fri, 23 Dec 2011 23:03:13 +0100 Subject: mmc: tmio_mmc: Hotplug code regrouping This patch regroups the code slightly, adds documentation and allows the rtpm counter of MMC_CAP_NEEDS_POLL devices to reach 0 again. Signed-off-by: Bastian Hecht [g.liakhovetski@gmx.de: restore pm_runtime_get_noresume()] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/tmio_mmc_pio.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 4208b3958069..abad01b37cfb 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -800,8 +800,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } else if (ios->power_mode != MMC_POWER_UP) { if (host->set_pwr && ios->power_mode == MMC_POWER_OFF) host->set_pwr(host->pdev, 0); - if ((pdata->flags & TMIO_MMC_HAS_COLD_CD) && - pdata->power) { + if (pdata->power) { pdata->power = false; pm_runtime_put(&host->pdev->dev); } @@ -915,6 +914,23 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, if (ret < 0) goto pm_disable; + /* + * There are 4 different scenarios for the card detection: + * 1) an external gpio irq handles the cd (best for power savings) + * 2) internal sdhi irq handles the cd + * 3) a worker thread polls the sdhi - indicated by MMC_CAP_NEEDS_POLL + * 4) the medium is non-removable - indicated by MMC_CAP_NONREMOVABLE + * + * While we increment the rtpm counter for all scenarios when the mmc + * core activates us by calling an appropriate set_ios(), we must + * additionally ensure that in case 2) the tmio mmc hardware stays + * powered on during runtime for the card detection to work. + */ + if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD + || mmc->caps & MMC_CAP_NEEDS_POLL + || mmc->caps & MMC_CAP_NONREMOVABLE)) + pm_runtime_get_noresume(&pdev->dev); + tmio_mmc_clk_stop(_host); tmio_mmc_reset(_host); @@ -933,12 +949,6 @@ int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, /* See if we also get DMA */ tmio_mmc_request_dma(_host, pdata); - /* We have to keep the device powered for its card detection to work */ - if (!(pdata->flags & TMIO_MMC_HAS_COLD_CD)) { - pdata->power = true; - pm_runtime_get_noresume(&pdev->dev); - } - mmc_add_host(mmc); /* Unmask the IRQs we want to know about */ @@ -974,7 +984,9 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) * the controller, the runtime PM is suspended and pdata->power == false, * so, our .runtime_resume() will not try to detect a card in the slot. */ - if (host->pdata->flags & TMIO_MMC_HAS_COLD_CD) + if (host->pdata->flags & TMIO_MMC_HAS_COLD_CD + || host->mmc->caps & MMC_CAP_NEEDS_POLL + || host->mmc->caps & MMC_CAP_NONREMOVABLE) pm_runtime_get_sync(&pdev->dev); mmc_remove_host(host->mmc); -- cgit v1.2.3 From 8a8284a98c1a58f5aa3eebce7971f81bcdb29d98 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 14 Dec 2011 19:31:51 +0100 Subject: mmc: sh_mmcif: process error interrupts first If an interrupt is coming with both error and data completion status bits set, it has to be handled as an error interrupt, for which error interrupts have to be processed first. The current version of the driver on the contrary doesn't recognise such interrupts as an error event, which leads to data corruption and breaks the error recovery. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 7bec007bcefa..0cba85a7f561 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -961,7 +961,12 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); - if (state & INT_RBSYE) { + if (state & INT_ERR_STS) { + /* error interrupts - process first */ + sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); + sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); + err = 1; + } else if (state & INT_RBSYE) { sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_RBSYE | INT_CRSPE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MRBSYE); @@ -989,11 +994,6 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~(INT_CMD12RBE | INT_CMD12CRE)); sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); - } else if (state & INT_ERR_STS) { - /* err interrupts */ - sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); - sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state); - err = 1; } else { dev_dbg(&host->pd->dev, "Unsupported interrupt: 0x%x\n", state); sh_mmcif_writel(host->addr, MMCIF_CE_INT, ~state); -- cgit v1.2.3 From ee4b88879f23badd54f5557852745fa28a1570f6 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 14 Dec 2011 19:31:52 +0100 Subject: mmc: sh_mmcif: cosmetic clean up This patch doesn't introduce any functional changes, it only simplifies some code fragments, removes superfluous parameters, fixes typos. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 79 +++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 45 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 0cba85a7f561..8aee127e60b8 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -124,6 +124,11 @@ #define MASK_MRBSYTO (1 << 1) #define MASK_MRSPTO (1 << 0) +#define MASK_START_CMD (MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | \ + MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | \ + MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | \ + MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO) + /* CE_HOST_STS1 */ #define STS1_CMDSEQ (1 << 31) @@ -176,8 +181,8 @@ struct sh_mmcif_host { long timeout; void __iomem *addr; struct completion intr_wait; + spinlock_t lock; /* protect sh_mmcif_host::state */ enum mmcif_state state; - spinlock_t lock; bool power; bool card_present; @@ -422,7 +427,7 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) static int sh_mmcif_error_manage(struct sh_mmcif_host *host) { u32 state1, state2; - int ret, timeout = 10000000; + int ret, timeout; host->sd_error = false; @@ -434,31 +439,30 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) if (state1 & STS1_CMDSEQ) { sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK); sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, ~CMD_CTRL_BREAK); - while (1) { - timeout--; - if (timeout < 0) { - dev_err(&host->pd->dev, - "Forceed end of command sequence timeout err\n"); - return -EIO; - } + for (timeout = 10000000; timeout; timeout--) { if (!(sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1) - & STS1_CMDSEQ)) + & STS1_CMDSEQ)) break; mdelay(1); } + if (!timeout) { + dev_err(&host->pd->dev, + "Forced end of command sequence timeout err\n"); + return -EIO; + } sh_mmcif_sync_reset(host); dev_dbg(&host->pd->dev, "Forced end of command sequence\n"); return -EIO; } if (state2 & STS2_CRC_ERR) { - dev_dbg(&host->pd->dev, ": Happened CRC error\n"); + dev_dbg(&host->pd->dev, ": CRC error\n"); ret = -EIO; } else if (state2 & STS2_TIMEOUT_ERR) { - dev_dbg(&host->pd->dev, ": Happened Timeout error\n"); + dev_dbg(&host->pd->dev, ": Timeout\n"); ret = -ETIMEDOUT; } else { - dev_dbg(&host->pd->dev, ": Happened End/Index error\n"); + dev_dbg(&host->pd->dev, ": End/Index error\n"); ret = -EIO; } return ret; @@ -681,55 +685,44 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, static int sh_mmcif_data_trans(struct sh_mmcif_host *host, struct mmc_request *mrq, u32 opc) { - int ret; - switch (opc) { case MMC_READ_MULTIPLE_BLOCK: - ret = sh_mmcif_multi_read(host, mrq); - break; + return sh_mmcif_multi_read(host, mrq); case MMC_WRITE_MULTIPLE_BLOCK: - ret = sh_mmcif_multi_write(host, mrq); - break; + return sh_mmcif_multi_write(host, mrq); case MMC_WRITE_BLOCK: - ret = sh_mmcif_single_write(host, mrq); - break; + return sh_mmcif_single_write(host, mrq); case MMC_READ_SINGLE_BLOCK: case MMC_SEND_EXT_CSD: - ret = sh_mmcif_single_read(host, mrq); - break; + return sh_mmcif_single_read(host, mrq); default: dev_err(&host->pd->dev, "UNSUPPORTED CMD = d'%08d\n", opc); - ret = -EINVAL; - break; + return -EINVAL; } - return ret; } static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, - struct mmc_request *mrq, struct mmc_command *cmd) + struct mmc_request *mrq) { + struct mmc_command *cmd = mrq->cmd; long time; - int ret = 0, mask = 0; - u32 opc = cmd->opcode; + int ret = 0; + u32 mask, opc = cmd->opcode; switch (opc) { - /* respons busy check */ + /* response busy check */ case MMC_SWITCH: case MMC_STOP_TRANSMISSION: case MMC_SET_WRITE_PROT: case MMC_CLR_WRITE_PROT: case MMC_ERASE: case MMC_GEN_CMD: - mask = MASK_MRBSYE; + mask = MASK_START_CMD | MASK_MRBSYE; break; default: - mask = MASK_MCRSPE; + mask = MASK_START_CMD | MASK_MCRSPE; break; } - mask |= MASK_MCMDVIO | MASK_MBUFVIO | MASK_MWDATERR | - MASK_MRDATERR | MASK_MRIDXERR | MASK_MRSPERR | - MASK_MCCSTO | MASK_MCRCSTO | MASK_MWDATTO | - MASK_MRDATTO | MASK_MRBSYTO | MASK_MRSPTO; if (host->data) { sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, 0); @@ -797,8 +790,9 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, } static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, - struct mmc_request *mrq, struct mmc_command *cmd) + struct mmc_request *mrq) { + struct mmc_command *cmd = mrq->stop; long time; if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) @@ -867,11 +861,11 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) sh_mmcif_start_dma_tx(host); } } - sh_mmcif_start_cmd(host, mrq, mrq->cmd); + sh_mmcif_start_cmd(host, mrq); host->data = NULL; if (!mrq->cmd->error && mrq->stop) - sh_mmcif_stop_cmd(host, mrq, mrq->stop); + sh_mmcif_stop_cmd(host, mrq); host->state = STATE_IDLE; mmc_request_done(mmc, mrq); } @@ -948,11 +942,6 @@ static struct mmc_host_ops sh_mmcif_ops = { .get_cd = sh_mmcif_get_cd, }; -static void sh_mmcif_detect(struct mmc_host *mmc) -{ - mmc_detect_change(mmc, 0); -} - static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; @@ -1114,7 +1103,7 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) goto clean_up3; } - sh_mmcif_detect(host->mmc); + mmc_detect_change(host->mmc, 0); dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); dev_dbg(&pdev->dev, "chip ver H'%04x\n", -- cgit v1.2.3 From f985da17f4d368896fb30d94531e4ffaa18e68d8 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Sun, 25 Dec 2011 21:07:52 +0100 Subject: mmc: sh_mmcif: process requests asynchronously This patch converts the sh_mmcif MMC host driver to process requests asynchronously instead of waiting in its .request() method for completion. This is achieved by using threaded IRQs. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 588 +++++++++++++++++++++++++++++++------------- 1 file changed, 416 insertions(+), 172 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 8aee127e60b8..9371f3a4939b 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -16,6 +16,32 @@ * */ +/* + * The MMCIF driver is now processing MMC requests asynchronously, according + * to the Linux MMC API requirement. + * + * The MMCIF driver processes MMC requests in up to 3 stages: command, optional + * data, and optional stop. To achieve asynchronous processing each of these + * stages is split into two halves: a top and a bottom half. The top half + * initialises the hardware, installs a timeout handler to handle completion + * timeouts, and returns. In case of the command stage this immediately returns + * control to the caller, leaving all further processing to run asynchronously. + * All further request processing is performed by the bottom halves. + * + * The bottom half further consists of a "hard" IRQ handler, an IRQ handler + * thread, a DMA completion callback, if DMA is used, a timeout work, and + * request- and stage-specific handler methods. + * + * Each bottom half run begins with either a hardware interrupt, a DMA callback + * invocation, or a timeout work run. In case of an error or a successful + * processing completion, the MMC core is informed and the request processing is + * finished. In case processing has to continue, i.e., if data has to be read + * from or written to the card, or if a stop command has to be sent, the next + * top half is called, which performs the necessary hardware handling and + * reschedules the timeout work. This returns the driver state machine into the + * bottom half waiting state. + */ + #include #include #include @@ -168,9 +194,22 @@ enum mmcif_state { STATE_IOS, }; +enum mmcif_wait_for { + MMCIF_WAIT_FOR_REQUEST, + MMCIF_WAIT_FOR_CMD, + MMCIF_WAIT_FOR_MREAD, + MMCIF_WAIT_FOR_MWRITE, + MMCIF_WAIT_FOR_READ, + MMCIF_WAIT_FOR_WRITE, + MMCIF_WAIT_FOR_READ_END, + MMCIF_WAIT_FOR_WRITE_END, + MMCIF_WAIT_FOR_STOP, +}; + struct sh_mmcif_host { struct mmc_host *mmc; struct mmc_data *data; + struct mmc_request *mrq; struct platform_device *pd; struct sh_dmae_slave dma_slave_tx; struct sh_dmae_slave dma_slave_rx; @@ -178,11 +217,17 @@ struct sh_mmcif_host { unsigned int clk; int bus_width; bool sd_error; + bool dying; long timeout; void __iomem *addr; - struct completion intr_wait; + u32 *pio_ptr; spinlock_t lock; /* protect sh_mmcif_host::state */ enum mmcif_state state; + enum mmcif_wait_for wait_for; + struct delayed_work timeout_work; + size_t blocksize; + int sg_idx; + int sg_blkidx; bool power; bool card_present; @@ -468,125 +513,183 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) return ret; } -static int sh_mmcif_single_read(struct sh_mmcif_host *host, - struct mmc_request *mrq) +static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p) { - struct mmc_data *data = mrq->data; - long time; - u32 blocksize, i, *p = sg_virt(data->sg); + struct mmc_data *data = host->mrq->data; + + host->sg_blkidx += host->blocksize; + + /* data->sg->length must be a multiple of host->blocksize? */ + BUG_ON(host->sg_blkidx > data->sg->length); + + if (host->sg_blkidx == data->sg->length) { + host->sg_blkidx = 0; + if (++host->sg_idx < data->sg_len) + host->pio_ptr = sg_virt(++data->sg); + } else { + host->pio_ptr = p; + } + + if (host->sg_idx == data->sg_len) + return false; + + return true; +} + +static void sh_mmcif_single_read(struct sh_mmcif_host *host, + struct mmc_request *mrq) +{ + host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & + BLOCK_SIZE_MASK) + 3; + + host->wait_for = MMCIF_WAIT_FOR_READ; + schedule_delayed_work(&host->timeout_work, host->timeout); /* buf read enable */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); - time = wait_for_completion_interruptible_timeout(&host->intr_wait, - host->timeout); - if (time <= 0 || host->sd_error) - return sh_mmcif_error_manage(host); - - blocksize = (BLOCK_SIZE_MASK & - sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3; - for (i = 0; i < blocksize / 4; i++) +} + +static bool sh_mmcif_read_block(struct sh_mmcif_host *host) +{ + struct mmc_data *data = host->mrq->data; + u32 *p = sg_virt(data->sg); + int i; + + if (host->sd_error) { + data->error = sh_mmcif_error_manage(host); + return false; + } + + for (i = 0; i < host->blocksize / 4; i++) *p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA); /* buffer read end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE); - time = wait_for_completion_interruptible_timeout(&host->intr_wait, - host->timeout); - if (time <= 0 || host->sd_error) - return sh_mmcif_error_manage(host); + host->wait_for = MMCIF_WAIT_FOR_READ_END; - return 0; + return true; } -static int sh_mmcif_multi_read(struct sh_mmcif_host *host, - struct mmc_request *mrq) +static void sh_mmcif_multi_read(struct sh_mmcif_host *host, + struct mmc_request *mrq) { struct mmc_data *data = mrq->data; - long time; - u32 blocksize, i, j, sec, *p; - - blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr, - MMCIF_CE_BLOCK_SET); - for (j = 0; j < data->sg_len; j++) { - p = sg_virt(data->sg); - for (sec = 0; sec < data->sg->length / blocksize; sec++) { - sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); - /* buf read enable */ - time = wait_for_completion_interruptible_timeout(&host->intr_wait, - host->timeout); - - if (time <= 0 || host->sd_error) - return sh_mmcif_error_manage(host); - - for (i = 0; i < blocksize / 4; i++) - *p++ = sh_mmcif_readl(host->addr, - MMCIF_CE_DATA); - } - if (j < data->sg_len - 1) - data->sg++; + + if (!data->sg_len || !data->sg->length) + return; + + host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & + BLOCK_SIZE_MASK; + + host->wait_for = MMCIF_WAIT_FOR_MREAD; + host->sg_idx = 0; + host->sg_blkidx = 0; + host->pio_ptr = sg_virt(data->sg); + schedule_delayed_work(&host->timeout_work, host->timeout); + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); +} + +static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) +{ + struct mmc_data *data = host->mrq->data; + u32 *p = host->pio_ptr; + int i; + + if (host->sd_error) { + data->error = sh_mmcif_error_manage(host); + return false; } - return 0; + + BUG_ON(!data->sg->length); + + for (i = 0; i < host->blocksize / 4; i++) + *p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA); + + if (!sh_mmcif_next_block(host, p)) + return false; + + schedule_delayed_work(&host->timeout_work, host->timeout); + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN); + + return true; } -static int sh_mmcif_single_write(struct sh_mmcif_host *host, +static void sh_mmcif_single_write(struct sh_mmcif_host *host, struct mmc_request *mrq) { - struct mmc_data *data = mrq->data; - long time; - u32 blocksize, i, *p = sg_virt(data->sg); + host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & + BLOCK_SIZE_MASK) + 3; - sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); + host->wait_for = MMCIF_WAIT_FOR_WRITE; + schedule_delayed_work(&host->timeout_work, host->timeout); /* buf write enable */ - time = wait_for_completion_interruptible_timeout(&host->intr_wait, - host->timeout); - if (time <= 0 || host->sd_error) - return sh_mmcif_error_manage(host); - - blocksize = (BLOCK_SIZE_MASK & - sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET)) + 3; - for (i = 0; i < blocksize / 4; i++) + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); +} + +static bool sh_mmcif_write_block(struct sh_mmcif_host *host) +{ + struct mmc_data *data = host->mrq->data; + u32 *p = sg_virt(data->sg); + int i; + + if (host->sd_error) { + data->error = sh_mmcif_error_manage(host); + return false; + } + + for (i = 0; i < host->blocksize / 4; i++) sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); /* buffer write end */ sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE); + host->wait_for = MMCIF_WAIT_FOR_WRITE_END; - time = wait_for_completion_interruptible_timeout(&host->intr_wait, - host->timeout); - if (time <= 0 || host->sd_error) - return sh_mmcif_error_manage(host); - - return 0; + return true; } -static int sh_mmcif_multi_write(struct sh_mmcif_host *host, - struct mmc_request *mrq) +static void sh_mmcif_multi_write(struct sh_mmcif_host *host, + struct mmc_request *mrq) { struct mmc_data *data = mrq->data; - long time; - u32 i, sec, j, blocksize, *p; - blocksize = BLOCK_SIZE_MASK & sh_mmcif_readl(host->addr, - MMCIF_CE_BLOCK_SET); + if (!data->sg_len || !data->sg->length) + return; - for (j = 0; j < data->sg_len; j++) { - p = sg_virt(data->sg); - for (sec = 0; sec < data->sg->length / blocksize; sec++) { - sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); - /* buf write enable*/ - time = wait_for_completion_interruptible_timeout(&host->intr_wait, - host->timeout); + host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) & + BLOCK_SIZE_MASK; - if (time <= 0 || host->sd_error) - return sh_mmcif_error_manage(host); + host->wait_for = MMCIF_WAIT_FOR_MWRITE; + host->sg_idx = 0; + host->sg_blkidx = 0; + host->pio_ptr = sg_virt(data->sg); + schedule_delayed_work(&host->timeout_work, host->timeout); + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); +} - for (i = 0; i < blocksize / 4; i++) - sh_mmcif_writel(host->addr, - MMCIF_CE_DATA, *p++); - } - if (j < data->sg_len - 1) - data->sg++; +static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) +{ + struct mmc_data *data = host->mrq->data; + u32 *p = host->pio_ptr; + int i; + + if (host->sd_error) { + data->error = sh_mmcif_error_manage(host); + return false; } - return 0; + + BUG_ON(!data->sg->length); + + for (i = 0; i < host->blocksize / 4; i++) + sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++); + + if (!sh_mmcif_next_block(host, p)) + return false; + + schedule_delayed_work(&host->timeout_work, host->timeout); + sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN); + + return true; } static void sh_mmcif_get_response(struct sh_mmcif_host *host, @@ -683,18 +786,22 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, } static int sh_mmcif_data_trans(struct sh_mmcif_host *host, - struct mmc_request *mrq, u32 opc) + struct mmc_request *mrq, u32 opc) { switch (opc) { case MMC_READ_MULTIPLE_BLOCK: - return sh_mmcif_multi_read(host, mrq); + sh_mmcif_multi_read(host, mrq); + return 0; case MMC_WRITE_MULTIPLE_BLOCK: - return sh_mmcif_multi_write(host, mrq); + sh_mmcif_multi_write(host, mrq); + return 0; case MMC_WRITE_BLOCK: - return sh_mmcif_single_write(host, mrq); + sh_mmcif_single_write(host, mrq); + return 0; case MMC_READ_SINGLE_BLOCK: case MMC_SEND_EXT_CSD: - return sh_mmcif_single_read(host, mrq); + sh_mmcif_single_read(host, mrq); + return 0; default: dev_err(&host->pd->dev, "UNSUPPORTED CMD = d'%08d\n", opc); return -EINVAL; @@ -705,9 +812,8 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, struct mmc_request *mrq) { struct mmc_command *cmd = mrq->cmd; - long time; - int ret = 0; - u32 mask, opc = cmd->opcode; + u32 opc = cmd->opcode; + u32 mask; switch (opc) { /* response busy check */ @@ -738,62 +844,14 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, /* set cmd */ sh_mmcif_writel(host->addr, MMCIF_CE_CMD_SET, opc); - time = wait_for_completion_interruptible_timeout(&host->intr_wait, - host->timeout); - if (time <= 0) { - cmd->error = sh_mmcif_error_manage(host); - return; - } - if (host->sd_error) { - switch (cmd->opcode) { - case MMC_ALL_SEND_CID: - case MMC_SELECT_CARD: - case MMC_APP_CMD: - cmd->error = -ETIMEDOUT; - break; - default: - dev_dbg(&host->pd->dev, "Cmd(d'%d) err\n", - cmd->opcode); - cmd->error = sh_mmcif_error_manage(host); - break; - } - host->sd_error = false; - return; - } - if (!(cmd->flags & MMC_RSP_PRESENT)) { - cmd->error = 0; - return; - } - sh_mmcif_get_response(host, cmd); - if (host->data) { - if (!host->dma_active) { - ret = sh_mmcif_data_trans(host, mrq, cmd->opcode); - } else { - long time = - wait_for_completion_interruptible_timeout(&host->dma_complete, - host->timeout); - if (!time) - ret = -ETIMEDOUT; - else if (time < 0) - ret = time; - sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, - BUF_ACC_DMAREN | BUF_ACC_DMAWEN); - host->dma_active = false; - } - if (ret < 0) - mrq->data->bytes_xfered = 0; - else - mrq->data->bytes_xfered = - mrq->data->blocks * mrq->data->blksz; - } - cmd->error = ret; + host->wait_for = MMCIF_WAIT_FOR_CMD; + schedule_delayed_work(&host->timeout_work, host->timeout); } static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, struct mmc_request *mrq) { struct mmc_command *cmd = mrq->stop; - long time; if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); @@ -805,14 +863,8 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, return; } - time = wait_for_completion_interruptible_timeout(&host->intr_wait, - host->timeout); - if (time <= 0 || host->sd_error) { - cmd->error = sh_mmcif_error_manage(host); - return; - } - sh_mmcif_get_cmd12response(host, cmd); - cmd->error = 0; + host->wait_for = MMCIF_WAIT_FOR_STOP; + schedule_delayed_work(&host->timeout_work, host->timeout); } static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) @@ -851,23 +903,11 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) default: break; } + + host->mrq = mrq; host->data = mrq->data; - if (mrq->data) { - if (mrq->data->flags & MMC_DATA_READ) { - if (host->chan_rx) - sh_mmcif_start_dma_rx(host); - } else { - if (host->chan_tx) - sh_mmcif_start_dma_tx(host); - } - } - sh_mmcif_start_cmd(host, mrq); - host->data = NULL; - if (!mrq->cmd->error && mrq->stop) - sh_mmcif_stop_cmd(host, mrq); - host->state = STATE_IDLE; - mmc_request_done(mmc, mrq); + sh_mmcif_start_cmd(host, mrq); } static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -942,6 +982,157 @@ static struct mmc_host_ops sh_mmcif_ops = { .get_cd = sh_mmcif_get_cd, }; +static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) +{ + struct mmc_command *cmd = host->mrq->cmd; + long time; + + if (host->sd_error) { + switch (cmd->opcode) { + case MMC_ALL_SEND_CID: + case MMC_SELECT_CARD: + case MMC_APP_CMD: + cmd->error = -ETIMEDOUT; + host->sd_error = false; + break; + default: + cmd->error = sh_mmcif_error_manage(host); + dev_dbg(&host->pd->dev, "Cmd(d'%d) error %d\n", + cmd->opcode, cmd->error); + break; + } + return false; + } + if (!(cmd->flags & MMC_RSP_PRESENT)) { + cmd->error = 0; + return false; + } + + sh_mmcif_get_response(host, cmd); + + if (!host->data) + return false; + + if (host->mrq->data->flags & MMC_DATA_READ) { + if (host->chan_rx) + sh_mmcif_start_dma_rx(host); + } else { + if (host->chan_tx) + sh_mmcif_start_dma_tx(host); + } + + if (!host->dma_active) { + host->data->error = sh_mmcif_data_trans(host, host->mrq, cmd->opcode); + if (!host->data->error) + return true; + return false; + } + + /* Running in the IRQ thread, can sleep */ + time = wait_for_completion_interruptible_timeout(&host->dma_complete, + host->timeout); + if (host->sd_error) { + dev_err(host->mmc->parent, + "Error IRQ while waiting for DMA completion!\n"); + /* Woken up by an error IRQ: abort DMA */ + if (host->data->flags & MMC_DATA_READ) + dmaengine_terminate_all(host->chan_rx); + else + dmaengine_terminate_all(host->chan_tx); + host->data->error = sh_mmcif_error_manage(host); + } else if (!time) { + host->data->error = -ETIMEDOUT; + } else if (time < 0) { + host->data->error = time; + } + sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, + BUF_ACC_DMAREN | BUF_ACC_DMAWEN); + host->dma_active = false; + + if (host->data->error) + host->data->bytes_xfered = 0; + + return false; +} + +static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) +{ + struct sh_mmcif_host *host = dev_id; + struct mmc_request *mrq = host->mrq; + + cancel_delayed_work_sync(&host->timeout_work); + + /* + * All handlers return true, if processing continues, and false, if the + * request has to be completed - successfully or not + */ + switch (host->wait_for) { + case MMCIF_WAIT_FOR_REQUEST: + /* We're too late, the timeout has already kicked in */ + return IRQ_HANDLED; + case MMCIF_WAIT_FOR_CMD: + if (sh_mmcif_end_cmd(host)) + /* Wait for data */ + return IRQ_HANDLED; + break; + case MMCIF_WAIT_FOR_MREAD: + if (sh_mmcif_mread_block(host)) + /* Wait for more data */ + return IRQ_HANDLED; + break; + case MMCIF_WAIT_FOR_READ: + if (sh_mmcif_read_block(host)) + /* Wait for data end */ + return IRQ_HANDLED; + break; + case MMCIF_WAIT_FOR_MWRITE: + if (sh_mmcif_mwrite_block(host)) + /* Wait data to write */ + return IRQ_HANDLED; + break; + case MMCIF_WAIT_FOR_WRITE: + if (sh_mmcif_write_block(host)) + /* Wait for data end */ + return IRQ_HANDLED; + break; + case MMCIF_WAIT_FOR_STOP: + if (host->sd_error) { + mrq->stop->error = sh_mmcif_error_manage(host); + break; + } + sh_mmcif_get_cmd12response(host, mrq->stop); + mrq->stop->error = 0; + break; + case MMCIF_WAIT_FOR_READ_END: + case MMCIF_WAIT_FOR_WRITE_END: + if (host->sd_error) + mrq->data->error = sh_mmcif_error_manage(host); + break; + default: + BUG(); + } + + if (host->wait_for != MMCIF_WAIT_FOR_STOP) { + host->data = NULL; + + if (!mrq->cmd->error && mrq->data && !mrq->data->error) + mrq->data->bytes_xfered = + mrq->data->blocks * mrq->data->blksz; + + if (mrq->stop && !mrq->cmd->error && (!mrq->data || !mrq->data->error)) { + sh_mmcif_stop_cmd(host, mrq); + if (!mrq->stop->error) + return IRQ_HANDLED; + } + } + + host->wait_for = MMCIF_WAIT_FOR_REQUEST; + host->state = STATE_IDLE; + mmc_request_done(host->mmc, mrq); + + return IRQ_HANDLED; +} + static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; @@ -993,14 +1184,58 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) host->sd_error = true; dev_dbg(&host->pd->dev, "int err state = %08x\n", state); } - if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) - complete(&host->intr_wait); - else + if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) { + if (!host->dma_active) + return IRQ_WAKE_THREAD; + else if (host->sd_error) + mmcif_dma_complete(host); + } else { dev_dbg(&host->pd->dev, "Unexpected IRQ 0x%x\n", state); + } return IRQ_HANDLED; } +static void mmcif_timeout_work(struct work_struct *work) +{ + struct delayed_work *d = container_of(work, struct delayed_work, work); + struct sh_mmcif_host *host = container_of(d, struct sh_mmcif_host, timeout_work); + struct mmc_request *mrq = host->mrq; + + if (host->dying) + /* Don't run after mmc_remove_host() */ + return; + + /* + * Handle races with cancel_delayed_work(), unless + * cancel_delayed_work_sync() is used + */ + switch (host->wait_for) { + case MMCIF_WAIT_FOR_CMD: + mrq->cmd->error = sh_mmcif_error_manage(host); + break; + case MMCIF_WAIT_FOR_STOP: + mrq->stop->error = sh_mmcif_error_manage(host); + break; + case MMCIF_WAIT_FOR_MREAD: + case MMCIF_WAIT_FOR_MWRITE: + case MMCIF_WAIT_FOR_READ: + case MMCIF_WAIT_FOR_WRITE: + case MMCIF_WAIT_FOR_READ_END: + case MMCIF_WAIT_FOR_WRITE_END: + host->data->error = sh_mmcif_error_manage(host); + break; + default: + BUG(); + } + + host->state = STATE_IDLE; + host->wait_for = MMCIF_WAIT_FOR_REQUEST; + host->data = NULL; + host->mrq = NULL; + mmc_request_done(host->mmc, mrq); +} + static int __devinit sh_mmcif_probe(struct platform_device *pdev) { int ret = 0, irq[2]; @@ -1054,7 +1289,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) host->clk = clk_get_rate(host->hclk); host->pd = pdev; - init_completion(&host->intr_wait); spin_lock_init(&host->lock); mmc->ops = &sh_mmcif_ops; @@ -1091,18 +1325,20 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - ret = request_irq(irq[0], sh_mmcif_intr, 0, "sh_mmc:error", host); + ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:error", host); if (ret) { dev_err(&pdev->dev, "request_irq error (sh_mmc:error)\n"); goto clean_up3; } - ret = request_irq(irq[1], sh_mmcif_intr, 0, "sh_mmc:int", host); + ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host); if (ret) { free_irq(irq[0], host); dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); goto clean_up3; } + INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); + mmc_detect_change(host->mmc, 0); dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); @@ -1129,11 +1365,19 @@ static int __devexit sh_mmcif_remove(struct platform_device *pdev) struct sh_mmcif_host *host = platform_get_drvdata(pdev); int irq[2]; + host->dying = true; pm_runtime_get_sync(&pdev->dev); mmc_remove_host(host->mmc); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); + /* + * FIXME: cancel_delayed_work(_sync)() and free_irq() race with the + * mmc_remove_host() call above. But swapping order doesn't help either + * (a query on the linux-mmc mailing list didn't bring any replies). + */ + cancel_delayed_work_sync(&host->timeout_work); + if (host->addr) iounmap(host->addr); -- cgit v1.2.3 From 699834045f1ec30156dd51c362a6840e737baaba Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 26 Dec 2011 12:52:13 -0500 Subject: mmc: sh_mmcif: remove now superfluous sh_mmcif_host::data member Signed-off-by: Guennadi Liakhovetski Signed-off-by: Chris Ball --- drivers/mmc/host/sh_mmcif.c | 94 ++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 44 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 9371f3a4939b..4a2c5b2355f2 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -208,7 +208,6 @@ enum mmcif_wait_for { struct sh_mmcif_host { struct mmc_host *mmc; - struct mmc_data *data; struct mmc_request *mrq; struct platform_device *pd; struct sh_dmae_slave dma_slave_tx; @@ -253,19 +252,21 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host, static void mmcif_dma_complete(void *arg) { struct sh_mmcif_host *host = arg; + struct mmc_data *data = host->mrq->data; + dev_dbg(&host->pd->dev, "Command completed\n"); - if (WARN(!host->data, "%s: NULL data in DMA completion!\n", + if (WARN(!data, "%s: NULL data in DMA completion!\n", dev_name(&host->pd->dev))) return; - if (host->data->flags & MMC_DATA_READ) + if (data->flags & MMC_DATA_READ) dma_unmap_sg(host->chan_rx->device->dev, - host->data->sg, host->data->sg_len, + data->sg, data->sg_len, DMA_FROM_DEVICE); else dma_unmap_sg(host->chan_tx->device->dev, - host->data->sg, host->data->sg_len, + data->sg, data->sg_len, DMA_TO_DEVICE); complete(&host->dma_complete); @@ -273,13 +274,14 @@ static void mmcif_dma_complete(void *arg) static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) { - struct scatterlist *sg = host->data->sg; + struct mmc_data *data = host->mrq->data; + struct scatterlist *sg = data->sg; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_rx; dma_cookie_t cookie = -EINVAL; int ret; - ret = dma_map_sg(chan->device->dev, sg, host->data->sg_len, + ret = dma_map_sg(chan->device->dev, sg, data->sg_len, DMA_FROM_DEVICE); if (ret > 0) { host->dma_active = true; @@ -295,7 +297,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) dma_async_issue_pending(chan); } dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n", - __func__, host->data->sg_len, ret, cookie); + __func__, data->sg_len, ret, cookie); if (!desc) { /* DMA failed, fall back to PIO */ @@ -316,18 +318,19 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) } dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__, - desc, cookie, host->data->sg_len); + desc, cookie, data->sg_len); } static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) { - struct scatterlist *sg = host->data->sg; + struct mmc_data *data = host->mrq->data; + struct scatterlist *sg = data->sg; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_tx; dma_cookie_t cookie = -EINVAL; int ret; - ret = dma_map_sg(chan->device->dev, sg, host->data->sg_len, + ret = dma_map_sg(chan->device->dev, sg, data->sg_len, DMA_TO_DEVICE); if (ret > 0) { host->dma_active = true; @@ -343,7 +346,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) dma_async_issue_pending(chan); } dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n", - __func__, host->data->sg_len, ret, cookie); + __func__, data->sg_len, ret, cookie); if (!desc) { /* DMA failed, fall back to PIO */ @@ -711,8 +714,11 @@ static void sh_mmcif_get_cmd12response(struct sh_mmcif_host *host, } static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, - struct mmc_request *mrq, struct mmc_command *cmd, u32 opc) + struct mmc_request *mrq) { + struct mmc_data *data = mrq->data; + struct mmc_command *cmd = mrq->cmd; + u32 opc = cmd->opcode; u32 tmp = 0; /* Response Type check */ @@ -744,7 +750,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, break; } /* WDAT / DATW */ - if (host->data) { + if (data) { tmp |= CMD_SET_WDAT; switch (host->bus_width) { case MMC_BUS_WIDTH_1: @@ -768,7 +774,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, if (opc == MMC_READ_MULTIPLE_BLOCK || opc == MMC_WRITE_MULTIPLE_BLOCK) { tmp |= CMD_SET_CMLTE | CMD_SET_CMD12EN; sh_mmcif_bitset(host, MMCIF_CE_BLOCK_SET, - mrq->data->blocks << 16); + data->blocks << 16); } /* RIDXC[1:0] check bits */ if (opc == MMC_SEND_OP_COND || opc == MMC_ALL_SEND_CID || @@ -782,7 +788,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, opc == MMC_SEND_CSD || opc == MMC_SEND_CID) tmp |= CMD_SET_CRC7C_INTERNAL; - return opc = ((opc << 24) | tmp); + return (opc << 24) | tmp; } static int sh_mmcif_data_trans(struct sh_mmcif_host *host, @@ -830,12 +836,12 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, break; } - if (host->data) { + if (mrq->data) { sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, 0); sh_mmcif_writel(host->addr, MMCIF_CE_BLOCK_SET, mrq->data->blksz); } - opc = sh_mmcif_set_cmd(host, mrq, cmd, opc); + opc = sh_mmcif_set_cmd(host, mrq); sh_mmcif_writel(host->addr, MMCIF_CE_INT, 0xD80430C0); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, mask); @@ -851,15 +857,16 @@ static void sh_mmcif_start_cmd(struct sh_mmcif_host *host, static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, struct mmc_request *mrq) { - struct mmc_command *cmd = mrq->stop; - - if (mrq->cmd->opcode == MMC_READ_MULTIPLE_BLOCK) + switch (mrq->cmd->opcode) { + case MMC_READ_MULTIPLE_BLOCK: sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); - else if (mrq->cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) + break; + case MMC_WRITE_MULTIPLE_BLOCK: sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); - else { + break; + default: dev_err(&host->pd->dev, "unsupported stop cmd\n"); - cmd->error = sh_mmcif_error_manage(host); + mrq->stop->error = sh_mmcif_error_manage(host); return; } @@ -905,7 +912,6 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) } host->mrq = mrq; - host->data = mrq->data; sh_mmcif_start_cmd(host, mrq); } @@ -985,6 +991,7 @@ static struct mmc_host_ops sh_mmcif_ops = { static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) { struct mmc_command *cmd = host->mrq->cmd; + struct mmc_data *data = host->mrq->data; long time; if (host->sd_error) { @@ -1010,10 +1017,10 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) sh_mmcif_get_response(host, cmd); - if (!host->data) + if (!data) return false; - if (host->mrq->data->flags & MMC_DATA_READ) { + if (data->flags & MMC_DATA_READ) { if (host->chan_rx) sh_mmcif_start_dma_rx(host); } else { @@ -1022,8 +1029,8 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) } if (!host->dma_active) { - host->data->error = sh_mmcif_data_trans(host, host->mrq, cmd->opcode); - if (!host->data->error) + data->error = sh_mmcif_data_trans(host, host->mrq, cmd->opcode); + if (!data->error) return true; return false; } @@ -1035,22 +1042,22 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) dev_err(host->mmc->parent, "Error IRQ while waiting for DMA completion!\n"); /* Woken up by an error IRQ: abort DMA */ - if (host->data->flags & MMC_DATA_READ) + if (data->flags & MMC_DATA_READ) dmaengine_terminate_all(host->chan_rx); else dmaengine_terminate_all(host->chan_tx); - host->data->error = sh_mmcif_error_manage(host); + data->error = sh_mmcif_error_manage(host); } else if (!time) { - host->data->error = -ETIMEDOUT; + data->error = -ETIMEDOUT; } else if (time < 0) { - host->data->error = time; + data->error = time; } sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); host->dma_active = false; - if (host->data->error) - host->data->bytes_xfered = 0; + if (data->error) + data->bytes_xfered = 0; return false; } @@ -1059,6 +1066,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; struct mmc_request *mrq = host->mrq; + struct mmc_data *data = mrq->data; cancel_delayed_work_sync(&host->timeout_work); @@ -1106,20 +1114,18 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) case MMCIF_WAIT_FOR_READ_END: case MMCIF_WAIT_FOR_WRITE_END: if (host->sd_error) - mrq->data->error = sh_mmcif_error_manage(host); + data->error = sh_mmcif_error_manage(host); break; default: BUG(); } if (host->wait_for != MMCIF_WAIT_FOR_STOP) { - host->data = NULL; + if (!mrq->cmd->error && data && !data->error) + data->bytes_xfered = + data->blocks * data->blksz; - if (!mrq->cmd->error && mrq->data && !mrq->data->error) - mrq->data->bytes_xfered = - mrq->data->blocks * mrq->data->blksz; - - if (mrq->stop && !mrq->cmd->error && (!mrq->data || !mrq->data->error)) { + if (mrq->stop && !mrq->cmd->error && (!data || !data->error)) { sh_mmcif_stop_cmd(host, mrq); if (!mrq->stop->error) return IRQ_HANDLED; @@ -1128,6 +1134,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) host->wait_for = MMCIF_WAIT_FOR_REQUEST; host->state = STATE_IDLE; + host->mrq = NULL; mmc_request_done(host->mmc, mrq); return IRQ_HANDLED; @@ -1223,7 +1230,7 @@ static void mmcif_timeout_work(struct work_struct *work) case MMCIF_WAIT_FOR_WRITE: case MMCIF_WAIT_FOR_READ_END: case MMCIF_WAIT_FOR_WRITE_END: - host->data->error = sh_mmcif_error_manage(host); + mrq->data->error = sh_mmcif_error_manage(host); break; default: BUG(); @@ -1231,7 +1238,6 @@ static void mmcif_timeout_work(struct work_struct *work) host->state = STATE_IDLE; host->wait_for = MMCIF_WAIT_FOR_REQUEST; - host->data = NULL; host->mrq = NULL; mmc_request_done(host->mmc, mrq); } -- cgit v1.2.3 From d83b6e035f983ec2833bf175ae093281b42ba551 Mon Sep 17 00:00:00 2001 From: Balaji TK Date: Tue, 20 Dec 2011 15:12:00 +0530 Subject: mmc: omap: remove clock rate hard coding MMC master clock rate can vary for each instance of the MMC controller on the device. Use clk_get_rate instead to get the value. Signed-off-by: Balaji TK Reviewed-by: Venkatraman S Tested-by: Hebbar, Gururaja Acked-by: Santosh Shilimkar Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index d5fe43d53c51..5b35c7e5a26d 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -120,7 +120,6 @@ #define MMC_AUTOSUSPEND_DELAY 100 #define MMC_TIMEOUT_MS 20 -#define OMAP_MMC_MASTER_CLOCK 96000000 #define OMAP_MMC_MIN_CLOCK 400000 #define OMAP_MMC_MAX_CLOCK 52000000 #define DRIVER_NAME "omap_hsmmc" @@ -598,12 +597,12 @@ static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host) } /* Calculate divisor for the given clock frequency */ -static u16 calc_divisor(struct mmc_ios *ios) +static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios) { u16 dsor = 0; if (ios->clock) { - dsor = DIV_ROUND_UP(OMAP_MMC_MASTER_CLOCK, ios->clock); + dsor = DIV_ROUND_UP(clk_get_rate(host->fclk), ios->clock); if (dsor > 250) dsor = 250; } @@ -623,7 +622,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) regval = OMAP_HSMMC_READ(host->base, SYSCTL); regval = regval & ~(CLKD_MASK | DTO_MASK); - regval = regval | (calc_divisor(ios) << 6) | (DTO << 16); + regval = regval | (calc_divisor(host, ios) << 6) | (DTO << 16); OMAP_HSMMC_WRITE(host->base, SYSCTL, regval); OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); -- cgit v1.2.3 From ceb6143b2df81c8a554bd5de898087926568cc51 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 27 Dec 2011 15:48:41 +0200 Subject: mmc: sdhci: fix vmmc handling Presently the vmmc regulator is enabled when the host controller is added and disabled when it is removed. However, the vmmc regulator should be under the control of the upper layers via ->set_ios(). Make it so. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ab6018fc2338..6a35d0368117 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1170,7 +1170,7 @@ out: host->clock = clock; } -static void sdhci_set_power(struct sdhci_host *host, unsigned short power) +static int sdhci_set_power(struct sdhci_host *host, unsigned short power) { u8 pwr = 0; @@ -1193,13 +1193,13 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) } if (host->pwr == pwr) - return; + return -1; host->pwr = pwr; if (pwr == 0) { sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); - return; + return 0; } /* @@ -1226,6 +1226,8 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) */ if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) mdelay(10); + + return power; } /*****************************************************************************\ @@ -1307,12 +1309,17 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) { unsigned long flags; + int vdd_bit = -1; u8 ctrl; spin_lock_irqsave(&host->lock, flags); - if (host->flags & SDHCI_DEVICE_DEAD) - goto out; + if (host->flags & SDHCI_DEVICE_DEAD) { + spin_unlock_irqrestore(&host->lock, flags); + if (host->vmmc && ios->power_mode == MMC_POWER_OFF) + mmc_regulator_set_ocr(host->mmc, host->vmmc, 0); + return; + } /* * Reset the chip on each power off. @@ -1326,9 +1333,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) sdhci_set_clock(host, ios->clock); if (ios->power_mode == MMC_POWER_OFF) - sdhci_set_power(host, -1); + vdd_bit = sdhci_set_power(host, -1); else - sdhci_set_power(host, ios->vdd); + vdd_bit = sdhci_set_power(host, ios->vdd); + + if (host->vmmc && vdd_bit != -1) { + spin_unlock_irqrestore(&host->lock, flags); + mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit); + spin_lock_irqsave(&host->lock, flags); + } if (host->ops->platform_send_init_74_clocks) host->ops->platform_send_init_74_clocks(host, ios->power_mode); @@ -1453,7 +1466,6 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) if(host->quirks & SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS) sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); -out: mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -2357,9 +2369,6 @@ int sdhci_suspend_host(struct sdhci_host *host) free_irq(host->irq, host); - if (host->vmmc) - ret = regulator_disable(host->vmmc); - return ret; } @@ -2369,12 +2378,6 @@ int sdhci_resume_host(struct sdhci_host *host) { int ret; - if (host->vmmc) { - int ret = regulator_enable(host->vmmc); - if (ret) - return ret; - } - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) host->ops->enable_dma(host); @@ -2936,8 +2939,6 @@ int sdhci_add_host(struct sdhci_host *host) if (IS_ERR(host->vmmc)) { pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); host->vmmc = NULL; - } else { - regulator_enable(host->vmmc); } sdhci_init(host, 0); @@ -3026,10 +3027,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->card_tasklet); tasklet_kill(&host->finish_tasklet); - if (host->vmmc) { - regulator_disable(host->vmmc); + if (host->vmmc) regulator_put(host->vmmc); - } kfree(host->adma_desc); kfree(host->align_buffer); -- cgit v1.2.3 From c79396c191bc19703df6eb6bbd0f673ed0df6c9d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 27 Dec 2011 15:48:42 +0200 Subject: mmc: sdhci: prevent card detection activity for non-removable cards Do not enable card detection interrupts for non-removable cards. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6a35d0368117..e6c6cd6e95f2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -146,10 +146,9 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) { u32 present, irqs; - if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) - return; - - if (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION) + if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || + (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION) || + !mmc_card_is_removable(host->mmc)) return; present = sdhci_readl(host, SDHCI_PRESENT_STATE) & -- cgit v1.2.3 From 52c506f0bc72530fb786838e7ffd4f158a2e5c3a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 27 Dec 2011 15:48:43 +0200 Subject: mmc: sdhci-pci: add platform data Add a means of getting platform data for the SDHCI PCI devices. The data is stored against the slot not the device in order to support multi-slot devices. The data allows platform-specific setup (such as getting GPIO numbers from firmware or setting up wl12xx for SDIO) to be done in platform support files instead of the sdhci-pci driver. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/Makefile | 2 +- drivers/mmc/Makefile | 3 +-- drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-pci-data.c | 5 +++++ drivers/mmc/host/sdhci-pci.c | 32 ++++++++++++++++++++++++++++---- include/linux/mmc/sdhci-pci-data.h | 18 ++++++++++++++++++ 6 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 drivers/mmc/host/sdhci-pci-data.c create mode 100644 include/linux/mmc/sdhci-pci-data.h (limited to 'drivers/mmc/host') diff --git a/drivers/Makefile b/drivers/Makefile index 1b3142127bf5..c07be024b962 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -97,7 +97,7 @@ obj-$(CONFIG_EISA) += eisa/ obj-y += lguest/ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_CPU_IDLE) += cpuidle/ -obj-$(CONFIG_MMC) += mmc/ +obj-y += mmc/ obj-$(CONFIG_MEMSTICK) += memstick/ obj-y += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 12eef393e216..400756ec7c49 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -6,5 +6,4 @@ subdir-ccflags-$(CONFIG_MMC_DEBUG) := -DDEBUG obj-$(CONFIG_MMC) += core/ obj-$(CONFIG_MMC) += card/ -obj-$(CONFIG_MMC) += host/ - +obj-$(subst m,y,$(CONFIG_MMC)) += host/ diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index b4b83f302e32..745f8fce2519 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o +obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o diff --git a/drivers/mmc/host/sdhci-pci-data.c b/drivers/mmc/host/sdhci-pci-data.c new file mode 100644 index 000000000000..a611217769f5 --- /dev/null +++ b/drivers/mmc/host/sdhci-pci-data.c @@ -0,0 +1,5 @@ +#include +#include + +struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno); +EXPORT_SYMBOL_GPL(sdhci_pci_get_data); diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index d2e77fb21b26..4e8f324e2c84 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "sdhci.h" @@ -61,6 +62,7 @@ struct sdhci_pci_fixes { struct sdhci_pci_slot { struct sdhci_pci_chip *chip; struct sdhci_host *host; + struct sdhci_pci_data *data; int pci_bar; int rst_n_gpio; @@ -1188,11 +1190,12 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = { \*****************************************************************************/ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( - struct pci_dev *pdev, struct sdhci_pci_chip *chip, int bar) + struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar, + int slotno) { struct sdhci_pci_slot *slot; struct sdhci_host *host; - int ret; + int ret, bar = first_bar + slotno; if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar); @@ -1227,6 +1230,20 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( slot->pci_bar = bar; slot->rst_n_gpio = -EINVAL; + /* Retrieve platform data if there is any */ + if (*sdhci_pci_get_data) + slot->data = sdhci_pci_get_data(pdev, slotno); + + if (slot->data) { + if (slot->data->setup) { + ret = slot->data->setup(slot->data); + if (ret) { + dev_err(&pdev->dev, "platform setup failed\n"); + goto free; + } + } + } + host->hw_name = "PCI"; host->ops = &sdhci_pci_ops; host->quirks = chip->quirks; @@ -1236,7 +1253,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( ret = pci_request_region(pdev, bar, mmc_hostname(host->mmc)); if (ret) { dev_err(&pdev->dev, "cannot request region\n"); - goto free; + goto cleanup; } host->ioaddr = pci_ioremap_bar(pdev, bar); @@ -1270,6 +1287,10 @@ unmap: release: pci_release_region(pdev, bar); +cleanup: + if (slot->data && slot->data->cleanup) + slot->data->cleanup(slot->data); + free: sdhci_free_host(host); @@ -1291,6 +1312,9 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) if (slot->chip->fixes && slot->chip->fixes->remove_slot) slot->chip->fixes->remove_slot(slot, dead); + if (slot->data && slot->data->cleanup) + slot->data->cleanup(slot->data); + pci_release_region(slot->chip->pdev, slot->pci_bar); sdhci_free_host(slot->host); @@ -1377,7 +1401,7 @@ static int __devinit sdhci_pci_probe(struct pci_dev *pdev, slots = chip->num_slots; /* Quirk may have changed this */ for (i = 0; i < slots; i++) { - slot = sdhci_pci_probe_slot(pdev, chip, first_bar + i); + slot = sdhci_pci_probe_slot(pdev, chip, first_bar, i); if (IS_ERR(slot)) { for (i--; i >= 0; i--) sdhci_pci_remove_slot(chip->slots[i]); diff --git a/include/linux/mmc/sdhci-pci-data.h b/include/linux/mmc/sdhci-pci-data.h new file mode 100644 index 000000000000..8959604a13d3 --- /dev/null +++ b/include/linux/mmc/sdhci-pci-data.h @@ -0,0 +1,18 @@ +#ifndef LINUX_MMC_SDHCI_PCI_DATA_H +#define LINUX_MMC_SDHCI_PCI_DATA_H + +struct pci_dev; + +struct sdhci_pci_data { + struct pci_dev *pdev; + int slotno; + int rst_n_gpio; /* Set to -EINVAL if unused */ + int cd_gpio; /* Set to -EINVAL if unused */ + int (*setup)(struct sdhci_pci_data *data); + void (*cleanup)(struct sdhci_pci_data *data); +}; + +extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, + int slotno); + +#endif -- cgit v1.2.3 From c5e027a4a19d6267e36107fc32b5a4f3cd27976a Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 27 Dec 2011 15:48:44 +0200 Subject: mmc: sdhci-pci: get gpio numbers from platform data Retrieve the GPIO numbers for hardware reset and card detect from platform data. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 109 ++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 68 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 4e8f324e2c84..646680a5993a 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include @@ -173,32 +172,9 @@ static int mrst_hc_probe(struct sdhci_pci_chip *chip) return 0; } -/* Medfield eMMC hardware reset GPIOs */ -static int mfd_emmc0_rst_gpio = -EINVAL; -static int mfd_emmc1_rst_gpio = -EINVAL; - -static int mfd_emmc_gpio_parse(struct sfi_table_header *table) -{ - struct sfi_table_simple *sb = (struct sfi_table_simple *)table; - struct sfi_gpio_table_entry *entry; - int i, num; - - num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); - entry = (struct sfi_gpio_table_entry *)sb->pentry; - - for (i = 0; i < num; i++, entry++) { - if (!strncmp(entry->pin_name, "emmc0_rst", SFI_NAME_LEN)) - mfd_emmc0_rst_gpio = entry->pin_no; - else if (!strncmp(entry->pin_name, "emmc1_rst", SFI_NAME_LEN)) - mfd_emmc1_rst_gpio = entry->pin_no; - } - - return 0; -} - #ifdef CONFIG_PM_RUNTIME -static irqreturn_t mfd_sd_cd(int irq, void *dev_id) +static irqreturn_t sdhci_pci_sd_cd(int irq, void *dev_id) { struct sdhci_pci_slot *slot = dev_id; struct sdhci_host *host = slot->host; @@ -207,15 +183,16 @@ static irqreturn_t mfd_sd_cd(int irq, void *dev_id) return IRQ_HANDLED; } -#define MFLD_SD_CD_PIN 69 - -static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot) +static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) { - int err, irq, gpio = MFLD_SD_CD_PIN; + int err, irq, gpio = slot->cd_gpio; slot->cd_gpio = -EINVAL; slot->cd_irq = -EINVAL; + if (!gpio_is_valid(gpio)) + return; + err = gpio_request(gpio, "sd_cd"); if (err < 0) goto out; @@ -228,7 +205,7 @@ static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot) if (irq < 0) goto out_free; - err = request_irq(irq, mfd_sd_cd, IRQF_TRIGGER_RISING | + err = request_irq(irq, sdhci_pci_sd_cd, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "sd_cd", slot); if (err) goto out_free; @@ -237,65 +214,41 @@ static int mfd_sd_probe_slot(struct sdhci_pci_slot *slot) slot->cd_irq = irq; slot->host->quirks2 |= SDHCI_QUIRK2_OWN_CARD_DETECTION; - return 0; + return; out_free: gpio_free(gpio); out: dev_warn(&slot->chip->pdev->dev, "failed to setup card detect wake up\n"); - return 0; } -static void mfd_sd_remove_slot(struct sdhci_pci_slot *slot, int dead) +static void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) { if (slot->cd_irq >= 0) free_irq(slot->cd_irq, slot); - gpio_free(slot->cd_gpio); + if (gpio_is_valid(slot->cd_gpio)) + gpio_free(slot->cd_gpio); } #else -#define mfd_sd_probe_slot NULL -#define mfd_sd_remove_slot NULL +static inline void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) +{ +} + +static inline void sdhci_pci_remove_own_cd(struct sdhci_pci_slot *slot) +{ +} #endif static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) { - const char *name = NULL; - int gpio = -EINVAL; - - sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, mfd_emmc_gpio_parse); - - switch (slot->chip->pdev->device) { - case PCI_DEVICE_ID_INTEL_MFD_EMMC0: - gpio = mfd_emmc0_rst_gpio; - name = "eMMC0_reset"; - break; - case PCI_DEVICE_ID_INTEL_MFD_EMMC1: - gpio = mfd_emmc1_rst_gpio; - name = "eMMC1_reset"; - break; - } - - if (!gpio_request(gpio, name)) { - gpio_direction_output(gpio, 1); - slot->rst_n_gpio = gpio; - slot->host->mmc->caps |= MMC_CAP_HW_RESET; - } - slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE; - slot->host->mmc->caps2 = MMC_CAP2_BOOTPART_NOACC; - return 0; } -static void mfd_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead) -{ - gpio_free(slot->rst_n_gpio); -} - static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = { .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, .probe_slot = mrst_hc_probe_slot, @@ -309,8 +262,6 @@ static const struct sdhci_pci_fixes sdhci_intel_mrst_hc1_hc2 = { static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .allow_runtime_pm = true, - .probe_slot = mfd_sd_probe_slot, - .remove_slot = mfd_sd_remove_slot, }; static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { @@ -322,7 +273,6 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .allow_runtime_pm = true, .probe_slot = mfd_emmc_probe_slot, - .remove_slot = mfd_emmc_remove_slot, }; /* O2Micro extra registers */ @@ -1229,6 +1179,7 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( slot->host = host; slot->pci_bar = bar; slot->rst_n_gpio = -EINVAL; + slot->cd_gpio = -EINVAL; /* Retrieve platform data if there is any */ if (*sdhci_pci_get_data) @@ -1242,6 +1193,8 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( goto free; } } + slot->rst_n_gpio = slot->data->rst_n_gpio; + slot->cd_gpio = slot->data->cd_gpio; } host->hw_name = "PCI"; @@ -1269,15 +1222,30 @@ static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( goto unmap; } + if (gpio_is_valid(slot->rst_n_gpio)) { + if (!gpio_request(slot->rst_n_gpio, "eMMC_reset")) { + gpio_direction_output(slot->rst_n_gpio, 1); + slot->host->mmc->caps |= MMC_CAP_HW_RESET; + } else { + dev_warn(&pdev->dev, "failed to request rst_n_gpio\n"); + slot->rst_n_gpio = -EINVAL; + } + } + host->mmc->pm_caps = MMC_PM_KEEP_POWER | MMC_PM_WAKE_SDIO_IRQ; ret = sdhci_add_host(host); if (ret) goto remove; + sdhci_pci_add_own_cd(slot); + return slot; remove: + if (gpio_is_valid(slot->rst_n_gpio)) + gpio_free(slot->rst_n_gpio); + if (chip->fixes && chip->fixes->remove_slot) chip->fixes->remove_slot(slot, 0); @@ -1302,6 +1270,8 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) int dead; u32 scratch; + sdhci_pci_remove_own_cd(slot); + dead = 0; scratch = readl(slot->host->ioaddr + SDHCI_INT_STATUS); if (scratch == (u32)-1) @@ -1309,6 +1279,9 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) sdhci_remove_host(slot->host, dead); + if (gpio_is_valid(slot->rst_n_gpio)) + gpio_free(slot->rst_n_gpio); + if (slot->chip->fixes && slot->chip->fixes->remove_slot) slot->chip->fixes->remove_slot(slot, dead); -- cgit v1.2.3 From e2a0a5829c4069ee4a0f28c7301187ffaba91a46 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 27 Dec 2011 15:48:45 +0200 Subject: mmc: sdhci-pci: remove SDHCI_QUIRK2_OWN_CARD_DETECTION Even if a driver provides separate card detection, an interrupt is still needed to abort mmc requests that are in progress. SDHCI_QUIRK2_OWN_CARD_DETECTION prevents that, so remove it. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 1 - drivers/mmc/host/sdhci.c | 1 - include/linux/mmc/sdhci.h | 2 -- 3 files changed, 4 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 646680a5993a..83a152e9b976 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -212,7 +212,6 @@ static void sdhci_pci_add_own_cd(struct sdhci_pci_slot *slot) slot->cd_gpio = gpio; slot->cd_irq = irq; - slot->host->quirks2 |= SDHCI_QUIRK2_OWN_CARD_DETECTION; return; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e6c6cd6e95f2..6f1fd02fe01b 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -147,7 +147,6 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) u32 present, irqs; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) || - (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION) || !mmc_card_is_removable(host->mmc)) return; diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index e4b69353678d..dad7a469f09c 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -90,8 +90,6 @@ struct sdhci_host { unsigned int quirks2; /* More deviations from spec. */ -#define SDHCI_QUIRK2_OWN_CARD_DETECTION (1<<0) - int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ -- cgit v1.2.3 From 30832ab56c80d96cfaf5a786524f0d8c57fadfa1 Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 27 Dec 2011 15:48:46 +0200 Subject: mmc: sdhci: Always pass clock request value zero to set_clock host op To allow the set_clock host op to disable the SDCLK source when not needed, always call the host op when the requested clock speed is zero. Do this even if host->clock already equals zero, because the SDHCI driver may set that value (without calling the host op) to force an update at the next (non-zero-speed) call. Signed-off-by: Todd Poynor Acked-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 6f1fd02fe01b..e06299734f5d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1068,7 +1068,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) u16 clk = 0; unsigned long timeout; - if (clock == host->clock) + if (clock && clock == host->clock) return; host->mmc->actual_clock = 0; -- cgit v1.2.3 From 93933508ce3753b9ef1fefb75531b5b1622de03f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 27 Dec 2011 15:48:47 +0200 Subject: mmc: sdhci-pci: enable runtime PM for Medfield SDIO Runtime PM for SDIO is no longer enabled by default (see 5c7f0e083d2d98ba14ddd10e88f001a0ead4cae4) so it must now be enabled per platform, in this case Medfield uses it. Signed-off-by: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-pci.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 83a152e9b976..7165e6a09274 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -248,6 +248,12 @@ static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot) return 0; } +static int mfd_sdio_probe_slot(struct sdhci_pci_slot *slot) +{ + slot->host->mmc->caps |= MMC_CAP_POWER_OFF_CARD; + return 0; +} + static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = { .quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT, .probe_slot = mrst_hc_probe_slot, @@ -266,6 +272,7 @@ static const struct sdhci_pci_fixes sdhci_intel_mfd_sd = { static const struct sdhci_pci_fixes sdhci_intel_mfd_sdio = { .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .allow_runtime_pm = true, + .probe_slot = mfd_sdio_probe_slot, }; static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = { -- cgit v1.2.3 From 7efab4f35740c63502e438886cf1e4aa3f3b800f Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Fri, 30 Dec 2011 12:35:13 +1100 Subject: mmc: omap_hsmmc: use threaded irq handler for card-detect. As the card-detect irq handler just schedules work to be done by a thread, we can use request_threaded_irq to do much of the work for us. This means that interrupts which arrive by handle_nested_irq actually work. Reviewed-by: Felipe Balbi Tested-by: Grazvydas Ignotas Signed-off-by: NeilBrown Acked-by: Kishore Kadiyala Signed-off-by: Chris Ball --- drivers/mmc/host/omap_hsmmc.c | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 5b35c7e5a26d..09fc4f8967ad 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -162,7 +161,6 @@ struct omap_hsmmc_host { */ struct regulator *vcc; struct regulator *vcc_aux; - struct work_struct mmc_carddetect_work; void __iomem *base; resource_size_t mapbase; spinlock_t irq_lock; /* Prevent races with irq handler */ @@ -1279,17 +1277,16 @@ static void omap_hsmmc_protect_card(struct omap_hsmmc_host *host) } /* - * Work Item to notify the core about card insertion/removal + * irq handler to notify the core about card insertion/removal */ -static void omap_hsmmc_detect(struct work_struct *work) +static irqreturn_t omap_hsmmc_detect(int irq, void *dev_id) { - struct omap_hsmmc_host *host = - container_of(work, struct omap_hsmmc_host, mmc_carddetect_work); + struct omap_hsmmc_host *host = dev_id; struct omap_mmc_slot_data *slot = &mmc_slot(host); int carddetect; if (host->suspended) - return; + return IRQ_HANDLED; sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); @@ -1304,19 +1301,6 @@ static void omap_hsmmc_detect(struct work_struct *work) mmc_detect_change(host->mmc, (HZ * 200) / 1000); else mmc_detect_change(host->mmc, (HZ * 50) / 1000); -} - -/* - * ISR for handling card insertion and removal - */ -static irqreturn_t omap_hsmmc_cd_handler(int irq, void *dev_id) -{ - struct omap_hsmmc_host *host = (struct omap_hsmmc_host *)dev_id; - - if (host->suspended) - return IRQ_HANDLED; - schedule_work(&host->mmc_carddetect_work); - return IRQ_HANDLED; } @@ -1918,7 +1902,6 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) host->next_data.cookie = 1; platform_set_drvdata(pdev, host); - INIT_WORK(&host->mmc_carddetect_work, omap_hsmmc_detect); mmc->ops = &omap_hsmmc_ops; @@ -2046,10 +2029,11 @@ static int __init omap_hsmmc_probe(struct platform_device *pdev) /* Request IRQ for card detect */ if ((mmc_slot(host).card_detect_irq)) { - ret = request_irq(mmc_slot(host).card_detect_irq, - omap_hsmmc_cd_handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - mmc_hostname(mmc), host); + ret = request_threaded_irq(mmc_slot(host).card_detect_irq, + NULL, + omap_hsmmc_detect, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + mmc_hostname(mmc), host); if (ret) { dev_dbg(mmc_dev(host->mmc), "Unable to grab MMC CD IRQ\n"); @@ -2128,7 +2112,6 @@ static int omap_hsmmc_remove(struct platform_device *pdev) free_irq(host->irq, host); if (mmc_slot(host).card_detect_irq) free_irq(mmc_slot(host).card_detect_irq, host); - flush_work_sync(&host->mmc_carddetect_work); pm_runtime_put_sync(host->dev); pm_runtime_disable(host->dev); @@ -2175,7 +2158,6 @@ static int omap_hsmmc_suspend(struct device *dev) return ret; } } - cancel_work_sync(&host->mmc_carddetect_work); ret = mmc_suspend_host(host->mmc); if (ret == 0) { -- cgit v1.2.3 From c6ced0db08010ed75df221a2946c5228454b38d5 Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 28 Dec 2011 11:11:12 +0800 Subject: mmc: sdhci: Fix tuning timer incorrect setting when suspending host When suspending host, the tuning timer shoule be deactivated. And the HOST_NEEDS_TUNING flag should be set after tuning timer is deactivated. Signed-off-by: Philip Rakity Signed-off-by: Aaron Lu Acked-by: Adrian Hunter Cc: stable Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index e06299734f5d..988ae06d1a09 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2356,9 +2356,8 @@ int sdhci_suspend_host(struct sdhci_host *host) /* Disable tuning since we are suspending */ if (host->version >= SDHCI_SPEC_300 && host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1) { + del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; - mod_timer(&host->tuning_timer, jiffies + - host->tuning_count * HZ); } ret = mmc_suspend_host(host->mmc); -- cgit v1.2.3 From dd8df17fe83483d7ea06ff229895e35a42071599 Mon Sep 17 00:00:00 2001 From: Alexander Elbs Date: Tue, 3 Jan 2012 23:26:53 -0500 Subject: mmc: sd: Fix SDR12 timing regression This patch fixes a failure to recognize SD cards reported on a Dell Vostro with O2 Micro SD card reader. Patch 49c468f ("mmc: sd: add support for uhs bus speed mode selection") caused the problem, by setting the SDHCI_CTRL_HISPD flag even for legacy timings. Signed-off-by: Alexander Elbs Acked-by: Philip Rakity Acked-by: Arindam Nath Cc: stable Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 988ae06d1a09..3d338b4b416a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1385,8 +1385,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) if ((ios->timing == MMC_TIMING_UHS_SDR50) || (ios->timing == MMC_TIMING_UHS_SDR104) || (ios->timing == MMC_TIMING_UHS_DDR50) || - (ios->timing == MMC_TIMING_UHS_SDR25) || - (ios->timing == MMC_TIMING_UHS_SDR12)) + (ios->timing == MMC_TIMING_UHS_SDR25)) ctrl |= SDHCI_CTRL_HISPD; ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); -- cgit v1.2.3 From 3f514291df96e0a34bc61c7c456ca1d3fdc1f1bb Mon Sep 17 00:00:00 2001 From: Seungwon Jeon Date: Mon, 2 Jan 2012 16:00:02 +0900 Subject: mmc: dw_mmc: Clear the DDR mode for non-DDR UHS_REG should be cleared for non-DDR mode. But currently there is no way to clear DDR mode, if it is already set once. This patch adds clearing DDR mode for non-DDR mode. Signed-off-by: Seungwon Jeon Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index c583b943f615..94e223825a45 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -707,12 +707,15 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) break; } + regs = mci_readl(slot->host, UHS_REG); + /* DDR mode set */ - if (ios->timing == MMC_TIMING_UHS_DDR50) { - regs = mci_readl(slot->host, UHS_REG); + if (ios->timing == MMC_TIMING_UHS_DDR50) regs |= (0x1 << slot->id) << 16; - mci_writel(slot->host, UHS_REG, regs); - } + else + regs &= ~(0x1 << slot->id) << 16; + + mci_writel(slot->host, UHS_REG, regs); if (ios->clock) { /* -- cgit v1.2.3 From 38a60ea2e7b33ab33ee11d6ef527f259edb102cf Mon Sep 17 00:00:00 2001 From: Aaron Lu Date: Wed, 4 Jan 2012 10:07:43 +0800 Subject: mmc: sdhci: Deal with failure case in sdhci_suspend_host If there are errors happened in sdhci_suspend_host, handle it so that when the function returns with an error, the host's behaviour is the same before this function call, e.g. card detection is enabled and tuning timer is active, etc. Signed-off-by: Philip Rakity Signed-off-by: Aaron Lu Cc: Adrian Hunter Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 3d338b4b416a..0636e9a587b1 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2349,19 +2349,30 @@ out: int sdhci_suspend_host(struct sdhci_host *host) { int ret; + bool has_tuning_timer; sdhci_disable_card_detection(host); /* Disable tuning since we are suspending */ - if (host->version >= SDHCI_SPEC_300 && host->tuning_count && - host->tuning_mode == SDHCI_TUNING_MODE_1) { + has_tuning_timer = host->version >= SDHCI_SPEC_300 && + host->tuning_count && host->tuning_mode == SDHCI_TUNING_MODE_1; + if (has_tuning_timer) { del_timer_sync(&host->tuning_timer); host->flags &= ~SDHCI_NEEDS_RETUNING; } ret = mmc_suspend_host(host->mmc); - if (ret) + if (ret) { + if (has_tuning_timer) { + host->flags |= SDHCI_NEEDS_RETUNING; + mod_timer(&host->tuning_timer, jiffies + + host->tuning_count * HZ); + } + + sdhci_enable_card_detection(host); + return ret; + } free_irq(host->irq, host); -- cgit v1.2.3 From 984589e59f5796b4ef9a778c6a1937fc9319c423 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Wed, 4 Jan 2012 11:48:42 +0530 Subject: mmc: sdhci-spear: Fix compilation error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the inclusion of following patch (59b5bc3929b37): "mmc: sdhci: remove "state" argument from sdhci_suspend_host" we get a compilation error for sdhci-spear: drivers/mmc/host/sdhci-spear.c:283:2: error: too many arguments to function ‘sdhci_suspend_host’ This patch fixes this error. Signed-off-by: Viresh Kumar Cc: stable Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci-spear.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 883548eaa09e..b7f8b33c5f19 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -277,10 +277,9 @@ static int sdhci_suspend(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); struct spear_sdhci *sdhci = dev_get_platdata(dev); - pm_message_t state = {.event = 0}; int ret; - ret = sdhci_suspend_host(host, state); + ret = sdhci_suspend_host(host); if (!ret) clk_disable(sdhci->clk); -- cgit v1.2.3 From ee5d19b20a711dca3848450979e3cd20b6b795cc Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 5 Jan 2012 19:12:57 +0900 Subject: mmc: dw_mmc: fixed wrong bit operation for SDMMC_GET_FCNT() In status register, fifo_count is bit[29:17]. (0x1FFF is correct) Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Acked-by: Will Newton Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 72c071f6e001..df392a1143f2 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -126,7 +126,7 @@ #define SDMMC_CMD_RESP_EXP BIT(6) #define SDMMC_CMD_INDX(n) ((n) & 0x1F) /* Status register defines */ -#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FF) +#define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF) /* Internal DMAC interrupt defines */ #define SDMMC_IDMAC_INT_AI BIT(9) #define SDMMC_IDMAC_INT_NI BIT(8) -- cgit v1.2.3 From 069c9f142822d552ec885572945d8bce9eff0519 Mon Sep 17 00:00:00 2001 From: Girish K S Date: Fri, 6 Jan 2012 09:56:39 +0530 Subject: mmc: host: Adds support for eMMC 4.5 HS200 mode This patch adds support for the HS200 mode on the host side. Also enables the tuning feature required when the HS200 mode is selected. Signed-off-by: Girish K S Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 57 ++++++++++++++++++++++++++++++++++------------- drivers/mmc/host/sdhci.h | 1 + include/linux/mmc/sdhci.h | 1 + 3 files changed, 44 insertions(+), 15 deletions(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0636e9a587b1..96f4e548ba22 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -49,7 +49,7 @@ 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); +static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); static void sdhci_tuning_timer(unsigned long data); #ifdef CONFIG_PM_RUNTIME @@ -1014,7 +1014,8 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) flags |= SDHCI_CMD_INDEX; /* CMD19 is special in that the Data Present Select should be set */ - if (cmd->data || (cmd->opcode == MMC_SEND_TUNING_BLOCK)) + if (cmd->data || cmd->opcode == MMC_SEND_TUNING_BLOCK || + cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) flags |= SDHCI_CMD_DATA; sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); @@ -1287,7 +1288,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) if ((host->flags & SDHCI_NEEDS_RETUNING) && !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) { spin_unlock_irqrestore(&host->lock, flags); - sdhci_execute_tuning(mmc); + sdhci_execute_tuning(mmc, mrq->cmd->opcode); spin_lock_irqsave(&host->lock, flags); /* Restore original mmc_request structure */ @@ -1382,7 +1383,8 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) unsigned int clock; /* In case of UHS-I modes, set High Speed Enable */ - if ((ios->timing == MMC_TIMING_UHS_SDR50) || + if ((ios->timing == MMC_TIMING_MMC_HS200) || + (ios->timing == MMC_TIMING_UHS_SDR50) || (ios->timing == MMC_TIMING_UHS_SDR104) || (ios->timing == MMC_TIMING_UHS_DDR50) || (ios->timing == MMC_TIMING_UHS_SDR25)) @@ -1435,7 +1437,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); /* Select Bus Speed Mode for host */ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; - if (ios->timing == MMC_TIMING_UHS_SDR12) + if (ios->timing == MMC_TIMING_MMC_HS200) + ctrl_2 |= SDHCI_CTRL_HS_SDR200; + else if (ios->timing == MMC_TIMING_UHS_SDR12) ctrl_2 |= SDHCI_CTRL_UHS_SDR12; else if (ios->timing == MMC_TIMING_UHS_SDR25) ctrl_2 |= SDHCI_CTRL_UHS_SDR25; @@ -1682,7 +1686,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc, return err; } -static int sdhci_execute_tuning(struct mmc_host *mmc) +static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host; u16 ctrl; @@ -1690,6 +1694,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) int tuning_loop_counter = MAX_TUNING_LOOP; unsigned long timeout; int err = 0; + bool requires_tuning_nonuhs = false; host = mmc_priv(mmc); @@ -1700,13 +1705,19 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); /* - * Host Controller needs tuning only in case of SDR104 mode - * and for SDR50 mode when Use Tuning for SDR50 is set in + * The Host Controller needs tuning only in case of SDR104 mode + * and for SDR50 mode when Use Tuning for SDR50 is set in the * Capabilities register. + * If the Host Controller supports the HS200 mode then the + * tuning function has to be executed. */ + if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) && + (host->flags & SDHCI_SDR50_NEEDS_TUNING || + host->flags & SDHCI_HS200_NEEDS_TUNING)) + requires_tuning_nonuhs = true; + if (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR104) || - (((ctrl & SDHCI_CTRL_UHS_MASK) == SDHCI_CTRL_UHS_SDR50) && - (host->flags & SDHCI_SDR50_NEEDS_TUNING))) + requires_tuning_nonuhs) ctrl |= SDHCI_CTRL_EXEC_TUNING; else { spin_unlock(&host->lock); @@ -1742,7 +1753,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) if (!tuning_loop_counter && !timeout) break; - cmd.opcode = MMC_SEND_TUNING_BLOCK; + cmd.opcode = opcode; cmd.arg = 0; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; cmd.retries = 0; @@ -1757,7 +1768,17 @@ static int sdhci_execute_tuning(struct mmc_host *mmc) * block to the Host Controller. So we set the block size * to 64 here. */ - sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), SDHCI_BLOCK_SIZE); + if (cmd.opcode == MMC_SEND_TUNING_BLOCK_HS200) { + if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 128), + SDHCI_BLOCK_SIZE); + else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), + SDHCI_BLOCK_SIZE); + } else { + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, 64), + SDHCI_BLOCK_SIZE); + } /* * The tuning block is sent by the card to the host controller. @@ -2140,12 +2161,14 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { } static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) { + u32 command; BUG_ON(intmask == 0); /* CMD19 generates _only_ Buffer Read Ready interrupt */ if (intmask & SDHCI_INT_DATA_AVAIL) { - if (SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) == - MMC_SEND_TUNING_BLOCK) { + command = SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)); + if (command == MMC_SEND_TUNING_BLOCK || + command == MMC_SEND_TUNING_BLOCK_HS200) { host->tuning_done = 1; wake_up(&host->buf_ready_int); return; @@ -2747,10 +2770,14 @@ int sdhci_add_host(struct sdhci_host *host) if (caps[1] & SDHCI_SUPPORT_DDR50) mmc->caps |= MMC_CAP_UHS_DDR50; - /* Does the host needs tuning for SDR50? */ + /* Does the host need tuning for SDR50? */ if (caps[1] & SDHCI_USE_SDR50_TUNING) host->flags |= SDHCI_SDR50_NEEDS_TUNING; + /* Does the host need tuning for HS200? */ + if (mmc->caps2 & MMC_CAP2_HS200) + host->flags |= SDHCI_HS200_NEEDS_TUNING; + /* Driver Type(s) (A, C, D) supported by the host */ if (caps[1] & SDHCI_DRIVER_TYPE_A) mmc->caps |= MMC_CAP_DRIVER_TYPE_A; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index a04d4d0c6fd2..ad265b96b75b 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -158,6 +158,7 @@ #define SDHCI_CTRL_UHS_SDR50 0x0002 #define SDHCI_CTRL_UHS_SDR104 0x0003 #define SDHCI_CTRL_UHS_DDR50 0x0004 +#define SDHCI_CTRL_HS_SDR200 0x0005 /* reserved value in SDIO spec */ #define SDHCI_CTRL_VDD_180 0x0008 #define SDHCI_CTRL_DRV_TYPE_MASK 0x0030 #define SDHCI_CTRL_DRV_TYPE_B 0x0000 diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index dad7a469f09c..c750f85177d9 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -119,6 +119,7 @@ struct sdhci_host { #define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */ #define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */ #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ +#define SDHCI_HS200_NEEDS_TUNING (1<<10) /* HS200 needs tuning */ unsigned int version; /* SDHCI spec. version */ -- cgit v1.2.3 From 8234e86960b07f5a3cf9973de3f9ba9a50450464 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Wed, 11 Jan 2012 09:28:21 +0000 Subject: mmc: dw_mmc: miscaculated the fifo-depth with wrong bit operation In FIFOTH register, the RX_WMark field (bits[27:16]) defaults to FIFO_DEPTH - 1. When reading it, bits[26:16] were being used, so fix it to use the mask 0xfff instead of 0x7ff. Signed-off-by: Jaehoon Chung Reviewed-by: James Hogan Signed-off-by: Chris Ball --- drivers/mmc/host/dw_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 94e223825a45..0e342793ff14 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1949,7 +1949,7 @@ static int dw_mci_probe(struct platform_device *pdev) * should put it in the platform data. */ fifo_size = mci_readl(host, FIFOTH); - fifo_size = 1 + ((fifo_size >> 16) & 0x7ff); + fifo_size = 1 + ((fifo_size >> 16) & 0xfff); } else { fifo_size = host->pdata->fifo_depth; } -- cgit v1.2.3 From 3abc1e804253add41990cbe1461e79c1165108e0 Mon Sep 17 00:00:00 2001 From: Shaohui Xie Date: Thu, 29 Dec 2011 16:33:00 +0800 Subject: mmc: sdhci: restore the enabled dma when do reset all If dma is enabled, it'll be cleared when reset all is performed, this can be observed on some platforms, such as P2041 which has a version 2.3 controller, but platform like P4080 which has a version 2.2 controller, does not suffer this, so we will check if the dma is enabled, we should restore it after reset all. Signed-off-by: Shaohui Xie Signed-off-by: Chris Ball --- drivers/mmc/host/sdhci.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/mmc/host') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 96f4e548ba22..8d66706824a6 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -212,6 +212,11 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier); + + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { + if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL)) + host->ops->enable_dma(host); + } } static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios); -- cgit v1.2.3