From 1fb654fd97ff0c56ff1646dc9e73543abeb912df Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Thu, 23 Apr 2015 13:15:42 +0200 Subject: mmc: sdio: add reset callback to bus operations Some drivers schedule automatic hw resets. An example is mwifiex, which schedules a card reset if the command handler between driver and card firmware becomes out of sync Signed-off-by: Andreas Fenkart Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 5bc6c7dbbd60..f6c28a7ea8df 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1056,6 +1056,12 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host) return mmc_sdio_power_restore(host); } +static int mmc_sdio_reset(struct mmc_host *host) +{ + mmc_power_cycle(host, host->card->ocr); + return mmc_sdio_power_restore(host); +} + static const struct mmc_bus_ops mmc_sdio_ops = { .remove = mmc_sdio_remove, .detect = mmc_sdio_detect, @@ -1066,6 +1072,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = { .runtime_resume = mmc_sdio_runtime_resume, .power_restore = mmc_sdio_power_restore, .alive = mmc_sdio_alive, + .reset = mmc_sdio_reset, }; -- cgit v1.2.3 From 70830b4182ddbb6f7f29d632e0c6cc5405a8cd16 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Apr 2015 08:13:25 +0000 Subject: mmc: sh_mmcif: move mmcif_of_match to upside Signed-off-by: Kuninori Morimoto Tested-by: Keita Kobayashi Acked-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 7eff087cf515..9ab72e715d48 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -257,6 +257,12 @@ struct sh_mmcif_host { bool dma_active; }; +static const struct of_device_id mmcif_of_match[] = { + { .compatible = "renesas,sh-mmcif" }, + { } +}; +MODULE_DEVICE_TABLE(of, mmcif_of_match); + static inline void sh_mmcif_bitset(struct sh_mmcif_host *host, unsigned int reg, u32 val) { @@ -1543,12 +1549,6 @@ static int sh_mmcif_resume(struct device *dev) } #endif -static const struct of_device_id mmcif_of_match[] = { - { .compatible = "renesas,sh-mmcif" }, - { } -}; -MODULE_DEVICE_TABLE(of, mmcif_of_match); - static const struct dev_pm_ops sh_mmcif_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(sh_mmcif_suspend, sh_mmcif_resume) }; -- cgit v1.2.3 From 60985c39cf85696cc408706193ab7bc80f0833e7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Apr 2015 08:14:12 +0000 Subject: mmc: sh_mmcif: cleanup to use dev instead of &pdev->dev Signed-off-by: Kuninori Morimoto Tested-by: Keita Kobayashi Acked-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 9ab72e715d48..e294b70dbf5d 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1386,7 +1386,8 @@ static int sh_mmcif_probe(struct platform_device *pdev) int ret = 0, irq[2]; struct mmc_host *mmc; struct sh_mmcif_host *host; - struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct sh_mmcif_plat_data *pd = dev->platform_data; struct resource *res; void __iomem *reg; const char *name; @@ -1394,16 +1395,16 @@ static int sh_mmcif_probe(struct platform_device *pdev) irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); if (irq[0] < 0) { - dev_err(&pdev->dev, "Get irq error\n"); + dev_err(dev, "Get irq error\n"); return -ENXIO; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg = devm_ioremap_resource(&pdev->dev, res); + reg = devm_ioremap_resource(dev, res); if (IS_ERR(reg)) return PTR_ERR(reg); - mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev); + mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), dev); if (!mmc) return -ENOMEM; @@ -1436,20 +1437,20 @@ static int sh_mmcif_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); - pm_runtime_enable(&pdev->dev); + pm_runtime_enable(dev); host->power = false; - host->hclk = devm_clk_get(&pdev->dev, NULL); + host->hclk = devm_clk_get(dev, NULL); if (IS_ERR(host->hclk)) { ret = PTR_ERR(host->hclk); - dev_err(&pdev->dev, "cannot get clock: %d\n", ret); + dev_err(dev, "cannot get clock: %d\n", ret); goto err_pm; } ret = sh_mmcif_clk_update(host); if (ret < 0) goto err_pm; - ret = pm_runtime_resume(&pdev->dev); + ret = pm_runtime_resume(dev); if (ret < 0) goto err_clk; @@ -1458,19 +1459,19 @@ static int sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_sync_reset(host); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); - name = irq[1] < 0 ? dev_name(&pdev->dev) : "sh_mmc:error"; - ret = devm_request_threaded_irq(&pdev->dev, irq[0], sh_mmcif_intr, + name = irq[1] < 0 ? dev_name(dev) : "sh_mmc:error"; + ret = devm_request_threaded_irq(dev, irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, name, host); if (ret) { - dev_err(&pdev->dev, "request_irq error (%s)\n", name); + dev_err(dev, "request_irq error (%s)\n", name); goto err_clk; } if (irq[1] >= 0) { - ret = devm_request_threaded_irq(&pdev->dev, irq[1], + ret = devm_request_threaded_irq(dev, irq[1], sh_mmcif_intr, sh_mmcif_irqt, 0, "sh_mmc:int", host); if (ret) { - dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); + dev_err(dev, "request_irq error (sh_mmc:int)\n"); goto err_clk; } } @@ -1487,9 +1488,9 @@ static int sh_mmcif_probe(struct platform_device *pdev) if (ret < 0) goto err_clk; - dev_pm_qos_expose_latency_limit(&pdev->dev, 100); + dev_pm_qos_expose_latency_limit(dev, 100); - dev_info(&pdev->dev, "Chip version 0x%04x, clock rate %luMHz\n", + dev_info(dev, "Chip version 0x%04x, clock rate %luMHz\n", sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0xffff, clk_get_rate(host->hclk) / 1000000UL); @@ -1499,7 +1500,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) err_clk: clk_disable_unprepare(host->hclk); err_pm: - pm_runtime_disable(&pdev->dev); + pm_runtime_disable(dev); err_host: mmc_free_host(mmc); return ret; -- cgit v1.2.3 From 6aed678bcdee61ba8fb0b149dc4303b2f24993a2 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Apr 2015 08:15:08 +0000 Subject: mmc: sh_mmcif: remove unnecessary int clk from struct sh_mmcif_host struct sh_mmcif_host has 1) int clk, 2) struct clock *hclk, and host->clk = clk_get_rate(host->hclk). This int clk is not necessary. Let's remove it. And, current hclk is confusable naming. Let's rename it to clk. Signed-off-by: Kuninori Morimoto Tested-by: Keita Kobayashi Acked-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index e294b70dbf5d..2646358d7906 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -228,8 +228,7 @@ struct sh_mmcif_host { struct mmc_host *mmc; struct mmc_request *mrq; struct platform_device *pd; - struct clk *hclk; - unsigned int clk; + struct clk *clk; int bus_width; unsigned char timing; bool sd_error; @@ -484,17 +483,18 @@ static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) { struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; bool sup_pclk = p ? p->sup_pclk : false; + unsigned int current_clk = clk_get_rate(host->clk); sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR); if (!clk) return; - if (sup_pclk && clk == host->clk) + if (sup_pclk && clk == current_clk) sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); else sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & - ((fls(DIV_ROUND_UP(host->clk, + ((fls(DIV_ROUND_UP(current_clk, clk) - 1) - 1) << 16)); sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); @@ -980,12 +980,13 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) static int sh_mmcif_clk_update(struct sh_mmcif_host *host) { - int ret = clk_prepare_enable(host->hclk); + int ret = clk_prepare_enable(host->clk); if (!ret) { - host->clk = clk_get_rate(host->hclk); - host->mmc->f_max = host->clk / 2; - host->mmc->f_min = host->clk / 512; + unsigned int clk = clk_get_rate(host->clk); + + host->mmc->f_max = clk / 2; + host->mmc->f_min = clk / 512; } return ret; @@ -1034,7 +1035,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (host->power) { pm_runtime_put_sync(&host->pd->dev); - clk_disable_unprepare(host->hclk); + clk_disable_unprepare(host->clk); host->power = false; if (ios->power_mode == MMC_POWER_OFF) sh_mmcif_set_power(host, ios); @@ -1440,9 +1441,9 @@ static int sh_mmcif_probe(struct platform_device *pdev) pm_runtime_enable(dev); host->power = false; - host->hclk = devm_clk_get(dev, NULL); - if (IS_ERR(host->hclk)) { - ret = PTR_ERR(host->hclk); + host->clk = devm_clk_get(dev, NULL); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); dev_err(dev, "cannot get clock: %d\n", ret); goto err_pm; } @@ -1492,13 +1493,13 @@ static int sh_mmcif_probe(struct platform_device *pdev) dev_info(dev, "Chip version 0x%04x, clock rate %luMHz\n", sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0xffff, - clk_get_rate(host->hclk) / 1000000UL); + clk_get_rate(host->clk) / 1000000UL); - clk_disable_unprepare(host->hclk); + clk_disable_unprepare(host->clk); return ret; err_clk: - clk_disable_unprepare(host->hclk); + clk_disable_unprepare(host->clk); err_pm: pm_runtime_disable(dev); err_host: @@ -1511,7 +1512,7 @@ static int sh_mmcif_remove(struct platform_device *pdev) struct sh_mmcif_host *host = platform_get_drvdata(pdev); host->dying = true; - clk_prepare_enable(host->hclk); + clk_prepare_enable(host->clk); pm_runtime_get_sync(&pdev->dev); dev_pm_qos_hide_latency_limit(&pdev->dev); @@ -1526,7 +1527,7 @@ static int sh_mmcif_remove(struct platform_device *pdev) */ cancel_delayed_work_sync(&host->timeout_work); - clk_disable_unprepare(host->hclk); + clk_disable_unprepare(host->clk); mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); -- cgit v1.2.3 From 9bb09a30ee38dcb0db9f4c5de805fe11d286db9d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 23 Apr 2015 08:16:04 +0000 Subject: mmc: sh_mmcif: separate sh_mmcif_clk_update() into setup and prepare Current sh_mmcif_clk_update() is called from probe() and set_ios(), but, the purpose of later one is just clk_prepare_enable(). No need to setup mmc->f_max/f_min in many times. This patch separe sh_mmcif_clk_update() into clk_prepare_enable() and mmc->f_max/f_min setup. Signed-off-by: Kuninori Morimoto Tested-by: Keita Kobayashi Acked-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 2646358d7906..068b9a627417 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -978,18 +978,12 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) sh_mmcif_start_cmd(host, mrq); } -static int sh_mmcif_clk_update(struct sh_mmcif_host *host) +static void sh_mmcif_clk_setup(struct sh_mmcif_host *host) { - int ret = clk_prepare_enable(host->clk); + unsigned int clk = clk_get_rate(host->clk); - if (!ret) { - unsigned int clk = clk_get_rate(host->clk); - - host->mmc->f_max = clk / 2; - host->mmc->f_min = clk / 512; - } - - return ret; + host->mmc->f_max = clk / 2; + host->mmc->f_min = clk / 512; } static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios) @@ -1046,7 +1040,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->clock) { if (!host->power) { - sh_mmcif_clk_update(host); + clk_prepare_enable(host->clk); + pm_runtime_get_sync(&host->pd->dev); host->power = true; sh_mmcif_sync_reset(host); @@ -1447,10 +1442,13 @@ static int sh_mmcif_probe(struct platform_device *pdev) dev_err(dev, "cannot get clock: %d\n", ret); goto err_pm; } - ret = sh_mmcif_clk_update(host); + + ret = clk_prepare_enable(host->clk); if (ret < 0) goto err_pm; + sh_mmcif_clk_setup(host); + ret = pm_runtime_resume(dev); if (ret < 0) goto err_clk; -- cgit v1.2.3 From 638a9818f2bfd9ada6d4319116097e4ed70815cf Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Thu, 16 Apr 2015 10:03:14 +0200 Subject: mmc: pwrseq: dt: example with reset clock and active low pin Signed-off-by: Andreas Fenkart Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt index a462c50f19a8..ce0e76749671 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt +++ b/Documentation/devicetree/bindings/mmc/mmc-pwrseq-simple.txt @@ -21,5 +21,7 @@ Example: sdhci0_pwrseq { compatible = "mmc-pwrseq-simple"; - reset-gpios = <&gpio1 12 0>; + reset-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>; + clocks = <&clk_32768_ck>; + clock-names = "ext_clock"; } -- cgit v1.2.3 From 035ff831cdf3e3bbee61d5292d284a24f1035052 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 20 Apr 2015 15:51:42 +0200 Subject: mmc: host: sdhci-esdhc-imx: fix broken email address My Pengutronix address is not valid anymore, redirect people to the Pengutronix kernel team. Reported-by: Harald Geyer Signed-off-by: Wolfram Sang Acked-by: Robert Schwebel Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 82f512d87cb8..ffa976037cf2 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -4,7 +4,7 @@ * derived from the OF-version. * * Copyright (c) 2010 Pengutronix e.K. - * Author: Wolfram Sang + * Author: Wolfram Sang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -1151,5 +1151,5 @@ static struct platform_driver sdhci_esdhc_imx_driver = { module_platform_driver(sdhci_esdhc_imx_driver); MODULE_DESCRIPTION("SDHCI driver for Freescale i.MX eSDHC"); -MODULE_AUTHOR("Wolfram Sang "); +MODULE_AUTHOR("Wolfram Sang "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bd455029d01cb55dca62129282466ccea3698813 Mon Sep 17 00:00:00 2001 From: Joakim Tjernlund Date: Mon, 20 Apr 2015 23:12:13 +0200 Subject: mmc: sdhci-of-esdhc: Pre divider starts at 1 For PowerPC esdhc pre divider starts at 1, fixing the increases the actual clock from 40KHz to 50 KHz. Signed-off-by: Joakim Tjernlund Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 22e9111b11ff..1804bdbdb145 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -199,7 +199,7 @@ static unsigned int esdhc_of_get_min_clock(struct sdhci_host *host) static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) { - int pre_div = 2; + int pre_div = 1; int div = 1; u32 temp; @@ -229,7 +229,7 @@ static void esdhc_of_set_clock(struct sdhci_host *host, unsigned int clock) dev_dbg(mmc_dev(host->mmc), "desired SD clock: %d, actual: %d\n", clock, host->max_clk / pre_div / div); - + host->mmc->actual_clock = host->max_clk / pre_div / div; pre_div >>= 1; div--; -- cgit v1.2.3 From cee4e7a5c0e90bb897238e6e181c7192b691bb7f Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 24 Apr 2015 15:00:29 +0800 Subject: mmc: sdhci_f_sdh30: Staticize local functions Make local functions static. Signed-off-by: Axel Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_f_sdh30.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c index 2fe8b91481b3..5397e2755a14 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -49,7 +49,7 @@ struct f_sdhost_priv { struct device *dev; }; -void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host) +static void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host) { struct f_sdhost_priv *priv = sdhci_priv(host); u32 ctrl = 0; @@ -77,12 +77,12 @@ void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host) sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING); } -unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host) +static unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host) { return F_SDH30_MIN_CLOCK; } -void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask) +static void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask) { if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL); -- cgit v1.2.3 From 2dbf1dc305d5881d743a64255c406ce1c970e728 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 24 Apr 2015 15:02:00 +0800 Subject: mmc: sdhci_f_sdh30: Fix the size passed to sdhci_alloc_host sdhci_alloc_host() takes priv_size rather than sizeof(struct sdhci_host) + priv_size. Signed-off-by: Axel Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_f_sdh30.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c index 5397e2755a14..983b8b32ef96 100644 --- a/drivers/mmc/host/sdhci_f_sdh30.c +++ b/drivers/mmc/host/sdhci_f_sdh30.c @@ -114,8 +114,7 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev) return irq; } - host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) + - sizeof(struct f_sdhost_priv)); + host = sdhci_alloc_host(dev, sizeof(struct f_sdhost_priv)); if (IS_ERR(host)) return PTR_ERR(host); -- cgit v1.2.3 From 25db67e2ce2fdf43a99d89d201c356267ab06bf7 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Mon, 27 Apr 2015 00:01:06 +0100 Subject: mmc: TMIO: Ensure MFD cell is disabled on probe error path Signed-off-by: Ian Molton Signed-off-by: Ben Hutchings Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index f746df493892..3d368b830e7c 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -85,8 +85,10 @@ static int tmio_mmc_probe(struct platform_device *pdev) } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -EINVAL; + if (!res) { + ret = -EINVAL; + goto cell_disable; + } pdata->flags |= TMIO_MMC_HAVE_HIGH_REG; -- cgit v1.2.3 From 7df56bbb0e86f81faaf2ee5bf126c2fb5a0e3980 Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Mon, 27 Apr 2015 00:01:17 +0100 Subject: mmc: TMIO: Fix I/O mapping leak on error using devm_ioremap() Signed-off-by: Ian Molton [bwh: Forward-ported to 4.0] Signed-off-by: Ben Hutchings Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_pio.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index dba7e1c19dd7..e3dcf31a8bd6 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -1108,7 +1108,8 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host, if (ret < 0) goto host_free; - _host->ctl = ioremap(res_ctl->start, resource_size(res_ctl)); + _host->ctl = devm_ioremap(&pdev->dev, + res_ctl->start, resource_size(res_ctl)); if (!_host->ctl) { ret = -ENOMEM; goto host_free; @@ -1230,8 +1231,6 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host) pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - - iounmap(host->ctl); } EXPORT_SYMBOL(tmio_mmc_host_remove); -- cgit v1.2.3 From de501af98dfab9c41417bbf8b080b79110a4958c Mon Sep 17 00:00:00 2001 From: Ian Molton Date: Mon, 27 Apr 2015 00:01:36 +0100 Subject: mmc: TMIO: Use devm_request_irq() Clean up resource allocation and freeing. Signed-off-by: Ian Molton Signed-off-by: Ben Hutchings Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 3d368b830e7c..e897e7fc3b14 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -103,7 +103,8 @@ static int tmio_mmc_probe(struct platform_device *pdev) if (ret) goto host_free; - ret = request_irq(irq, tmio_mmc_irq, IRQF_TRIGGER_FALLING, + ret = devm_request_irq(&pdev->dev, irq, tmio_mmc_irq, + IRQF_TRIGGER_FALLING, dev_name(&pdev->dev), host); if (ret) goto host_remove; @@ -131,7 +132,6 @@ static int tmio_mmc_remove(struct platform_device *pdev) if (mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); - free_irq(platform_get_irq(pdev, 0), host); tmio_mmc_host_remove(host); if (cell->disable) cell->disable(pdev); -- cgit v1.2.3 From c4b13fb06a03e66d0d05720786b38faa8f8642df Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 27 Apr 2015 00:01:53 +0100 Subject: MAINTAINERS: mmc: Update Ian Molton's address for tmio_mmc driver Ian no longer works for Codethink so the current address doesn't work. Signed-off-by: Ben Hutchings Signed-off-by: Ulf Hansson --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 474bcb6c0bac..ed1b645b164d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10016,7 +10016,7 @@ F: include/linux/toshiba.h F: include/uapi/linux/toshiba.h TMIO MMC DRIVER -M: Ian Molton +M: Ian Molton L: linux-mmc@vger.kernel.org S: Maintained F: drivers/mmc/host/tmio_mmc* -- cgit v1.2.3 From d1ba44a4232aa6a95c19b394df53b07b0d61c9c8 Mon Sep 17 00:00:00 2001 From: Weijun Yang Date: Mon, 27 Apr 2015 08:15:13 +0000 Subject: mmc: sdhci-sirf: fix the tuning count in platform_execute_tuning hardware has 16bit to record the tuning count, so fix it to 16384. at the same time, tuned_phases[SIRF_TUNING_COUNT] is useless as the array is never used, so move it to a variant. Signed-off-by: Weijun Yang Signed-off-by: Barry Song Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-sirf.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 32848eb7ad80..2201f76b539d 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -17,7 +17,7 @@ #define SDHCI_CLK_DELAY_SETTING 0x4C #define SDHCI_SIRF_8BITBUS BIT(3) -#define SIRF_TUNING_COUNT 128 +#define SIRF_TUNING_COUNT 16384 struct sdhci_sirf_priv { int gpio_cd; @@ -46,7 +46,7 @@ static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width) static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode) { int tuning_seq_cnt = 3; - u8 phase, tuned_phases[SIRF_TUNING_COUNT]; + int phase; u8 tuned_phase_cnt = 0; int rc = 0, longest_range = 0; int start = -1, end = 0, tuning_value = -1, range = 0; @@ -58,6 +58,7 @@ static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode) retry: phase = 0; + tuned_phase_cnt = 0; do { sdhci_writel(host, clock_setting | phase, @@ -65,7 +66,7 @@ retry: if (!mmc_send_tuning(mmc)) { /* Tuning is successful at this tuning point */ - tuned_phases[tuned_phase_cnt++] = phase; + tuned_phase_cnt++; dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", mmc_hostname(mmc), phase); if (start == -1) @@ -85,7 +86,7 @@ retry: start = -1; end = range = 0; } - } while (++phase < ARRAY_SIZE(tuned_phases)); + } while (++phase < SIRF_TUNING_COUNT); if (tuned_phase_cnt && tuning_value > 0) { /* -- cgit v1.2.3 From a1b0b977d2b2b9248ae26df0c7987d1696028309 Mon Sep 17 00:00:00 2001 From: Weijun Yang Date: Mon, 27 Apr 2015 08:15:14 +0000 Subject: mmc: sdhci-sirf: fake version and capbility registers chips have some issues for version and capbility registers, here we fake them. Signed-off-by: Weijun Yang Signed-off-by: Barry Song Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 1 + drivers/mmc/host/sdhci-sirf.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index b1f837e749fe..d5e107b78d60 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -219,6 +219,7 @@ config MMC_SDHCI_SIRF tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs" depends on ARCH_SIRF depends on MMC_SDHCI_PLTFM + select MMC_SDHCI_IO_ACCESSORS help This selects the SDHCI support for SiRF System-on-Chip devices. diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c index 2201f76b539d..0110bae25b7e 100644 --- a/drivers/mmc/host/sdhci-sirf.c +++ b/drivers/mmc/host/sdhci-sirf.c @@ -43,6 +43,39 @@ static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width) sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } +static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg) +{ + u32 val = readl(host->ioaddr + reg); + + if (unlikely((reg == SDHCI_CAPABILITIES_1) && + (host->mmc->caps & MMC_CAP_UHS_SDR50))) { + /* fake CAP_1 register */ + val = SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING; + } + + if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) { + u32 prss = val; + /* fake chips as V3.0 host conreoller */ + prss &= ~(0xFF << 16); + val = prss | (SDHCI_SPEC_300 << 16); + } + return val; +} + +static u16 sdhci_sirf_readw_le(struct sdhci_host *host, int reg) +{ + u16 ret = 0; + + ret = readw(host->ioaddr + reg); + + if (unlikely(reg == SDHCI_HOST_VERSION)) { + ret = readw(host->ioaddr + SDHCI_HOST_VERSION); + ret |= SDHCI_SPEC_300; + } + + return ret; +} + static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode) { int tuning_seq_cnt = 3; @@ -113,6 +146,8 @@ retry: } static struct sdhci_ops sdhci_sirf_ops = { + .read_l = sdhci_sirf_readl_le, + .read_w = sdhci_sirf_readw_le, .platform_execute_tuning = sdhci_sirf_execute_tuning, .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, -- cgit v1.2.3 From ed425fc46b953876d11e40c1adb24cc42cfd0911 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:49:17 +0900 Subject: mmc: davinci: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/davinci_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 1625f908dc70..ea2a2ebc6b91 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1161,7 +1161,7 @@ static void __init init_mmcsd_host(struct mmc_davinci_host *host) mmc_davinci_reset_ctrl(host, 0); } -static struct platform_device_id davinci_mmc_devtype[] = { +static const struct platform_device_id davinci_mmc_devtype[] = { { .name = "dm6441-mmc", .driver_data = MMC_CTLR_VERSION_1, -- cgit v1.2.3 From f61fcc32a989ef8dc1fefbc2fbc4cb6b1b38353a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:49:18 +0900 Subject: mmc: mxs: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxs-mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index a82411a2c024..d839147e591d 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -549,7 +549,7 @@ static const struct mmc_host_ops mxs_mmc_ops = { .enable_sdio_irq = mxs_mmc_enable_sdio_irq, }; -static struct platform_device_id mxs_ssp_ids[] = { +static const struct platform_device_id mxs_ssp_ids[] = { { .name = "imx23-mmc", .driver_data = IMX23_SSP, -- cgit v1.2.3 From f2483b0d196ce2d967a92b7b1217553550d6d053 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:49:19 +0900 Subject: mmc: rtsx: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 2 +- drivers/mmc/host/rtsx_usb_sdmmc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 1d3d6c4bfdc6..93137483ecde 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1474,7 +1474,7 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id rtsx_pci_sdmmc_ids[] = { +static const struct platform_device_id rtsx_pci_sdmmc_ids[] = { { .name = DRV_NAME_RTSX_PCI_SDMMC, }, { diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 88af827e086b..6c71fc9f76c7 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -1439,7 +1439,7 @@ static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id rtsx_usb_sdmmc_ids[] = { +static const struct platform_device_id rtsx_usb_sdmmc_ids[] = { { .name = "rtsx_usb_sdmmc", }, { -- cgit v1.2.3 From 6a643206ef5bb509a50902aa1b339eee1fc018e6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:49:20 +0900 Subject: mmc: s3cmci: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/s3cmci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 94cddf381ba3..6291d5042ef2 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1856,7 +1856,7 @@ static int s3cmci_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id s3cmci_driver_ids[] = { +static const struct platform_device_id s3cmci_driver_ids[] = { { .name = "s3c2410-sdi", .driver_data = 0, -- cgit v1.2.3 From f8cbf461f9fa8649ef3aae6068f303024cd4fca8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:49:21 +0900 Subject: mmc: sdhci-imx: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ffa976037cf2..8eed84326e2f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -161,7 +161,7 @@ struct pltfm_imx_data { u32 is_ddr; }; -static struct platform_device_id imx_esdhc_devtype[] = { +static const struct platform_device_id imx_esdhc_devtype[] = { { .name = "sdhci-esdhc-imx25", .driver_data = (kernel_ulong_t) &esdhc_imx25_data, -- cgit v1.2.3 From 4d0aa49157a38eb2be8a2ed3bf9900afa796b8d5 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 00:49:22 +0900 Subject: mmc: sdhci-s3c: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-s3c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index c6d2dd7317c1..70c724bc6fc7 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -736,7 +736,7 @@ static struct sdhci_s3c_drv_data exynos4_sdhci_drv_data = { #define EXYNOS4_SDHCI_DRV_DATA ((kernel_ulong_t)NULL) #endif -static struct platform_device_id sdhci_s3c_driver_ids[] = { +static const struct platform_device_id sdhci_s3c_driver_ids[] = { { .name = "s3c-sdhci", .driver_data = (kernel_ulong_t)NULL, -- cgit v1.2.3 From 308f3f8d8112ea9af6e10564f4012f2d1f94f062 Mon Sep 17 00:00:00 2001 From: Suman Tripathi Date: Mon, 4 May 2015 19:09:51 +0530 Subject: mmc: sdhci-of-arasan: Add the support for sdhci-arasan4.9a This patch adds the quirks and compatible string in sdhci-of-arasan.c to support sdhci-arasan4.9a version of controller. Signed-off-by: Suman Tripathi Reviewed-by: Michal Simek Acked-by: Arnd Bergmann Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 3 ++- drivers/mmc/host/sdhci-of-arasan.c | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt index 98ee2abbe138..7e9490313d5a 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt @@ -8,7 +8,8 @@ Device Tree Bindings for the Arasan SDHCI Controller [3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt Required Properties: - - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' + - compatible: Compatibility string. Must be 'arasan,sdhci-8.9a' or + 'arasan,sdhci-4.9a' - reg: From mmc bindings: Register location and length. - clocks: From clock bindings: Handles to clock inputs. - clock-names: From clock bindings: Tuple including "clk_xin" and "clk_ahb" diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 6287d426c96b..21c0c08dfe54 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -20,6 +20,7 @@ */ #include +#include #include "sdhci-pltfm.h" #define SDHCI_ARASAN_CLK_CTRL_OFFSET 0x2c @@ -168,6 +169,11 @@ static int sdhci_arasan_probe(struct platform_device *pdev) goto clk_disable_all; } + if (of_device_is_compatible(pdev->dev.of_node, "arasan,sdhci-4.9a")) { + host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; + host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; + } + sdhci_get_of_property(pdev); pltfm_host = sdhci_priv(host); pltfm_host->priv = sdhci_arasan; @@ -208,6 +214,7 @@ static int sdhci_arasan_remove(struct platform_device *pdev) static const struct of_device_id sdhci_arasan_of_match[] = { { .compatible = "arasan,sdhci-8.9a" }, + { .compatible = "arasan,sdhci-4.9a" }, { } }; MODULE_DEVICE_TABLE(of, sdhci_arasan_of_match); -- cgit v1.2.3 From 59d22309c46e807cef8d892316f6d08b715d6652 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 5 May 2015 17:11:54 +0800 Subject: mmc: sdhci-{pxav2,pxav3}: Use of_match_ptr() macro This eliminates having an #ifdef returning NULL for the case when OF is disabled. Signed-off-by: Axel Lin Acked-by: Jisheng Zhang Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav2.c | 4 +--- drivers/mmc/host/sdhci-pxav3.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index f98008b5ea77..beffd8615489 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -252,9 +252,7 @@ static int sdhci_pxav2_remove(struct platform_device *pdev) static struct platform_driver sdhci_pxav2_driver = { .driver = { .name = "sdhci-pxav2", -#ifdef CONFIG_OF - .of_match_table = sdhci_pxav2_of_match, -#endif + .of_match_table = of_match_ptr(sdhci_pxav2_of_match), .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_pxav2_probe, diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index b5103a247bc1..01cb2d309a6a 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -578,9 +578,7 @@ static const struct dev_pm_ops sdhci_pxav3_pmops = { static struct platform_driver sdhci_pxav3_driver = { .driver = { .name = "sdhci-pxav3", -#ifdef CONFIG_OF - .of_match_table = sdhci_pxav3_of_match, -#endif + .of_match_table = of_match_ptr(sdhci_pxav3_of_match), .pm = SDHCI_PXAV3_PMOPS, }, .probe = sdhci_pxav3_probe, -- cgit v1.2.3 From dfa13ebbe3340e538b988f5608efd9ff2ca7fc35 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:12 +0300 Subject: mmc: host: Add facility to support re-tuning Currently, there is core support for tuning during initialization. There can also be a need to re-tune periodically (e.g. sdhci) or to re-tune after the host controller is powered off (e.g. after PM runtime suspend / resume) or to re-tune in response to CRC errors. The main requirements for re-tuning are: - ability to enable / disable re-tuning - ability to flag that re-tuning is needed - ability to re-tune before any request - ability to hold off re-tuning if the card is busy - ability to hold off re-tuning if re-tuning is in progress - ability to run a re-tuning timer To support those requirements 7 members are added to struct mmc_host: unsigned int can_retune:1; /* re-tuning can be used */ unsigned int doing_retune:1; /* re-tuning in progress */ unsigned int retune_now:1; /* do re-tuning at next req */ int need_retune; /* re-tuning is needed */ int hold_retune; /* hold off re-tuning */ unsigned int retune_period; /* re-tuning period in secs */ struct timer_list retune_timer; /* for periodic re-tuning */ need_retune is an integer so it can be set without needing synchronization. hold_retune is a integer to allow nesting. Various simple functions are provided to set / clear those variables. Subsequent patches take those functions into use. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/host.h | 6 +++++ include/linux/mmc/host.h | 23 ++++++++++++++++ 3 files changed, 97 insertions(+) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 8be0df758e68..e90e02fc596a 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -301,6 +301,73 @@ static inline void mmc_host_clk_sysfs_init(struct mmc_host *host) #endif +void mmc_retune_enable(struct mmc_host *host) +{ + host->can_retune = 1; + if (host->retune_period) + mod_timer(&host->retune_timer, + jiffies + host->retune_period * HZ); +} + +void mmc_retune_disable(struct mmc_host *host) +{ + host->can_retune = 0; + del_timer_sync(&host->retune_timer); + host->retune_now = 0; + host->need_retune = 0; +} + +void mmc_retune_timer_stop(struct mmc_host *host) +{ + del_timer_sync(&host->retune_timer); +} +EXPORT_SYMBOL(mmc_retune_timer_stop); + +void mmc_retune_hold(struct mmc_host *host) +{ + if (!host->hold_retune) + host->retune_now = 1; + host->hold_retune += 1; +} + +void mmc_retune_release(struct mmc_host *host) +{ + if (host->hold_retune) + host->hold_retune -= 1; + else + WARN_ON(1); +} + +int mmc_retune(struct mmc_host *host) +{ + int err; + + if (host->retune_now) + host->retune_now = 0; + else + return 0; + + if (!host->need_retune || host->doing_retune || !host->card) + return 0; + + host->need_retune = 0; + + host->doing_retune = 1; + + err = mmc_execute_tuning(host->card); + + host->doing_retune = 0; + + return err; +} + +static void mmc_retune_timer(unsigned long data) +{ + struct mmc_host *host = (struct mmc_host *)data; + + mmc_retune_needed(host); +} + /** * mmc_of_parse() - parse host's device-tree node * @host: host whose node should be parsed. @@ -504,6 +571,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) #ifdef CONFIG_PM host->pm_notify.notifier_call = mmc_pm_notify; #endif + setup_timer(&host->retune_timer, mmc_retune_timer, (unsigned long)host); /* * By default, hosts do not support SGIO or large requests. diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index f2ab9e578126..992bf5397633 100644 --- a/drivers/mmc/core/host.h +++ b/drivers/mmc/core/host.h @@ -15,5 +15,11 @@ int mmc_register_host_class(void); void mmc_unregister_host_class(void); +void mmc_retune_enable(struct mmc_host *host); +void mmc_retune_disable(struct mmc_host *host); +void mmc_retune_hold(struct mmc_host *host); +void mmc_retune_release(struct mmc_host *host); +int mmc_retune(struct mmc_host *host); + #endif diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index b5bedaec6223..f471193ef6d6 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -321,10 +322,18 @@ struct mmc_host { #ifdef CONFIG_MMC_DEBUG unsigned int removed:1; /* host is being removed */ #endif + unsigned int can_retune:1; /* re-tuning can be used */ + unsigned int doing_retune:1; /* re-tuning in progress */ + unsigned int retune_now:1; /* do re-tuning at next req */ int rescan_disable; /* disable card detection */ int rescan_entered; /* used with nonremovable devices */ + int need_retune; /* re-tuning is needed */ + int hold_retune; /* hold off re-tuning */ + unsigned int retune_period; /* re-tuning period in secs */ + struct timer_list retune_timer; /* for periodic re-tuning */ + bool trigger_card_event; /* card_event necessary */ struct mmc_card *card; /* device attached to this host */ @@ -513,4 +522,18 @@ static inline bool mmc_card_hs400(struct mmc_card *card) return card->host->ios.timing == MMC_TIMING_MMC_HS400; } +void mmc_retune_timer_stop(struct mmc_host *host); + +static inline void mmc_retune_needed(struct mmc_host *host) +{ + if (host->can_retune) + host->need_retune = 1; +} + +static inline void mmc_retune_recheck(struct mmc_host *host) +{ + if (host->hold_retune <= 1) + host->retune_now = 1; +} + #endif /* LINUX_MMC_HOST_H */ -- cgit v1.2.3 From 79d5a65aeea43920bf3ff60791f317570dd6f54f Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:13 +0300 Subject: mmc: core: Enable / disable re-tuning Enable re-tuning when tuning is executed and disable re-tuning when card is no longer initialized. In the case of SDIO suspend, the card can keep power. In that case, re-tuning need not be disabled, but, if a re-tuning timer is being used, ensure it is disabled and assume that re-tuning will be needed upon resume since it is not known how long the suspend will last. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 4 ++++ drivers/mmc/core/sdio.c | 6 +++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 92e7671426eb..007c44443332 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1109,6 +1109,8 @@ int mmc_execute_tuning(struct mmc_card *card) if (err) pr_err("%s: tuning execution failed\n", mmc_hostname(host)); + else + mmc_retune_enable(host); return err; } @@ -1140,6 +1142,8 @@ void mmc_set_bus_width(struct mmc_host *host, unsigned int width) */ void mmc_set_initial_state(struct mmc_host *host) { + mmc_retune_disable(host); + if (mmc_host_is_spi(host)) host->ios.chip_select = MMC_CS_HIGH; else diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index f6c28a7ea8df..5c1423a3e7d7 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -934,8 +934,12 @@ static int mmc_sdio_suspend(struct mmc_host *host) mmc_release_host(host); } - if (!mmc_card_keep_power(host)) + if (!mmc_card_keep_power(host)) { mmc_power_off(host); + } else if (host->retune_period) { + mmc_retune_timer_stop(host); + mmc_retune_needed(host); + } return 0; } -- cgit v1.2.3 From 90a81489b0a9d7b56df2dcf68498fd3a03deb354 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:14 +0300 Subject: mmc: core: Add support for re-tuning before each request At the start of each request, re-tune if needed and then hold off re-tuning again until the request is done. Note that though there is one function that starts requests (mmc_start_request) there are two that wait for the request to be done (mmc_wait_for_req_done and mmc_wait_for_data_req_done). Also note that mmc_wait_for_data_req_done can return even when the request is not done (which allows the block driver to prepare a newly arrived request while still waiting for the previous request). This patch ensures re-tuning is held for the duration of a request. Subsequent patches will also hold re-tuning at other times when it might cause a conflict. In addition, possibly a command is failing because re-tuning is needed. Use mmc_retune_recheck() to check re-tuning. At that point re-tuning is held, at least by the request, so mmc_retune_recheck() flags host->retune_now if the hold count is 1. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 007c44443332..ec3453fd39aa 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -186,12 +186,29 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) EXPORT_SYMBOL(mmc_request_done); +static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) +{ + int err; + + /* Assumes host controller has been runtime resumed by mmc_claim_host */ + err = mmc_retune(host); + if (err) { + mrq->cmd->error = err; + mmc_request_done(host, mrq); + return; + } + + host->ops->request(host, mrq); +} + static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) { #ifdef CONFIG_MMC_DEBUG unsigned int i, sz; struct scatterlist *sg; #endif + mmc_retune_hold(host); + if (mmc_card_removed(host->card)) return -ENOMEDIUM; @@ -252,7 +269,7 @@ static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) } mmc_host_clk_hold(host); led_trigger_event(host->led, LED_FULL); - host->ops->request(host, mrq); + __mmc_start_request(host, mrq); return 0; } @@ -417,22 +434,22 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host, host->areq); break; /* return err */ } else { + mmc_retune_recheck(host); pr_info("%s: req failed (CMD%u): %d, retrying...\n", mmc_hostname(host), cmd->opcode, cmd->error); cmd->retries--; cmd->error = 0; - host->ops->request(host, mrq); + __mmc_start_request(host, mrq); continue; /* wait for done/new event again */ } } else if (context_info->is_new_req) { context_info->is_new_req = false; - if (!next_req) { - err = MMC_BLK_NEW_REQUEST; - break; /* return err */ - } + if (!next_req) + return MMC_BLK_NEW_REQUEST; } } + mmc_retune_release(host); return err; } @@ -467,12 +484,16 @@ static void mmc_wait_for_req_done(struct mmc_host *host, mmc_card_removed(host->card)) break; + mmc_retune_recheck(host); + pr_debug("%s: req failed (CMD%u): %d, retrying...\n", mmc_hostname(host), cmd->opcode, cmd->error); cmd->retries--; cmd->error = 0; - host->ops->request(host, mrq); + __mmc_start_request(host, mrq); } + + mmc_retune_release(host); } /** -- cgit v1.2.3 From c6dbab9cb58f5519c65867c4b371e1ee730a8451 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:15 +0300 Subject: mmc: core: Hold re-tuning during switch commands Hold re-tuning during switch commands to prevent it from conflicting with the busy state or the CMD13 verification. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_ops.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 0ea042dc7443..4cad5f02837c 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -19,6 +19,7 @@ #include #include "core.h" +#include "host.h" #include "mmc_ops.h" #define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ @@ -474,6 +475,8 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, u32 status = 0; bool use_r1b_resp = use_busy_signal; + mmc_retune_hold(host); + /* * If the cmd timeout and the max_busy_timeout of the host are both * specified, let's validate them. A failure means we need to prevent @@ -506,11 +509,11 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); if (err) - return err; + goto out; /* No need to check card status in case of unblocking command */ if (!use_busy_signal) - return 0; + goto out; /* * CRC errors shall only be ignored in cases were CMD13 is used to poll @@ -529,7 +532,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, if (send_status) { err = __mmc_send_status(card, &status, ignore_crc); if (err) - return err; + goto out; } if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) break; @@ -543,29 +546,36 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, */ if (!send_status) { mmc_delay(timeout_ms); - return 0; + goto out; } /* Timeout if the device never leaves the program state. */ if (time_after(jiffies, timeout)) { pr_err("%s: Card stuck in programming state! %s\n", mmc_hostname(host), __func__); - return -ETIMEDOUT; + err = -ETIMEDOUT; + goto out; } } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); if (mmc_host_is_spi(host)) { - if (status & R1_SPI_ILLEGAL_COMMAND) - return -EBADMSG; + if (status & R1_SPI_ILLEGAL_COMMAND) { + err = -EBADMSG; + goto out; + } } else { if (status & 0xFDFFA000) pr_warn("%s: unexpected status %#x after switch\n", mmc_hostname(host), status); - if (status & R1_SWITCH_ERROR) - return -EBADMSG; + if (status & R1_SWITCH_ERROR) { + err = -EBADMSG; + goto out; + } } +out: + mmc_retune_release(host); - return 0; + return err; } EXPORT_SYMBOL_GPL(__mmc_switch); -- cgit v1.2.3 From 8f11d1064e01e1c8bf33ffef86072c2cb0c05b8c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:16 +0300 Subject: mmc: core: Hold re-tuning during erase commands Hold re-tuning during erase commands to prevent it from conflicting with the sequence of commands. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index ec3453fd39aa..ff1e38400dc5 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1995,6 +1995,8 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, unsigned long timeout; int err; + mmc_retune_hold(card->host); + /* * qty is used to calculate the erase timeout which depends on how many * erase groups (or allocation units in SD terminology) are affected. @@ -2098,6 +2100,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || (R1_CURRENT_STATE(cmd.resp[0]) == R1_STATE_PRG)); out: + mmc_retune_release(card->host); return err; } -- cgit v1.2.3 From 66073d8671c41fb0bc8c6e36531b4eafb70c990e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:17 +0300 Subject: mmc: core: Hold re-tuning while bkops ongoing Hold re-tuning during bkops to prevent it from conflicting with the busy state. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index ff1e38400dc5..adb64087a28a 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -318,12 +318,15 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) use_busy_signal = false; } + mmc_retune_hold(card->host); + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true, false); if (err) { pr_warn("%s: Error %d starting bkops\n", mmc_hostname(card->host), err); + mmc_retune_release(card->host); goto out; } @@ -334,6 +337,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception) */ if (!use_busy_signal) mmc_card_set_doing_bkops(card); + else + mmc_retune_release(card->host); out: mmc_release_host(card->host); } @@ -749,6 +754,7 @@ int mmc_stop_bkops(struct mmc_card *card) */ if (!err || (err == -EINVAL)) { mmc_card_clr_doing_bkops(card); + mmc_retune_release(card->host); err = 0; } -- cgit v1.2.3 From 436f8daa6f5a2943a20df8f3447da250b46f0d87 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:18 +0300 Subject: mmc: mmc: Hold re-tuning in mmc_sleep() The sleep command is issued after deselecting the card, but re-tuning won't work on a deselected card so re-tuning must be held. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f36c76f8b232..fd41b289ac91 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -21,6 +21,7 @@ #include #include "core.h" +#include "host.h" #include "bus.h" #include "mmc_ops.h" #include "sd_ops.h" @@ -1511,9 +1512,12 @@ static int mmc_sleep(struct mmc_host *host) unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000); int err; + /* Re-tuning can't be done once the card is deselected */ + mmc_retune_hold(host); + err = mmc_deselect_cards(host); if (err) - return err; + goto out_release; cmd.opcode = MMC_SLEEP_AWAKE; cmd.arg = card->rca << 16; @@ -1534,7 +1538,7 @@ static int mmc_sleep(struct mmc_host *host) err = mmc_wait_for_cmd(host, &cmd, 0); if (err) - return err; + goto out_release; /* * If the host does not wait while the card signals busy, then we will @@ -1545,6 +1549,8 @@ static int mmc_sleep(struct mmc_host *host) if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY)) mmc_delay(timeout_ms); +out_release: + mmc_retune_release(host); return err; } -- cgit v1.2.3 From ed16f58dc00d47439c201ab18ca4d981210bcafd Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:19 +0300 Subject: mmc: core: Separate out the mmc_switch status check so it can be re-used Make a separate function to do the mmc_switch status check so it can be re-used. This is preparation for adding support for HS400 re-tuning. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_ops.c | 30 ++++++++++++++++-------------- drivers/mmc/core/mmc_ops.h | 1 + 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 4cad5f02837c..0e9ae1c276c8 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -450,6 +450,21 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc) return err; } +int mmc_switch_status_error(struct mmc_host *host, u32 status) +{ + if (mmc_host_is_spi(host)) { + if (status & R1_SPI_ILLEGAL_COMMAND) + return -EBADMSG; + } else { + if (status & 0xFDFFA000) + pr_warn("%s: unexpected status %#x after switch\n", + mmc_hostname(host), status); + if (status & R1_SWITCH_ERROR) + return -EBADMSG; + } + return 0; +} + /** * __mmc_switch - modify EXT_CSD register * @card: the MMC card associated with the data transfer @@ -558,20 +573,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, } } while (R1_CURRENT_STATE(status) == R1_STATE_PRG); - if (mmc_host_is_spi(host)) { - if (status & R1_SPI_ILLEGAL_COMMAND) { - err = -EBADMSG; - goto out; - } - } else { - if (status & 0xFDFFA000) - pr_warn("%s: unexpected status %#x after switch\n", - mmc_hostname(host), status); - if (status & R1_SWITCH_ERROR) { - err = -EBADMSG; - goto out; - } - } + err = mmc_switch_status_error(host, status); out: mmc_retune_release(host); diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h index 6f4b00ed93de..f498f9ae21f0 100644 --- a/drivers/mmc/core/mmc_ops.h +++ b/drivers/mmc/core/mmc_ops.h @@ -27,6 +27,7 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc); int mmc_bus_test(struct mmc_card *card, u8 bus_width); int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status); int mmc_can_ext_csd(struct mmc_card *card); +int mmc_switch_status_error(struct mmc_host *host, u32 status); #endif -- cgit v1.2.3 From 6376f69d20a6905c1d83be451065f70200490b98 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:20 +0300 Subject: mmc: core: Add support for HS400 re-tuning HS400 re-tuning must be done in HS200 mode. Add the ability to switch from HS400 mode to HS200 mode before re-tuning and switch back to HS400 after re-tuning. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.h | 2 ++ drivers/mmc/core/host.c | 17 ++++++++++ drivers/mmc/core/mmc.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+) diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index cfba3c05aab1..e6f2de7074ad 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -88,6 +88,8 @@ void mmc_remove_card_debugfs(struct mmc_card *card); void mmc_init_context_info(struct mmc_host *host); int mmc_execute_tuning(struct mmc_card *card); +int mmc_hs200_to_hs400(struct mmc_card *card); +int mmc_hs400_to_hs200(struct mmc_card *card); #endif diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index e90e02fc596a..86c495b7a335 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -340,6 +340,7 @@ void mmc_retune_release(struct mmc_host *host) int mmc_retune(struct mmc_host *host) { + bool return_to_hs400 = false; int err; if (host->retune_now) @@ -354,8 +355,24 @@ int mmc_retune(struct mmc_host *host) host->doing_retune = 1; + if (host->ios.timing == MMC_TIMING_MMC_HS400) { + err = mmc_hs400_to_hs200(host->card); + if (err) + goto out; + + return_to_hs400 = true; + + if (host->ops->prepare_hs400_tuning) + host->ops->prepare_hs400_tuning(host, &host->ios); + } + err = mmc_execute_tuning(host->card); + if (err) + goto out; + if (return_to_hs400) + err = mmc_hs200_to_hs400(host->card); +out: host->doing_retune = 0; return err; diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index fd41b289ac91..a8028633253a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1092,6 +1092,94 @@ static int mmc_select_hs400(struct mmc_card *card) return 0; } +int mmc_hs200_to_hs400(struct mmc_card *card) +{ + return mmc_select_hs400(card); +} + +/* Caller must hold re-tuning */ +static int mmc_switch_status(struct mmc_card *card) +{ + u32 status; + int err; + + err = mmc_send_status(card, &status); + if (err) + return err; + + return mmc_switch_status_error(card->host, status); +} + +int mmc_hs400_to_hs200(struct mmc_card *card) +{ + struct mmc_host *host = card->host; + bool send_status = true; + unsigned int max_dtr; + int err; + + if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) + send_status = false; + + /* Reduce frequency to HS */ + max_dtr = card->ext_csd.hs_max_dtr; + mmc_set_clock(host, max_dtr); + + /* Switch HS400 to HS DDR */ + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, + EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time, + true, send_status, true); + if (err) + goto out_err; + + mmc_set_timing(host, MMC_TIMING_MMC_DDR52); + + if (!send_status) { + err = mmc_switch_status(card); + if (err) + goto out_err; + } + + /* Switch HS DDR to HS */ + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, + EXT_CSD_BUS_WIDTH_8, card->ext_csd.generic_cmd6_time, + true, send_status, true); + if (err) + goto out_err; + + mmc_set_timing(host, MMC_TIMING_MMC_HS); + + if (!send_status) { + err = mmc_switch_status(card); + if (err) + goto out_err; + } + + /* Switch HS to HS200 */ + err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, + EXT_CSD_TIMING_HS200, + card->ext_csd.generic_cmd6_time, true, send_status, + true); + if (err) + goto out_err; + + mmc_set_timing(host, MMC_TIMING_MMC_HS200); + + if (!send_status) { + err = mmc_switch_status(card); + if (err) + goto out_err; + } + + mmc_set_bus_speed(card); + + return 0; + +out_err: + pr_err("%s: %s failed, error %d\n", mmc_hostname(card->host), + __func__, err); + return err; +} + /* * For device supporting HS200 mode, the following sequence * should be done before executing the tuning process. -- cgit v1.2.3 From 66c39dfc92f9d35ed9f713833156547842086891 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:21 +0300 Subject: mmc: sdhci: Change to new way of doing re-tuning Make use of mmc core support for re-tuning instead of doing it all in the sdhci driver. This patch also changes to flag the need for re-tuning always after runtime suspend when tuning has been used at initialization. Previously it was only done if the re-tuning timer was in use. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 112 ++++++----------------------------------------- drivers/mmc/host/sdhci.h | 3 -- 2 files changed, 13 insertions(+), 102 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c80287a02735..b34584420a8f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -52,7 +52,6 @@ static void sdhci_finish_data(struct sdhci_host *); static void sdhci_finish_command(struct sdhci_host *); static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode); -static void sdhci_tuning_timer(unsigned long data); static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable); static int sdhci_pre_dma_transfer(struct sdhci_host *host, struct mmc_data *data, @@ -254,17 +253,6 @@ static void sdhci_init(struct sdhci_host *host, int soft) static void sdhci_reinit(struct sdhci_host *host) { sdhci_init(host, 0); - /* - * Retuning stuffs are affected by different cards inserted and only - * applicable to UHS-I cards. So reset these fields to their initial - * value when card is removed. - */ - if (host->flags & SDHCI_USING_RETUNING_TIMER) { - host->flags &= ~SDHCI_USING_RETUNING_TIMER; - - del_timer_sync(&host->tuning_timer); - host->flags &= ~SDHCI_NEEDS_RETUNING; - } sdhci_enable_card_detection(host); } @@ -1353,7 +1341,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) struct sdhci_host *host; int present; unsigned long flags; - u32 tuning_opcode; host = mmc_priv(mmc); @@ -1387,39 +1374,6 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq->cmd->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); } else { - u32 present_state; - - present_state = sdhci_readl(host, SDHCI_PRESENT_STATE); - /* - * Check if the re-tuning timer has already expired and there - * is no on-going data transfer and DAT0 is not busy. If so, - * we need to execute tuning procedure before sending command. - */ - if ((host->flags & SDHCI_NEEDS_RETUNING) && - !(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ)) && - (present_state & SDHCI_DATA_0_LVL_MASK)) { - if (mmc->card) { - /* eMMC uses cmd21 but sd and sdio use cmd19 */ - tuning_opcode = - mmc->card->type == MMC_TYPE_MMC ? - MMC_SEND_TUNING_BLOCK_HS200 : - MMC_SEND_TUNING_BLOCK; - - /* Here we need to set the host->mrq to NULL, - * in case the pending finish_tasklet - * finishes it incorrectly. - */ - host->mrq = NULL; - - spin_unlock_irqrestore(&host->lock, flags); - sdhci_execute_tuning(mmc, tuning_opcode); - spin_lock_irqsave(&host->lock, flags); - - /* Restore original mmc_request structure */ - host->mrq = mrq; - } - } - if (mrq->sbc && !(host->flags & SDHCI_AUTO_CMD23)) sdhci_send_command(host, mrq->sbc); else @@ -2065,23 +2019,18 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode) } out: - host->flags &= ~SDHCI_NEEDS_RETUNING; - if (tuning_count) { - host->flags |= SDHCI_USING_RETUNING_TIMER; - mod_timer(&host->tuning_timer, jiffies + tuning_count * HZ); + /* + * In case tuning fails, host controllers which support + * re-tuning can try tuning again at a later time, when the + * re-tuning timer expires. So for these controllers, we + * return 0. Since there might be other controllers who do not + * have this capability, we return error for them. + */ + err = 0; } - /* - * In case tuning fails, host controllers which support re-tuning can - * try tuning again at a later time, when the re-tuning timer expires. - * So for these controllers, we return 0. Since there might be other - * controllers who do not have this capability, we return error for - * them. SDHCI_USING_RETUNING_TIMER means the host is currently using - * a retuning timer to do the retuning for the card. - */ - if (err && (host->flags & SDHCI_USING_RETUNING_TIMER)) - err = 0; + host->mmc->retune_period = err ? 0 : tuning_count; sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); @@ -2337,20 +2286,6 @@ static void sdhci_timeout_timer(unsigned long data) spin_unlock_irqrestore(&host->lock, flags); } -static void sdhci_tuning_timer(unsigned long data) -{ - struct sdhci_host *host; - unsigned long flags; - - host = (struct sdhci_host *)data; - - spin_lock_irqsave(&host->lock, flags); - - host->flags |= SDHCI_NEEDS_RETUNING; - - spin_unlock_irqrestore(&host->lock, flags); -} - /*****************************************************************************\ * * * Interrupt handling * @@ -2728,11 +2663,8 @@ int sdhci_suspend_host(struct sdhci_host *host) { sdhci_disable_card_detection(host); - /* Disable tuning since we are suspending */ - if (host->flags & SDHCI_USING_RETUNING_TIMER) { - del_timer_sync(&host->tuning_timer); - host->flags &= ~SDHCI_NEEDS_RETUNING; - } + mmc_retune_timer_stop(host->mmc); + mmc_retune_needed(host->mmc); if (!device_may_wakeup(mmc_dev(host->mmc))) { host->ier = 0; @@ -2782,10 +2714,6 @@ int sdhci_resume_host(struct sdhci_host *host) sdhci_enable_card_detection(host); - /* Set the re-tuning expiration flag */ - if (host->flags & SDHCI_USING_RETUNING_TIMER) - host->flags |= SDHCI_NEEDS_RETUNING; - return ret; } @@ -2822,11 +2750,8 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) { unsigned long flags; - /* Disable tuning since we are suspending */ - if (host->flags & SDHCI_USING_RETUNING_TIMER) { - del_timer_sync(&host->tuning_timer); - host->flags &= ~SDHCI_NEEDS_RETUNING; - } + mmc_retune_timer_stop(host->mmc); + mmc_retune_needed(host->mmc); spin_lock_irqsave(&host->lock, flags); host->ier &= SDHCI_INT_CARD_INT; @@ -2869,10 +2794,6 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) spin_unlock_irqrestore(&host->lock, flags); } - /* Set the re-tuning expiration flag */ - if (host->flags & SDHCI_USING_RETUNING_TIMER) - host->flags |= SDHCI_NEEDS_RETUNING; - spin_lock_irqsave(&host->lock, flags); host->runtime_suspended = false; @@ -3408,13 +3329,6 @@ int sdhci_add_host(struct sdhci_host *host) init_waitqueue_head(&host->buf_ready_int); - if (host->version >= SDHCI_SPEC_300) { - /* Initialize re-tuning timer */ - init_timer(&host->tuning_timer); - host->tuning_timer.data = (unsigned long)host; - host->tuning_timer.function = sdhci_tuning_timer; - } - sdhci_init(host, 0); ret = request_threaded_irq(host->irq, sdhci_irq, sdhci_thread_irq, diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index e639b7f435e5..923ff223aea4 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -432,13 +432,11 @@ struct sdhci_host { #define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */ #define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */ #define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */ -#define SDHCI_NEEDS_RETUNING (1<<5) /* Host needs retuning */ #define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */ #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_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */ -#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */ #define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */ #define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */ @@ -504,7 +502,6 @@ struct sdhci_host { unsigned int tuning_count; /* Timer count for re-tuning */ unsigned int tuning_mode; /* Re-tuning mode supported by host */ #define SDHCI_TUNING_MODE_1 0 - struct timer_list tuning_timer; /* Timer for tuning */ struct sdhci_host_next next_data; unsigned long private[0] ____cacheline_aligned; -- cgit v1.2.3 From bd11e8bd03cae9e0499c34f67c55408566f6a089 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:22 +0300 Subject: mmc: core: Flag re-tuning is needed on CRC errors CRC errors could possibly be alleviated by re-tuning so flag re-tuning needed in those cases. Note this has no effect if re-tuning has not been enabled. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index adb64087a28a..c93062bc4f2d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -133,6 +133,12 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) struct mmc_command *cmd = mrq->cmd; int err = cmd->error; + /* Flag re-tuning needed on CRC errors */ + if (err == -EILSEQ || (mrq->sbc && mrq->sbc->error == -EILSEQ) || + (mrq->data && mrq->data->error == -EILSEQ) || + (mrq->stop && mrq->stop->error == -EILSEQ)) + mmc_retune_needed(host); + if (err && cmd->retries && mmc_host_is_spi(host)) { if (cmd->resp[0] & R1_SPI_ILLEGAL_COMMAND) cmd->retries = 0; -- cgit v1.2.3 From 6f398ad2075d9af4158d37442e9ca22e528f06c1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:23 +0300 Subject: mmc: block: Check re-tuning in the recovery path If re-tuning is needed, do it in the recovery path to give recovery commands a better chance of success. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 60f7141a6b02..5af52fd9e37a 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -913,6 +913,9 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req, if (!err) break; + /* Re-tune if needed */ + mmc_retune_recheck(card->host); + prev_cmd_status_valid = false; pr_err("%s: error %d sending status command, %sing\n", req->rq_disk->disk_name, err, retry ? "retry" : "abort"); -- cgit v1.2.3 From b8360a4945c49c07568892b5958c08e01bf7d6d2 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:24 +0300 Subject: mmc: block: Retry errored data requests when re-tuning is needed Retry errored data requests when re-tuning is needed and add a flag to struct mmc_blk_request so that the retry is only done once. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 11 ++++++++++- drivers/mmc/card/queue.h | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 5af52fd9e37a..eefdf2510542 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1207,6 +1207,7 @@ static int mmc_blk_err_check(struct mmc_card *card, mmc_active); struct mmc_blk_request *brq = &mq_mrq->brq; struct request *req = mq_mrq->req; + int need_retune = card->host->need_retune; int ecc_err = 0, gen_err = 0; /* @@ -1274,6 +1275,12 @@ static int mmc_blk_err_check(struct mmc_card *card, } if (brq->data.error) { + if (need_retune && !brq->retune_retry_done) { + pr_info("%s: retrying because a re-tune was needed\n", + req->rq_disk->disk_name); + brq->retune_retry_done = 1; + return MMC_BLK_RETRY; + } pr_err("%s: error %d transferring data, sector %u, nr %u, cmd response %#x, card status %#x\n", req->rq_disk->disk_name, brq->data.error, (unsigned)blk_rq_pos(req), @@ -1833,7 +1840,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; struct mmc_blk_request *brq = &mq->mqrq_cur->brq; - int ret = 1, disable_multi = 0, retry = 0, type; + int ret = 1, disable_multi = 0, retry = 0, type, retune_retry_done = 0; enum mmc_blk_status status; struct mmc_queue_req *mq_rq; struct request *req = rqc; @@ -1917,6 +1924,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) break; goto cmd_abort; case MMC_BLK_RETRY: + retune_retry_done = brq->retune_retry_done; if (retry++ < 5) break; /* Fall through */ @@ -1979,6 +1987,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) mmc_start_req(card->host, &mq_rq->mmc_active, NULL); } + mq_rq->brq.retune_retry_done = retune_retry_done; } } while (ret); diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h index 99e6521e6169..36cddab57d77 100644 --- a/drivers/mmc/card/queue.h +++ b/drivers/mmc/card/queue.h @@ -12,6 +12,7 @@ struct mmc_blk_request { struct mmc_command cmd; struct mmc_command stop; struct mmc_data data; + int retune_retry_done; }; enum mmc_packed_type { -- cgit v1.2.3 From 0250fdf257b1e5febba19b7cc536a3c9431e50bf Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Thu, 7 May 2015 13:10:25 +0300 Subject: mmc: core: Don't print reset warning if reset is not supported Check the error code for EOPNOTSUPP and do not print reset warning in that case. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index c93062bc4f2d..8c61ddd7748c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2371,7 +2371,8 @@ int mmc_hw_reset(struct mmc_host *host) ret = host->bus_ops->reset(host); mmc_bus_put(host); - pr_warn("%s: tried to reset card\n", mmc_hostname(host)); + if (ret != -EOPNOTSUPP) + pr_warn("%s: tried to reset card\n", mmc_hostname(host)); return ret; } -- cgit v1.2.3 From 9f6e0bff2afb52a4c29f5ca8a4db01810357974e Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 6 May 2015 20:31:19 +0200 Subject: mmc: Add support for disabling write-protect detection It is not uncommon to see systems where there is no physical write-protect signal (e.g. when using eMMC or microSD card slots). For some controllers, which have a dedicated write-protection detection logic (like SDHCI controllers), the get_ro() callback can return bogus data in such a case. Instead of handling this on a per controller basis this patch adds a new capability flag to the MMC core that can be set to specify that the result of get_ro() is invalid. When the flag is set the core will not call get_ro() and assume that the card is always read-write. Signed-off-by: Lars-Peter Clausen Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 30 +++++++++++++++++++++++------- include/linux/mmc/host.h | 1 + 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 31a9ef256d06..8f6864a2a055 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -804,6 +804,28 @@ int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card) return 0; } +static int mmc_sd_get_ro(struct mmc_host *host) +{ + int ro; + + /* + * Some systems don't feature a write-protect pin and don't need one. + * E.g. because they only have micro-SD card slot. For those systems + * assume that the SD card is always read-write. + */ + if (host->caps2 & MMC_CAP2_NO_WRITE_PROTECT) + return 0; + + if (!host->ops->get_ro) + return -1; + + mmc_host_clk_hold(host); + ro = host->ops->get_ro(host); + mmc_host_clk_release(host); + + return ro; +} + int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, bool reinit) { @@ -855,13 +877,7 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, * Check if read-only switch is active. */ if (!reinit) { - int ro = -1; - - if (host->ops->get_ro) { - mmc_host_clk_hold(card->host); - ro = host->ops->get_ro(host); - mmc_host_clk_release(card->host); - } + int ro = mmc_sd_get_ro(host); if (ro < 0) { pr_warn("%s: host does not support reading read-only switch, assuming write-enable\n", diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index f471193ef6d6..433eccb50838 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -286,6 +286,7 @@ struct mmc_host { MMC_CAP2_HS400_1_2V) #define MMC_CAP2_HSX00_1_2V (MMC_CAP2_HS200_1_2V_SDR | MMC_CAP2_HS400_1_2V) #define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17) +#define MMC_CAP2_NO_WRITE_PROTECT (1 << 18) /* No physical write protect pin, assume that card is always read-write */ mmc_pm_flag_t pm_caps; /* supported pm features */ -- cgit v1.2.3 From 19f4424677455a01b93163975cfbcffa9adb3336 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 6 May 2015 20:31:20 +0200 Subject: mmc: dt: Allow to specify that no write protect signal is present Allow to specify in the device-tree that no physical write-protect signal is connected to a particular instance of a MMC controller. Setting the property will cause the core will assume that the SD card is always read-write. The name for the new property is 'disable-wp' and was chosen based on the property with the same function from the Synopsys designware mobile storage host controller DT bindings specification. Signed-off-by: Lars-Peter Clausen Cc: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc.txt | 5 +++++ drivers/mmc/core/host.c | 3 +++ 2 files changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 438899e8829b..0384fc3f64e8 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -21,6 +21,11 @@ Optional properties: below for the case, when a GPIO is used for the CD line - wp-inverted: when present, polarity on the WP line is inverted. See the note below for the case, when a GPIO is used for the WP line +- disable-wp: When set no physical WP line is present. This property should + only be specified when the controller has a dedicated write-protect + detection logic. If a GPIO is always used for the write-protect detection + logic it is sufficient to not specify wp-gpios property in the absence of a WP + line. - max-frequency: maximum operating clock frequency - no-1-8-v: when present, denotes that 1.8v card voltage is not supported on this system, even if the controller claims it is. diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 86c495b7a335..99a9c9011c50 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -484,6 +484,9 @@ int mmc_of_parse(struct mmc_host *host) else if (ret != -ENOENT) return ret; + if (of_property_read_bool(np, "disable-wp")) + host->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; + /* See the comment on CD inversion above */ if (ro_cap_invert ^ ro_gpio_invert) host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; -- cgit v1.2.3 From 15064119273735c115fba381823b0746508bae3a Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 9 May 2015 09:57:08 -0300 Subject: mmc: sdhci-esdhc-imx: Move mmc_of_parse() to the dt probe mmc_of_parse() should be placed inside sdhci_esdhc_imx_probe_dt() as it suits only for the dt case. Signed-off-by: Fabio Estevam Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 8eed84326e2f..0107eb315b55 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -903,7 +903,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, mmc_of_parse_voltage(np, &host->ocr_mask); - return 0; + /* call to generic mmc_of_parse to support additional capabilities */ + return mmc_of_parse(host->mmc); } #else static inline int @@ -1048,11 +1049,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; } - /* call to generic mmc_of_parse to support additional capabilities */ - err = mmc_of_parse(host->mmc); - if (err) - goto disable_clk; - err = sdhci_add_host(host); if (err) goto disable_clk; -- cgit v1.2.3 From 7ccddeb08a632c713eca0a5f13bcbfa7e6e83982 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 9 May 2015 09:57:09 -0300 Subject: mmc: sdhci-esdhc-imx: Do not break platform data boards The only user of this driver that has not been converted to fully device tree is the i.MX35 SoC. There is a i.MX35-based board (mach-pcm043.c) that uses platform data to pass wp_gpio and cd_gpio information. Commit 8d86e4fcccf61ba ("mmc: sdhci-esdhc-imx: Call mmc_of_parse()") broke the platform data case by removing mmc_gpio_request_ro() and mmc_gpio_request_cd(), so restore the functionality for the non-dt case. Also, restore the check for ESDHC_CD_CONTROLLER so that we can still support the "fsl,cd-controller" property. Signed-off-by: Fabio Estevam Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 0107eb315b55..ef290a5b34c4 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -925,6 +925,7 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) struct esdhc_platform_data *boarddata; int err; struct pltfm_imx_data *imx_data; + bool dt = true; host = sdhci_pltfm_init(pdev, &sdhci_esdhc_imx_pdata, 0); if (IS_ERR(host)) @@ -1012,11 +1013,44 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) } imx_data->boarddata = *((struct esdhc_platform_data *) host->mmc->parent->platform_data); + dt = false; + } + /* write_protect */ + if (boarddata->wp_type == ESDHC_WP_GPIO && !dt) { + err = mmc_gpio_request_ro(host->mmc, boarddata->wp_gpio); + if (err) { + dev_err(mmc_dev(host->mmc), + "failed to request write-protect gpio!\n"); + goto disable_clk; + } + host->mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; } /* card_detect */ - if (boarddata->cd_type == ESDHC_CD_CONTROLLER) + switch (boarddata->cd_type) { + case ESDHC_CD_GPIO: + if (dt) + break; + err = mmc_gpio_request_cd(host->mmc, boarddata->cd_gpio, 0); + if (err) { + dev_err(mmc_dev(host->mmc), + "failed to request card-detect gpio!\n"); + goto disable_clk; + } + /* fall through */ + + case ESDHC_CD_CONTROLLER: + /* we have a working card_detect back */ host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; + break; + + case ESDHC_CD_PERMANENT: + host->mmc->caps |= MMC_CAP_NONREMOVABLE; + break; + + case ESDHC_CD_NONE: + break; + } switch (boarddata->max_bus_width) { case 8: -- cgit v1.2.3 From 30a9b9e1efe63e883063afc0f44a6f204b90c334 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 9 May 2015 18:44:49 -0300 Subject: mmc: card: mmc_test: Simplify a trivial if-return sequence Simplify a trivial if-return sequence. Possibly combine with a preceding function call. The semantic patch that makes this change is available in scripts/coccinelle/misc/simple_return.cocci. More information about semantic patching is available at http://coccinelle.lip6.fr/ Signed-off-by: Fabio Estevam Signed-off-by: Ulf Hansson --- drivers/mmc/card/mmc_test.c | 100 +++++++------------------------------------- 1 file changed, 15 insertions(+), 85 deletions(-) diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 53b741398b93..5e90b3f2d57c 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -268,8 +268,6 @@ static int mmc_test_wait_busy(struct mmc_test_card *test) static int mmc_test_buffer_transfer(struct mmc_test_card *test, u8 *buffer, unsigned addr, unsigned blksz, int write) { - int ret; - struct mmc_request mrq = {0}; struct mmc_command cmd = {0}; struct mmc_command stop = {0}; @@ -292,11 +290,7 @@ static int mmc_test_buffer_transfer(struct mmc_test_card *test, if (data.error) return data.error; - ret = mmc_test_wait_busy(test); - if (ret) - return ret; - - return 0; + return mmc_test_wait_busy(test); } static void mmc_test_free_mem(struct mmc_test_mem *mem) @@ -994,11 +988,7 @@ static int mmc_test_basic_write(struct mmc_test_card *test) sg_init_one(&sg, test->buffer, 512); - ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1); - if (ret) - return ret; - - return 0; + return mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 1); } static int mmc_test_basic_read(struct mmc_test_card *test) @@ -1012,44 +1002,29 @@ static int mmc_test_basic_read(struct mmc_test_card *test) sg_init_one(&sg, test->buffer, 512); - ret = mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0); - if (ret) - return ret; - - return 0; + return mmc_test_simple_transfer(test, &sg, 1, 0, 1, 512, 0); } static int mmc_test_verify_write(struct mmc_test_card *test) { - int ret; struct scatterlist sg; sg_init_one(&sg, test->buffer, 512); - ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1); - if (ret) - return ret; - - return 0; + return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1); } static int mmc_test_verify_read(struct mmc_test_card *test) { - int ret; struct scatterlist sg; sg_init_one(&sg, test->buffer, 512); - ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0); - if (ret) - return ret; - - return 0; + return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0); } static int mmc_test_multi_write(struct mmc_test_card *test) { - int ret; unsigned int size; struct scatterlist sg; @@ -1066,16 +1041,11 @@ static int mmc_test_multi_write(struct mmc_test_card *test) sg_init_one(&sg, test->buffer, size); - ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1); - if (ret) - return ret; - - return 0; + return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1); } static int mmc_test_multi_read(struct mmc_test_card *test) { - int ret; unsigned int size; struct scatterlist sg; @@ -1092,11 +1062,7 @@ static int mmc_test_multi_read(struct mmc_test_card *test) sg_init_one(&sg, test->buffer, size); - ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0); - if (ret) - return ret; - - return 0; + return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0); } static int mmc_test_pow2_write(struct mmc_test_card *test) @@ -1263,11 +1229,7 @@ static int mmc_test_xfersize_write(struct mmc_test_card *test) if (ret) return ret; - ret = mmc_test_broken_transfer(test, 1, 512, 1); - if (ret) - return ret; - - return 0; + return mmc_test_broken_transfer(test, 1, 512, 1); } static int mmc_test_xfersize_read(struct mmc_test_card *test) @@ -1278,11 +1240,7 @@ static int mmc_test_xfersize_read(struct mmc_test_card *test) if (ret) return ret; - ret = mmc_test_broken_transfer(test, 1, 512, 0); - if (ret) - return ret; - - return 0; + return mmc_test_broken_transfer(test, 1, 512, 0); } static int mmc_test_multi_xfersize_write(struct mmc_test_card *test) @@ -1296,11 +1254,7 @@ static int mmc_test_multi_xfersize_write(struct mmc_test_card *test) if (ret) return ret; - ret = mmc_test_broken_transfer(test, 2, 512, 1); - if (ret) - return ret; - - return 0; + return mmc_test_broken_transfer(test, 2, 512, 1); } static int mmc_test_multi_xfersize_read(struct mmc_test_card *test) @@ -1314,48 +1268,33 @@ static int mmc_test_multi_xfersize_read(struct mmc_test_card *test) if (ret) return ret; - ret = mmc_test_broken_transfer(test, 2, 512, 0); - if (ret) - return ret; - - return 0; + return mmc_test_broken_transfer(test, 2, 512, 0); } #ifdef CONFIG_HIGHMEM static int mmc_test_write_high(struct mmc_test_card *test) { - int ret; struct scatterlist sg; sg_init_table(&sg, 1); sg_set_page(&sg, test->highmem, 512, 0); - ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1); - if (ret) - return ret; - - return 0; + return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 1); } static int mmc_test_read_high(struct mmc_test_card *test) { - int ret; struct scatterlist sg; sg_init_table(&sg, 1); sg_set_page(&sg, test->highmem, 512, 0); - ret = mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0); - if (ret) - return ret; - - return 0; + return mmc_test_transfer(test, &sg, 1, 0, 1, 512, 0); } static int mmc_test_multi_write_high(struct mmc_test_card *test) { - int ret; unsigned int size; struct scatterlist sg; @@ -1373,16 +1312,11 @@ static int mmc_test_multi_write_high(struct mmc_test_card *test) sg_init_table(&sg, 1); sg_set_page(&sg, test->highmem, size, 0); - ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1); - if (ret) - return ret; - - return 0; + return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 1); } static int mmc_test_multi_read_high(struct mmc_test_card *test) { - int ret; unsigned int size; struct scatterlist sg; @@ -1400,11 +1334,7 @@ static int mmc_test_multi_read_high(struct mmc_test_card *test) sg_init_table(&sg, 1); sg_set_page(&sg, test->highmem, size, 0); - ret = mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0); - if (ret) - return ret; - - return 0; + return mmc_test_transfer(test, &sg, 1, 0, size/512, 512, 0); } #else -- cgit v1.2.3 From 6d91641613a66681d0b291e6f848107d289f70e8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 9 May 2015 18:44:50 -0300 Subject: mmc: host: mxcmmc: Simplify a trivial if-return sequence Simplify a trivial if-return sequence. Possibly combine with a preceding function call. The semantic patch that makes this change is available in scripts/coccinelle/misc/simple_return.cocci. More information about semantic patching is available at http://coccinelle.lip6.fr/ Signed-off-by: Fabio Estevam Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxcmmc.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 317d709f7550..d110f9e98c4b 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -605,11 +605,7 @@ static int mxcmci_push(struct mxcmci_host *host, void *_buf, int bytes) mxcmci_writel(host, cpu_to_le32(tmp), MMC_REG_BUFFER_ACCESS); } - stat = mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); - if (stat) - return stat; - - return 0; + return mxcmci_poll_status(host, STATUS_BUF_WRITE_RDY); } static int mxcmci_transfer_data(struct mxcmci_host *host) -- cgit v1.2.3 From bf3a35ac8038d6f3104c4b9fd8a6970c6b42b56f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 9 May 2015 18:44:51 -0300 Subject: mmc: host: sdhci: Use BUG_ON() Use BUG_ON() instead of an 'if' condition followed by BUG(). The semantic patch that makes this change is available in scripts/coccinelle/misc/bugon.cocci. More information about semantic patching is available at http://coccinelle.lip6.fr/ Signed-off-by: Fabio Estevam Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b34584420a8f..58c1770e879d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -316,8 +316,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host) local_irq_save(flags); while (blksize) { - if (!sg_miter_next(&host->sg_miter)) - BUG(); + BUG_ON(!sg_miter_next(&host->sg_miter)); len = min(host->sg_miter.length, blksize); @@ -362,8 +361,7 @@ static void sdhci_write_block_pio(struct sdhci_host *host) local_irq_save(flags); while (blksize) { - if (!sg_miter_next(&host->sg_miter)) - BUG(); + BUG_ON(!sg_miter_next(&host->sg_miter)); len = min(host->sg_miter.length, blksize); -- cgit v1.2.3 From ded8a5f9613dbb62cd74f9f26a9caa3193eb3764 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 11 May 2015 07:34:53 +0000 Subject: mmc: cast u8 to unsigned long long to avoid unexpected error card->ext_csd.enhanced_area_offset is defined as "unsigned long long", and, ext_csd[] is defined as u8. unsigned long long data might have strange data if first bit of ext_csd[] was 1. this patch cast it to (unsigned long long) Special thanks to coverity ex) u8 data8; u64 data64; data8 = 0x80; data64 = (data8 << 24); // 0xffffffff80000000 data64 = (((unsigned long long)data8) << 24); // 0x80000000; Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index a8028633253a..03c94c2f23e1 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -267,8 +267,10 @@ static void mmc_manage_enhanced_area(struct mmc_card *card, u8 *ext_csd) * calculate the enhanced data area offset, in bytes */ card->ext_csd.enhanced_area_offset = - (ext_csd[139] << 24) + (ext_csd[138] << 16) + - (ext_csd[137] << 8) + ext_csd[136]; + (((unsigned long long)ext_csd[139]) << 24) + + (((unsigned long long)ext_csd[138]) << 16) + + (((unsigned long long)ext_csd[137]) << 8) + + (((unsigned long long)ext_csd[136])); if (mmc_card_blockaddr(card)) card->ext_csd.enhanced_area_offset <<= 9; /* -- cgit v1.2.3 From 087de9ed3d076d071f09b09ef77d6107c1e1df82 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 11 May 2015 07:35:28 +0000 Subject: mmc: cast unsigned int to typeof(sector_t) to avoid unexpected error card->csd.capacity is defined as "unsigned int", and sector_t is defined as "u64" or "unsigned long" (depends on CONFIG_LBDAF). Thus, sector_t data might have strange data (see below). This patch cast it to typeof(sector_t) Special thanks to coverity ex) if sector_t was u64 unsigned int data; sector_t sector; data = 0x800000; sector = (data << 8); // 0xffffffff80000000 sector = (((typeof(sector_t))data) << 8); // 0x80000000 or data = 0x80000000; sector = (data << 8); // 0x0 sector = (((typeof(sector_t))data) << 8); // 0x8000000000 Signed-off-by: Kuninori Morimoto Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index eefdf2510542..fc0317124276 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2229,7 +2229,8 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) * The CSD capacity field is in units of read_blkbits. * set_capacity takes units of 512 bytes. */ - size = card->csd.capacity << (card->csd.read_blkbits - 9); + size = (typeof(sector_t))card->csd.capacity + << (card->csd.read_blkbits - 9); } return mmc_blk_alloc_req(card, &card->dev, size, false, NULL, -- cgit v1.2.3 From d9c7eb3895f755ab0e458a216961964ebffd9edb Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Thu, 14 May 2015 16:45:17 +0800 Subject: Document: dw_mmc-k3: add document of hi6220 mmc Add bindings for hi6220 mmc support Signed-off-by: Zhangfei Gao Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/k3-dw-mshc.txt | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt index 3b3544931437..df370585cbcc 100644 --- a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt @@ -13,6 +13,10 @@ Required Properties: * compatible: should be one of the following. - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extensions. + - "hisilicon,hi6220-dw-mshc": for controllers with hi6220 specific extensions. + +Optional Properties: +- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral. Example: @@ -42,3 +46,27 @@ Example: cap-mmc-highspeed; cap-sd-highspeed; }; + + /* for Hi6220 */ + + dwmmc_1: dwmmc1@f723e000 { + compatible = "hisilicon,hi6220-dw-mshc"; + num-slots = <0x1>; + bus-width = <0x4>; + disable-wp; + cap-sd-highspeed; + sd-uhs-sdr12; + sd-uhs-sdr25; + card-detect-delay = <200>; + hisilicon,peripheral-syscon = <&ao_ctrl>; + reg = <0x0 0xf723e000 0x0 0x1000>; + interrupts = <0x0 0x49 0x4>; + clocks = <&clock_sys HI6220_MMC1_CIUCLK>, <&clock_sys HI6220_MMC1_CLK>; + clock-names = "ciu", "biu"; + cd-gpios = <&gpio1 0 1>; + pinctrl-names = "default", "idle"; + pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>; + pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>; + vqmmc-supply = <&ldo7>; + vmmc-supply = <&ldo10>; + }; -- cgit v1.2.3 From 8f7849c4f9aa2fbed629736c8930d8d1d2c426bc Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Thu, 14 May 2015 16:45:18 +0800 Subject: mmc: dw_mmc: add switch_voltage switch_voltage is required on some platform since special register accessing Signed-off-by: Jorge A. Ramirez-Ortiz Signed-off-by: Dan Yuan Signed-off-by: Zhangfei Gao Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 4 ++++ drivers/mmc/host/dw_mmc.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 5f5adafb253a..ce66565d50ef 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1236,11 +1236,15 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) { struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; + const struct dw_mci_drv_data *drv_data = host->drv_data; u32 uhs; u32 v18 = SDMMC_UHS_18V << slot->id; int min_uv, max_uv; int ret; + if (drv_data && drv_data->switch_voltage) + return drv_data->switch_voltage(mmc, ios); + /* * Program the voltage. Note that some instances of dw_mmc may use * the UHS_REG for this. For other instances (like exynos) the UHS_REG diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index f45ab91de339..c7236170bf98 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -287,5 +287,7 @@ struct dw_mci_drv_data { int (*execute_tuning)(struct dw_mci_slot *slot); int (*prepare_hs400_tuning)(struct dw_mci *host, struct mmc_ios *ios); + int (*switch_voltage)(struct mmc_host *mmc, + struct mmc_ios *ios); }; #endif /* _DW_MMC_H_ */ -- cgit v1.2.3 From 0293efddaddfcbbbf01ce3537549bc21fcca0a50 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Thu, 14 May 2015 16:45:19 +0800 Subject: mmc: dw_mmc: k3: support hi6220 Support hi6220, tested on hikey board emmc: support hs sd: support hs, sdr12, sdr25 Signed-off-by: Jorge A. Ramirez-Ortiz Signed-off-by: Dan Yuan Signed-off-by: Zhangfei Gao Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-k3.c | 103 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 650f9cc3f7a6..137670b74faa 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -8,16 +8,30 @@ * (at your option) any later version. */ -#include -#include #include +#include #include #include +#include #include +#include +#include +#include #include "dw_mmc.h" #include "dw_mmc-pltfm.h" +/* + * hi6220 sd only support io voltage 1.8v and 3v + * Also need config AO_SCTRL_SEL18 accordingly + */ +#define AO_SCTRL_SEL18 BIT(10) +#define AO_SCTRL_CTRL3 0x40C + +struct k3_priv { + struct regmap *reg; +}; + static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios) { int ret; @@ -33,8 +47,93 @@ static const struct dw_mci_drv_data k3_drv_data = { .set_ios = dw_mci_k3_set_ios, }; +static int dw_mci_hi6220_parse_dt(struct dw_mci *host) +{ + struct k3_priv *priv; + + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->reg = syscon_regmap_lookup_by_phandle(host->dev->of_node, + "hisilicon,peripheral-syscon"); + if (IS_ERR(priv->reg)) + priv->reg = NULL; + + host->priv = priv; + return 0; +} + +static int dw_mci_hi6220_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct dw_mci_slot *slot = mmc_priv(mmc); + struct k3_priv *priv; + struct dw_mci *host; + int min_uv, max_uv; + int ret; + + host = slot->host; + priv = host->priv; + + if (!priv || !priv->reg) + return 0; + + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + ret = regmap_update_bits(priv->reg, AO_SCTRL_CTRL3, + AO_SCTRL_SEL18, 0); + min_uv = 3000000; + max_uv = 3000000; + } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + ret = regmap_update_bits(priv->reg, AO_SCTRL_CTRL3, + AO_SCTRL_SEL18, AO_SCTRL_SEL18); + min_uv = 1800000; + max_uv = 1800000; + } else { + dev_dbg(host->dev, "voltage not supported\n"); + return -EINVAL; + } + + if (ret) { + dev_dbg(host->dev, "switch voltage failed\n"); + return ret; + } + + if (IS_ERR_OR_NULL(mmc->supply.vqmmc)) + return 0; + + ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv); + if (ret) { + dev_dbg(host->dev, "Regulator set error %d: %d - %d\n", + ret, min_uv, max_uv); + return ret; + } + + return 0; +} + +static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + int ret; + unsigned int clock; + + clock = (ios->clock <= 25000000) ? 25000000 : ios->clock; + + ret = clk_set_rate(host->biu_clk, clock); + if (ret) + dev_warn(host->dev, "failed to set rate %uHz\n", clock); + + host->bus_hz = clk_get_rate(host->biu_clk); +} + +static const struct dw_mci_drv_data hi6220_data = { + .switch_voltage = dw_mci_hi6220_switch_voltage, + .set_ios = dw_mci_hi6220_set_ios, + .parse_dt = dw_mci_hi6220_parse_dt, +}; + static const struct of_device_id dw_mci_k3_match[] = { { .compatible = "hisilicon,hi4511-dw-mshc", .data = &k3_drv_data, }, + { .compatible = "hisilicon,hi6220-dw-mshc", .data = &hi6220_data, }, {}, }; MODULE_DEVICE_TABLE(of, dw_mci_k3_match); -- cgit v1.2.3 From eff8f2f5df1c509c873cdc70c84eb2ee75b41e65 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 6 May 2015 20:31:22 +0200 Subject: mmc: dw_mmc: Use core to handle absent write protect line Use the new MMC_CAP2_NO_WRITE_PROTECT to let the core handle the case where no write protect line is present instead of having custom driver code to handle it. dw_mci_of_get_slot_quirks() is slightly refactored to directly modify the mmc_host capabilities instead of returning a quirk mask. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 53 +++++++++++++++------------------------------- drivers/mmc/host/dw_mmc.h | 3 --- include/linux/mmc/dw_mmc.h | 6 ------ 3 files changed, 17 insertions(+), 45 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index ce66565d50ef..55179f1001fb 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1282,10 +1282,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) int gpio_ro = mmc_gpio_get_ro(mmc); /* Use platform get_ro function, else try on board write protect */ - if ((slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) || - (slot->host->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT)) - read_only = 0; - else if (!IS_ERR_VALUE(gpio_ro)) + if (!IS_ERR_VALUE(gpio_ro)) read_only = gpio_ro; else read_only = @@ -2284,9 +2281,10 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) } #ifdef CONFIG_OF -/* given a slot id, find out the device node representing that slot */ -static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) +/* given a slot, find out the device node representing that slot */ +static struct device_node *dw_mci_of_find_slot_node(struct dw_mci_slot *slot) { + struct device *dev = slot->mmc->parent; struct device_node *np; const __be32 *addr; int len; @@ -2298,42 +2296,28 @@ static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) addr = of_get_property(np, "reg", &len); if (!addr || (len < sizeof(int))) continue; - if (be32_to_cpup(addr) == slot) + if (be32_to_cpup(addr) == slot->id) return np; } return NULL; } -static struct dw_mci_of_slot_quirks { - char *quirk; - int id; -} of_slot_quirks[] = { - { - .quirk = "disable-wp", - .id = DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT, - }, -}; - -static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) +static void dw_mci_slot_of_parse(struct dw_mci_slot *slot) { - struct device_node *np = dw_mci_of_find_slot_node(dev, slot); - int quirks = 0; - int idx; + struct device_node *np = dw_mci_of_find_slot_node(slot); - /* get quirks */ - for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++) - if (of_get_property(np, of_slot_quirks[idx].quirk, NULL)) { - dev_warn(dev, "Slot quirk %s is deprecated\n", - of_slot_quirks[idx].quirk); - quirks |= of_slot_quirks[idx].id; - } + if (!np) + return; - return quirks; + if (of_property_read_bool(np, "disable-wp")) { + slot->mmc->caps2 |= MMC_CAP2_NO_WRITE_PROTECT; + dev_warn(slot->mmc->parent, + "Slot quirk 'disable-wp' is deprecated\n"); + } } #else /* CONFIG_OF */ -static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) +static void dw_mci_slot_of_parse(struct dw_mci_slot *slot) { - return 0; } #endif /* CONFIG_OF */ @@ -2356,8 +2340,6 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) slot->host = host; host->slot[id] = slot; - slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id); - mmc->ops = &dw_mci_ops; if (of_property_read_u32_array(host->dev->of_node, "clock-freq-min-max", freq, 2)) { @@ -2395,6 +2377,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (host->pdata->caps2) mmc->caps2 = host->pdata->caps2; + dw_mci_slot_of_parse(slot); + ret = mmc_of_parse(mmc); if (ret) goto err_host_allocated; @@ -2622,9 +2606,6 @@ static struct dw_mci_of_quirks { { .quirk = "broken-cd", .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, - }, { - .quirk = "disable-wp", - .id = DW_MCI_QUIRK_NO_WRITE_PROTECT, }, }; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index c7236170bf98..8ce4674730a6 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -227,7 +227,6 @@ extern int dw_mci_resume(struct dw_mci *host); * struct dw_mci_slot - MMC slot state * @mmc: The mmc_host representing this slot. * @host: The MMC controller this slot is using. - * @quirks: Slot-level quirks (DW_MCI_SLOT_QUIRK_XXX) * @ctype: Card type for this slot. * @mrq: mmc_request currently being processed or waiting to be * processed, or NULL when the slot is idle. @@ -245,8 +244,6 @@ struct dw_mci_slot { struct mmc_host *mmc; struct dw_mci *host; - int quirks; - u32 ctype; struct mmc_request *mrq; diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index 12111993a317..5be97676f1fa 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -226,12 +226,6 @@ struct dw_mci_dma_ops { #define DW_MCI_QUIRK_HIGHSPEED BIT(2) /* Unreliable card detection */ #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) -/* No write protect */ -#define DW_MCI_QUIRK_NO_WRITE_PROTECT BIT(4) - -/* Slot level quirks */ -/* This slot has no write protect */ -#define DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT BIT(0) struct dma_pdata; -- cgit v1.2.3 From 7026fd663b9032aed092a40209e959d414ce53b6 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Thu, 14 May 2015 16:59:44 +0800 Subject: mmc: dw_mmc: k3: Fix modalias to make module auto-loading work Make the modalias match driver name, this is required to make module auto-loading work. Signed-off-by: Zhangfei Gao Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-k3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 137670b74faa..63c2e2ed1288 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -193,4 +193,4 @@ module_platform_driver(dw_mci_k3_pltfm_driver); MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:dwmmc-k3"); +MODULE_ALIAS("platform:dwmmc_k3"); -- cgit v1.2.3 From 2fc546fd8ed0616bf9ec3172be9d0cbc7052a071 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Thu, 14 May 2015 16:59:45 +0800 Subject: mmc: dw_mmc: exynos: Fix modalias to make module auto-loading work Make the modalias match driver name, this is required to make module auto-loading work. Signed-off-by: Zhangfei Gao Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index e761eb1b1441..1e75309898b7 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -556,4 +556,4 @@ module_platform_driver(dw_mci_exynos_pltfm_driver); MODULE_DESCRIPTION("Samsung Specific DW-MSHC Driver Extension"); MODULE_AUTHOR("Thomas Abraham Date: Thu, 14 May 2015 16:59:46 +0800 Subject: mmc: dw_mmc: rockchip: Fix modalias to make module auto-loading work Make the modalias match driver name, this is required to make module auto-loading work. Signed-off-by: Zhangfei Gao CC: Doug Anderson Reviewed-by: Doug Anderson Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-rockchip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index dbf166f94f1b..de15121bba7d 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -153,5 +153,5 @@ module_platform_driver(dw_mci_rockchip_pltfm_driver); MODULE_AUTHOR("Addy Ke "); MODULE_DESCRIPTION("Rockchip Specific DW-MSHC Driver Extension"); -MODULE_ALIAS("platform:dwmmc-rockchip"); +MODULE_ALIAS("platform:dwmmc_rockchip"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From e6ac184bddf89be37644c066b3fc819b91f54d94 Mon Sep 17 00:00:00 2001 From: Zhangfei Gao Date: Thu, 14 May 2015 16:59:47 +0800 Subject: mmc: sdhci-st: Fix modalias to make module auto-loading work Make the modalias match driver name, this is required to make module auto-loading work. Signed-off-by: Zhangfei Gao CC: Peter Griffin Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-st.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index 682f2bb0f4bf..969c2b0d57fd 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -509,4 +509,4 @@ module_platform_driver(sdhci_st_driver); MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs"); MODULE_AUTHOR("Giuseppe Cavallaro "); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:st-sdhci"); +MODULE_ALIAS("platform:sdhci-st"); -- cgit v1.2.3 From 7c5209c315ea0f3102413ed1d6309be94b1e792f Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Tue, 12 May 2015 14:46:11 -0700 Subject: mmc: core: Increase delay for voltage to stabilize from 3.3V to 1.8V Since the regulator used for the SDMMC IO voltage is not expected to draw a lot of current, most systems will probably use an inexpensive LDO for it. LDO regulators apparently have the feature that they don't actively drive the voltage down--they wait for other components in the system to drag the voltage down. Thus they will transition faster under heavy loads and slower under light loads. During an SDMMC voltage change from 3.3V to 1.8V, we are almost certainly under a light load. To be specific: * The regulator is hooked through pulls to CMD0-3 and DAT. Probably the CMD pulls are something like 47K and the DAT is something like 10K. * The card is supposed to be driving DAT0-3 low during voltage change which will draw _some_ current, but not a lot. * The regulator is also provided to the SDMMC host controller, but the SDMMC host controller is in open drain mode during the voltage change and so shouldn't be drawing much current. In order to keep the SDMMC host working properly (or for noise reasons), there might also be a capacitor attached to the SDMMC IO regulator. This also will have the effect of slowing down transitions of the regulator, especially under light loads. From experimental evidence, we've seen the voltage change fail if the card doesn't detect that the voltage fell to less than about 2.3V when we turn on the clock. On one device (that admittedly had a 47K CMD pullup instead of a 10K CMD pullup) we saw that the voltage was just about 2.3V after 5ms and thus the voltage change would sometimes fail. Doubling the delay gave margin and made the voltage change work 100% of the time, despite the slightly weaker CMD pull. At the moment submitting this as an RFC patch since my problem _could_ be fixed by increasing the pull strength (or using a smaller capacitor). However being a little bit more lenient to strange hardware could also be a good thing. Signed-off-by: Doug Anderson Acked-by: Mark Brown Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 8c61ddd7748c..a262a0bf2f86 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1588,8 +1588,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr) goto power_cycle; } - /* Keep clock gated for at least 5 ms */ - mmc_delay(5); + /* Keep clock gated for at least 10 ms, though spec only says 5 ms */ + mmc_delay(10); host->ios.clock = clock; mmc_set_ios(host); -- cgit v1.2.3 From 75fda77b258f9aa90ce27fd7c8be2ecbdb7fc5d4 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 18 May 2015 19:52:58 +0200 Subject: mmc: mmc-test: use swap() in mmc_test_nonblock_transfer() Use kernel.h macro definition. Signed-off-by: Fabian Frederick Signed-off-by: Ulf Hansson --- drivers/mmc/card/mmc_test.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c index 5e90b3f2d57c..b78cf5d403a3 100644 --- a/drivers/mmc/card/mmc_test.c +++ b/drivers/mmc/card/mmc_test.c @@ -820,9 +820,7 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test, mmc_test_nonblock_reset(&mrq1, &cmd1, &stop1, &data1); } - done_areq = cur_areq; - cur_areq = other_areq; - other_areq = done_areq; + swap(cur_areq, other_areq); dev_addr += blocks; } -- cgit v1.2.3 From d9fbe003ead7205cfd6e623f90f3eb9a56a0d445 Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Fri, 15 May 2015 10:46:02 +0800 Subject: powerpc/dts: add eSDHC compatible list Add eSDHC compatible list for P2041/P3041/P4080/P5020/P5040. Signed-off-by: Yangbo Lu Acked-by: Scott Wood Signed-off-by: Ulf Hansson --- arch/powerpc/boot/dts/fsl/p2041si-post.dtsi | 1 + arch/powerpc/boot/dts/fsl/p3041si-post.dtsi | 1 + arch/powerpc/boot/dts/fsl/p4080si-post.dtsi | 1 + arch/powerpc/boot/dts/fsl/p5020si-post.dtsi | 1 + arch/powerpc/boot/dts/fsl/p5040si-post.dtsi | 1 + 5 files changed, 5 insertions(+) diff --git a/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi b/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi index f2feacfd9a25..b6a0e88ee5ce 100644 --- a/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p2041si-post.dtsi @@ -370,6 +370,7 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + compatible = "fsl,p2041-esdhc", "fsl,esdhc"; fsl,iommu-parent = <&pamu1>; fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ sdhci,auto-cmd12; diff --git a/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi b/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi index d6fea37395ad..cf18f7bf824f 100644 --- a/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p3041si-post.dtsi @@ -397,6 +397,7 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + compatible = "fsl,p3041-esdhc", "fsl,esdhc"; fsl,iommu-parent = <&pamu1>; fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ sdhci,auto-cmd12; diff --git a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi index 89482c9b2301..90431c0b53ad 100644 --- a/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p4080si-post.dtsi @@ -469,6 +469,7 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + compatible = "fsl,p4080-esdhc", "fsl,esdhc"; fsl,iommu-parent = <&pamu1>; fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ voltage-ranges = <3300 3300>; diff --git a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi index 6e04851e2fc9..8be61d11349e 100644 --- a/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p5020si-post.dtsi @@ -384,6 +384,7 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + compatible = "fsl,p5020-esdhc", "fsl,esdhc"; fsl,iommu-parent = <&pamu1>; fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ sdhci,auto-cmd12; diff --git a/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi b/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi index 5e44dfa1e1a5..48e232f2d50d 100644 --- a/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi +++ b/arch/powerpc/boot/dts/fsl/p5040si-post.dtsi @@ -362,6 +362,7 @@ /include/ "qoriq-esdhc-0.dtsi" sdhc@114000 { + compatible = "fsl,p5040-esdhc", "fsl,esdhc"; fsl,iommu-parent = <&pamu2>; fsl,liodn-reg = <&guts 0x530>; /* eSDHCLIODNR */ sdhci,auto-cmd12; -- cgit v1.2.3 From 43e943a04049d8f4ee96a940517a5fcb0f558ad2 Mon Sep 17 00:00:00 2001 From: Petri Gynther Date: Wed, 20 May 2015 14:35:00 -0700 Subject: mmc: sdhci: fix driver type B and D handling in sdhci_do_set_ios() sdhci_do_set_ios() doesn't currently program SDHCI_HOST_CONTROL2 register correctly when host->preset_enabled == false. Add code to handle the missing cases MMC_SET_DRIVER_TYPE_B and MMC_SET_DRIVER_TYPE_D. Signed-off-by: Petri Gynther Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 58c1770e879d..b35ec79e7205 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1514,8 +1514,17 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) ctrl_2 &= ~SDHCI_CTRL_DRV_TYPE_MASK; if (ios->drv_type == MMC_SET_DRIVER_TYPE_A) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_A; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_B) + ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B; else if (ios->drv_type == MMC_SET_DRIVER_TYPE_C) ctrl_2 |= SDHCI_CTRL_DRV_TYPE_C; + else if (ios->drv_type == MMC_SET_DRIVER_TYPE_D) + ctrl_2 |= SDHCI_CTRL_DRV_TYPE_D; + else { + pr_warn("%s: invalid driver type, default to " + "driver type B\n", mmc_hostname(mmc)); + ctrl_2 |= SDHCI_CTRL_DRV_TYPE_B; + } sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } else { -- cgit v1.2.3 From 585c3a5a0bccad2f52a37345217db9ab5b5a3273 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 14 May 2015 07:21:18 +0000 Subject: mmc: sh_mmcif: add sh_mmcif_host_to_dev() macro and use it. Current sh_mmcif driver is directly using &host->pd->dev in all place. It is not big problem, but it is unreadable, and it can be cause of future bug. This patch adds new sh_mmcif_host_to_dev() and use it. Signed-off-by: Kuninori Morimoto Tested-by: Keita Kobayashi Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 114 +++++++++++++++++++++++++++----------------- 1 file changed, 71 insertions(+), 43 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 068b9a627417..29835aedd1d2 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -262,6 +262,8 @@ static const struct of_device_id mmcif_of_match[] = { }; MODULE_DEVICE_TABLE(of, mmcif_of_match); +#define sh_mmcif_host_to_dev(host) (&host->pd->dev) + static inline void sh_mmcif_bitset(struct sh_mmcif_host *host, unsigned int reg, u32 val) { @@ -278,11 +280,12 @@ static void mmcif_dma_complete(void *arg) { struct sh_mmcif_host *host = arg; struct mmc_request *mrq = host->mrq; + struct device *dev = sh_mmcif_host_to_dev(host); - dev_dbg(&host->pd->dev, "Command completed\n"); + dev_dbg(dev, "Command completed\n"); if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion!\n", - dev_name(&host->pd->dev))) + dev_name(dev))) return; complete(&host->dma_complete); @@ -294,6 +297,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) struct scatterlist *sg = data->sg; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_rx; + struct device *dev = sh_mmcif_host_to_dev(host); dma_cookie_t cookie = -EINVAL; int ret; @@ -312,7 +316,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN); dma_async_issue_pending(chan); } - dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n", + dev_dbg(dev, "%s(): mapped %d -> %d, cookie %d\n", __func__, data->sg_len, ret, cookie); if (!desc) { @@ -328,12 +332,12 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) host->chan_tx = NULL; dma_release_channel(chan); } - dev_warn(&host->pd->dev, + dev_warn(dev, "DMA failed: %d, falling back to PIO\n", ret); sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); } - dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__, + dev_dbg(dev, "%s(): desc %p, cookie %d, sg[%d]\n", __func__, desc, cookie, data->sg_len); } @@ -343,6 +347,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) struct scatterlist *sg = data->sg; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *chan = host->chan_tx; + struct device *dev = sh_mmcif_host_to_dev(host); dma_cookie_t cookie = -EINVAL; int ret; @@ -361,7 +366,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAWEN); dma_async_issue_pending(chan); } - dev_dbg(&host->pd->dev, "%s(): mapped %d -> %d, cookie %d\n", + dev_dbg(dev, "%s(): mapped %d -> %d, cookie %d\n", __func__, data->sg_len, ret, cookie); if (!desc) { @@ -377,12 +382,12 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) host->chan_rx = NULL; dma_release_channel(chan); } - dev_warn(&host->pd->dev, + dev_warn(dev, "DMA failed: %d, falling back to PIO\n", ret); sh_mmcif_bitclr(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN | BUF_ACC_DMAWEN); } - dev_dbg(&host->pd->dev, "%s(): desc %p, cookie %d\n", __func__, + dev_dbg(dev, "%s(): desc %p, cookie %d\n", __func__, desc, cookie); } @@ -395,6 +400,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, struct dma_chan *chan; void *slave_data = NULL; struct resource *res; + struct device *dev = sh_mmcif_host_to_dev(host); dma_cap_mask_t mask; int ret; @@ -407,10 +413,10 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, (void *)pdata->slave_id_rx; chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, - slave_data, &host->pd->dev, + slave_data, dev, direction == DMA_MEM_TO_DEV ? "tx" : "rx"); - dev_dbg(&host->pd->dev, "%s: %s: got channel %p\n", __func__, + dev_dbg(dev, "%s: %s: got channel %p\n", __func__, direction == DMA_MEM_TO_DEV ? "TX" : "RX", chan); if (!chan) @@ -440,12 +446,13 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, static void sh_mmcif_request_dma(struct sh_mmcif_host *host, struct sh_mmcif_plat_data *pdata) { + struct device *dev = sh_mmcif_host_to_dev(host); host->dma_active = false; if (pdata) { if (pdata->slave_id_tx <= 0 || pdata->slave_id_rx <= 0) return; - } else if (!host->pd->dev.of_node) { + } else if (!dev->of_node) { return; } @@ -481,7 +488,8 @@ static void sh_mmcif_release_dma(struct sh_mmcif_host *host) static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) { - struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; + struct device *dev = sh_mmcif_host_to_dev(host); + struct sh_mmcif_plat_data *p = dev->platform_data; bool sup_pclk = p ? p->sup_pclk : false; unsigned int current_clk = clk_get_rate(host->clk); @@ -520,6 +528,7 @@ static void sh_mmcif_sync_reset(struct sh_mmcif_host *host) static int sh_mmcif_error_manage(struct sh_mmcif_host *host) { + struct device *dev = sh_mmcif_host_to_dev(host); u32 state1, state2; int ret, timeout; @@ -527,8 +536,8 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) state1 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS1); state2 = sh_mmcif_readl(host->addr, MMCIF_CE_HOST_STS2); - dev_dbg(&host->pd->dev, "ERR HOST_STS1 = %08x\n", state1); - dev_dbg(&host->pd->dev, "ERR HOST_STS2 = %08x\n", state2); + dev_dbg(dev, "ERR HOST_STS1 = %08x\n", state1); + dev_dbg(dev, "ERR HOST_STS2 = %08x\n", state2); if (state1 & STS1_CMDSEQ) { sh_mmcif_bitset(host, MMCIF_CE_CMD_CTRL, CMD_CTRL_BREAK); @@ -540,25 +549,25 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host) mdelay(1); } if (!timeout) { - dev_err(&host->pd->dev, + dev_err(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"); + dev_dbg(dev, "Forced end of command sequence\n"); return -EIO; } if (state2 & STS2_CRC_ERR) { - dev_err(&host->pd->dev, " CRC error: state %u, wait %u\n", + dev_err(dev, " CRC error: state %u, wait %u\n", host->state, host->wait_for); ret = -EIO; } else if (state2 & STS2_TIMEOUT_ERR) { - dev_err(&host->pd->dev, " Timeout: state %u, wait %u\n", + dev_err(dev, " Timeout: state %u, wait %u\n", host->state, host->wait_for); ret = -ETIMEDOUT; } else { - dev_dbg(&host->pd->dev, " End/Index error: state %u, wait %u\n", + dev_dbg(dev, " End/Index error: state %u, wait %u\n", host->state, host->wait_for); ret = -EIO; } @@ -599,13 +608,14 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host, static bool sh_mmcif_read_block(struct sh_mmcif_host *host) { + struct device *dev = sh_mmcif_host_to_dev(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); - dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); + dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } @@ -640,13 +650,14 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host, static bool sh_mmcif_mread_block(struct sh_mmcif_host *host) { + struct device *dev = sh_mmcif_host_to_dev(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); - dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); + dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } @@ -677,13 +688,14 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host, static bool sh_mmcif_write_block(struct sh_mmcif_host *host) { + struct device *dev = sh_mmcif_host_to_dev(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); - dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); + dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } @@ -718,13 +730,14 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host, static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host) { + struct device *dev = sh_mmcif_host_to_dev(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); - dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, data->error); + dev_dbg(dev, "%s(): %d\n", __func__, data->error); return false; } @@ -762,6 +775,7 @@ 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 device *dev = sh_mmcif_host_to_dev(host); struct mmc_data *data = mrq->data; struct mmc_command *cmd = mrq->cmd; u32 opc = cmd->opcode; @@ -781,7 +795,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, tmp |= CMD_SET_RTYP_17B; break; default: - dev_err(&host->pd->dev, "Unsupported response type.\n"); + dev_err(dev, "Unsupported response type.\n"); break; } switch (opc) { @@ -809,7 +823,7 @@ static u32 sh_mmcif_set_cmd(struct sh_mmcif_host *host, tmp |= CMD_SET_DATW_8; break; default: - dev_err(&host->pd->dev, "Unsupported bus width.\n"); + dev_err(dev, "Unsupported bus width.\n"); break; } switch (host->timing) { @@ -852,6 +866,8 @@ 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 device *dev = sh_mmcif_host_to_dev(host); + switch (opc) { case MMC_READ_MULTIPLE_BLOCK: sh_mmcif_multi_read(host, mrq); @@ -867,7 +883,7 @@ static int sh_mmcif_data_trans(struct sh_mmcif_host *host, sh_mmcif_single_read(host, mrq); return 0; default: - dev_err(&host->pd->dev, "Unsupported CMD%d\n", opc); + dev_err(dev, "Unsupported CMD%d\n", opc); return -EINVAL; } } @@ -924,6 +940,8 @@ 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 device *dev = sh_mmcif_host_to_dev(host); + switch (mrq->cmd->opcode) { case MMC_READ_MULTIPLE_BLOCK: sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12DRE); @@ -932,7 +950,7 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MCMD12RBE); break; default: - dev_err(&host->pd->dev, "unsupported stop cmd\n"); + dev_err(dev, "unsupported stop cmd\n"); mrq->stop->error = sh_mmcif_error_manage(host); return; } @@ -943,11 +961,13 @@ static void sh_mmcif_stop_cmd(struct sh_mmcif_host *host, static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct sh_mmcif_host *host = mmc_priv(mmc); + struct device *dev = sh_mmcif_host_to_dev(host); unsigned long flags; spin_lock_irqsave(&host->lock, flags); if (host->state != STATE_IDLE) { - dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state); + dev_dbg(dev, "%s() rejected, state %u\n", + __func__, host->state); spin_unlock_irqrestore(&host->lock, flags); mrq->cmd->error = -EAGAIN; mmc_request_done(mmc, mrq); @@ -999,11 +1019,13 @@ static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios) static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sh_mmcif_host *host = mmc_priv(mmc); + struct device *dev = sh_mmcif_host_to_dev(host); unsigned long flags; spin_lock_irqsave(&host->lock, flags); if (host->state != STATE_IDLE) { - dev_dbg(&host->pd->dev, "%s() rejected, state %u\n", __func__, host->state); + dev_dbg(dev, "%s() rejected, state %u\n", + __func__, host->state); spin_unlock_irqrestore(&host->lock, flags); return; } @@ -1014,7 +1036,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->power_mode == MMC_POWER_UP) { if (!host->card_present) { /* See if we also get DMA */ - sh_mmcif_request_dma(host, host->pd->dev.platform_data); + sh_mmcif_request_dma(host, dev->platform_data); host->card_present = true; } sh_mmcif_set_power(host, ios); @@ -1028,7 +1050,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } if (host->power) { - pm_runtime_put_sync(&host->pd->dev); + pm_runtime_put_sync(dev); clk_disable_unprepare(host->clk); host->power = false; if (ios->power_mode == MMC_POWER_OFF) @@ -1042,7 +1064,7 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!host->power) { clk_prepare_enable(host->clk); - pm_runtime_get_sync(&host->pd->dev); + pm_runtime_get_sync(dev); host->power = true; sh_mmcif_sync_reset(host); } @@ -1057,7 +1079,8 @@ static void sh_mmcif_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) static int sh_mmcif_get_cd(struct mmc_host *mmc) { struct sh_mmcif_host *host = mmc_priv(mmc); - struct sh_mmcif_plat_data *p = host->pd->dev.platform_data; + struct device *dev = sh_mmcif_host_to_dev(host); + struct sh_mmcif_plat_data *p = dev->platform_data; int ret = mmc_gpio_get_cd(mmc); if (ret >= 0) @@ -1079,6 +1102,7 @@ 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; + struct device *dev = sh_mmcif_host_to_dev(host); long time; if (host->sd_error) { @@ -1092,7 +1116,7 @@ static bool sh_mmcif_end_cmd(struct sh_mmcif_host *host) cmd->error = sh_mmcif_error_manage(host); break; } - dev_dbg(&host->pd->dev, "CMD%d error %d\n", + dev_dbg(dev, "CMD%d error %d\n", cmd->opcode, cmd->error); host->sd_error = false; return false; @@ -1172,6 +1196,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; struct mmc_request *mrq; + struct device *dev = sh_mmcif_host_to_dev(host); bool wait = false; unsigned long flags; int wait_work; @@ -1186,7 +1211,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) mrq = host->mrq; if (!mrq) { - dev_dbg(&host->pd->dev, "IRQ thread state %u, wait %u: NULL mrq!\n", + dev_dbg(dev, "IRQ thread state %u, wait %u: NULL mrq!\n", host->state, host->wait_for); mutex_unlock(&host->thread_lock); return IRQ_HANDLED; @@ -1224,7 +1249,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) case MMCIF_WAIT_FOR_STOP: if (host->sd_error) { mrq->stop->error = sh_mmcif_error_manage(host); - dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->stop->error); + dev_dbg(dev, "%s(): %d\n", __func__, mrq->stop->error); break; } sh_mmcif_get_cmd12response(host, mrq->stop); @@ -1234,7 +1259,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) case MMCIF_WAIT_FOR_WRITE_END: if (host->sd_error) { mrq->data->error = sh_mmcif_error_manage(host); - dev_dbg(&host->pd->dev, "%s(): %d\n", __func__, mrq->data->error); + dev_dbg(dev, "%s(): %d\n", __func__, mrq->data->error); } break; default: @@ -1277,6 +1302,7 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; + struct device *dev = sh_mmcif_host_to_dev(host); u32 state, mask; state = sh_mmcif_readl(host->addr, MMCIF_CE_INT); @@ -1288,22 +1314,22 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) sh_mmcif_bitclr(host, MMCIF_CE_INT_MASK, state & MASK_CLEAN); if (state & ~MASK_CLEAN) - dev_dbg(&host->pd->dev, "IRQ state = 0x%08x incompletely cleared\n", + dev_dbg(dev, "IRQ state = 0x%08x incompletely cleared\n", state); if (state & INT_ERR_STS || state & ~INT_ALL) { host->sd_error = true; - dev_dbg(&host->pd->dev, "int err state = 0x%08x\n", state); + dev_dbg(dev, "int err state = 0x%08x\n", state); } if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) { if (!host->mrq) - dev_dbg(&host->pd->dev, "NULL IRQ state = 0x%08x\n", state); + dev_dbg(dev, "NULL IRQ state = 0x%08x\n", state); 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); + dev_dbg(dev, "Unexpected IRQ 0x%x\n", state); } return IRQ_HANDLED; @@ -1314,6 +1340,7 @@ 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; + struct device *dev = sh_mmcif_host_to_dev(host); unsigned long flags; if (host->dying) @@ -1326,7 +1353,7 @@ static void mmcif_timeout_work(struct work_struct *work) return; } - dev_err(&host->pd->dev, "Timeout waiting for %u on CMD%u\n", + dev_err(dev, "Timeout waiting for %u on CMD%u\n", host->wait_for, mrq->cmd->opcode); host->state = STATE_TIMEOUT; @@ -1363,7 +1390,8 @@ static void mmcif_timeout_work(struct work_struct *work) static void sh_mmcif_init_ocr(struct sh_mmcif_host *host) { - struct sh_mmcif_plat_data *pd = host->pd->dev.platform_data; + struct device *dev = sh_mmcif_host_to_dev(host); + struct sh_mmcif_plat_data *pd = dev->platform_data; struct mmc_host *mmc = host->mmc; mmc_regulator_get_supply(mmc); -- cgit v1.2.3 From 1b1a694d53cbe42eba4feb260eba4acc316c44f0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 14 May 2015 07:21:36 +0000 Subject: mmc: sh_mmcif: use sh_mmcif_xxx prefix for all functions Current sh_mmcif driver is using sh_mmcif_xxx and mmcif_xxx for functions. This patch used sh_mmcif_xxx for all functions. Signed-off-by: Kuninori Morimoto Tested-by: Keita Kobayashi Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 29835aedd1d2..e13c5f41fac4 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -205,14 +205,14 @@ #define CLKDEV_MMC_DATA 20000000 /* 20MHz */ #define CLKDEV_INIT 400000 /* 400 KHz */ -enum mmcif_state { +enum sh_mmcif_state { STATE_IDLE, STATE_REQUEST, STATE_IOS, STATE_TIMEOUT, }; -enum mmcif_wait_for { +enum sh_mmcif_wait_for { MMCIF_WAIT_FOR_REQUEST, MMCIF_WAIT_FOR_CMD, MMCIF_WAIT_FOR_MREAD, @@ -237,8 +237,8 @@ struct sh_mmcif_host { void __iomem *addr; u32 *pio_ptr; spinlock_t lock; /* protect sh_mmcif_host::state */ - enum mmcif_state state; - enum mmcif_wait_for wait_for; + enum sh_mmcif_state state; + enum sh_mmcif_wait_for wait_for; struct delayed_work timeout_work; size_t blocksize; int sg_idx; @@ -256,11 +256,11 @@ struct sh_mmcif_host { bool dma_active; }; -static const struct of_device_id mmcif_of_match[] = { +static const struct of_device_id sh_mmcif_of_match[] = { { .compatible = "renesas,sh-mmcif" }, { } }; -MODULE_DEVICE_TABLE(of, mmcif_of_match); +MODULE_DEVICE_TABLE(of, sh_mmcif_of_match); #define sh_mmcif_host_to_dev(host) (&host->pd->dev) @@ -276,7 +276,7 @@ static inline void sh_mmcif_bitclr(struct sh_mmcif_host *host, writel(~val & readl(host->addr + reg), host->addr + reg); } -static void mmcif_dma_complete(void *arg) +static void sh_mmcif_dma_complete(void *arg) { struct sh_mmcif_host *host = arg; struct mmc_request *mrq = host->mrq; @@ -310,7 +310,7 @@ static void sh_mmcif_start_dma_rx(struct sh_mmcif_host *host) } if (desc) { - desc->callback = mmcif_dma_complete; + desc->callback = sh_mmcif_dma_complete; desc->callback_param = host; cookie = dmaengine_submit(desc); sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAREN); @@ -360,7 +360,7 @@ static void sh_mmcif_start_dma_tx(struct sh_mmcif_host *host) } if (desc) { - desc->callback = mmcif_dma_complete; + desc->callback = sh_mmcif_dma_complete; desc->callback_param = host; cookie = dmaengine_submit(desc); sh_mmcif_bitset(host, MMCIF_CE_BUF_ACC, BUF_ACC_DMAWEN); @@ -1327,7 +1327,7 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) if (!host->dma_active) return IRQ_WAKE_THREAD; else if (host->sd_error) - mmcif_dma_complete(host); + sh_mmcif_dma_complete(host); } else { dev_dbg(dev, "Unexpected IRQ 0x%x\n", state); } @@ -1335,7 +1335,7 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) return IRQ_HANDLED; } -static void mmcif_timeout_work(struct work_struct *work) +static void sh_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); @@ -1481,7 +1481,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) if (ret < 0) goto err_clk; - INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); + INIT_DELAYED_WORK(&host->timeout_work, sh_mmcif_timeout_work); sh_mmcif_sync_reset(host); sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); @@ -1587,7 +1587,7 @@ static struct platform_driver sh_mmcif_driver = { .driver = { .name = DRIVER_NAME, .pm = &sh_mmcif_dev_pm_ops, - .of_match_table = mmcif_of_match, + .of_match_table = sh_mmcif_of_match, }, }; -- cgit v1.2.3 From 89d49a706ca417c55c046d2580403be578cadaab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 14 May 2015 07:22:46 +0000 Subject: mmc: sh_mmcif: calculate best clock with parent clock MMCIF IP on R-Car series has parent clock which can be set several rate, and it was not implemented on old SH-Mobile series (= SH-Mobile series parent clock was fixed rate) R-Car series MMCIF can use more high speed access if it setups parent clock. This patch adds parent clock setup method. It will be used if DT has "max-frequency", and then, this driver assumes it is booted on R-Car Gen2 or later SoC. Because SH-Mobile series (which doesn't boot from DT) and R-Car series (which boots from DT) have different divider. Signed-off-by: Kuninori Morimoto Tested-by: Keita Kobayashi Signed-off-by: Ulf Hansson [Ulf: Silence compiler warning] --- .../devicetree/bindings/mmc/renesas,mmcif.txt | 3 + drivers/mmc/host/sh_mmcif.c | 84 +++++++++++++++++++--- 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt index 299081f94abd..d38942f6c5ae 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt +++ b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt @@ -18,6 +18,8 @@ Required properties: dma-names property. - dma-names: must contain "tx" for the transmit DMA channel and "rx" for the receive DMA channel. +- max-frequency: Maximum operating clock frequency, driver uses default clock + frequency if it is not set. Example: R8A7790 (R-Car H2) MMCIF0 @@ -29,4 +31,5 @@ Example: R8A7790 (R-Car H2) MMCIF0 clocks = <&mstp3_clks R8A7790_CLK_MMCIF0>; dmas = <&dmac0 0xd1>, <&dmac0 0xd2>; dma-names = "tx", "rx"; + max-frequency = <97500000>; }; diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index e13c5f41fac4..5a1fdd405b1a 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -224,6 +225,9 @@ enum sh_mmcif_wait_for { MMCIF_WAIT_FOR_STOP, }; +/* + * difference for each SoC + */ struct sh_mmcif_host { struct mmc_host *mmc; struct mmc_request *mrq; @@ -248,6 +252,7 @@ struct sh_mmcif_host { bool ccs_enable; /* Command Completion Signal support */ bool clk_ctrl2_enable; struct mutex thread_lock; + u32 clkdiv_map; /* see CE_CLK_CTRL::CLKDIV */ /* DMA support */ struct dma_chan *chan_rx; @@ -492,19 +497,55 @@ static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk) struct sh_mmcif_plat_data *p = dev->platform_data; bool sup_pclk = p ? p->sup_pclk : false; unsigned int current_clk = clk_get_rate(host->clk); + unsigned int clkdiv; sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); sh_mmcif_bitclr(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR); if (!clk) return; - if (sup_pclk && clk == current_clk) - sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_SUP_PCLK); - else - sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & - ((fls(DIV_ROUND_UP(current_clk, - clk) - 1) - 1) << 16)); + if (host->clkdiv_map) { + unsigned int freq, best_freq, myclk, div, diff_min, diff; + int i; + + clkdiv = 0; + diff_min = ~0; + best_freq = 0; + for (i = 31; i >= 0; i--) { + if (!((1 << i) & host->clkdiv_map)) + continue; + + /* + * clk = parent_freq / div + * -> parent_freq = clk x div + */ + + div = 1 << (i + 1); + freq = clk_round_rate(host->clk, clk * div); + myclk = freq / div; + diff = (myclk > clk) ? myclk - clk : clk - myclk; + + if (diff <= diff_min) { + best_freq = freq; + clkdiv = i; + diff_min = diff; + } + } + + dev_dbg(dev, "clk %u/%u (%u, 0x%x)\n", + (best_freq / (1 << (clkdiv + 1))), clk, + best_freq, clkdiv); + + clk_set_rate(host->clk, best_freq); + clkdiv = clkdiv << 16; + } else if (sup_pclk && clk == current_clk) { + clkdiv = CLK_SUP_PCLK; + } else { + clkdiv = (fls(DIV_ROUND_UP(current_clk, clk) - 1) - 1) << 16; + } + + sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_CLEAR & clkdiv); sh_mmcif_bitset(host, MMCIF_CE_CLK_CTRL, CLK_ENABLE); } @@ -1000,10 +1041,35 @@ static void sh_mmcif_request(struct mmc_host *mmc, struct mmc_request *mrq) static void sh_mmcif_clk_setup(struct sh_mmcif_host *host) { - unsigned int clk = clk_get_rate(host->clk); + struct device *dev = sh_mmcif_host_to_dev(host); + + if (host->mmc->f_max) { + unsigned int f_max, f_min = 0, f_min_old; + + f_max = host->mmc->f_max; + for (f_min_old = f_max; f_min_old > 2;) { + f_min = clk_round_rate(host->clk, f_min_old / 2); + if (f_min == f_min_old) + break; + f_min_old = f_min; + } + + /* + * This driver assumes this SoC is R-Car Gen2 or later + */ + host->clkdiv_map = 0x3ff; + + host->mmc->f_max = f_max / (1 << ffs(host->clkdiv_map)); + host->mmc->f_min = f_min / (1 << fls(host->clkdiv_map)); + } else { + unsigned int clk = clk_get_rate(host->clk); + + host->mmc->f_max = clk / 2; + host->mmc->f_min = clk / 512; + } - host->mmc->f_max = clk / 2; - host->mmc->f_min = clk / 512; + dev_dbg(dev, "clk max/min = %d/%d\n", + host->mmc->f_max, host->mmc->f_min); } static void sh_mmcif_set_power(struct sh_mmcif_host *host, struct mmc_ios *ios) -- cgit v1.2.3 From 75e8a2288c4fabd6c2f752e8fd3bf7f60be7d3a4 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:51 +0200 Subject: mmc: core: Reset driver type to default IO state variable drv_type could be set during card initialization. Consequently, it must be reset to the default value when setting the initial state. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a262a0bf2f86..95009f90ae07 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1184,6 +1184,7 @@ void mmc_set_initial_state(struct mmc_host *host) host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; host->ios.bus_width = MMC_BUS_WIDTH_1; host->ios.timing = MMC_TIMING_LEGACY; + host->ios.drv_type = 0; mmc_set_ios(host); } -- cgit v1.2.3 From b4f30a174e1fda8118eda038b5d8d5260db36ad5 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:52 +0200 Subject: mmc: core: Allow card drive strength to be different to host Initialization of UHS-I modes for SD and SDIO cards employs a callback to allow the host driver to choose a drive strength value. Currently that assumes the card drive strength and host driver type must be the same value. Change to let the callback make that decision and return both the card drive strength and host driver type. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 33 +++++++++++++-------------------- drivers/mmc/core/sdio.c | 43 ++++++++++++++++++------------------------- include/linux/mmc/host.h | 3 ++- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 8f6864a2a055..5edd7d8b033e 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -388,18 +388,9 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) { int host_drv_type = SD_DRIVER_TYPE_B; int card_drv_type = SD_DRIVER_TYPE_B; - int drive_strength; + int drive_strength, drv_type; int err; - /* - * If the host doesn't support any of the Driver Types A,C or D, - * or there is no board specific handler then default Driver - * Type B is used. - */ - if (!(card->host->caps & (MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C - | MMC_CAP_DRIVER_TYPE_D))) - return 0; - if (!card->host->ops->select_drive_strength) return 0; @@ -430,20 +421,22 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) mmc_host_clk_hold(card->host); drive_strength = card->host->ops->select_drive_strength( card->sw_caps.uhs_max_dtr, - host_drv_type, card_drv_type); + host_drv_type, card_drv_type, &drv_type); mmc_host_clk_release(card->host); - err = mmc_sd_switch(card, 1, 2, drive_strength, status); - if (err) - return err; - - if ((status[15] & 0xF) != drive_strength) { - pr_warn("%s: Problem setting drive strength!\n", - mmc_hostname(card->host)); - return 0; + if (drive_strength) { + err = mmc_sd_switch(card, 1, 2, drive_strength, status); + if (err) + return err; + if ((status[15] & 0xF) != drive_strength) { + pr_warn("%s: Problem setting drive strength!\n", + mmc_hostname(card->host)); + return 0; + } } - mmc_set_driver_type(card->host, drive_strength); + if (drv_type) + mmc_set_driver_type(card->host, drv_type); return 0; } diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 5c1423a3e7d7..9d87aeb7c752 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -404,21 +404,10 @@ static void sdio_select_driver_type(struct mmc_card *card) { int host_drv_type = SD_DRIVER_TYPE_B; int card_drv_type = SD_DRIVER_TYPE_B; - int drive_strength; + int drive_strength, drv_type; unsigned char card_strength; int err; - /* - * If the host doesn't support any of the Driver Types A,C or D, - * or there is no board specific handler then default Driver - * Type B is used. - */ - if (!(card->host->caps & - (MMC_CAP_DRIVER_TYPE_A | - MMC_CAP_DRIVER_TYPE_C | - MMC_CAP_DRIVER_TYPE_D))) - return; - if (!card->host->ops->select_drive_strength) return; @@ -448,23 +437,27 @@ static void sdio_select_driver_type(struct mmc_card *card) */ drive_strength = card->host->ops->select_drive_strength( card->sw_caps.uhs_max_dtr, - host_drv_type, card_drv_type); + host_drv_type, card_drv_type, &drv_type); - /* if error just use default for drive strength B */ - err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0, - &card_strength); - if (err) - return; + if (drive_strength) { + /* if error just use default for drive strength B */ + err = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_DRIVE_STRENGTH, 0, + &card_strength); + if (err) + return; - card_strength &= ~(SDIO_DRIVE_DTSx_MASK<host, drive_strength); + if (drv_type) + mmc_set_driver_type(card->host, drv_type); } diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 433eccb50838..da33d18c66c8 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -132,7 +132,8 @@ struct mmc_host_ops { /* Prepare HS400 target operating frequency depending host driver */ int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); - int (*select_drive_strength)(unsigned int max_dtr, int host_drv, int card_drv); + int (*select_drive_strength)(unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type); void (*hw_reset)(struct mmc_host *host); void (*card_event)(struct mmc_host *host); -- cgit v1.2.3 From fa021cef1af64cb4ba11c3c0910ef45085c58016 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:53 +0200 Subject: mmc: core: Simplify card drive strength mask Card drive strength selection uses a callback to which a mask of supported drive strengths is passed. Currently, the bits are checked against the values in the SD specifications. That is not necessary because the callback will anyway match the mask against a valid value. Simplify by taking the mask as is but still ensuring that the default mandatory value (type B) is always supported. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 12 ++---------- drivers/mmc/core/sdio.c | 12 ++---------- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 5edd7d8b033e..9b72ea6b3177 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -387,8 +387,7 @@ out: static int sd_select_driver_type(struct mmc_card *card, u8 *status) { int host_drv_type = SD_DRIVER_TYPE_B; - int card_drv_type = SD_DRIVER_TYPE_B; - int drive_strength, drv_type; + int card_drv_type, drive_strength, drv_type; int err; if (!card->host->ops->select_drive_strength) @@ -403,14 +402,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) if (card->host->caps & MMC_CAP_DRIVER_TYPE_D) host_drv_type |= SD_DRIVER_TYPE_D; - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A) - card_drv_type |= SD_DRIVER_TYPE_A; - - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C) - card_drv_type |= SD_DRIVER_TYPE_C; - - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D) - card_drv_type |= SD_DRIVER_TYPE_D; + card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B; /* * The drive strength that the hardware can support diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 9d87aeb7c752..ef82f3d029e8 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -403,8 +403,7 @@ static unsigned char host_drive_to_sdio_drive(int host_strength) static void sdio_select_driver_type(struct mmc_card *card) { int host_drv_type = SD_DRIVER_TYPE_B; - int card_drv_type = SD_DRIVER_TYPE_B; - int drive_strength, drv_type; + int card_drv_type, drive_strength, drv_type; unsigned char card_strength; int err; @@ -420,14 +419,7 @@ static void sdio_select_driver_type(struct mmc_card *card) if (card->host->caps & MMC_CAP_DRIVER_TYPE_D) host_drv_type |= SD_DRIVER_TYPE_D; - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_A) - card_drv_type |= SD_DRIVER_TYPE_A; - - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_C) - card_drv_type |= SD_DRIVER_TYPE_C; - - if (card->sw_caps.sd3_drv_type & SD_DRIVER_TYPE_D) - card_drv_type |= SD_DRIVER_TYPE_D; + card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B; /* * The drive strength that the hardware can support -- cgit v1.2.3 From f168359efbb99d6f8591bb666d6510bb78df2d07 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:54 +0200 Subject: mmc: core: Add 'card' to drive strength selection callback In preparation for supporting also eMMC drive strength, add the 'card' as a parameter so that the callback can distinguish different types of cards if necessary. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 2 +- drivers/mmc/core/sdio.c | 2 +- include/linux/mmc/host.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 9b72ea6b3177..63f9163b8dda 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -411,7 +411,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) * return what is possible given the options */ mmc_host_clk_hold(card->host); - drive_strength = card->host->ops->select_drive_strength( + drive_strength = card->host->ops->select_drive_strength(card, card->sw_caps.uhs_max_dtr, host_drv_type, card_drv_type, &drv_type); mmc_host_clk_release(card->host); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index ef82f3d029e8..d3d13047f316 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -427,7 +427,7 @@ static void sdio_select_driver_type(struct mmc_card *card) * information and let the hardware specific code * return what is possible given the options */ - drive_strength = card->host->ops->select_drive_strength( + drive_strength = card->host->ops->select_drive_strength(card, card->sw_caps.uhs_max_dtr, host_drv_type, card_drv_type, &drv_type); diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index da33d18c66c8..1369e54faeb7 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -132,7 +132,8 @@ struct mmc_host_ops { /* Prepare HS400 target operating frequency depending host driver */ int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios); - int (*select_drive_strength)(unsigned int max_dtr, int host_drv, + int (*select_drive_strength)(struct mmc_card *card, + unsigned int max_dtr, int host_drv, int card_drv, int *drv_type); void (*hw_reset)(struct mmc_host *host); void (*card_event)(struct mmc_host *host); -- cgit v1.2.3 From e23350b35deb77ef8e33c35dbb0ed1dab9e8ab86 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:55 +0200 Subject: mmc: core: Factor out common code in drive strength selection Make a new function out of common code used for drive strength selection. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/core.h | 2 ++ drivers/mmc/core/sd.c | 27 +++------------------------ drivers/mmc/core/sdio.c | 25 +++---------------------- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 95009f90ae07..9ad73f30f744 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1639,6 +1639,44 @@ void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type) mmc_host_clk_release(host); } +int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr, + int card_drv_type, int *drv_type) +{ + struct mmc_host *host = card->host; + int host_drv_type = SD_DRIVER_TYPE_B; + int drive_strength; + + *drv_type = 0; + + if (!host->ops->select_drive_strength) + return 0; + + /* Use SD definition of driver strength for hosts */ + if (host->caps & MMC_CAP_DRIVER_TYPE_A) + host_drv_type |= SD_DRIVER_TYPE_A; + + if (host->caps & MMC_CAP_DRIVER_TYPE_C) + host_drv_type |= SD_DRIVER_TYPE_C; + + if (host->caps & MMC_CAP_DRIVER_TYPE_D) + host_drv_type |= SD_DRIVER_TYPE_D; + + /* + * The drive strength that the hardware can support + * depends on the board design. Pass the appropriate + * information and let the hardware specific code + * return what is possible given the options + */ + mmc_host_clk_hold(host); + drive_strength = host->ops->select_drive_strength(card, max_dtr, + host_drv_type, + card_drv_type, + drv_type); + mmc_host_clk_release(host); + + return drive_strength; +} + /* * Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index e6f2de7074ad..1a22a82209b2 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -50,6 +50,8 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage, u32 ocr); int __mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); void mmc_set_timing(struct mmc_host *host, unsigned int timing); void mmc_set_driver_type(struct mmc_host *host, unsigned int drv_type); +int mmc_select_drive_strength(struct mmc_card *card, unsigned int max_dtr, + int card_drv_type, int *drv_type); void mmc_power_up(struct mmc_host *host, u32 ocr); void mmc_power_off(struct mmc_host *host); void mmc_power_cycle(struct mmc_host *host, u32 ocr); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 63f9163b8dda..9771b84db4b3 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -386,35 +386,14 @@ out: static int sd_select_driver_type(struct mmc_card *card, u8 *status) { - int host_drv_type = SD_DRIVER_TYPE_B; int card_drv_type, drive_strength, drv_type; int err; - if (!card->host->ops->select_drive_strength) - return 0; - - if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) - host_drv_type |= SD_DRIVER_TYPE_A; - - if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) - host_drv_type |= SD_DRIVER_TYPE_C; - - if (card->host->caps & MMC_CAP_DRIVER_TYPE_D) - host_drv_type |= SD_DRIVER_TYPE_D; - card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B; - /* - * The drive strength that the hardware can support - * depends on the board design. Pass the appropriate - * information and let the hardware specific code - * return what is possible given the options - */ - mmc_host_clk_hold(card->host); - drive_strength = card->host->ops->select_drive_strength(card, - card->sw_caps.uhs_max_dtr, - host_drv_type, card_drv_type, &drv_type); - mmc_host_clk_release(card->host); + drive_strength = mmc_select_drive_strength(card, + card->sw_caps.uhs_max_dtr, + card_drv_type, &drv_type); if (drive_strength) { err = mmc_sd_switch(card, 1, 2, drive_strength, status); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index d3d13047f316..73b091331c96 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -402,34 +402,15 @@ static unsigned char host_drive_to_sdio_drive(int host_strength) static void sdio_select_driver_type(struct mmc_card *card) { - int host_drv_type = SD_DRIVER_TYPE_B; int card_drv_type, drive_strength, drv_type; unsigned char card_strength; int err; - if (!card->host->ops->select_drive_strength) - return; - - if (card->host->caps & MMC_CAP_DRIVER_TYPE_A) - host_drv_type |= SD_DRIVER_TYPE_A; - - if (card->host->caps & MMC_CAP_DRIVER_TYPE_C) - host_drv_type |= SD_DRIVER_TYPE_C; - - if (card->host->caps & MMC_CAP_DRIVER_TYPE_D) - host_drv_type |= SD_DRIVER_TYPE_D; - card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B; - /* - * The drive strength that the hardware can support - * depends on the board design. Pass the appropriate - * information and let the hardware specific code - * return what is possible given the options - */ - drive_strength = card->host->ops->select_drive_strength(card, - card->sw_caps.uhs_max_dtr, - host_drv_type, card_drv_type, &drv_type); + drive_strength = mmc_select_drive_strength(card, + card->sw_caps.uhs_max_dtr, + card_drv_type, &drv_type); if (drive_strength) { /* if error just use default for drive strength B */ -- cgit v1.2.3 From 3853a042325e8f497c199020979c4fc824528c6e Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:56 +0200 Subject: mmc: core: Record card drive strength In preparation for adding drive strength support for eMMC, add drive_strength to struct mmc_card to record the card drive strength for UHS-I modes and HS200 / HS400. For eMMC this will be needed when switching between HS200 and HS400. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 3 +++ drivers/mmc/core/sdio.c | 3 +++ include/linux/mmc/card.h | 1 + 3 files changed, 7 insertions(+) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 9771b84db4b3..b99e25b9bcdc 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -389,6 +389,8 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) int card_drv_type, drive_strength, drv_type; int err; + card->drive_strength = 0; + card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B; drive_strength = mmc_select_drive_strength(card, @@ -404,6 +406,7 @@ static int sd_select_driver_type(struct mmc_card *card, u8 *status) mmc_hostname(card->host)); return 0; } + card->drive_strength = drive_strength; } if (drv_type) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 73b091331c96..b91abedcfdca 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -406,6 +406,8 @@ static void sdio_select_driver_type(struct mmc_card *card) unsigned char card_strength; int err; + card->drive_strength = 0; + card_drv_type = card->sw_caps.sd3_drv_type | SD_DRIVER_TYPE_B; drive_strength = mmc_select_drive_strength(card, @@ -427,6 +429,7 @@ static void sdio_select_driver_type(struct mmc_card *card) card_strength, NULL); if (err) return; + card->drive_strength = drive_strength; } if (drv_type) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 19f0175c0afa..2f073d555793 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -305,6 +305,7 @@ struct mmc_card { unsigned int sd_bus_speed; /* Bus Speed Mode set for the card */ unsigned int mmc_avail_type; /* supported device type by both host and card */ + unsigned int drive_strength; /* for UHS-I, HS200 or HS400 */ struct dentry *debugfs_root; struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ -- cgit v1.2.3 From b097e07f57930eda774c83aa46e8e401686d01dc Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:57 +0200 Subject: mmc: mmc: Read card's valid driver strength mask In preparation for supporing drive strength selection for eMMC, read the card's valid driver strengths. Note that though the SD spec uses the term "drive strength", the JEDEC eMMC spec uses the term "driver strength". Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 1 + include/linux/mmc/card.h | 1 + include/linux/mmc/mmc.h | 1 + 3 files changed, 3 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 03c94c2f23e1..9b808d1d6ca7 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -437,6 +437,7 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.raw_trim_mult = ext_csd[EXT_CSD_TRIM_MULT]; card->ext_csd.raw_partition_support = ext_csd[EXT_CSD_PARTITION_SUPPORT]; + card->ext_csd.raw_driver_strength = ext_csd[EXT_CSD_DRIVER_STRENGTH]; if (card->ext_csd.rev >= 4) { if (ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED] & EXT_CSD_PART_SETTING_COMPLETED) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 2f073d555793..4d3776d25925 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -97,6 +97,7 @@ struct mmc_ext_csd { u8 raw_erased_mem_count; /* 181 */ u8 raw_ext_csd_structure; /* 194 */ u8 raw_card_type; /* 196 */ + u8 raw_driver_strength; /* 197 */ u8 out_of_int_time; /* 198 */ u8 raw_pwr_cl_52_195; /* 200 */ u8 raw_pwr_cl_26_195; /* 201 */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 124f562118b8..4819cfbc3795 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -302,6 +302,7 @@ struct _mmc_csd { #define EXT_CSD_REV 192 /* RO */ #define EXT_CSD_STRUCTURE 194 /* RO */ #define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_DRIVER_STRENGTH 197 /* RO */ #define EXT_CSD_OUT_OF_INTERRUPT_TIME 198 /* RO */ #define EXT_CSD_PART_SWITCH_TIME 199 /* RO */ #define EXT_CSD_PWR_CL_52_195 200 /* RO */ -- cgit v1.2.3 From cc4f414c885cd04f7227ad9bcd6b18fd78d718d9 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:58 +0200 Subject: mmc: mmc: Add driver strength selection Add the ability to set eMMC driver strength for HS200 and HS400. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 45 ++++++++++++++++++++++++++++++++++++++------- include/linux/mmc/mmc.h | 3 +++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 9b808d1d6ca7..e519e3110a20 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1044,6 +1044,7 @@ static int mmc_select_hs400(struct mmc_card *card) { struct mmc_host *host = card->host; int err = 0; + u8 val; /* * HS400 mode requires 8-bit bus width @@ -1059,8 +1060,10 @@ static int mmc_select_hs400(struct mmc_card *card) mmc_set_timing(card->host, MMC_TIMING_MMC_HS); mmc_set_bus_speed(card); + val = EXT_CSD_TIMING_HS | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS, + EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, true, true, true); if (err) { @@ -1079,8 +1082,10 @@ static int mmc_select_hs400(struct mmc_card *card) return err; } + val = EXT_CSD_TIMING_HS400 | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS400, + EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, true, true, true); if (err) { @@ -1119,6 +1124,7 @@ int mmc_hs400_to_hs200(struct mmc_card *card) bool send_status = true; unsigned int max_dtr; int err; + u8 val; if (host->caps & MMC_CAP_WAIT_WHILE_BUSY) send_status = false; @@ -1128,8 +1134,10 @@ int mmc_hs400_to_hs200(struct mmc_card *card) mmc_set_clock(host, max_dtr); /* Switch HS400 to HS DDR */ + val = EXT_CSD_TIMING_HS | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, - EXT_CSD_TIMING_HS, card->ext_csd.generic_cmd6_time, + val, card->ext_csd.generic_cmd6_time, true, send_status, true); if (err) goto out_err; @@ -1158,10 +1166,11 @@ int mmc_hs400_to_hs200(struct mmc_card *card) } /* Switch HS to HS200 */ + val = EXT_CSD_TIMING_HS200 | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, - EXT_CSD_TIMING_HS200, - card->ext_csd.generic_cmd6_time, true, send_status, - true); + val, card->ext_csd.generic_cmd6_time, true, + send_status, true); if (err) goto out_err; @@ -1183,6 +1192,23 @@ out_err: return err; } +static void mmc_select_driver_type(struct mmc_card *card) +{ + int card_drv_type, drive_strength, drv_type; + + card_drv_type = card->ext_csd.raw_driver_strength | + mmc_driver_type_mask(0); + + drive_strength = mmc_select_drive_strength(card, + card->ext_csd.hs200_max_dtr, + card_drv_type, &drv_type); + + card->drive_strength = drive_strength; + + if (drv_type) + mmc_set_driver_type(card->host, drv_type); +} + /* * For device supporting HS200 mode, the following sequence * should be done before executing the tuning process. @@ -1194,6 +1220,7 @@ static int mmc_select_hs200(struct mmc_card *card) { struct mmc_host *host = card->host; int err = -EINVAL; + u8 val; if (card->mmc_avail_type & EXT_CSD_CARD_TYPE_HS200_1_2V) err = __mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120); @@ -1205,14 +1232,18 @@ static int mmc_select_hs200(struct mmc_card *card) if (err) goto err; + mmc_select_driver_type(card); + /* * Set the bus width(4 or 8) with host's support and * switch to HS200 mode if bus width is set successfully. */ err = mmc_select_bus_width(card); if (!IS_ERR_VALUE(err)) { + val = EXT_CSD_TIMING_HS200 | + card->drive_strength << EXT_CSD_DRV_STR_SHIFT; err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200, + EXT_CSD_HS_TIMING, val, card->ext_csd.generic_cmd6_time, true, true, true); if (!err) diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index 4819cfbc3795..15f2c4a0a62c 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -391,6 +391,7 @@ struct _mmc_csd { #define EXT_CSD_TIMING_HS 1 /* High speed */ #define EXT_CSD_TIMING_HS200 2 /* HS200 */ #define EXT_CSD_TIMING_HS400 3 /* HS400 */ +#define EXT_CSD_DRV_STR_SHIFT 4 /* Driver Strength shift */ #define EXT_CSD_SEC_ER_EN BIT(0) #define EXT_CSD_SEC_BD_BLK_EN BIT(2) @@ -442,4 +443,6 @@ struct _mmc_csd { #define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */ #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ +#define mmc_driver_type_mask(n) (1 << (n)) + #endif /* LINUX_MMC_MMC_H */ -- cgit v1.2.3 From cb8496482e8886bbd6bd79462d8062db572b0473 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:12:59 +0200 Subject: mmc: sdhci: Add a callback to select drive strength Add a callbak to let host drivers select drive strength. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 13 +++++++++++++ drivers/mmc/host/sdhci.h | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b35ec79e7205..1b4861ddfb38 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2048,6 +2048,18 @@ out_unlock: return err; } +static int sdhci_select_drive_strength(struct mmc_card *card, + unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type) +{ + struct sdhci_host *host = mmc_priv(card->host); + + if (!host->ops->select_drive_strength) + return 0; + + return host->ops->select_drive_strength(host, card, max_dtr, host_drv, + card_drv, drv_type); +} static void sdhci_enable_preset_value(struct sdhci_host *host, bool enable) { @@ -2192,6 +2204,7 @@ static const struct mmc_host_ops sdhci_ops = { .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, .prepare_hs400_tuning = sdhci_prepare_hs400_tuning, .execute_tuning = sdhci_execute_tuning, + .select_drive_strength = sdhci_select_drive_strength, .card_event = sdhci_card_event, .card_busy = sdhci_card_busy, }; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 923ff223aea4..5521d29368e4 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -538,6 +538,10 @@ struct sdhci_ops { void (*platform_init)(struct sdhci_host *host); void (*card_event)(struct sdhci_host *host); void (*voltage_switch)(struct sdhci_host *host); + int (*select_drive_strength)(struct sdhci_host *host, + struct mmc_card *card, + unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type); }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS -- cgit v1.2.3 From e1bfad6d936d7149a83423e2a7244dd5771f27e7 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:13:00 +0200 Subject: mmc: sdhci-pci: Add support for drive strength selection for SPT Implement the select_drive_strength callback to provide drive strength selection for Intel SPT. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-data.c | 3 ++ drivers/mmc/host/sdhci-pci.c | 83 ++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-pci.h | 4 ++ include/linux/mmc/sdhci-pci-data.h | 2 + 4 files changed, 92 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-data.c b/drivers/mmc/host/sdhci-pci-data.c index a611217769f5..56fddc622a54 100644 --- a/drivers/mmc/host/sdhci-pci-data.c +++ b/drivers/mmc/host/sdhci-pci-data.c @@ -3,3 +3,6 @@ struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno); EXPORT_SYMBOL_GPL(sdhci_pci_get_data); + +int sdhci_pci_spt_drive_strength; +EXPORT_SYMBOL_GPL(sdhci_pci_spt_drive_strength); diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 7a3fc16d0a6c..6ecd158d8084 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -266,6 +267,69 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host) usleep_range(300, 1000); } +static int spt_select_drive_strength(struct sdhci_host *host, + struct mmc_card *card, + unsigned int max_dtr, + int host_drv, int card_drv, int *drv_type) +{ + int drive_strength; + + if (sdhci_pci_spt_drive_strength > 0) + drive_strength = sdhci_pci_spt_drive_strength & 0xf; + else + drive_strength = 1; /* 33-ohm */ + + if ((mmc_driver_type_mask(drive_strength) & card_drv) == 0) + drive_strength = 0; /* Default 50-ohm */ + + return drive_strength; +} + +/* Try to read the drive strength from the card */ +static void spt_read_drive_strength(struct sdhci_host *host) +{ + u32 val, i, t; + u16 m; + + if (sdhci_pci_spt_drive_strength) + return; + + sdhci_pci_spt_drive_strength = -1; + + m = sdhci_readw(host, SDHCI_HOST_CONTROL2) & 0x7; + if (m != 3 && m != 5) + return; + val = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (val & 0x3) + return; + sdhci_writel(host, 0x007f0023, SDHCI_INT_ENABLE); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); + sdhci_writew(host, 0x10, SDHCI_TRANSFER_MODE); + sdhci_writeb(host, 0xe, SDHCI_TIMEOUT_CONTROL); + sdhci_writew(host, 512, SDHCI_BLOCK_SIZE); + sdhci_writew(host, 1, SDHCI_BLOCK_COUNT); + sdhci_writel(host, 0, SDHCI_ARGUMENT); + sdhci_writew(host, 0x83b, SDHCI_COMMAND); + for (i = 0; i < 1000; i++) { + val = sdhci_readl(host, SDHCI_INT_STATUS); + if (val & 0xffff8000) + return; + if (val & 0x20) + break; + udelay(1); + } + val = sdhci_readl(host, SDHCI_PRESENT_STATE); + if (!(val & 0x800)) + return; + for (i = 0; i < 47; i++) + val = sdhci_readl(host, SDHCI_BUFFER); + t = val & 0xf00; + if (t != 0x200 && t != 0x300) + return; + + sdhci_pci_spt_drive_strength = 0x10 | ((val >> 12) & 0xf); +} + static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | @@ -276,6 +340,10 @@ static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) slot->hw_reset = sdhci_pci_int_hw_reset; if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BSW_EMMC) slot->host->timeout_clk = 1000; /* 1000 kHz i.e. 1 MHz */ + if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_SPT_EMMC) { + spt_read_drive_strength(slot->host); + slot->select_drive_strength = spt_select_drive_strength; + } return 0; } @@ -1203,6 +1271,20 @@ static void sdhci_pci_hw_reset(struct sdhci_host *host) slot->hw_reset(host); } +static int sdhci_pci_select_drive_strength(struct sdhci_host *host, + struct mmc_card *card, + unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type) +{ + struct sdhci_pci_slot *slot = sdhci_priv(host); + + if (!slot->select_drive_strength) + return 0; + + return slot->select_drive_strength(host, card, max_dtr, host_drv, + card_drv, drv_type); +} + static const struct sdhci_ops sdhci_pci_ops = { .set_clock = sdhci_set_clock, .enable_dma = sdhci_pci_enable_dma, @@ -1210,6 +1292,7 @@ static const struct sdhci_ops sdhci_pci_ops = { .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, + .select_drive_strength = sdhci_pci_select_drive_strength, }; /*****************************************************************************\ diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 1ec684d06d54..541f1cad5247 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -72,6 +72,10 @@ struct sdhci_pci_slot { bool cd_override_level; void (*hw_reset)(struct sdhci_host *host); + int (*select_drive_strength)(struct sdhci_host *host, + struct mmc_card *card, + unsigned int max_dtr, int host_drv, + int card_drv, int *drv_type); }; struct sdhci_pci_chip { diff --git a/include/linux/mmc/sdhci-pci-data.h b/include/linux/mmc/sdhci-pci-data.h index 8959604a13d3..fda15b6d4135 100644 --- a/include/linux/mmc/sdhci-pci-data.h +++ b/include/linux/mmc/sdhci-pci-data.h @@ -15,4 +15,6 @@ struct sdhci_pci_data { extern struct sdhci_pci_data *(*sdhci_pci_get_data)(struct pci_dev *pdev, int slotno); +extern int sdhci_pci_spt_drive_strength; + #endif -- cgit v1.2.3 From b69587e2d5b09a192c45c604ea1f9e8d51f4c3a1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Feb 2015 14:13:01 +0200 Subject: mmc: sdhci-pci: Enable HS400 for some Intel host controllers Enable detection of HS400 support via capability bit-63 for some Intel host controllers. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 6ecd158d8084..f208f2072d0c 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -370,6 +370,7 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { .probe_slot = byt_emmc_probe_slot, .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 | SDHCI_QUIRK2_STOP_WITH_TC, }; -- cgit v1.2.3 From 915be485bdf24f2261b4dc776625c930e1491d7a Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 27 May 2015 18:13:26 +0800 Subject: mmc: sdhci-esdhc-imx: merge the same register check into one place In esdhc_writel_le() function, there's duplicated checking of the same register as follows: "if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE))". Merge them into one and remove the duplicated one. Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index ef290a5b34c4..0c89293ed853 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -313,6 +313,11 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) data |= ESDHC_CTRL_D3CD; writel(data, host->ioaddr + SDHCI_HOST_CONTROL); } + + if (val & SDHCI_INT_ADMA_ERROR) { + val &= ~SDHCI_INT_ADMA_ERROR; + val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR; + } } if (unlikely((imx_data->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) @@ -333,13 +338,6 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) } } - if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) { - if (val & SDHCI_INT_ADMA_ERROR) { - val &= ~SDHCI_INT_ADMA_ERROR; - val |= ESDHC_INT_VENDOR_SPEC_DMA_ERR; - } - } - writel(val, host->ioaddr + reg); } -- cgit v1.2.3 From b7321042aa363daf58912ada174d05a53bf96059 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 27 May 2015 18:13:27 +0800 Subject: mmc: sdhci-esdhc-imx: usdhc does not have missing card interrupt issue The usdhc does not have missing card interrupt issue, so don't execute workaround for usdhc. Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 0c89293ed853..fda07eb2ed15 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -298,7 +298,7 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg) u32 data; if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) { - if (val & SDHCI_INT_CARD_INT) { + if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) { /* * Clear and then set D3CD bit to avoid missing the * card interrupt. This is a eSDHC controller problem -- cgit v1.2.3 From 18094430d6b50432591906784d51bb605982b8d8 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 27 May 2015 18:13:28 +0800 Subject: mmc: sdhci-esdhc-imx: add ADMA Length Mismatch errata fix The uSDHC has an ADMA Length Mismatch errata ERR004536 which may cause ADMA work abnormally. The errata has already been fixed for i.MX6Q TO1.2 and i.MX6DL TO1.1 by enable the bit 7 in 0x6c register. Unfortunately this fix is not included in i.MX6SL. So we disable ADMA for i.MX6SL and use SDMA instead. Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index fda07eb2ed15..a3b82d183686 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -112,6 +112,12 @@ #define ESDHC_FLAG_STD_TUNING BIT(5) /* The IP has SDHCI_CAPABILITIES_1 register */ #define ESDHC_FLAG_HAVE_CAP1 BIT(6) +/* + * The IP has errata ERR004536 + * uSDHC: ADMA Length Mismatch Error occurs if the AHB read access is slow, + * when reading data from the card + */ +#define ESDHC_FLAG_ERR004536 BIT(7) struct esdhc_soc_data { u32 flags; @@ -139,7 +145,7 @@ static struct esdhc_soc_data usdhc_imx6q_data = { static struct esdhc_soc_data usdhc_imx6sl_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING - | ESDHC_FLAG_HAVE_CAP1, + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536, }; struct pltfm_imx_data { @@ -991,6 +997,13 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) writel(0x08100810, host->ioaddr + ESDHC_WTMK_LVL); host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; host->mmc->caps |= MMC_CAP_1_8V_DDR; + + /* + * errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL + * TO1.1, it's harmless for MX6SL + */ + writel(readl(host->ioaddr + 0x6c) | BIT(7), + host->ioaddr + 0x6c); } if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) @@ -1002,6 +1015,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP, host->ioaddr + ESDHC_TUNING_CTRL); + if (imx_data->socdata->flags & ESDHC_FLAG_ERR004536) + host->quirks |= SDHCI_QUIRK_BROKEN_ADMA; + boarddata = &imx_data->boarddata; if (sdhci_esdhc_imx_probe_dt(pdev, host, boarddata) < 0) { if (!host->mmc->parent->platform_data) { -- cgit v1.2.3 From e4a51cebf4dcae47a1312956fa8a237dc8efcb48 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 27 May 2015 18:13:29 +0800 Subject: mmc: sdhci-esdhc-imx: using specific compatible string in binding doc Using specific compatible string in binding doc to make the binding more clear. It's also used to avoid checkpatch warning in the future like follows: WARNING: DT compatible string "fsl,imx6sx-usdhc" appears un-documented -- check ./Documentation/devicetree/bindings/ + { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, }, total: 0 errors, 1 warnings, 18 lines checked Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt index 415c5575cbf7..5d0376b8f202 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.txt @@ -7,7 +7,14 @@ This file documents differences between the core properties described by mmc.txt and the properties used by the sdhci-esdhc-imx driver. Required properties: -- compatible : Should be "fsl,-esdhc" +- compatible : Should be "fsl,-esdhc", the supported chips include + "fsl,imx25-esdhc" + "fsl,imx35-esdhc" + "fsl,imx51-esdhc" + "fsl,imx53-esdhc" + "fsl,imx6q-usdhc" + "fsl,imx6sl-usdhc" + "fsl,imx6sx-usdhc" Optional properties: - fsl,cd-controller : Indicate to use controller internal card detection -- cgit v1.2.3 From 913d4951a50fba6acd96491c5b2f089a69308a60 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 27 May 2015 18:13:30 +0800 Subject: mmc: sdhci-esdhc-imx: add imx6sx support The imx6sx usdhc is derived from imx6sl, the difference is minor. imx6sx have the errata ESDHC_FLAG_ERR004536 fixed. So introduce a new compatible string for imx6sx to distinguish them. Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index a3b82d183686..bcb56734e92f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -148,6 +148,11 @@ static struct esdhc_soc_data usdhc_imx6sl_data = { | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536, }; +static struct esdhc_soc_data usdhc_imx6sx_data = { + .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING + | ESDHC_FLAG_HAVE_CAP1, +}; + struct pltfm_imx_data { u32 scratchpad; struct pinctrl *pinctrl; @@ -188,6 +193,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx35-esdhc", .data = &esdhc_imx35_data, }, { .compatible = "fsl,imx51-esdhc", .data = &esdhc_imx51_data, }, { .compatible = "fsl,imx53-esdhc", .data = &esdhc_imx53_data, }, + { .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data, }, { .compatible = "fsl,imx6sl-usdhc", .data = &usdhc_imx6sl_data, }, { .compatible = "fsl,imx6q-usdhc", .data = &usdhc_imx6q_data, }, { /* sentinel */ } -- cgit v1.2.3 From 4245afff8b241426792d60b5b9078583f52d4029 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 27 May 2015 18:13:31 +0800 Subject: mmc: sdhci-esdhc-imx: add quirk SDHCI_QUIRK2_BROKEN_HS200 for imx6qdl The iMX6Q/DL can not support HS200 mode while iMX6SL and iMX6SX can, so introduce a new flag to distinguish them. Signed-off-by: Dong Aisheng Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index bcb56734e92f..faf0cb910c96 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -118,6 +118,8 @@ * when reading data from the card */ #define ESDHC_FLAG_ERR004536 BIT(7) +/* The IP supports HS200 mode */ +#define ESDHC_FLAG_HS200 BIT(8) struct esdhc_soc_data { u32 flags; @@ -145,12 +147,13 @@ static struct esdhc_soc_data usdhc_imx6q_data = { static struct esdhc_soc_data usdhc_imx6sl_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING - | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536, + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536 + | ESDHC_FLAG_HS200, }; static struct esdhc_soc_data usdhc_imx6sx_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING - | ESDHC_FLAG_HAVE_CAP1, + | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200, }; struct pltfm_imx_data { @@ -1004,6 +1007,9 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; host->mmc->caps |= MMC_CAP_1_8V_DDR; + if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200)) + host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; + /* * errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1.2 and MX6DL * TO1.1, it's harmless for MX6SL -- cgit v1.2.3 From 74fd5e30cc0b201a3800eb3743e059f34bd57fbf Mon Sep 17 00:00:00 2001 From: Yangbo Lu Date: Mon, 1 Jun 2015 13:47:12 +0800 Subject: mmc: sdhci-of-esdhc: enable interrupt mode to detect card Enable interrupt mode to detect card instead of polling mode for P1020/P4080/P5020/P5040/T1040 by removing the quirk SDHCI_QUIRK_BROKEN_CARD_DETECTION. This could improve data transferring performance and avoid the call trace caused by polling card status sometime. Signed-off-by: Yangbo Lu Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-esdhc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 1804bdbdb145..797be7549a15 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -361,6 +361,13 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) sdhci_get_of_property(pdev); np = pdev->dev.of_node; + if (of_device_is_compatible(np, "fsl,p5040-esdhc") || + of_device_is_compatible(np, "fsl,p5020-esdhc") || + of_device_is_compatible(np, "fsl,p4080-esdhc") || + of_device_is_compatible(np, "fsl,p1020-esdhc") || + of_device_is_compatible(np, "fsl,t1040-esdhc")) + host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; + if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { /* * Freescale messed up with P2020 as it has a non-standard -- cgit v1.2.3 From 475c9e43bfa76198c2d0b4310ad406ea2206aba0 Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 14:06:11 -0700 Subject: mmc: sdhci-bcm2835: Clean up platform allocations if sdhci init fails. Signed-off-by: Eric Anholt Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-bcm2835.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 0ef0343c603a..32f4046546b5 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -173,8 +173,11 @@ static int bcm2835_sdhci_probe(struct platform_device *pdev) goto err; } - return sdhci_add_host(host); + ret = sdhci_add_host(host); + if (ret) + goto err; + return 0; err: sdhci_pltfm_free(pdev); return ret; -- cgit v1.2.3 From 1e5a0a9a58e2977e6542074b25b96be93208dccf Mon Sep 17 00:00:00 2001 From: Eric Anholt Date: Fri, 29 May 2015 14:06:12 -0700 Subject: mmc: sdhci-bcm2835: Actually enable the clock We're currently using a fixed frequency clock specified in the DT, so enabling is a no-op. However, the RPi firmware-based clocks driver can actually disable unused clocks, so when switching to use it we ended up losing our MMC clock once all devices were probed. Signed-off-by: Eric Anholt Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-bcm2835.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-bcm2835.c b/drivers/mmc/host/sdhci-bcm2835.c index 32f4046546b5..1c65d4690e70 100644 --- a/drivers/mmc/host/sdhci-bcm2835.c +++ b/drivers/mmc/host/sdhci-bcm2835.c @@ -172,12 +172,19 @@ static int bcm2835_sdhci_probe(struct platform_device *pdev) ret = PTR_ERR(pltfm_host->clk); goto err; } + ret = clk_prepare_enable(pltfm_host->clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable host clk\n"); + goto err; + } ret = sdhci_add_host(host); if (ret) - goto err; + goto err_clk; return 0; +err_clk: + clk_disable_unprepare(pltfm_host->clk); err: sdhci_pltfm_free(pdev); return ret; -- cgit v1.2.3 From 364549ddc29dd5490e81039cb85fbf0123365754 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 1 Jun 2015 11:14:57 +0200 Subject: mmc: core: Remove redundant ->power_restore() callback for MMC Since the ->reset() callback is implemented for MMC, the ->power_restore() callback has become redundant, let's remove it. Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index e519e3110a20..e726903170a8 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1910,17 +1910,6 @@ static int mmc_runtime_resume(struct mmc_host *host) return 0; } -static int mmc_power_restore(struct mmc_host *host) -{ - int ret; - - mmc_claim_host(host); - ret = mmc_init_card(host, host->card->ocr, host->card); - mmc_release_host(host); - - return ret; -} - int mmc_can_reset(struct mmc_card *card) { u8 rst_n_function; @@ -1958,7 +1947,7 @@ static int mmc_reset(struct mmc_host *host) mmc_set_initial_state(host); mmc_host_clk_release(host); - return mmc_power_restore(host); + return mmc_init_card(host, card->ocr, card); } static const struct mmc_bus_ops mmc_ops = { @@ -1968,7 +1957,6 @@ static const struct mmc_bus_ops mmc_ops = { .resume = mmc_resume, .runtime_suspend = mmc_runtime_suspend, .runtime_resume = mmc_runtime_resume, - .power_restore = mmc_power_restore, .alive = mmc_alive, .shutdown = mmc_shutdown, .reset = mmc_reset, -- cgit v1.2.3 From 3056c49c35c1e8cc303e79c7085579ff0729a83b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 1 Jun 2015 11:14:58 +0200 Subject: mmc: core: Remove redundant ->power_restore() callback for SD Since the ->reset() callback is implemented for SD, the ->power_restore() callback has become redundant, let's remove it. Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index b99e25b9bcdc..4e7366ab187f 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1164,21 +1164,10 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) return 0; } -static int mmc_sd_power_restore(struct mmc_host *host) -{ - int ret; - - mmc_claim_host(host); - ret = mmc_sd_init_card(host, host->card->ocr, host->card); - mmc_release_host(host); - - return ret; -} - static int mmc_sd_reset(struct mmc_host *host) { mmc_power_cycle(host, host->card->ocr); - return mmc_sd_power_restore(host); + return mmc_sd_init_card(host, host->card->ocr, host->card); } static const struct mmc_bus_ops mmc_sd_ops = { @@ -1188,7 +1177,6 @@ static const struct mmc_bus_ops mmc_sd_ops = { .runtime_resume = mmc_sd_runtime_resume, .suspend = mmc_sd_suspend, .resume = mmc_sd_resume, - .power_restore = mmc_sd_power_restore, .alive = mmc_sd_alive, .shutdown = mmc_sd_suspend, .reset = mmc_sd_reset, -- cgit v1.2.3 From 1ef48e3ded7d25a6c382c4c74f44474e76038275 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 1 Jun 2015 12:18:25 +0200 Subject: mmc: core: Attach PM domain prior probing of SDIO func driver Other subsystem buses attach PM domains during probe, but prior calling the driver's ->probe() method. During the removal phase, detaching the PM domain will be done after invoking the driver's ->remove() callback. Convert the SDIO bus to follow this behavior and add error handling. Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio_bus.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c index bee02e644d62..7e327a6dd53d 100644 --- a/drivers/mmc/core/sdio_bus.c +++ b/drivers/mmc/core/sdio_bus.c @@ -137,6 +137,10 @@ static int sdio_bus_probe(struct device *dev) if (!id) return -ENODEV; + ret = dev_pm_domain_attach(dev, false); + if (ret == -EPROBE_DEFER) + return ret; + /* Unbound SDIO functions are always suspended. * During probe, the function is set active and the usage count * is incremented. If the driver supports runtime PM, @@ -166,6 +170,7 @@ static int sdio_bus_probe(struct device *dev) disable_runtimepm: if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) pm_runtime_put_noidle(dev); + dev_pm_domain_detach(dev, false); return ret; } @@ -197,6 +202,8 @@ static int sdio_bus_remove(struct device *dev) if (func->card->host->caps & MMC_CAP_POWER_OFF_CARD) pm_runtime_put_sync(dev); + dev_pm_domain_detach(dev, false); + return ret; } @@ -316,10 +323,8 @@ int sdio_add_func(struct sdio_func *func) sdio_set_of_node(func); sdio_acpi_set_handle(func); ret = device_add(&func->dev); - if (ret == 0) { + if (ret == 0) sdio_func_set_present(func); - dev_pm_domain_attach(&func->dev, false); - } return ret; } @@ -335,7 +340,6 @@ void sdio_remove_func(struct sdio_func *func) if (!sdio_func_present(func)) return; - dev_pm_domain_detach(&func->dev, false); device_del(&func->dev); of_node_put(func->dev.of_node); put_device(&func->dev); -- cgit v1.2.3 From 83dc9fecd5d4c84e9405c91098ae9dc07c201b90 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Tue, 2 Jun 2015 18:38:35 +0800 Subject: mmc: sdhci-pxav3: fix device wakeup initialization MMC_PM_KEEP_POWER doesn't imply MMC_PM_WAKE_SDIO_IRQ, we should only enable device wake up when MMC_PM_WAKE_SDIO_IRQ is set. And "pm_flags" is the requested pm features, we should not set it in the host driver. At the same time, device wakeup is disabled by default, so there's no need to disable device wakeup explicitly. This patch fixes the warning as following: [ 64.616651] ------------[ cut here ]------------ [ 64.616665] WARNING: CPU: 0 PID: 79 at linux/kernel/irq/manage.c:603 irq_set_irq_wake+0xf0/0x11c() [ 64.616667] Unbalanced IRQ 87 wake disable Signed-off-by: Jisheng Zhang Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index 01cb2d309a6a..9cd5fc62f130 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -457,12 +457,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); - if (host->mmc->pm_caps & MMC_PM_KEEP_POWER) { + if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) device_init_wakeup(&pdev->dev, 1); - host->mmc->pm_flags |= MMC_PM_WAKE_SDIO_IRQ; - } else { - device_init_wakeup(&pdev->dev, 0); - } pm_runtime_put_autosuspend(&pdev->dev); -- cgit v1.2.3 From 5fd26c7ecb32082745b0bd33c8e35badd1cb5a91 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 5 Jun 2015 11:40:08 +0200 Subject: mmc: sdhci: Restore behavior while creating OCR mask Commit 3a48edc4bd68 ("mmc: sdhci: Use mmc core regulator infrastucture") changed the behavior for how to assign the ocr_avail mask for the mmc host. More precisely it started to mask the bits instead of assigning them. Restore the behavior, but also make it clear that an OCR mask created from an external regulator overrides the other ones. The OCR mask is determined by one of the following with this priority: 1. Supported ranges of external regulator if one supplies VDD 2. Host OCR mask if set by the driver (based on DT properties) 3. The capabilities reported by the controller itself Fixes: 3a48edc4bd68 ("mmc: sdhci: Use mmc core regulator infrastucture") Cc: Tim Kryger Reported-by: Yangbo Lu Signed-off-by: Ulf Hansson Reviewed-by: Tim Kryger --- drivers/mmc/host/sdhci.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1b4861ddfb38..706bb604688a 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3256,13 +3256,14 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_MAX_CURRENT_MULTIPLIER; } - /* If OCR set by external regulators, use it instead */ + /* If OCR set by host, use it instead. */ + if (host->ocr_mask) + ocr_avail = host->ocr_mask; + + /* If OCR set by external regulators, give it highest prio. */ if (mmc->ocr_avail) ocr_avail = mmc->ocr_avail; - if (host->ocr_mask) - ocr_avail &= host->ocr_mask; - mmc->ocr_avail = ocr_avail; mmc->ocr_avail_sdio = ocr_avail; if (host->ocr_avail_sdio) -- cgit v1.2.3 From 048fd7e665cef45630102dc8bf81971fbc82e52c Mon Sep 17 00:00:00 2001 From: Prabu Thangamuthu Date: Thu, 28 May 2015 12:21:06 +0000 Subject: mmc: dw_mmc: insmod followed by rmmod will hung for eMMC Remove module of dw_mmc driver will hung for eMMC devices if we follow the steps which are listed below, insmod dw_mmc.ko insmod dw_mmc-pci.ko rmmod dw_mmc-pci.ko The root cause for this issue is, dw_mci_remove() will disable all the interrupts by programming 0x0 to INTMASK register then it will call dw_mci_cleanup_slot(). But dw_mci_cleanup_slot() is issuing CMD6 to disable the eMMC boot partition and it is waiting for Command Complete interrupt. Since INTMASK was already cleared by dw_mci_remove(), Command Complete interrupt is not reaching the system. This leads to process hung. Signed-off-by: Prabu Thangamuthu Signed-off-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 55179f1001fb..40e9d8e45f25 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2926,15 +2926,15 @@ void dw_mci_remove(struct dw_mci *host) { int i; - mci_writel(host, RINTSTS, 0xFFFFFFFF); - mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ - for (i = 0; i < host->num_slots; i++) { dev_dbg(host->dev, "remove slot %d\n", i); if (host->slot[i]) dw_mci_cleanup_slot(host->slot[i], i); } + mci_writel(host, RINTSTS, 0xFFFFFFFF); + mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ + /* disable clock to CIU */ mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); -- cgit v1.2.3 From 7551847ca079c362663385be20e6731ad32f1a3e Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Wed, 10 Jun 2015 18:30:53 +0200 Subject: mmc: queue: use swap() in mmc_queue_thread() Use kernel.h macro definition. Thanks to Julia Lawall for Coccinelle scripting support. Signed-off-by: Fabian Frederick Signed-off-by: Ulf Hansson --- drivers/mmc/card/queue.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index 8efa3684aef8..d34e09b4161e 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -56,7 +56,6 @@ static int mmc_queue_thread(void *d) down(&mq->thread_sem); do { struct request *req = NULL; - struct mmc_queue_req *tmp; unsigned int cmd_flags = 0; spin_lock_irq(q->queue_lock); @@ -86,9 +85,7 @@ static int mmc_queue_thread(void *d) mq->mqrq_prev->brq.mrq.data = NULL; mq->mqrq_prev->req = NULL; - tmp = mq->mqrq_prev; - mq->mqrq_prev = mq->mqrq_cur; - mq->mqrq_cur = tmp; + swap(mq->mqrq_prev, mq->mqrq_cur); } else { if (kthread_should_stop()) { set_current_state(TASK_RUNNING); -- cgit v1.2.3 From d45337328b1fb86a8f045f6c9938e9e08e6d7134 Mon Sep 17 00:00:00 2001 From: Vincent Wan Date: Thu, 11 Jun 2015 20:11:45 +0800 Subject: pci_ids: Add AMD KERNCZ device ID support The KERNCZ is new AMD SB/FCH generation name, like HUDSON2. 0x790b is the device ID for this generation. Signed-off-by: Wan ZongShun Acked-by: Bjorn Helgaas Signed-off-by: Ulf Hansson --- include/linux/pci_ids.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 2f7b9a40f627..cb63a7b522ef 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -579,6 +579,7 @@ #define PCI_DEVICE_ID_AMD_HUDSON2_SATA_IDE 0x7800 #define PCI_DEVICE_ID_AMD_HUDSON2_SMBUS 0x780b #define PCI_DEVICE_ID_AMD_HUDSON2_IDE 0x780c +#define PCI_DEVICE_ID_AMD_KERNCZ_SMBUS 0x790b #define PCI_VENDOR_ID_TRIDENT 0x1023 #define PCI_DEVICE_ID_TRIDENT_4DWAVE_DX 0x2000 -- cgit v1.2.3 From bcb299947f5d77393d84f493b4857bcd7308e6dd Mon Sep 17 00:00:00 2001 From: Vincent Wan Date: Thu, 11 Jun 2015 20:11:46 +0800 Subject: i2c-piix4: Use Macro for AMD CZ SMBus device ID Change AMD CZ SMBUS device ID from 0x790b to use Macro definition Signed-off-by: Wan ZongShun Acked-by: Wolfram Sang Acked-by: Jean Delvare Signed-off-by: Ulf Hansson --- drivers/i2c/busses/i2c-piix4.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 67cbec6796a0..630bce68bf38 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -245,7 +245,7 @@ static int piix4_setup_sb800(struct pci_dev *PIIX4_dev, PIIX4_dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && PIIX4_dev->revision >= 0x41) || (PIIX4_dev->vendor == PCI_VENDOR_ID_AMD && - PIIX4_dev->device == 0x790b && + PIIX4_dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && PIIX4_dev->revision >= 0x49)) smb_en = 0x00; else @@ -545,7 +545,7 @@ static const struct pci_device_id piix4_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x790b) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4) }, { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, -- cgit v1.2.3 From b5e97d6e58617b9d6a3838ede6ccd3883035dbf3 Mon Sep 17 00:00:00 2001 From: Vincent Wan Date: Thu, 11 Jun 2015 20:11:47 +0800 Subject: mmc: sdhci-pci: Change AMD SDHCI quirk application scope Change this quirk to apply to AMD Carrizo platform. Signed-off-by: Wan ZongShun Tested-by: Nath, Arindam Tested-by: Ramesh, Ramya Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index f208f2072d0c..94f54d2772e8 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -724,14 +724,37 @@ static const struct sdhci_pci_fixes sdhci_rtsx = { .probe_slot = rtsx_probe_slot, }; +/*AMD chipset generation*/ +enum amd_chipset_gen { + AMD_CHIPSET_BEFORE_ML, + AMD_CHIPSET_CZ, + AMD_CHIPSET_NL, + AMD_CHIPSET_UNKNOWN, +}; + static int amd_probe(struct sdhci_pci_chip *chip) { struct pci_dev *smbus_dev; + enum amd_chipset_gen gen; smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL); + if (smbus_dev) { + gen = AMD_CHIPSET_BEFORE_ML; + } else { + smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD, + PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, NULL); + if (smbus_dev) { + if (smbus_dev->revision < 0x51) + gen = AMD_CHIPSET_CZ; + else + gen = AMD_CHIPSET_NL; + } else { + gen = AMD_CHIPSET_UNKNOWN; + } + } - if (smbus_dev && (smbus_dev->revision < 0x51)) { + if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) { chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD; chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; } -- cgit v1.2.3 From 62a7f368ffbc13d9aedfdd7aeae711b177db69ac Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 12 Jun 2015 11:45:02 +0200 Subject: mmc: sdhci: fix low memory corruption When dma mapping (dma_map_sg) fails in sdhci_pre_dma_transfer, -EINVAL is returned. There are 3 callers of sdhci_pre_dma_transfer: * sdhci_pre_req and sdhci_adma_table_pre: handle negative return * sdhci_prepare_data: handles 0 (error) and "else" (good) only sdhci_prepare_data is therefore broken. When it receives -EINVAL from sdhci_pre_dma_transfer, it assumes 1 sg mapping was mapped. Later, this non-existent mapping with address 0 is kmap'ped and written to: Corrupted low memory at ffff880000001000 (1000 phys) = 22b7d67df2f6d1cf Corrupted low memory at ffff880000001008 (1008 phys) = 63848a5216b7dd95 Corrupted low memory at ffff880000001010 (1010 phys) = 330eb7ddef39e427 Corrupted low memory at ffff880000001018 (1018 phys) = 8017ac7295039bda Corrupted low memory at ffff880000001020 (1020 phys) = 8ce039eac119074f ... So teach sdhci_prepare_data to understand negative return values from sdhci_pre_dma_transfer and disable DMA in that case, as well as for zero. It was introduced in 348487cb28e66b032bae1b38424d81bf5b444408 (mmc: sdhci: use pipeline mmc requests to improve performance). The commit seems to be suspicious also by assigning host->sg_count both in sdhci_pre_dma_transfer and sdhci_adma_table_pre. Signed-off-by: Jiri Slaby Cc: stable@vger.kernel.org # 4.0+ Fixes: 348487cb28e6 Cc: Ulf Hansson Cc: Haibo Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 706bb604688a..bc1445238fb3 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -834,7 +834,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) int sg_cnt; sg_cnt = sdhci_pre_dma_transfer(host, data, NULL); - if (sg_cnt == 0) { + if (sg_cnt <= 0) { /* * This only happens when someone fed * us an invalid request. -- cgit v1.2.3 From 29535f7b797df35cc9b6b3bca635591cdd3dd2a8 Mon Sep 17 00:00:00 2001 From: Ding Wang Date: Mon, 18 May 2015 20:14:15 +0800 Subject: mmc: card: Fixup request missing in mmc_blk_issue_rw_rq The current handler of MMC_BLK_CMD_ERR in mmc_blk_issue_rw_rq function may cause new coming request permanent missing when the ongoing request (previoulsy started) complete end. The problem scenario is as follows: (1) Request A is ongoing; (2) Request B arrived, and finally mmc_blk_issue_rw_rq() is called; (3) Request A encounters the MMC_BLK_CMD_ERR error; (4) In the error handling of MMC_BLK_CMD_ERR, suppose mmc_blk_cmd_err() end request A completed and return zero. Continue the error handling, suppose mmc_blk_reset() reset device success; (5) Continue the execution, while loop completed because variable ret is zero now; (6) Finally, mmc_blk_issue_rw_rq() return without processing request B. The process related to the missing request may wait that IO request complete forever, possibly crashing the application or hanging the system. Fix this issue by starting new request when reset success. Signed-off-by: Ding Wang Fixes: 67716327eec7 ("mmc: block: add eMMC hardware reset support") Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index fc0317124276..c9c3d20b784b 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -1920,9 +1920,11 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc) break; case MMC_BLK_CMD_ERR: ret = mmc_blk_cmd_err(md, card, brq, req, ret); - if (!mmc_blk_reset(md, card->host, type)) - break; - goto cmd_abort; + if (mmc_blk_reset(md, card->host, type)) + goto cmd_abort; + if (!ret) + goto start_new_req; + break; case MMC_BLK_RETRY: retune_retry_done = brq->retune_retry_done; if (retry++ < 5) -- cgit v1.2.3 From 4c31d50d3ee67b4fdc1824ed51b6f707681d0136 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Mon, 15 Jun 2015 19:20:47 +0800 Subject: mmc: dt-bindings: add Mediatek MMC bindings Document the device-tree binding of Mediatek MMC host Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mtk-sd.txt | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/mtk-sd.txt diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.txt b/Documentation/devicetree/bindings/mmc/mtk-sd.txt new file mode 100644 index 000000000000..a1adfa495ad3 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/mtk-sd.txt @@ -0,0 +1,32 @@ +* MTK MMC controller + +The MTK MSDC can act as a MMC controller +to support MMC, SD, and SDIO types of memory cards. + +This file documents differences between the core properties in mmc.txt +and the properties used by the msdc driver. + +Required properties: +- compatible: Should be "mediatek,mt8173-mmc","mediatek,mt8135-mmc" +- interrupts: Should contain MSDC interrupt number +- clocks: MSDC source clock, HCLK +- clock-names: "source", "hclk" +- pinctrl-names: should be "default", "state_uhs" +- pinctrl-0: should contain default/high speed pin ctrl +- pinctrl-1: should contain uhs mode pin ctrl +- vmmc-supply: power to the Core +- vqmmc-supply: power to the IO + +Examples: +mmc0: mmc@11230000 { + compatible = "mediatek,mt8173-mmc", "mediatek,mt8135-mmc"; + reg = <0 0x11230000 0 0x108>; + interrupts = ; + vmmc-supply = <&mt6397_vemc_3v3_reg>; + vqmmc-supply = <&mt6397_vio18_reg>; + clocks = <&pericfg CLK_PERI_MSDC30_0>, <&topckgen CLK_TOP_MSDC50_0_H_SEL>; + clock-names = "source", "hclk"; + pinctrl-names = "default", "state_uhs"; + pinctrl-0 = <&mmc0_pins_default>; + pinctrl-1 = <&mmc0_pins_uhs>; +}; -- cgit v1.2.3 From 208489032bdd8d4a7de50f3057c175058f271956 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Mon, 15 Jun 2015 19:20:48 +0800 Subject: mmc: mediatek: Add Mediatek MMC driver Add Mediatek MMC driver code Support eMMC/SD/SDIO Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 8 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/mtk-sd.c | 1379 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/core.h | 1 + 4 files changed, 1389 insertions(+) create mode 100644 drivers/mmc/host/mtk-sd.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index d5e107b78d60..fd9a58e216a5 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -776,3 +776,11 @@ config MMC_TOSHIBA_PCI tristate "Toshiba Type A SD/MMC Card Interface Driver" depends on PCI help + +config MMC_MTK + tristate "MediaTek SD/MMC Card Interface support" + help + This selects the MediaTek(R) Secure digital and Multimedia card Interface. + If you have a machine with a integrated SD/MMC card reader, say Y or M here. + This is needed if support for any SD/SDIO/MMC devices is required. + If unsure, say N. diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index e3ab5b968651..e928d61c5f4b 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o +obj-$(CONFIG_MMC_MTK) += mtk-sd.o obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c new file mode 100644 index 000000000000..fd965cb81133 --- /dev/null +++ b/drivers/mmc/host/mtk-sd.c @@ -0,0 +1,1379 @@ +/* + * Copyright (c) 2014-2015 MediaTek Inc. + * Author: Chaotian.Jing + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MAX_BD_NUM 1024 + +/*--------------------------------------------------------------------------*/ +/* Common Definition */ +/*--------------------------------------------------------------------------*/ +#define MSDC_BUS_1BITS 0x0 +#define MSDC_BUS_4BITS 0x1 +#define MSDC_BUS_8BITS 0x2 + +#define MSDC_BURST_64B 0x6 + +/*--------------------------------------------------------------------------*/ +/* Register Offset */ +/*--------------------------------------------------------------------------*/ +#define MSDC_CFG 0x0 +#define MSDC_IOCON 0x04 +#define MSDC_PS 0x08 +#define MSDC_INT 0x0c +#define MSDC_INTEN 0x10 +#define MSDC_FIFOCS 0x14 +#define SDC_CFG 0x30 +#define SDC_CMD 0x34 +#define SDC_ARG 0x38 +#define SDC_STS 0x3c +#define SDC_RESP0 0x40 +#define SDC_RESP1 0x44 +#define SDC_RESP2 0x48 +#define SDC_RESP3 0x4c +#define SDC_BLK_NUM 0x50 +#define SDC_ACMD_RESP 0x80 +#define MSDC_DMA_SA 0x90 +#define MSDC_DMA_CTRL 0x98 +#define MSDC_DMA_CFG 0x9c +#define MSDC_PATCH_BIT 0xb0 +#define MSDC_PATCH_BIT1 0xb4 +#define MSDC_PAD_TUNE 0xec + +/*--------------------------------------------------------------------------*/ +/* Register Mask */ +/*--------------------------------------------------------------------------*/ + +/* MSDC_CFG mask */ +#define MSDC_CFG_MODE (0x1 << 0) /* RW */ +#define MSDC_CFG_CKPDN (0x1 << 1) /* RW */ +#define MSDC_CFG_RST (0x1 << 2) /* RW */ +#define MSDC_CFG_PIO (0x1 << 3) /* RW */ +#define MSDC_CFG_CKDRVEN (0x1 << 4) /* RW */ +#define MSDC_CFG_BV18SDT (0x1 << 5) /* RW */ +#define MSDC_CFG_BV18PSS (0x1 << 6) /* R */ +#define MSDC_CFG_CKSTB (0x1 << 7) /* R */ +#define MSDC_CFG_CKDIV (0xff << 8) /* RW */ +#define MSDC_CFG_CKMOD (0x3 << 16) /* RW */ + +/* MSDC_IOCON mask */ +#define MSDC_IOCON_SDR104CKS (0x1 << 0) /* RW */ +#define MSDC_IOCON_RSPL (0x1 << 1) /* RW */ +#define MSDC_IOCON_DSPL (0x1 << 2) /* RW */ +#define MSDC_IOCON_DDLSEL (0x1 << 3) /* RW */ +#define MSDC_IOCON_DDR50CKD (0x1 << 4) /* RW */ +#define MSDC_IOCON_DSPLSEL (0x1 << 5) /* RW */ +#define MSDC_IOCON_W_DSPL (0x1 << 8) /* RW */ +#define MSDC_IOCON_D0SPL (0x1 << 16) /* RW */ +#define MSDC_IOCON_D1SPL (0x1 << 17) /* RW */ +#define MSDC_IOCON_D2SPL (0x1 << 18) /* RW */ +#define MSDC_IOCON_D3SPL (0x1 << 19) /* RW */ +#define MSDC_IOCON_D4SPL (0x1 << 20) /* RW */ +#define MSDC_IOCON_D5SPL (0x1 << 21) /* RW */ +#define MSDC_IOCON_D6SPL (0x1 << 22) /* RW */ +#define MSDC_IOCON_D7SPL (0x1 << 23) /* RW */ +#define MSDC_IOCON_RISCSZ (0x3 << 24) /* RW */ + +/* MSDC_PS mask */ +#define MSDC_PS_CDEN (0x1 << 0) /* RW */ +#define MSDC_PS_CDSTS (0x1 << 1) /* R */ +#define MSDC_PS_CDDEBOUNCE (0xf << 12) /* RW */ +#define MSDC_PS_DAT (0xff << 16) /* R */ +#define MSDC_PS_CMD (0x1 << 24) /* R */ +#define MSDC_PS_WP (0x1 << 31) /* R */ + +/* MSDC_INT mask */ +#define MSDC_INT_MMCIRQ (0x1 << 0) /* W1C */ +#define MSDC_INT_CDSC (0x1 << 1) /* W1C */ +#define MSDC_INT_ACMDRDY (0x1 << 3) /* W1C */ +#define MSDC_INT_ACMDTMO (0x1 << 4) /* W1C */ +#define MSDC_INT_ACMDCRCERR (0x1 << 5) /* W1C */ +#define MSDC_INT_DMAQ_EMPTY (0x1 << 6) /* W1C */ +#define MSDC_INT_SDIOIRQ (0x1 << 7) /* W1C */ +#define MSDC_INT_CMDRDY (0x1 << 8) /* W1C */ +#define MSDC_INT_CMDTMO (0x1 << 9) /* W1C */ +#define MSDC_INT_RSPCRCERR (0x1 << 10) /* W1C */ +#define MSDC_INT_CSTA (0x1 << 11) /* R */ +#define MSDC_INT_XFER_COMPL (0x1 << 12) /* W1C */ +#define MSDC_INT_DXFER_DONE (0x1 << 13) /* W1C */ +#define MSDC_INT_DATTMO (0x1 << 14) /* W1C */ +#define MSDC_INT_DATCRCERR (0x1 << 15) /* W1C */ +#define MSDC_INT_ACMD19_DONE (0x1 << 16) /* W1C */ +#define MSDC_INT_DMA_BDCSERR (0x1 << 17) /* W1C */ +#define MSDC_INT_DMA_GPDCSERR (0x1 << 18) /* W1C */ +#define MSDC_INT_DMA_PROTECT (0x1 << 19) /* W1C */ + +/* MSDC_INTEN mask */ +#define MSDC_INTEN_MMCIRQ (0x1 << 0) /* RW */ +#define MSDC_INTEN_CDSC (0x1 << 1) /* RW */ +#define MSDC_INTEN_ACMDRDY (0x1 << 3) /* RW */ +#define MSDC_INTEN_ACMDTMO (0x1 << 4) /* RW */ +#define MSDC_INTEN_ACMDCRCERR (0x1 << 5) /* RW */ +#define MSDC_INTEN_DMAQ_EMPTY (0x1 << 6) /* RW */ +#define MSDC_INTEN_SDIOIRQ (0x1 << 7) /* RW */ +#define MSDC_INTEN_CMDRDY (0x1 << 8) /* RW */ +#define MSDC_INTEN_CMDTMO (0x1 << 9) /* RW */ +#define MSDC_INTEN_RSPCRCERR (0x1 << 10) /* RW */ +#define MSDC_INTEN_CSTA (0x1 << 11) /* RW */ +#define MSDC_INTEN_XFER_COMPL (0x1 << 12) /* RW */ +#define MSDC_INTEN_DXFER_DONE (0x1 << 13) /* RW */ +#define MSDC_INTEN_DATTMO (0x1 << 14) /* RW */ +#define MSDC_INTEN_DATCRCERR (0x1 << 15) /* RW */ +#define MSDC_INTEN_ACMD19_DONE (0x1 << 16) /* RW */ +#define MSDC_INTEN_DMA_BDCSERR (0x1 << 17) /* RW */ +#define MSDC_INTEN_DMA_GPDCSERR (0x1 << 18) /* RW */ +#define MSDC_INTEN_DMA_PROTECT (0x1 << 19) /* RW */ + +/* MSDC_FIFOCS mask */ +#define MSDC_FIFOCS_RXCNT (0xff << 0) /* R */ +#define MSDC_FIFOCS_TXCNT (0xff << 16) /* R */ +#define MSDC_FIFOCS_CLR (0x1 << 31) /* RW */ + +/* SDC_CFG mask */ +#define SDC_CFG_SDIOINTWKUP (0x1 << 0) /* RW */ +#define SDC_CFG_INSWKUP (0x1 << 1) /* RW */ +#define SDC_CFG_BUSWIDTH (0x3 << 16) /* RW */ +#define SDC_CFG_SDIO (0x1 << 19) /* RW */ +#define SDC_CFG_SDIOIDE (0x1 << 20) /* RW */ +#define SDC_CFG_INTATGAP (0x1 << 21) /* RW */ +#define SDC_CFG_DTOC (0xff << 24) /* RW */ + +/* SDC_STS mask */ +#define SDC_STS_SDCBUSY (0x1 << 0) /* RW */ +#define SDC_STS_CMDBUSY (0x1 << 1) /* RW */ +#define SDC_STS_SWR_COMPL (0x1 << 31) /* RW */ + +/* MSDC_DMA_CTRL mask */ +#define MSDC_DMA_CTRL_START (0x1 << 0) /* W */ +#define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */ +#define MSDC_DMA_CTRL_RESUME (0x1 << 2) /* W */ +#define MSDC_DMA_CTRL_MODE (0x1 << 8) /* RW */ +#define MSDC_DMA_CTRL_LASTBUF (0x1 << 10) /* RW */ +#define MSDC_DMA_CTRL_BRUSTSZ (0x7 << 12) /* RW */ + +/* MSDC_DMA_CFG mask */ +#define MSDC_DMA_CFG_STS (0x1 << 0) /* R */ +#define MSDC_DMA_CFG_DECSEN (0x1 << 1) /* RW */ +#define MSDC_DMA_CFG_AHBHPROT2 (0x2 << 8) /* RW */ +#define MSDC_DMA_CFG_ACTIVEEN (0x2 << 12) /* RW */ +#define MSDC_DMA_CFG_CS12B16B (0x1 << 16) /* RW */ + +/* MSDC_PATCH_BIT mask */ +#define MSDC_PATCH_BIT_ODDSUPP (0x1 << 1) /* RW */ +#define MSDC_INT_DAT_LATCH_CK_SEL (0x7 << 7) +#define MSDC_CKGEN_MSDC_DLY_SEL (0x1f << 10) +#define MSDC_PATCH_BIT_IODSSEL (0x1 << 16) /* RW */ +#define MSDC_PATCH_BIT_IOINTSEL (0x1 << 17) /* RW */ +#define MSDC_PATCH_BIT_BUSYDLY (0xf << 18) /* RW */ +#define MSDC_PATCH_BIT_WDOD (0xf << 22) /* RW */ +#define MSDC_PATCH_BIT_IDRTSEL (0x1 << 26) /* RW */ +#define MSDC_PATCH_BIT_CMDFSEL (0x1 << 27) /* RW */ +#define MSDC_PATCH_BIT_INTDLSEL (0x1 << 28) /* RW */ +#define MSDC_PATCH_BIT_SPCPUSH (0x1 << 29) /* RW */ +#define MSDC_PATCH_BIT_DECRCTMO (0x1 << 30) /* RW */ + +#define REQ_CMD_EIO (0x1 << 0) +#define REQ_CMD_TMO (0x1 << 1) +#define REQ_DAT_ERR (0x1 << 2) +#define REQ_STOP_EIO (0x1 << 3) +#define REQ_STOP_TMO (0x1 << 4) +#define REQ_CMD_BUSY (0x1 << 5) + +#define MSDC_PREPARE_FLAG (0x1 << 0) +#define MSDC_ASYNC_FLAG (0x1 << 1) +#define MSDC_MMAP_FLAG (0x1 << 2) + +#define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */ +#define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */ + +/*--------------------------------------------------------------------------*/ +/* Descriptor Structure */ +/*--------------------------------------------------------------------------*/ +struct mt_gpdma_desc { + u32 gpd_info; +#define GPDMA_DESC_HWO (0x1 << 0) +#define GPDMA_DESC_BDP (0x1 << 1) +#define GPDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */ +#define GPDMA_DESC_INT (0x1 << 16) + u32 next; + u32 ptr; + u32 gpd_data_len; +#define GPDMA_DESC_BUFLEN (0xffff) /* bit0 ~ bit15 */ +#define GPDMA_DESC_EXTLEN (0xff << 16) /* bit16 ~ bit23 */ + u32 arg; + u32 blknum; + u32 cmd; +}; + +struct mt_bdma_desc { + u32 bd_info; +#define BDMA_DESC_EOL (0x1 << 0) +#define BDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */ +#define BDMA_DESC_BLKPAD (0x1 << 17) +#define BDMA_DESC_DWPAD (0x1 << 18) + u32 next; + u32 ptr; + u32 bd_data_len; +#define BDMA_DESC_BUFLEN (0xffff) /* bit0 ~ bit15 */ +}; + +struct msdc_dma { + struct scatterlist *sg; /* I/O scatter list */ + struct mt_gpdma_desc *gpd; /* pointer to gpd array */ + struct mt_bdma_desc *bd; /* pointer to bd array */ + dma_addr_t gpd_addr; /* the physical address of gpd array */ + dma_addr_t bd_addr; /* the physical address of bd array */ +}; + +struct msdc_host { + struct device *dev; + struct mmc_host *mmc; /* mmc structure */ + int cmd_rsp; + + spinlock_t lock; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + int error; + + void __iomem *base; /* host base address */ + + struct msdc_dma dma; /* dma channel */ + u64 dma_mask; + + u32 timeout_ns; /* data timeout ns */ + u32 timeout_clks; /* data timeout clks */ + + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_uhs; + struct delayed_work req_timeout; + int irq; /* host interrupt */ + + struct clk *src_clk; /* msdc source clock */ + struct clk *h_clk; /* msdc h_clk */ + u32 mclk; /* mmc subsystem clock frequency */ + u32 src_clk_freq; /* source clock frequency */ + u32 sclk; /* SD/MS bus clock frequency */ + bool ddr; + bool vqmmc_enabled; +}; + +static void sdr_set_bits(void __iomem *reg, u32 bs) +{ + u32 val = readl(reg); + + val |= bs; + writel(val, reg); +} + +static void sdr_clr_bits(void __iomem *reg, u32 bs) +{ + u32 val = readl(reg); + + val &= ~bs; + writel(val, reg); +} + +static void sdr_set_field(void __iomem *reg, u32 field, u32 val) +{ + unsigned int tv = readl(reg); + + tv &= ~field; + tv |= ((val) << (ffs((unsigned int)field) - 1)); + writel(tv, reg); +} + +static void sdr_get_field(void __iomem *reg, u32 field, u32 *val) +{ + unsigned int tv = readl(reg); + + *val = ((tv & field) >> (ffs((unsigned int)field) - 1)); +} + +static void msdc_reset_hw(struct msdc_host *host) +{ + u32 val; + + sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_RST); + while (readl(host->base + MSDC_CFG) & MSDC_CFG_RST) + cpu_relax(); + + sdr_set_bits(host->base + MSDC_FIFOCS, MSDC_FIFOCS_CLR); + while (readl(host->base + MSDC_FIFOCS) & MSDC_FIFOCS_CLR) + cpu_relax(); + + val = readl(host->base + MSDC_INT); + writel(val, host->base + MSDC_INT); +} + +static void msdc_cmd_next(struct msdc_host *host, + struct mmc_request *mrq, struct mmc_command *cmd); + +static u32 data_ints_mask = MSDC_INTEN_XFER_COMPL | MSDC_INTEN_DATTMO | + MSDC_INTEN_DATCRCERR | MSDC_INTEN_DMA_BDCSERR | + MSDC_INTEN_DMA_GPDCSERR | MSDC_INTEN_DMA_PROTECT; + +static u8 msdc_dma_calcs(u8 *buf, u32 len) +{ + u32 i, sum = 0; + + for (i = 0; i < len; i++) + sum += buf[i]; + return 0xff - (u8) sum; +} + +static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma, + struct mmc_data *data) +{ + unsigned int j, dma_len; + dma_addr_t dma_address; + u32 dma_ctrl; + struct scatterlist *sg; + struct mt_gpdma_desc *gpd; + struct mt_bdma_desc *bd; + + sg = data->sg; + + gpd = dma->gpd; + bd = dma->bd; + + /* modify gpd */ + gpd->gpd_info |= GPDMA_DESC_HWO; + gpd->gpd_info |= GPDMA_DESC_BDP; + /* need to clear first. use these bits to calc checksum */ + gpd->gpd_info &= ~GPDMA_DESC_CHECKSUM; + gpd->gpd_info |= msdc_dma_calcs((u8 *) gpd, 16) << 8; + + /* modify bd */ + for_each_sg(data->sg, sg, data->sg_count, j) { + dma_address = sg_dma_address(sg); + dma_len = sg_dma_len(sg); + + /* init bd */ + bd[j].bd_info &= ~BDMA_DESC_BLKPAD; + bd[j].bd_info &= ~BDMA_DESC_DWPAD; + bd[j].ptr = (u32)dma_address; + bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN; + bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN); + + if (j == data->sg_count - 1) /* the last bd */ + bd[j].bd_info |= BDMA_DESC_EOL; + else + bd[j].bd_info &= ~BDMA_DESC_EOL; + + /* checksume need to clear first */ + bd[j].bd_info &= ~BDMA_DESC_CHECKSUM; + bd[j].bd_info |= msdc_dma_calcs((u8 *)(&bd[j]), 16) << 8; + } + + sdr_set_field(host->base + MSDC_DMA_CFG, MSDC_DMA_CFG_DECSEN, 1); + dma_ctrl = readl_relaxed(host->base + MSDC_DMA_CTRL); + dma_ctrl &= ~(MSDC_DMA_CTRL_BRUSTSZ | MSDC_DMA_CTRL_MODE); + dma_ctrl |= (MSDC_BURST_64B << 12 | 1 << 8); + writel_relaxed(dma_ctrl, host->base + MSDC_DMA_CTRL); + writel((u32)dma->gpd_addr, host->base + MSDC_DMA_SA); +} + +static void msdc_prepare_data(struct msdc_host *host, struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (!(data->host_cookie & MSDC_PREPARE_FLAG)) { + bool read = (data->flags & MMC_DATA_READ) != 0; + + data->host_cookie |= MSDC_PREPARE_FLAG; + data->sg_count = dma_map_sg(host->dev, data->sg, data->sg_len, + read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + } +} + +static void msdc_unprepare_data(struct msdc_host *host, struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (data->host_cookie & MSDC_ASYNC_FLAG) + return; + + if (data->host_cookie & MSDC_PREPARE_FLAG) { + bool read = (data->flags & MMC_DATA_READ) != 0; + + dma_unmap_sg(host->dev, data->sg, data->sg_len, + read ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + data->host_cookie &= ~MSDC_PREPARE_FLAG; + } +} + +/* clock control primitives */ +static void msdc_set_timeout(struct msdc_host *host, u32 ns, u32 clks) +{ + u32 timeout, clk_ns; + u32 mode = 0; + + host->timeout_ns = ns; + host->timeout_clks = clks; + if (host->sclk == 0) { + timeout = 0; + } else { + clk_ns = 1000000000UL / host->sclk; + timeout = (ns + clk_ns - 1) / clk_ns + clks; + /* in 1048576 sclk cycle unit */ + timeout = (timeout + (0x1 << 20) - 1) >> 20; + sdr_get_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD, &mode); + /*DDR mode will double the clk cycles for data timeout */ + timeout = mode >= 2 ? timeout * 2 : timeout; + timeout = timeout > 1 ? timeout - 1 : 0; + timeout = timeout > 255 ? 255 : timeout; + } + sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, timeout); +} + +static void msdc_gate_clock(struct msdc_host *host) +{ + clk_disable_unprepare(host->src_clk); + clk_disable_unprepare(host->h_clk); +} + +static void msdc_ungate_clock(struct msdc_host *host) +{ + clk_prepare_enable(host->h_clk); + clk_prepare_enable(host->src_clk); + while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB)) + cpu_relax(); +} + +static void msdc_set_mclk(struct msdc_host *host, int ddr, u32 hz) +{ + u32 mode; + u32 flags; + u32 div; + u32 sclk; + + if (!hz) { + dev_dbg(host->dev, "set mclk to 0\n"); + host->mclk = 0; + sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN); + return; + } + + flags = readl(host->base + MSDC_INTEN); + sdr_clr_bits(host->base + MSDC_INTEN, flags); + if (ddr) { /* may need to modify later */ + mode = 0x2; /* ddr mode and use divisor */ + if (hz >= (host->src_clk_freq >> 2)) { + div = 0; /* mean div = 1/4 */ + sclk = host->src_clk_freq >> 2; /* sclk = clk / 4 */ + } else { + div = (host->src_clk_freq + ((hz << 2) - 1)) / (hz << 2); + sclk = (host->src_clk_freq >> 2) / div; + div = (div >> 1); + } + } else if (hz >= host->src_clk_freq) { + mode = 0x1; /* no divisor */ + div = 0; + sclk = host->src_clk_freq; + } else { + mode = 0x0; /* use divisor */ + if (hz >= (host->src_clk_freq >> 1)) { + div = 0; /* mean div = 1/2 */ + sclk = host->src_clk_freq >> 1; /* sclk = clk / 2 */ + } else { + div = (host->src_clk_freq + ((hz << 2) - 1)) / (hz << 2); + sclk = (host->src_clk_freq >> 2) / div; + } + } + sdr_set_field(host->base + MSDC_CFG, MSDC_CFG_CKMOD | MSDC_CFG_CKDIV, + (mode << 8) | (div % 0xff)); + sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_CKPDN); + while (!(readl(host->base + MSDC_CFG) & MSDC_CFG_CKSTB)) + cpu_relax(); + host->sclk = sclk; + host->mclk = hz; + host->ddr = ddr; + /* need because clk changed. */ + msdc_set_timeout(host, host->timeout_ns, host->timeout_clks); + sdr_set_bits(host->base + MSDC_INTEN, flags); + + dev_dbg(host->dev, "sclk: %d, ddr: %d\n", host->sclk, ddr); +} + +static inline u32 msdc_cmd_find_resp(struct msdc_host *host, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + u32 resp; + + switch (mmc_resp_type(cmd)) { + /* Actually, R1, R5, R6, R7 are the same */ + case MMC_RSP_R1: + resp = 0x1; + break; + case MMC_RSP_R1B: + resp = 0x7; + break; + case MMC_RSP_R2: + resp = 0x2; + break; + case MMC_RSP_R3: + resp = 0x3; + break; + case MMC_RSP_NONE: + default: + resp = 0x0; + break; + } + + return resp; +} + +static inline u32 msdc_cmd_prepare_raw_cmd(struct msdc_host *host, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + /* rawcmd : + * vol_swt << 30 | auto_cmd << 28 | blklen << 16 | go_irq << 15 | + * stop << 14 | rw << 13 | dtype << 11 | rsptyp << 7 | brk << 6 | opcode + */ + u32 opcode = cmd->opcode; + u32 resp = msdc_cmd_find_resp(host, mrq, cmd); + u32 rawcmd = (opcode & 0x3f) | ((resp & 0x7) << 7); + + host->cmd_rsp = resp; + + if ((opcode == SD_IO_RW_DIRECT && cmd->flags == (unsigned int) -1) || + opcode == MMC_STOP_TRANSMISSION) + rawcmd |= (0x1 << 14); + else if (opcode == SD_SWITCH_VOLTAGE) + rawcmd |= (0x1 << 30); + else if (opcode == SD_APP_SEND_SCR || + opcode == SD_APP_SEND_NUM_WR_BLKS || + (opcode == SD_SWITCH && mmc_cmd_type(cmd) == MMC_CMD_ADTC) || + (opcode == SD_APP_SD_STATUS && mmc_cmd_type(cmd) == MMC_CMD_ADTC) || + (opcode == MMC_SEND_EXT_CSD && mmc_cmd_type(cmd) == MMC_CMD_ADTC)) + rawcmd |= (0x1 << 11); + + if (cmd->data) { + struct mmc_data *data = cmd->data; + + if (mmc_op_multi(opcode)) { + if (mmc_card_mmc(host->mmc->card) && mrq->sbc && + !(mrq->sbc->arg & 0xFFFF0000)) + rawcmd |= 0x2 << 28; /* AutoCMD23 */ + } + + rawcmd |= ((data->blksz & 0xFFF) << 16); + if (data->flags & MMC_DATA_WRITE) + rawcmd |= (0x1 << 13); + if (data->blocks > 1) + rawcmd |= (0x2 << 11); + else + rawcmd |= (0x1 << 11); + /* Always use dma mode */ + sdr_clr_bits(host->base + MSDC_CFG, MSDC_CFG_PIO); + + if (host->timeout_ns != data->timeout_ns || + host->timeout_clks != data->timeout_clks) + msdc_set_timeout(host, data->timeout_ns, + data->timeout_clks); + + writel(data->blocks, host->base + SDC_BLK_NUM); + } + return rawcmd; +} + +static void msdc_start_data(struct msdc_host *host, struct mmc_request *mrq, + struct mmc_command *cmd, struct mmc_data *data) +{ + bool read; + + WARN_ON(host->data); + host->data = data; + read = data->flags & MMC_DATA_READ; + + mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); + msdc_dma_setup(host, &host->dma, data); + sdr_set_bits(host->base + MSDC_INTEN, data_ints_mask); + sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_START, 1); + dev_dbg(host->dev, "DMA start\n"); + dev_dbg(host->dev, "%s: cmd=%d DMA data: %d blocks; read=%d\n", + __func__, cmd->opcode, data->blocks, read); +} + +static int msdc_auto_cmd_done(struct msdc_host *host, int events, + struct mmc_command *cmd) +{ + u32 *rsp = cmd->resp; + + rsp[0] = readl(host->base + SDC_ACMD_RESP); + + if (events & MSDC_INT_ACMDRDY) { + cmd->error = 0; + } else { + msdc_reset_hw(host); + if (events & MSDC_INT_ACMDCRCERR) { + cmd->error = -EILSEQ; + host->error |= REQ_STOP_EIO; + } else if (events & MSDC_INT_ACMDTMO) { + cmd->error = -ETIMEDOUT; + host->error |= REQ_STOP_TMO; + } + dev_err(host->dev, + "%s: AUTO_CMD%d arg=%08X; rsp %08X; cmd_error=%d\n", + __func__, cmd->opcode, cmd->arg, rsp[0], cmd->error); + } + return cmd->error; +} + +static void msdc_track_cmd_data(struct msdc_host *host, + struct mmc_command *cmd, struct mmc_data *data) +{ + if (host->error) + dev_dbg(host->dev, "%s: cmd=%d arg=%08X; host->error=0x%08X\n", + __func__, cmd->opcode, cmd->arg, host->error); +} + +static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) +{ + unsigned long flags; + bool ret; + + ret = cancel_delayed_work(&host->req_timeout); + if (!ret) { + /* delay work already running */ + return; + } + spin_lock_irqsave(&host->lock, flags); + host->mrq = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + msdc_track_cmd_data(host, mrq->cmd, mrq->data); + if (mrq->data) + msdc_unprepare_data(host, mrq); + mmc_request_done(host->mmc, mrq); +} + +/* returns true if command is fully handled; returns false otherwise */ +static bool msdc_cmd_done(struct msdc_host *host, int events, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + bool done = false; + bool sbc_error; + unsigned long flags; + u32 *rsp = cmd->resp; + + if (mrq->sbc && cmd == mrq->cmd && + (events & (MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR + | MSDC_INT_ACMDTMO))) + msdc_auto_cmd_done(host, events, mrq->sbc); + + sbc_error = mrq->sbc && mrq->sbc->error; + + if (!sbc_error && !(events & (MSDC_INT_CMDRDY + | MSDC_INT_RSPCRCERR + | MSDC_INT_CMDTMO))) + return done; + + spin_lock_irqsave(&host->lock, flags); + done = !host->cmd; + host->cmd = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + if (done) + return true; + + sdr_clr_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY | + MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO | + MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR | + MSDC_INTEN_ACMDTMO); + writel(cmd->arg, host->base + SDC_ARG); + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + rsp[0] = readl(host->base + SDC_RESP3); + rsp[1] = readl(host->base + SDC_RESP2); + rsp[2] = readl(host->base + SDC_RESP1); + rsp[3] = readl(host->base + SDC_RESP0); + } else { + rsp[0] = readl(host->base + SDC_RESP0); + } + } + + if (!sbc_error && !(events & MSDC_INT_CMDRDY)) { + msdc_reset_hw(host); + if (events & MSDC_INT_RSPCRCERR) { + cmd->error = -EILSEQ; + host->error |= REQ_CMD_EIO; + } else if (events & MSDC_INT_CMDTMO) { + cmd->error = -ETIMEDOUT; + host->error |= REQ_CMD_TMO; + } + } + if (cmd->error) + dev_dbg(host->dev, + "%s: cmd=%d arg=%08X; rsp %08X; cmd_error=%d\n", + __func__, cmd->opcode, cmd->arg, rsp[0], + cmd->error); + + msdc_cmd_next(host, mrq, cmd); + return true; +} + +/* It is the core layer's responsibility to ensure card status + * is correct before issue a request. but host design do below + * checks recommended. + */ +static inline bool msdc_cmd_is_ready(struct msdc_host *host, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + /* The max busy time we can endure is 20ms */ + unsigned long tmo = jiffies + msecs_to_jiffies(20); + + while ((readl(host->base + SDC_STS) & SDC_STS_CMDBUSY) && + time_before(jiffies, tmo)) + cpu_relax(); + if (readl(host->base + SDC_STS) & SDC_STS_CMDBUSY) { + dev_err(host->dev, "CMD bus busy detected\n"); + host->error |= REQ_CMD_BUSY; + msdc_cmd_done(host, MSDC_INT_CMDTMO, mrq, cmd); + return false; + } + + if (mmc_resp_type(cmd) == MMC_RSP_R1B || cmd->data) { + tmo = jiffies + msecs_to_jiffies(20); + /* R1B or with data, should check SDCBUSY */ + while ((readl(host->base + SDC_STS) & SDC_STS_SDCBUSY) && + time_before(jiffies, tmo)) + cpu_relax(); + if (readl(host->base + SDC_STS) & SDC_STS_SDCBUSY) { + dev_err(host->dev, "Controller busy detected\n"); + host->error |= REQ_CMD_BUSY; + msdc_cmd_done(host, MSDC_INT_CMDTMO, mrq, cmd); + return false; + } + } + return true; +} + +static void msdc_start_command(struct msdc_host *host, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + u32 rawcmd; + + WARN_ON(host->cmd); + host->cmd = cmd; + + if (!msdc_cmd_is_ready(host, mrq, cmd)) + return; + + if ((readl(host->base + MSDC_FIFOCS) & MSDC_FIFOCS_TXCNT) >> 16 || + readl(host->base + MSDC_FIFOCS) & MSDC_FIFOCS_RXCNT) { + dev_err(host->dev, "TX/RX FIFO non-empty before start of IO. Reset\n"); + msdc_reset_hw(host); + } + + cmd->error = 0; + rawcmd = msdc_cmd_prepare_raw_cmd(host, mrq, cmd); + mod_delayed_work(system_wq, &host->req_timeout, DAT_TIMEOUT); + + sdr_set_bits(host->base + MSDC_INTEN, MSDC_INTEN_CMDRDY | + MSDC_INTEN_RSPCRCERR | MSDC_INTEN_CMDTMO | + MSDC_INTEN_ACMDRDY | MSDC_INTEN_ACMDCRCERR | + MSDC_INTEN_ACMDTMO); + writel(cmd->arg, host->base + SDC_ARG); + writel(rawcmd, host->base + SDC_CMD); +} + +static void msdc_cmd_next(struct msdc_host *host, + struct mmc_request *mrq, struct mmc_command *cmd) +{ + if (cmd->error || (mrq->sbc && mrq->sbc->error)) + msdc_request_done(host, mrq); + else if (cmd == mrq->sbc) + msdc_start_command(host, mrq, mrq->cmd); + else if (!cmd->data) + msdc_request_done(host, mrq); + else + msdc_start_data(host, mrq, cmd, cmd->data); +} + +static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct msdc_host *host = mmc_priv(mmc); + + host->error = 0; + WARN_ON(host->mrq); + host->mrq = mrq; + + if (mrq->data) + msdc_prepare_data(host, mrq); + + /* if SBC is required, we have HW option and SW option. + * if HW option is enabled, and SBC does not have "special" flags, + * use HW option, otherwise use SW option + */ + if (mrq->sbc && (!mmc_card_mmc(mmc->card) || + (mrq->sbc->arg & 0xFFFF0000))) + msdc_start_command(host, mrq, mrq->sbc); + else + msdc_start_command(host, mrq, mrq->cmd); +} + +static void msdc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, + bool is_first_req) +{ + struct msdc_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data) + return; + + msdc_prepare_data(host, mrq); + data->host_cookie |= MSDC_ASYNC_FLAG; +} + +static void msdc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, + int err) +{ + struct msdc_host *host = mmc_priv(mmc); + struct mmc_data *data; + + data = mrq->data; + if (!data) + return; + if (data->host_cookie) { + data->host_cookie &= ~MSDC_ASYNC_FLAG; + msdc_unprepare_data(host, mrq); + } +} + +static void msdc_data_xfer_next(struct msdc_host *host, + struct mmc_request *mrq, struct mmc_data *data) +{ + if (mmc_op_multi(mrq->cmd->opcode) && mrq->stop && !mrq->stop->error && + (!data->bytes_xfered || !mrq->sbc)) + msdc_start_command(host, mrq, mrq->stop); + else + msdc_request_done(host, mrq); +} + +static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, + struct mmc_request *mrq, struct mmc_data *data) +{ + struct mmc_command *stop = data->stop; + unsigned long flags; + bool done; + unsigned int check_data = events & + (MSDC_INT_XFER_COMPL | MSDC_INT_DATCRCERR | MSDC_INT_DATTMO + | MSDC_INT_DMA_BDCSERR | MSDC_INT_DMA_GPDCSERR + | MSDC_INT_DMA_PROTECT); + + spin_lock_irqsave(&host->lock, flags); + done = !host->data; + if (check_data) + host->data = NULL; + spin_unlock_irqrestore(&host->lock, flags); + + if (done) + return true; + + if (check_data || (stop && stop->error)) { + dev_dbg(host->dev, "DMA status: 0x%8X\n", + readl(host->base + MSDC_DMA_CFG)); + sdr_set_field(host->base + MSDC_DMA_CTRL, MSDC_DMA_CTRL_STOP, + 1); + while (readl(host->base + MSDC_DMA_CFG) & MSDC_DMA_CFG_STS) + cpu_relax(); + sdr_clr_bits(host->base + MSDC_INTEN, data_ints_mask); + dev_dbg(host->dev, "DMA stop\n"); + + if ((events & MSDC_INT_XFER_COMPL) && (!stop || !stop->error)) { + data->bytes_xfered = data->blocks * data->blksz; + } else { + dev_err(host->dev, "interrupt events: %x\n", events); + msdc_reset_hw(host); + host->error |= REQ_DAT_ERR; + data->bytes_xfered = 0; + + if (events & MSDC_INT_DATTMO) + data->error = -ETIMEDOUT; + + dev_err(host->dev, "%s: cmd=%d; blocks=%d", + __func__, mrq->cmd->opcode, data->blocks); + dev_err(host->dev, "data_error=%d xfer_size=%d\n", + (int)data->error, data->bytes_xfered); + } + + msdc_data_xfer_next(host, mrq, data); + done = true; + } + return done; +} + +static void msdc_set_buswidth(struct msdc_host *host, u32 width) +{ + u32 val = readl(host->base + SDC_CFG); + + val &= ~SDC_CFG_BUSWIDTH; + + switch (width) { + default: + case MMC_BUS_WIDTH_1: + val |= (MSDC_BUS_1BITS << 16); + break; + case MMC_BUS_WIDTH_4: + val |= (MSDC_BUS_4BITS << 16); + break; + case MMC_BUS_WIDTH_8: + val |= (MSDC_BUS_8BITS << 16); + break; + } + + writel(val, host->base + SDC_CFG); + dev_dbg(host->dev, "Bus Width = %d", width); +} + +static int msdc_ops_switch_volt(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct msdc_host *host = mmc_priv(mmc); + int min_uv, max_uv; + int ret = 0; + + if (!IS_ERR(mmc->supply.vqmmc)) { + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + min_uv = 3300000; + max_uv = 3300000; + } else if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) { + min_uv = 1800000; + max_uv = 1800000; + } else { + dev_err(host->dev, "Unsupported signal voltage!\n"); + return -EINVAL; + } + + ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv); + if (ret) { + dev_err(host->dev, + "Regulator set error %d: %d - %d\n", + ret, min_uv, max_uv); + } else { + /* Apply different pinctrl settings for different signal voltage */ + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_180) + pinctrl_select_state(host->pinctrl, host->pins_uhs); + else + pinctrl_select_state(host->pinctrl, host->pins_default); + } + } + return ret; +} + +static int msdc_card_busy(struct mmc_host *mmc) +{ + struct msdc_host *host = mmc_priv(mmc); + u32 status = readl(host->base + MSDC_PS); + + /* check if any pin between dat[0:3] is low */ + if (((status >> 16) & 0xf) != 0xf) + return 1; + + return 0; +} + +static void msdc_request_timeout(struct work_struct *work) +{ + struct msdc_host *host = container_of(work, struct msdc_host, + req_timeout.work); + + /* simulate HW timeout status */ + dev_err(host->dev, "%s: aborting cmd/data/mrq\n", __func__); + if (host->mrq) { + dev_err(host->dev, "%s: aborting mrq=%p cmd=%d\n", __func__, + host->mrq, host->mrq->cmd->opcode); + if (host->cmd) { + dev_err(host->dev, "%s: aborting cmd=%d\n", + __func__, host->cmd->opcode); + msdc_cmd_done(host, MSDC_INT_CMDTMO, host->mrq, + host->cmd); + } else if (host->data) { + dev_err(host->dev, "%s: abort data: cmd%d; %d blocks\n", + __func__, host->mrq->cmd->opcode, + host->data->blocks); + msdc_data_xfer_done(host, MSDC_INT_DATTMO, host->mrq, + host->data); + } + } +} + +static irqreturn_t msdc_irq(int irq, void *dev_id) +{ + struct msdc_host *host = (struct msdc_host *) dev_id; + + while (true) { + unsigned long flags; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + u32 events, event_mask; + + spin_lock_irqsave(&host->lock, flags); + events = readl(host->base + MSDC_INT); + event_mask = readl(host->base + MSDC_INTEN); + /* clear interrupts */ + writel(events & event_mask, host->base + MSDC_INT); + + mrq = host->mrq; + cmd = host->cmd; + data = host->data; + spin_unlock_irqrestore(&host->lock, flags); + + if (!(events & event_mask)) + break; + + if (!mrq) { + dev_err(host->dev, + "%s: MRQ=NULL; events=%08X; event_mask=%08X\n", + __func__, events, event_mask); + WARN_ON(1); + break; + } + + dev_dbg(host->dev, "%s: events=%08X\n", __func__, events); + + if (cmd) + msdc_cmd_done(host, events, mrq, cmd); + else if (data) + msdc_data_xfer_done(host, events, mrq, data); + } + + return IRQ_HANDLED; +} + +static void msdc_init_hw(struct msdc_host *host) +{ + u32 val; + + /* Configure to MMC/SD mode, clock free running */ + sdr_set_bits(host->base + MSDC_CFG, MSDC_CFG_MODE | MSDC_CFG_CKPDN); + + /* Reset */ + msdc_reset_hw(host); + + /* Disable card detection */ + sdr_clr_bits(host->base + MSDC_PS, MSDC_PS_CDEN); + + /* Disable and clear all interrupts */ + writel(0, host->base + MSDC_INTEN); + val = readl(host->base + MSDC_INT); + writel(val, host->base + MSDC_INT); + + writel(0, host->base + MSDC_PAD_TUNE); + writel(0, host->base + MSDC_IOCON); + sdr_set_field(host->base + MSDC_IOCON, MSDC_IOCON_DDLSEL, 1); + writel(0x403c004f, host->base + MSDC_PATCH_BIT); + sdr_set_field(host->base + MSDC_PATCH_BIT, MSDC_CKGEN_MSDC_DLY_SEL, 1); + writel(0xffff0089, host->base + MSDC_PATCH_BIT1); + /* Configure to enable SDIO mode. + * it's must otherwise sdio cmd5 failed + */ + sdr_set_bits(host->base + SDC_CFG, SDC_CFG_SDIO); + + /* disable detect SDIO device interrupt function */ + sdr_clr_bits(host->base + SDC_CFG, SDC_CFG_SDIOIDE); + + /* Configure to default data timeout */ + sdr_set_field(host->base + SDC_CFG, SDC_CFG_DTOC, 3); + + dev_dbg(host->dev, "init hardware done!"); +} + +static void msdc_deinit_hw(struct msdc_host *host) +{ + u32 val; + /* Disable and clear all interrupts */ + writel(0, host->base + MSDC_INTEN); + + val = readl(host->base + MSDC_INT); + writel(val, host->base + MSDC_INT); +} + +/* init gpd and bd list in msdc_drv_probe */ +static void msdc_init_gpd_bd(struct msdc_host *host, struct msdc_dma *dma) +{ + struct mt_gpdma_desc *gpd = dma->gpd; + struct mt_bdma_desc *bd = dma->bd; + int i; + + memset(gpd, 0, sizeof(struct mt_gpdma_desc)); + + gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */ + gpd->ptr = (u32)dma->bd_addr; /* physical address */ + + memset(bd, 0, sizeof(struct mt_bdma_desc) * MAX_BD_NUM); + for (i = 0; i < (MAX_BD_NUM - 1); i++) + bd[i].next = (u32)dma->bd_addr + sizeof(*bd) * (i + 1); +} + +static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct msdc_host *host = mmc_priv(mmc); + int ret; + u32 ddr = 0; + + if (ios->timing == MMC_TIMING_UHS_DDR50 || + ios->timing == MMC_TIMING_MMC_DDR52) + ddr = 1; + + msdc_set_buswidth(host, ios->bus_width); + + /* Suspend/Resume will do power off/on */ + switch (ios->power_mode) { + case MMC_POWER_UP: + if (!IS_ERR(mmc->supply.vmmc)) { + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, + ios->vdd); + if (ret) { + dev_err(host->dev, "Failed to set vmmc power!\n"); + return; + } + } + break; + case MMC_POWER_ON: + if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret) + dev_err(host->dev, "Failed to set vqmmc power!\n"); + else + host->vqmmc_enabled = true; + } + break; + case MMC_POWER_OFF: + if (!IS_ERR(mmc->supply.vmmc)) + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + + if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) { + regulator_disable(mmc->supply.vqmmc); + host->vqmmc_enabled = false; + } + break; + default: + break; + } + + if (host->mclk != ios->clock || host->ddr != ddr) + msdc_set_mclk(host, ddr, ios->clock); +} + +static struct mmc_host_ops mt_msdc_ops = { + .post_req = msdc_post_req, + .pre_req = msdc_pre_req, + .request = msdc_ops_request, + .set_ios = msdc_ops_set_ios, + .start_signal_voltage_switch = msdc_ops_switch_volt, + .card_busy = msdc_card_busy, +}; + +static int msdc_drv_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct msdc_host *host; + struct resource *res; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No DT found\n"); + return -EINVAL; + } + /* Allocate MMC host for this device */ + mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + ret = mmc_of_parse(mmc); + if (ret) + goto host_free; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + host->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(host->base)) { + ret = PTR_ERR(host->base); + goto host_free; + } + + ret = mmc_regulator_get_supply(mmc); + if (ret == -EPROBE_DEFER) + goto host_free; + + host->src_clk = devm_clk_get(&pdev->dev, "source"); + if (IS_ERR(host->src_clk)) { + ret = PTR_ERR(host->src_clk); + goto host_free; + } + + host->h_clk = devm_clk_get(&pdev->dev, "hclk"); + if (IS_ERR(host->h_clk)) { + ret = PTR_ERR(host->h_clk); + goto host_free; + } + + host->irq = platform_get_irq(pdev, 0); + if (host->irq < 0) { + ret = -EINVAL; + goto host_free; + } + + host->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR(host->pinctrl)) { + ret = PTR_ERR(host->pinctrl); + dev_err(&pdev->dev, "Cannot find pinctrl!\n"); + goto host_free; + } + + host->pins_default = pinctrl_lookup_state(host->pinctrl, "default"); + if (IS_ERR(host->pins_default)) { + ret = PTR_ERR(host->pins_default); + dev_err(&pdev->dev, "Cannot find pinctrl default!\n"); + goto host_free; + } + + host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs"); + if (IS_ERR(host->pins_uhs)) { + ret = PTR_ERR(host->pins_uhs); + dev_err(&pdev->dev, "Cannot find pinctrl uhs!\n"); + goto host_free; + } + + host->dev = &pdev->dev; + host->mmc = mmc; + host->src_clk_freq = clk_get_rate(host->src_clk); + /* Set host parameters to mmc */ + mmc->ops = &mt_msdc_ops; + mmc->f_min = host->src_clk_freq / (4 * 255); + + mmc->caps |= MMC_CAP_ERASE | MMC_CAP_CMD23; + /* MMC core transfer sizes tunable parameters */ + mmc->max_segs = MAX_BD_NUM; + mmc->max_seg_size = BDMA_DESC_BUFLEN; + mmc->max_blk_size = 2048; + mmc->max_req_size = 512 * 1024; + mmc->max_blk_count = mmc->max_req_size / 512; + host->dma_mask = DMA_BIT_MASK(32); + mmc_dev(mmc)->dma_mask = &host->dma_mask; + + host->timeout_clks = 3 * 1048576; + host->dma.gpd = dma_alloc_coherent(&pdev->dev, + sizeof(struct mt_gpdma_desc), + &host->dma.gpd_addr, GFP_KERNEL); + host->dma.bd = dma_alloc_coherent(&pdev->dev, + MAX_BD_NUM * sizeof(struct mt_bdma_desc), + &host->dma.bd_addr, GFP_KERNEL); + if (!host->dma.gpd || !host->dma.bd) { + ret = -ENOMEM; + goto release_mem; + } + msdc_init_gpd_bd(host, &host->dma); + INIT_DELAYED_WORK(&host->req_timeout, msdc_request_timeout); + spin_lock_init(&host->lock); + + platform_set_drvdata(pdev, mmc); + msdc_ungate_clock(host); + msdc_init_hw(host); + + ret = devm_request_irq(&pdev->dev, host->irq, msdc_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, pdev->name, host); + if (ret) + goto release; + + ret = mmc_add_host(mmc); + if (ret) + goto release; + + return 0; + +release: + platform_set_drvdata(pdev, NULL); + msdc_deinit_hw(host); + msdc_gate_clock(host); +release_mem: + if (host->dma.gpd) + dma_free_coherent(&pdev->dev, + sizeof(struct mt_gpdma_desc), + host->dma.gpd, host->dma.gpd_addr); + if (host->dma.bd) + dma_free_coherent(&pdev->dev, + MAX_BD_NUM * sizeof(struct mt_bdma_desc), + host->dma.bd, host->dma.bd_addr); +host_free: + mmc_free_host(mmc); + + return ret; +} + +static int msdc_drv_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct msdc_host *host; + + mmc = platform_get_drvdata(pdev); + host = mmc_priv(mmc); + + platform_set_drvdata(pdev, NULL); + mmc_remove_host(host->mmc); + msdc_deinit_hw(host); + msdc_gate_clock(host); + + dma_free_coherent(&pdev->dev, + sizeof(struct mt_gpdma_desc), + host->dma.gpd, host->dma.gpd_addr); + dma_free_coherent(&pdev->dev, MAX_BD_NUM * sizeof(struct mt_bdma_desc), + host->dma.bd, host->dma.bd_addr); + + mmc_free_host(host->mmc); + + return 0; +} + +static const struct of_device_id msdc_of_ids[] = { + { .compatible = "mediatek,mt8135-mmc", }, + {} +}; + +static struct platform_driver mt_msdc_driver = { + .probe = msdc_drv_probe, + .remove = msdc_drv_remove, + .driver = { + .name = "mtk-msdc", + .of_match_table = msdc_of_ids, + }, +}; + +module_platform_driver(mt_msdc_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MediaTek SD/MMC Card Driver"); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index de722d4e9d61..258daf914c6d 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -121,6 +121,7 @@ struct mmc_data { struct mmc_request *mrq; /* associated request */ unsigned int sg_len; /* size of scatter list */ + int sg_count; /* mapped sg entries */ struct scatterlist *sg; /* I/O scatter list */ s32 host_cookie; /* host private data */ }; -- cgit v1.2.3 From 4b8a43e90ccf88c91475b802d1388c0779be2bda Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Mon, 15 Jun 2015 19:20:49 +0800 Subject: mmc: mediatek: Add PM support for MMC driver Add PM support for Mediatek MMC driver Save/restore registers when PM Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 89 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index fd965cb81133..7153500dd007 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include @@ -213,6 +215,7 @@ #define MSDC_ASYNC_FLAG (0x1 << 1) #define MSDC_MMAP_FLAG (0x1 << 2) +#define MTK_MMC_AUTOSUSPEND_DELAY 50 #define CMD_TIMEOUT (HZ/10 * 5) /* 100ms x5 */ #define DAT_TIMEOUT (HZ * 5) /* 1000ms x5 */ @@ -255,6 +258,15 @@ struct msdc_dma { dma_addr_t bd_addr; /* the physical address of bd array */ }; +struct msdc_save_para { + u32 msdc_cfg; + u32 iocon; + u32 sdc_cfg; + u32 pad_tune; + u32 patch_bit0; + u32 patch_bit1; +}; + struct msdc_host { struct device *dev; struct mmc_host *mmc; /* mmc structure */ @@ -287,6 +299,7 @@ struct msdc_host { u32 sclk; /* SD/MS bus clock frequency */ bool ddr; bool vqmmc_enabled; + struct msdc_save_para save_para; /* used when gate HCLK */ }; static void sdr_set_bits(void __iomem *reg, u32 bs) @@ -678,6 +691,9 @@ static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) if (mrq->data) msdc_unprepare_data(host, mrq); mmc_request_done(host->mmc, mrq); + + pm_runtime_mark_last_busy(host->dev); + pm_runtime_put_autosuspend(host->dev); } /* returns true if command is fully handled; returns false otherwise */ @@ -832,6 +848,8 @@ static void msdc_ops_request(struct mmc_host *mmc, struct mmc_request *mrq) WARN_ON(host->mrq); host->mrq = mrq; + pm_runtime_get_sync(host->dev); + if (mrq->data) msdc_prepare_data(host, mrq); @@ -1146,6 +1164,8 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) int ret; u32 ddr = 0; + pm_runtime_get_sync(host->dev); + if (ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_DDR52) ddr = 1; @@ -1160,7 +1180,7 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ios->vdd); if (ret) { dev_err(host->dev, "Failed to set vmmc power!\n"); - return; + goto end; } } break; @@ -1188,6 +1208,10 @@ static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (host->mclk != ios->clock || host->ddr != ddr) msdc_set_mclk(host, ddr, ios->clock); + +end: + pm_runtime_mark_last_busy(host->dev); + pm_runtime_put_autosuspend(host->dev); } static struct mmc_host_ops mt_msdc_ops = { @@ -1311,12 +1335,18 @@ static int msdc_drv_probe(struct platform_device *pdev) if (ret) goto release; + pm_runtime_set_active(host->dev); + pm_runtime_set_autosuspend_delay(host->dev, MTK_MMC_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(host->dev); + pm_runtime_enable(host->dev); ret = mmc_add_host(mmc); + if (ret) - goto release; + goto end; return 0; - +end: + pm_runtime_disable(host->dev); release: platform_set_drvdata(pdev, NULL); msdc_deinit_hw(host); @@ -1344,11 +1374,15 @@ static int msdc_drv_remove(struct platform_device *pdev) mmc = platform_get_drvdata(pdev); host = mmc_priv(mmc); + pm_runtime_get_sync(host->dev); + platform_set_drvdata(pdev, NULL); mmc_remove_host(host->mmc); msdc_deinit_hw(host); msdc_gate_clock(host); + pm_runtime_disable(host->dev); + pm_runtime_put_noidle(host->dev); dma_free_coherent(&pdev->dev, sizeof(struct mt_gpdma_desc), host->dma.gpd, host->dma.gpd_addr); @@ -1360,6 +1394,54 @@ static int msdc_drv_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static void msdc_save_reg(struct msdc_host *host) +{ + host->save_para.msdc_cfg = readl(host->base + MSDC_CFG); + host->save_para.iocon = readl(host->base + MSDC_IOCON); + host->save_para.sdc_cfg = readl(host->base + SDC_CFG); + host->save_para.pad_tune = readl(host->base + MSDC_PAD_TUNE); + host->save_para.patch_bit0 = readl(host->base + MSDC_PATCH_BIT); + host->save_para.patch_bit1 = readl(host->base + MSDC_PATCH_BIT1); +} + +static void msdc_restore_reg(struct msdc_host *host) +{ + writel(host->save_para.msdc_cfg, host->base + MSDC_CFG); + writel(host->save_para.iocon, host->base + MSDC_IOCON); + writel(host->save_para.sdc_cfg, host->base + SDC_CFG); + writel(host->save_para.pad_tune, host->base + MSDC_PAD_TUNE); + writel(host->save_para.patch_bit0, host->base + MSDC_PATCH_BIT); + writel(host->save_para.patch_bit1, host->base + MSDC_PATCH_BIT1); +} + +static int msdc_runtime_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct msdc_host *host = mmc_priv(mmc); + + msdc_save_reg(host); + msdc_gate_clock(host); + return 0; +} + +static int msdc_runtime_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct msdc_host *host = mmc_priv(mmc); + + msdc_ungate_clock(host); + msdc_restore_reg(host); + return 0; +} +#endif + +static const struct dev_pm_ops msdc_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(msdc_runtime_suspend, msdc_runtime_resume, NULL) +}; + static const struct of_device_id msdc_of_ids[] = { { .compatible = "mediatek,mt8135-mmc", }, {} @@ -1371,6 +1453,7 @@ static struct platform_driver mt_msdc_driver = { .driver = { .name = "mtk-msdc", .of_match_table = msdc_of_ids, + .pm = &msdc_dev_pm_ops, }, }; -- cgit v1.2.3 From a8c27c0bea45c6531fbc1e3be79ddd5a9bc1ba3e Mon Sep 17 00:00:00 2001 From: Rabin Vincent Date: Sun, 14 Jun 2015 19:26:11 +0200 Subject: mmc: queue: prevent soft lockups on PREEMPT=n On systems with CONFIG_PREEMPT=n, under certain circumstances, mmcqd can continuously process requests for several seconds without blocking, triggering the soft lockup watchdog. For example, this can happen if mmcqd runs on the CPU which services the controller's interrupt, and a process on a different CPU continuously writes to the MMC block device. NMI watchdog: BUG: soft lockup - CPU#0 stuck for 22s! [mmcqd/0:664] CPU: 0 PID: 664 Comm: mmcqd/0 Not tainted 4.1.0-rc7+ #4 PC is at _raw_spin_unlock_irqrestore+0x24/0x28 LR is at mmc_start_request+0x104/0x134 ... [<805112a8>] (_raw_spin_unlock_irqrestore) from [<803db664>] (mmc_start_request+0x104/0x134) [<803db664>] (mmc_start_request) from [<803dc008>] (mmc_start_req+0x274/0x394) [<803dc008>] (mmc_start_req) from [<803eb2c4>] (mmc_blk_issue_rw_rq+0xd0/0xb98) [<803eb2c4>] (mmc_blk_issue_rw_rq) from [<803ebe8c>] (mmc_blk_issue_rq+0x100/0x470) [<803ebe8c>] (mmc_blk_issue_rq) from [<803ecab8>] (mmc_queue_thread+0xd0/0x170) [<803ecab8>] (mmc_queue_thread) from [<8003fd14>] (kthread+0xe0/0xfc) [<8003fd14>] (kthread) from [<8000f768>] (ret_from_fork+0x14/0x2c) Fix it by adding a cond_resched() in the request handling loop so that other processes get a chance to run. Signed-off-by: Rabin Vincent Signed-off-by: Ulf Hansson --- drivers/mmc/card/queue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index d34e09b4161e..b5a2b145d89f 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -68,6 +68,7 @@ static int mmc_queue_thread(void *d) set_current_state(TASK_RUNNING); cmd_flags = req ? req->cmd_flags : 0; mq->issue_fn(mq, req); + cond_resched(); if (mq->flags & MMC_QUEUE_NEW_REQUEST) { mq->flags &= ~MMC_QUEUE_NEW_REQUEST; continue; /* fetch again */ -- cgit v1.2.3