From 57aac3377f6a9dc9c4882c344b00e8a876cb44dd Mon Sep 17 00:00:00 2001 From: Phil Edworthy Date: Tue, 13 Mar 2018 14:26:40 +0000 Subject: mmc: sdhci-of-arasan: Add quirk to avoid unexpected interrupt msgs On SD 2.00 cards we get lots of these messages: "mmc0: Got data interrupt 0x00000002 even though no data operation was in progress" By applying the SDHCI_QUIRK2_STOP_WITH_TC quirk, the messages no longer happen. A single card claiming to be SD 3.00 compliant also generates the interrupts, but since the card's manfacturing date is 2002 mar, it's unlikely to really be SD 3.00. This card is a 8GB SanDisk 'SU08G' 8.0 (SDHC class 4). This has been reported on Xilinx devices that also use the Arasan IP. See https://patchwork.kernel.org/patch/8062871/ This has been tested on the Renesas RZ/ND-DB board with the RZ/N1 SoC. The Arasan IP in this device is version 1.39a and uses a max SD clock of 50MHz and does not support DDR modes. Signed-off-by: Phil Edworthy Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index c33a5f7393bd..ab66e323bcb8 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -290,7 +290,8 @@ static const struct sdhci_pltfm_data sdhci_arasan_pdata = { .ops = &sdhci_arasan_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN | + SDHCI_QUIRK2_STOP_WITH_TC, }; static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask) -- cgit v1.2.3 From 486e6661367b40f927aadbed73237693396cbf94 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 26 Mar 2018 17:33:14 +1100 Subject: mmc: pwrseq: Use kmalloc_array instead of stack VLA The use of stack Variable Length Arrays needs to be avoided, as they can be a vector for stack exhaustion, which can be both a runtime bug (kernel Oops) or a security flaw (overwriting memory beyond the stack). Also, in general, as code evolves it is easy to lose track of how big a VLA can get. Thus, we can end up having runtime failures that are hard to debug. As part of the directive[1] to remove all VLAs from the kernel, and build with -Wvla. Currently driver is using a VLA declared using the number of descriptors. This array is used to store integer values and is later used as an argument to `gpiod_set_array_value_cansleep()` This can be avoided by using `kmalloc_array()` to allocate memory for the array of integer values. Memory is free'd before return from function. >From the code it appears that it is safe to sleep so we can use GFP_KERNEL (based _cansleep() suffix of function `gpiod_set_array_value_cansleep()`. It can be expected that this patch will result in a small increase in overhead due to the use of `kmalloc_array()` [1] https://lkml.org/lkml/2018/3/7/621 Signed-off-by: Tobin C. Harding Signed-off-by: Ulf Hansson --- drivers/mmc/core/pwrseq_simple.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 13ef162cf066..a8b9fee4d62a 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -40,14 +40,18 @@ static void mmc_pwrseq_simple_set_gpios_value(struct mmc_pwrseq_simple *pwrseq, struct gpio_descs *reset_gpios = pwrseq->reset_gpios; if (!IS_ERR(reset_gpios)) { - int i; - int values[reset_gpios->ndescs]; + int i, *values; + int nvalues = reset_gpios->ndescs; - for (i = 0; i < reset_gpios->ndescs; i++) + values = kmalloc_array(nvalues, sizeof(int), GFP_KERNEL); + if (!values) + return; + + for (i = 0; i < nvalues; i++) values[i] = value; - gpiod_set_array_value_cansleep( - reset_gpios->ndescs, reset_gpios->desc, values); + gpiod_set_array_value_cansleep(nvalues, reset_gpios->desc, values); + kfree(values); } } -- cgit v1.2.3 From ff178981bd5fd1667f373098740cb1c6d6efa1ba Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 26 Mar 2018 17:26:25 +0800 Subject: mmc: dw_mmc: update actual clock for mmc debugfs Respect the actual clock for mmc debugfs to help better debug the hardware. mmc_host mmc0: Bus speed (slot 0) = 135475200Hz (slot req 150000000Hz, actual 135475200HZ div = 0) cat /sys/kernel/debug/mmc0/ios clock: 150000000 Hz actual clock: 135475200 Hz vdd: 21 (3.3 ~ 3.4 V) bus mode: 2 (push-pull) chip select: 0 (don't care) power mode: 2 (on) bus width: 3 (8 bits) timing spec: 9 (mmc HS200) signal voltage: 0 (1.80 V) driver type: 0 (driver type B) Cc: Xiao Yao Cc: Ziyuan Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 29a1afa81f66..623f4d27fa01 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1230,6 +1230,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) if (host->state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; + slot->mmc->actual_clock = 0; + if (!clock) { mci_writel(host, CLKENA, 0); mci_send_cmd(slot, sdmmc_cmd_bits, 0); @@ -1288,6 +1290,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* keep the last clock value that was requested from core */ slot->__clk_old = clock; + slot->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : + host->bus_hz; } host->current_speed = clock; -- cgit v1.2.3 From 7e8466e222798f309472d1a4b71943b2941ba961 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Wed, 28 Mar 2018 18:00:44 -0300 Subject: mmc: jz4740: Fix error exit path in driver's probe Currently, if jz4740_mmc_request_gpios() fails, the driver tries to release DMA resources. This is wrong because DMA is requested at a later stage. Signed-off-by: Paul Cercueil [Ezequiel: cleanup commit message] Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index a0168e9e4fce..97727cd34fc1 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1006,7 +1006,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) ret = jz4740_mmc_request_gpios(mmc, pdev); if (ret) - goto err_release_dma; + goto err_free_host; mmc->ops = &jz4740_mmc_ops; mmc->f_min = JZ_MMC_CLK_RATE / 128; @@ -1038,16 +1038,17 @@ static int jz4740_mmc_probe(struct platform_device* pdev) jz4740_mmc_clock_disable(host); timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); - host->use_dma = true; - if (host->use_dma && jz4740_mmc_acquire_dma_channels(host) != 0) - host->use_dma = false; + ret = jz4740_mmc_acquire_dma_channels(host); + if (ret == -EPROBE_DEFER) + goto err_free_irq; + host->use_dma = !ret; platform_set_drvdata(pdev, host); ret = mmc_add_host(mmc); if (ret) { dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); - goto err_free_irq; + goto err_release_dma; } dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n"); @@ -1057,13 +1058,13 @@ static int jz4740_mmc_probe(struct platform_device* pdev) return 0; +err_release_dma: + if (host->use_dma) + jz4740_mmc_release_dma_channels(host); err_free_irq: free_irq(host->irq, host); err_free_gpios: jz4740_mmc_free_gpios(pdev); -err_release_dma: - if (host->use_dma) - jz4740_mmc_release_dma_channels(host); err_free_host: mmc_free_host(mmc); -- cgit v1.2.3 From 7e7845f38b13a47dc0acebff47631a2c10378a3d Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:45 -0300 Subject: mmc: jz4780: Order headers alphabetically Just a minor cleanup to order the headers alphabetically. This helps prevent merge conflicts. Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 97727cd34fc1..eb57f514a459 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -13,24 +13,24 @@ * */ -#include -#include +#include +#include +#include +#include +#include #include +#include +#include #include #include -#include +#include +#include #include #include #include -#include #include -#include -#include -#include #include -#include -#include #include #include -- cgit v1.2.3 From 39e9ef1dca795d054f2708e284a3284228cdebbc Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:46 -0300 Subject: mmc: jz4740: Use dev_get_platdata Instead of accessing the platform data pointer directly, use the dev_get_platdata() helper. Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index eb57f514a459..b11f65077ce7 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -926,7 +926,7 @@ static int jz4740_mmc_request_gpio(struct device *dev, int gpio, static int jz4740_mmc_request_gpios(struct mmc_host *mmc, struct platform_device *pdev) { - struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; + struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev); int ret = 0; if (!pdata) @@ -955,7 +955,7 @@ static int jz4740_mmc_request_gpios(struct mmc_host *mmc, static void jz4740_mmc_free_gpios(struct platform_device *pdev) { - struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; + struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev); if (!pdata) return; @@ -971,7 +971,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) struct jz4740_mmc_host *host; struct jz4740_mmc_platform_data *pdata; - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); if (!mmc) { -- cgit v1.2.3 From 436a3cfddcb7a9b92a2035464139ea6be2afe74c Mon Sep 17 00:00:00 2001 From: Zubair Lutfullah Kakakhel Date: Wed, 28 Mar 2018 18:00:47 -0300 Subject: mmc: jz4740: Reset the device requesting the interrupt In case a bootloader leaves the device in a bad state, requesting the interrupt before resetting results in a bad interrupt loop. Signed-off-by: Zubair Lutfullah Kakakhel [Ezequiel: cleanup commit description] Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index b11f65077ce7..9f316d953b30 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1027,6 +1027,8 @@ static int jz4740_mmc_probe(struct platform_device* pdev) spin_lock_init(&host->lock); host->irq_mask = 0xffff; + jz4740_mmc_reset(host); + ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, dev_name(&pdev->dev), host); if (ret) { @@ -1034,7 +1036,6 @@ static int jz4740_mmc_probe(struct platform_device* pdev) goto err_free_gpios; } - jz4740_mmc_reset(host); jz4740_mmc_clock_disable(host); timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); -- cgit v1.2.3 From 61e11eba23c3c1e68d2a58f196eaa376d55ab366 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:48 -0300 Subject: mmc: jz4740: Introduce devicetree probe Add support to probe the device via devicetree, which will be used to support other SoCs such as the JZ4780. Based on commits from the CI20 repo, by Paul Cercueil and Alex Smith. Binding document based on work by Zubair Lutfullah Kakakhel. Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 51 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 9f316d953b30..03757cc55f52 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -107,6 +108,10 @@ #define JZ_MMC_CLK_RATE 24000000 +enum jz4740_mmc_version { + JZ_MMC_JZ4740, +}; + enum jz4740_mmc_state { JZ4740_MMC_STATE_READ_RESPONSE, JZ4740_MMC_STATE_TRANSFER_DATA, @@ -125,6 +130,8 @@ struct jz4740_mmc_host { struct jz4740_mmc_platform_data *pdata; struct clk *clk; + enum jz4740_mmc_version version; + int irq; int card_detect_irq; @@ -857,7 +864,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: jz4740_mmc_reset(host); - if (gpio_is_valid(host->pdata->gpio_power)) + if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) gpio_set_value(host->pdata->gpio_power, !host->pdata->power_active_low); host->cmdat |= JZ_MMC_CMDAT_INIT; @@ -866,7 +873,7 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_POWER_ON: break; default: - if (gpio_is_valid(host->pdata->gpio_power)) + if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) gpio_set_value(host->pdata->gpio_power, host->pdata->power_active_low); clk_disable_unprepare(host->clk); @@ -964,11 +971,18 @@ static void jz4740_mmc_free_gpios(struct platform_device *pdev) gpio_free(pdata->gpio_power); } +static const struct of_device_id jz4740_mmc_of_match[] = { + { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 }, + {}, +}; +MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match); + static int jz4740_mmc_probe(struct platform_device* pdev) { int ret; struct mmc_host *mmc; struct jz4740_mmc_host *host; + const struct of_device_id *match; struct jz4740_mmc_platform_data *pdata; pdata = dev_get_platdata(&pdev->dev); @@ -982,6 +996,27 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host = mmc_priv(mmc); host->pdata = pdata; + match = of_match_device(jz4740_mmc_of_match, &pdev->dev); + if (match) { + host->version = (enum jz4740_mmc_version)match->data; + ret = mmc_of_parse(mmc); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "could not parse of data: %d\n", ret); + goto err_free_host; + } + } else { + /* JZ4740 should be the only one using legacy probe */ + host->version = JZ_MMC_JZ4740; + mmc->caps |= MMC_CAP_SDIO_IRQ; + if (!(pdata && pdata->data_1bit)) + mmc->caps |= MMC_CAP_4_BIT_DATA; + ret = jz4740_mmc_request_gpios(mmc, pdev); + if (ret) + goto err_free_host; + } + host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) { ret = host->irq; @@ -1004,16 +1039,11 @@ static int jz4740_mmc_probe(struct platform_device* pdev) goto err_free_host; } - ret = jz4740_mmc_request_gpios(mmc, pdev); - if (ret) - goto err_free_host; - mmc->ops = &jz4740_mmc_ops; - mmc->f_min = JZ_MMC_CLK_RATE / 128; - mmc->f_max = JZ_MMC_CLK_RATE; + if (!mmc->f_max) + mmc->f_max = JZ_MMC_CLK_RATE; + mmc->f_min = mmc->f_max / 128; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA; - mmc->caps |= MMC_CAP_SDIO_IRQ; mmc->max_blk_size = (1 << 10) - 1; mmc->max_blk_count = (1 << 15) - 1; @@ -1118,6 +1148,7 @@ static struct platform_driver jz4740_mmc_driver = { .remove = jz4740_mmc_remove, .driver = { .name = "jz4740-mmc", + .of_match_table = of_match_ptr(jz4740_mmc_of_match), .pm = JZ4740_MMC_PM_OPS, }, }; -- cgit v1.2.3 From 48b6aa2f9eb245e83142d552068ec2de00c399bf Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:49 -0300 Subject: mmc: dt-bindings: add MMC support to JZ4740 SoC Add the devicetree binding for JZ4740/JZ4780 SoC MMC/SD controller. Cc: Mark Rutland Cc: devicetree@vger.kernel.org Reviewed-by: Rob Herring Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/jz4740.txt | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/jz4740.txt diff --git a/Documentation/devicetree/bindings/mmc/jz4740.txt b/Documentation/devicetree/bindings/mmc/jz4740.txt new file mode 100644 index 000000000000..7cd8c432d7c8 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/jz4740.txt @@ -0,0 +1,38 @@ +* Ingenic JZ47xx MMC controllers + +This file documents the device tree properties used for the MMC controller in +Ingenic JZ4740/JZ4780 SoCs. These are in addition to the core MMC properties +described in mmc.txt. + +Required properties: +- compatible: Should be one of the following: + - "ingenic,jz4740-mmc" for the JZ4740 + - "ingenic,jz4780-mmc" for the JZ4780 +- reg: Should contain the MMC controller registers location and length. +- interrupts: Should contain the interrupt specifier of the MMC controller. +- clocks: Clock for the MMC controller. + +Optional properties: +- dmas: List of DMA specifiers with the controller specific format + as described in the generic DMA client binding. A tx and rx + specifier is required. +- dma-names: RX and TX DMA request names. + Should be "rx" and "tx", in that order. + +For additional details on DMA client bindings see ../dma/dma.txt. + +Example: + +mmc0: mmc@13450000 { + compatible = "ingenic,jz4780-mmc"; + reg = <0x13450000 0x1000>; + + interrupt-parent = <&intc>; + interrupts = <37>; + + clocks = <&cgu JZ4780_CLK_MSC0>; + clock-names = "mmc"; + + dmas = <&dma JZ4780_DMA_MSC0_RX 0xffffffff>, <&dma JZ4780_DMA_MSC0_TX 0xffffffff>; + dma-names = "rx", "tx"; +}; -- cgit v1.2.3 From 6861fce620771d5f7629f2d9742a8431d932cdf0 Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 28 Mar 2018 18:00:50 -0300 Subject: mmc: jz4740: Set clock rate to mmc->f_max rather than JZ_MMC_CLK_RATE The maximum clock rate can be overridden by DT. The clock rate should be set to the DT-specified value rather than the constant JZ_MMC_CLK_RATE when this is done. If the maximum clock rate is not set by DT then mmc->f_max will be set to JZ_MMC_CLK_RATE. Tested-by: Mathieu Malaterre Signed-off-by: Alex Smith Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 03757cc55f52..aa635b458d2c 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -825,7 +825,7 @@ static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) int real_rate; jz4740_mmc_clock_disable(host); - clk_set_rate(host->clk, JZ_MMC_CLK_RATE); + clk_set_rate(host->clk, host->mmc->f_max); real_rate = clk_get_rate(host->clk); -- cgit v1.2.3 From 6a78768af70fa5efe8c85f5b679babc349e699de Mon Sep 17 00:00:00 2001 From: Alex Smith Date: Wed, 28 Mar 2018 18:00:51 -0300 Subject: mmc: jz4740: Add support for the JZ4780 Add support for the JZ4780 MMC controller to the jz47xx_mmc driver. There are a few minor differences from the 4740 to the 4780 that need to be handled, but otherwise the controllers behave the same. The IREG and IMASK registers are expanded to 32 bits. Additionally, some error conditions are now reported in both STATUS and IREG. Writing IREG before reading STATUS causes the bits in STATUS to be cleared, so STATUS must be read first to ensure we see and report error conditions correctly. Signed-off-by: Alex Smith Signed-off-by: Paul Cercueil Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 9 +++-- drivers/mmc/host/jz4740_mmc.c | 86 +++++++++++++++++++++++++++++++++---------- 2 files changed, 72 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9589f9c9046f..cf15684064a8 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -756,11 +756,12 @@ config MMC_SH_MMCIF config MMC_JZ4740 - tristate "JZ4740 SD/Multimedia Card Interface support" - depends on MACH_JZ4740 + tristate "Ingenic JZ47xx SD/Multimedia Card Interface support" + depends on MACH_JZ4740 || MACH_JZ4780 help - This selects support for the SD/MMC controller on Ingenic JZ4740 - SoCs. + This selects support for the SD/MMC controller on Ingenic + JZ4740, JZ4750, JZ4770 and JZ4780 SoCs. + If you have a board based on such a SoC and with a SD/MMC slot, say Y or M here. diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index aa635b458d2c..5d3efd59bda1 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1,5 +1,7 @@ /* * Copyright (C) 2009-2010, Lars-Peter Clausen + * Copyright (C) 2013, Imagination Technologies + * * JZ4740 SD/MMC controller driver * * This program is free software; you can redistribute it and/or modify it @@ -52,6 +54,7 @@ #define JZ_REG_MMC_RESP_FIFO 0x34 #define JZ_REG_MMC_RXFIFO 0x38 #define JZ_REG_MMC_TXFIFO 0x3C +#define JZ_REG_MMC_DMAC 0x44 #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) #define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) @@ -105,11 +108,15 @@ #define JZ_MMC_IRQ_PRG_DONE BIT(1) #define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) +#define JZ_MMC_DMAC_DMA_SEL BIT(1) +#define JZ_MMC_DMAC_DMA_EN BIT(0) #define JZ_MMC_CLK_RATE 24000000 enum jz4740_mmc_version { JZ_MMC_JZ4740, + JZ_MMC_JZ4750, + JZ_MMC_JZ4780, }; enum jz4740_mmc_state { @@ -144,7 +151,7 @@ struct jz4740_mmc_host { uint32_t cmdat; - uint16_t irq_mask; + uint32_t irq_mask; spinlock_t lock; @@ -166,6 +173,32 @@ struct jz4740_mmc_host { #define JZ4740_MMC_FIFO_HALF_SIZE 8 }; +static void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host, + uint32_t val) +{ + if (host->version >= JZ_MMC_JZ4750) + return writel(val, host->base + JZ_REG_MMC_IMASK); + else + return writew(val, host->base + JZ_REG_MMC_IMASK); +} + +static void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host, + uint32_t val) +{ + if (host->version >= JZ_MMC_JZ4780) + return writel(val, host->base + JZ_REG_MMC_IREG); + else + return writew(val, host->base + JZ_REG_MMC_IREG); +} + +static uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host) +{ + if (host->version >= JZ_MMC_JZ4780) + return readl(host->base + JZ_REG_MMC_IREG); + else + return readw(host->base + JZ_REG_MMC_IREG); +} + /*----------------------------------------------------------------------------*/ /* DMA infrastructure */ @@ -370,7 +403,7 @@ static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, else host->irq_mask |= irq; - writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK); + jz4740_mmc_write_irq_mask(host, host->irq_mask); spin_unlock_irqrestore(&host->lock, flags); } @@ -422,10 +455,10 @@ static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, unsigned int irq) { unsigned int timeout = 0x800; - uint16_t status; + uint32_t status; do { - status = readw(host->base + JZ_REG_MMC_IREG); + status = jz4740_mmc_read_irq_reg(host); } while (!(status & irq) && --timeout); if (timeout == 0) { @@ -525,7 +558,7 @@ static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO; uint32_t *buf; uint32_t d; - uint16_t status; + uint32_t status; size_t i, j; unsigned int timeout; @@ -661,8 +694,25 @@ static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, cmdat |= JZ_MMC_CMDAT_DATA_EN; if (cmd->data->flags & MMC_DATA_WRITE) cmdat |= JZ_MMC_CMDAT_WRITE; - if (host->use_dma) - cmdat |= JZ_MMC_CMDAT_DMA_EN; + if (host->use_dma) { + /* + * The 4780's MMC controller has integrated DMA ability + * in addition to being able to use the external DMA + * controller. It moves DMA control bits to a separate + * register. The DMA_SEL bit chooses the external + * controller over the integrated one. Earlier SoCs + * can only use the external controller, and have a + * single DMA enable bit in CMDAT. + */ + if (host->version >= JZ_MMC_JZ4780) { + writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL, + host->base + JZ_REG_MMC_DMAC); + } else { + cmdat |= JZ_MMC_CMDAT_DMA_EN; + } + } else if (host->version >= JZ_MMC_JZ4780) { + writel(0, host->base + JZ_REG_MMC_DMAC); + } writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); @@ -743,7 +793,7 @@ static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) host->state = JZ4740_MMC_STATE_SEND_STOP; break; } - writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE); case JZ4740_MMC_STATE_SEND_STOP: if (!req->stop) @@ -773,9 +823,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) { struct jz4740_mmc_host *host = devid; struct mmc_command *cmd = host->cmd; - uint16_t irq_reg, status, tmp; + uint32_t irq_reg, status, tmp; - irq_reg = readw(host->base + JZ_REG_MMC_IREG); + status = readl(host->base + JZ_REG_MMC_STATUS); + irq_reg = jz4740_mmc_read_irq_reg(host); tmp = irq_reg; irq_reg &= ~host->irq_mask; @@ -784,10 +835,10 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE); if (tmp != irq_reg) - writew(tmp & ~irq_reg, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg); if (irq_reg & JZ_MMC_IRQ_SDIO) { - writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO); mmc_signal_sdio_irq(host->mmc); irq_reg &= ~JZ_MMC_IRQ_SDIO; } @@ -796,8 +847,6 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) if (test_and_clear_bit(0, &host->waiting)) { del_timer(&host->timeout_timer); - status = readl(host->base + JZ_REG_MMC_STATUS); - if (status & JZ_MMC_STATUS_TIMEOUT_RES) { cmd->error = -ETIMEDOUT; } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { @@ -810,7 +859,7 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) } jz4740_mmc_set_irq_enabled(host, irq_reg, false); - writew(irq_reg, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, irq_reg); return IRQ_WAKE_THREAD; } @@ -844,9 +893,7 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) host->req = req; - writew(0xffff, host->base + JZ_REG_MMC_IREG); - - writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_write_irq_reg(host, ~0); jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); host->state = JZ4740_MMC_STATE_READ_RESPONSE; @@ -973,6 +1020,7 @@ static void jz4740_mmc_free_gpios(struct platform_device *pdev) static const struct of_device_id jz4740_mmc_of_match[] = { { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 }, + { .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 }, {}, }; MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match); @@ -1055,7 +1103,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host->mmc = mmc; host->pdev = pdev; spin_lock_init(&host->lock); - host->irq_mask = 0xffff; + host->irq_mask = ~0; jz4740_mmc_reset(host); -- cgit v1.2.3 From fb0ce9dd94ab2041cc124b053358d17db6f892e8 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:52 -0300 Subject: mmc: jz4740: Use dma_request_chan() Replace dma_request_channel() with dma_request_chan(), which also supports probing from the devicetree. Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 5d3efd59bda1..993386c9ea50 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -213,31 +213,23 @@ static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host) static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) { - dma_cap_mask_t mask; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - host->dma_tx = dma_request_channel(mask, NULL, host); - if (!host->dma_tx) { + host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx"); + if (IS_ERR(host->dma_tx)) { dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n"); - return -ENODEV; + return PTR_ERR(host->dma_tx); } - host->dma_rx = dma_request_channel(mask, NULL, host); - if (!host->dma_rx) { + host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx"); + if (IS_ERR(host->dma_rx)) { dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n"); - goto free_master_write; + dma_release_channel(host->dma_tx); + return PTR_ERR(host->dma_rx); } /* Initialize DMA pre request cookie */ host->next_data.cookie = 1; return 0; - -free_master_write: - dma_release_channel(host->dma_tx); - return -ENODEV; } static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host, -- cgit v1.2.3 From 2d972b6ac972b3d3c2e4336325745568c79a5dd3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:53 -0300 Subject: MIPS: dts: jz4780: Add DMA controller node to the devicetree Add the devicetree node to support the DMA controller found in JZ480 SoCs. Tested-by: Mathieu Malaterre Acked-by: James Hogan Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- arch/mips/boot/dts/ingenic/jz4780.dtsi | 12 +++++++++ include/dt-bindings/dma/jz4780-dma.h | 49 ++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 include/dt-bindings/dma/jz4780-dma.h diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi index 9b5794667aee..15a9801430bd 100644 --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include / { #address-cells = <1>; @@ -241,6 +242,17 @@ status = "disabled"; }; + dma: dma@13420000 { + compatible = "ingenic,jz4780-dma"; + reg = <0x13420000 0x10000>; + #dma-cells = <2>; + + interrupt-parent = <&intc>; + interrupts = <10>; + + clocks = <&cgu JZ4780_CLK_PDMA>; + }; + bch: bch@134d0000 { compatible = "ingenic,jz4780-bch"; reg = <0x134d0000 0x10000>; diff --git a/include/dt-bindings/dma/jz4780-dma.h b/include/dt-bindings/dma/jz4780-dma.h new file mode 100644 index 000000000000..df017fdfb44e --- /dev/null +++ b/include/dt-bindings/dma/jz4780-dma.h @@ -0,0 +1,49 @@ +#ifndef __DT_BINDINGS_DMA_JZ4780_DMA_H__ +#define __DT_BINDINGS_DMA_JZ4780_DMA_H__ + +/* + * Request type numbers for the JZ4780 DMA controller (written to the DRTn + * register for the channel). + */ +#define JZ4780_DMA_I2S1_TX 0x4 +#define JZ4780_DMA_I2S1_RX 0x5 +#define JZ4780_DMA_I2S0_TX 0x6 +#define JZ4780_DMA_I2S0_RX 0x7 +#define JZ4780_DMA_AUTO 0x8 +#define JZ4780_DMA_SADC_RX 0x9 +#define JZ4780_DMA_UART4_TX 0xc +#define JZ4780_DMA_UART4_RX 0xd +#define JZ4780_DMA_UART3_TX 0xe +#define JZ4780_DMA_UART3_RX 0xf +#define JZ4780_DMA_UART2_TX 0x10 +#define JZ4780_DMA_UART2_RX 0x11 +#define JZ4780_DMA_UART1_TX 0x12 +#define JZ4780_DMA_UART1_RX 0x13 +#define JZ4780_DMA_UART0_TX 0x14 +#define JZ4780_DMA_UART0_RX 0x15 +#define JZ4780_DMA_SSI0_TX 0x16 +#define JZ4780_DMA_SSI0_RX 0x17 +#define JZ4780_DMA_SSI1_TX 0x18 +#define JZ4780_DMA_SSI1_RX 0x19 +#define JZ4780_DMA_MSC0_TX 0x1a +#define JZ4780_DMA_MSC0_RX 0x1b +#define JZ4780_DMA_MSC1_TX 0x1c +#define JZ4780_DMA_MSC1_RX 0x1d +#define JZ4780_DMA_MSC2_TX 0x1e +#define JZ4780_DMA_MSC2_RX 0x1f +#define JZ4780_DMA_PCM0_TX 0x20 +#define JZ4780_DMA_PCM0_RX 0x21 +#define JZ4780_DMA_SMB0_TX 0x24 +#define JZ4780_DMA_SMB0_RX 0x25 +#define JZ4780_DMA_SMB1_TX 0x26 +#define JZ4780_DMA_SMB1_RX 0x27 +#define JZ4780_DMA_SMB2_TX 0x28 +#define JZ4780_DMA_SMB2_RX 0x29 +#define JZ4780_DMA_SMB3_TX 0x2a +#define JZ4780_DMA_SMB3_RX 0x2b +#define JZ4780_DMA_SMB4_TX 0x2c +#define JZ4780_DMA_SMB4_RX 0x2d +#define JZ4780_DMA_DES_TX 0x2e +#define JZ4780_DMA_DES_RX 0x2f + +#endif /* __DT_BINDINGS_DMA_JZ4780_DMA_H__ */ -- cgit v1.2.3 From 7f5a07f4c2d22cf9a07a1a959cd5561795c8d527 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:54 -0300 Subject: MIPS: dts: jz4780: Add MMC controller node to the devicetree Add the devicetree node to support the MMC host controller available in JZ480 SoCs. Acked-by: James Hogan Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- arch/mips/boot/dts/ingenic/jz4780.dtsi | 40 ++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi index 15a9801430bd..b72e53bb7292 100644 --- a/arch/mips/boot/dts/ingenic/jz4780.dtsi +++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi @@ -253,6 +253,46 @@ clocks = <&cgu JZ4780_CLK_PDMA>; }; + mmc0: mmc@13450000 { + compatible = "ingenic,jz4780-mmc"; + reg = <0x13450000 0x1000>; + + interrupt-parent = <&intc>; + interrupts = <37>; + + clocks = <&cgu JZ4780_CLK_MSC0>; + clock-names = "mmc"; + + cap-sd-highspeed; + cap-mmc-highspeed; + cap-sdio-irq; + dmas = <&dma JZ4780_DMA_MSC0_RX 0xffffffff>, + <&dma JZ4780_DMA_MSC0_TX 0xffffffff>; + dma-names = "rx", "tx"; + + status = "disabled"; + }; + + mmc1: mmc@13460000 { + compatible = "ingenic,jz4780-mmc"; + reg = <0x13460000 0x1000>; + + interrupt-parent = <&intc>; + interrupts = <36>; + + clocks = <&cgu JZ4780_CLK_MSC1>; + clock-names = "mmc"; + + cap-sd-highspeed; + cap-mmc-highspeed; + cap-sdio-irq; + dmas = <&dma JZ4780_DMA_MSC1_RX 0xffffffff>, + <&dma JZ4780_DMA_MSC1_TX 0xffffffff>; + dma-names = "rx", "tx"; + + status = "disabled"; + }; + bch: bch@134d0000 { compatible = "ingenic,jz4780-bch"; reg = <0x134d0000 0x10000>; -- cgit v1.2.3 From 671963bbb3a72211960662e9ac274c1fb9e08234 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:55 -0300 Subject: MIPS: dts: ci20: Enable MMC in the devicetree Now that we have support for JZ480 SoCs in the MMC driver, let's enable it on the devicetree. Acked-by: James Hogan Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- arch/mips/boot/dts/ingenic/ci20.dts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/arch/mips/boot/dts/ingenic/ci20.dts b/arch/mips/boot/dts/ingenic/ci20.dts index 38078594cf97..50cff3cbcc6d 100644 --- a/arch/mips/boot/dts/ingenic/ci20.dts +++ b/arch/mips/boot/dts/ingenic/ci20.dts @@ -36,6 +36,28 @@ clock-frequency = <48000000>; }; +&mmc0 { + status = "okay"; + + bus-width = <4>; + max-frequency = <50000000>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_mmc0>; + + cd-gpios = <&gpf 20 GPIO_ACTIVE_LOW>; +}; + +&mmc1 { + status = "okay"; + + bus-width = <4>; + max-frequency = <50000000>; + + pinctrl-names = "default"; + pinctrl-0 = <&pins_mmc1>; +}; + &uart0 { status = "okay"; @@ -203,4 +225,16 @@ groups = "nemc-cs6"; bias-disable; }; + + pins_mmc0: mmc0 { + function = "mmc0"; + groups = "mmc0-1bit-e", "mmc0-4bit-e"; + bias-disable; + }; + + pins_mmc1: mmc1 { + function = "mmc1"; + groups = "mmc1-1bit-d", "mmc1-4bit-d"; + bias-disable; + }; }; -- cgit v1.2.3 From aabaeb3f30f9561ed938be0b4e9da02a03576fc3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:56 -0300 Subject: MIPS: configs: ci20: Enable DMA and MMC support Enable the SD/MMC support, along with DMA engine support in the CI20 defconfig. Acked-by: James Hogan Tested-by: Mathieu Malaterre Signed-off-by: Ezequiel Garcia Signed-off-by: Ulf Hansson --- arch/mips/configs/ci20_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/mips/configs/ci20_defconfig b/arch/mips/configs/ci20_defconfig index b5f4ad8f2c45..f88b05fd3077 100644 --- a/arch/mips/configs/ci20_defconfig +++ b/arch/mips/configs/ci20_defconfig @@ -104,8 +104,11 @@ CONFIG_REGULATOR_FIXED_VOLTAGE=y # CONFIG_HID is not set # CONFIG_USB_SUPPORT is not set CONFIG_MMC=y +CONFIG_MMC_JZ4740=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_JZ4740=y +CONFIG_DMADEVICES=y +CONFIG_DMA_JZ4780=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_MEMORY=y # CONFIG_DNOTIFY is not set -- cgit v1.2.3 From 207b652cdbec3f7828251ded2c104eea6d036624 Mon Sep 17 00:00:00 2001 From: Andrew Gabbasov Date: Tue, 27 Feb 2018 17:03:49 +0530 Subject: mmc: card: Don't show eMMC RPMB and BOOT areas in /proc/partitions Since RPMB area is accessible via special ioctl only and boot areas are unlikely to contain any partitions, exclude them all from listing in /proc/partitions. This will hide them from various user-level software (e.g. fdisk), thus avoiding unnecessary access attempts. Signed-off-by: Andrew Gabbasov Signed-off-by: Harish Jenny K N Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 9e923cd1d80e..03e3d48b083e 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2353,7 +2353,8 @@ static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card, set_disk_ro(md->disk, md->read_only || default_ro); md->disk->flags = GENHD_FL_EXT_DEVT; if (area_type & (MMC_BLK_DATA_AREA_RPMB | MMC_BLK_DATA_AREA_BOOT)) - md->disk->flags |= GENHD_FL_NO_PART_SCAN; + md->disk->flags |= GENHD_FL_NO_PART_SCAN + | GENHD_FL_SUPPRESS_PARTITION_INFO; /* * As discussed on lkml, GENHD_FL_REMOVABLE should: -- cgit v1.2.3 From f53b3095bda6fd46a9b2f2ec962b5daa051d8a80 Mon Sep 17 00:00:00 2001 From: Nan Li Date: Tue, 3 Apr 2018 18:06:49 +0800 Subject: mmc: dt-bindings: update bindings doc to support Meson-AXG SoC Update the documentation to list support for Meson-AXG SoC explicitly. The new binding string is necessary since this SoC introduce a few IP difference comparing to previous old generation. Signed-off-by: Nan Li Signed-off-by: Yixun Lan Signed-off-by: Ulf Hansson Acked-by: Kevin Hilman --- Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt index 50bf611a4d2c..5add8d7d855f 100644 --- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt @@ -12,6 +12,7 @@ Required properties: - "amlogic,meson-gxbb-mmc" - "amlogic,meson-gxl-mmc" - "amlogic,meson-gxm-mmc" + - "amlogic,meson-axg-mmc" - clocks : A list of phandle + clock-specifier pairs for the clocks listed in clock-names. - clock-names: Should contain the following: "core" - Main peripheral bus clock -- cgit v1.2.3 From df06981514d43ff1101d3af748655b3df60751ce Mon Sep 17 00:00:00 2001 From: Nan Li Date: Tue, 3 Apr 2018 18:06:50 +0800 Subject: mmc: meson-axg: add support for the Meson-AXG platform Introduce the compatible data to cover the register offset & mask change of the eMMC controller in Amlogic's Meson-AXG SoC. Signed-off-by: Nan Li Signed-off-by: Yixun Lan Signed-off-by: Ulf Hansson Acked-by: Kevin Hilman --- drivers/mmc/host/meson-gx-mmc.c | 61 ++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 4f972b879fe6..56c90542ac29 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -47,15 +47,29 @@ #define CLK_CORE_PHASE_MASK GENMASK(9, 8) #define CLK_TX_PHASE_MASK GENMASK(11, 10) #define CLK_RX_PHASE_MASK GENMASK(13, 12) -#define CLK_TX_DELAY_MASK GENMASK(19, 16) -#define CLK_RX_DELAY_MASK GENMASK(23, 20) +#define CLK_V2_TX_DELAY_MASK GENMASK(19, 16) +#define CLK_V2_RX_DELAY_MASK GENMASK(23, 20) +#define CLK_V2_ALWAYS_ON BIT(24) + +#define CLK_V3_TX_DELAY_MASK GENMASK(21, 16) +#define CLK_V3_RX_DELAY_MASK GENMASK(27, 22) +#define CLK_V3_ALWAYS_ON BIT(28) + #define CLK_DELAY_STEP_PS 200 #define CLK_PHASE_STEP 30 #define CLK_PHASE_POINT_NUM (360 / CLK_PHASE_STEP) -#define CLK_ALWAYS_ON BIT(24) + +#define CLK_TX_DELAY_MASK(h) (h->data->tx_delay_mask) +#define CLK_RX_DELAY_MASK(h) (h->data->rx_delay_mask) +#define CLK_ALWAYS_ON(h) (h->data->always_on) #define SD_EMMC_DELAY 0x4 #define SD_EMMC_ADJUST 0x8 + +#define SD_EMMC_DELAY1 0x4 +#define SD_EMMC_DELAY2 0x8 +#define SD_EMMC_V3_ADJUST 0xc + #define SD_EMMC_CALOUT 0x10 #define SD_EMMC_START 0x40 #define START_DESC_INIT BIT(0) @@ -122,6 +136,12 @@ #define MUX_CLK_NUM_PARENTS 2 +struct meson_mmc_data { + unsigned int tx_delay_mask; + unsigned int rx_delay_mask; + unsigned int always_on; +}; + struct sd_emmc_desc { u32 cmd_cfg; u32 cmd_arg; @@ -131,6 +151,7 @@ struct sd_emmc_desc { struct meson_host { struct device *dev; + struct meson_mmc_data *data; struct mmc_host *mmc; struct mmc_command *cmd; @@ -474,7 +495,7 @@ static int meson_mmc_clk_init(struct meson_host *host) /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */ clk_reg = 0; - clk_reg |= CLK_ALWAYS_ON; + clk_reg |= CLK_ALWAYS_ON(host); clk_reg |= CLK_DIV_MASK; writel(clk_reg, host->regs + SD_EMMC_CLOCK); @@ -574,7 +595,7 @@ static int meson_mmc_clk_init(struct meson_host *host) tx->reg = host->regs + SD_EMMC_CLOCK; tx->phase_mask = CLK_TX_PHASE_MASK; - tx->delay_mask = CLK_TX_DELAY_MASK; + tx->delay_mask = CLK_TX_DELAY_MASK(host); tx->delay_step_ps = CLK_DELAY_STEP_PS; tx->hw.init = &init; @@ -597,7 +618,7 @@ static int meson_mmc_clk_init(struct meson_host *host) rx->reg = host->regs + SD_EMMC_CLOCK; rx->phase_mask = CLK_RX_PHASE_MASK; - rx->delay_mask = CLK_RX_DELAY_MASK; + rx->delay_mask = CLK_RX_DELAY_MASK(host); rx->delay_step_ps = CLK_DELAY_STEP_PS; rx->hw.init = &init; @@ -1184,6 +1205,13 @@ static int meson_mmc_probe(struct platform_device *pdev) goto free_host; } + host->data = (struct meson_mmc_data *) + of_device_get_match_data(&pdev->dev); + if (!host->data) { + ret = -EINVAL; + goto free_host; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->regs)) { @@ -1315,11 +1343,24 @@ static int meson_mmc_remove(struct platform_device *pdev) return 0; } +static const struct meson_mmc_data meson_gx_data = { + .tx_delay_mask = CLK_V2_TX_DELAY_MASK, + .rx_delay_mask = CLK_V2_RX_DELAY_MASK, + .always_on = CLK_V2_ALWAYS_ON, +}; + +static const struct meson_mmc_data meson_axg_data = { + .tx_delay_mask = CLK_V3_TX_DELAY_MASK, + .rx_delay_mask = CLK_V3_RX_DELAY_MASK, + .always_on = CLK_V3_ALWAYS_ON, +}; + static const struct of_device_id meson_mmc_of_match[] = { - { .compatible = "amlogic,meson-gx-mmc", }, - { .compatible = "amlogic,meson-gxbb-mmc", }, - { .compatible = "amlogic,meson-gxl-mmc", }, - { .compatible = "amlogic,meson-gxm-mmc", }, + { .compatible = "amlogic,meson-gx-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-gxbb-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-gxl-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-gxm-mmc", .data = &meson_gx_data }, + { .compatible = "amlogic,meson-axg-mmc", .data = &meson_axg_data }, {} }; MODULE_DEVICE_TABLE(of, meson_mmc_of_match); -- cgit v1.2.3 From e79dc1b4529b88c96188ec3c78445f69d69c05da Mon Sep 17 00:00:00 2001 From: Nan Li Date: Tue, 3 Apr 2018 18:06:51 +0800 Subject: mmc: meson: update doc to support Meson-AXG platform Explicitly update the docomentation to support the Meson-AXG platform. Signed-off-by: Nan Li Signed-off-by: Yixun Lan Signed-off-by: Ulf Hansson Acked-by: Kevin Hilman --- drivers/mmc/host/Kconfig | 4 ++-- drivers/mmc/host/meson-gx-mmc.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index cf15684064a8..fa02faf03d81 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -345,11 +345,11 @@ config MMC_SDHCI_IPROC If unsure, say N. config MMC_MESON_GX - tristate "Amlogic S905/GX* SD/MMC Host Controller support" + tristate "Amlogic S905/GX*/AXG SD/MMC Host Controller support" depends on ARCH_MESON && MMC help This selects support for the Amlogic SD/MMC Host Controller - found on the S905/GX* family of SoCs. This controller is + found on the S905/GX*/AXG family of SoCs. This controller is MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces. If you have a controller with this interface, say Y here. diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 56c90542ac29..55bbd67177df 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1376,6 +1376,6 @@ static struct platform_driver meson_mmc_driver = { module_platform_driver(meson_mmc_driver); -MODULE_DESCRIPTION("Amlogic S905*/GX* SD/eMMC driver"); +MODULE_DESCRIPTION("Amlogic S905*/GX*/AXG SD/eMMC driver"); MODULE_AUTHOR("Kevin Hilman "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ef6b75671b5f6bfeb5e59e2829643483a3af8c39 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 27 Mar 2018 18:29:53 +0900 Subject: mmc: sdhci-cadence: send tune request twice to work around errata Cadence sent out an errata report to their customers of this IP. This errata is not so severe, but the tune request should be sent twice to avoid the potential issue. Quote from the report: Problem Summary --------------- The IP6116 SD/eMMC PHY design has a timing issue on receive data path. This issue may lead to an incorrect values of read/write pointers of the synchronization FIFO. Such a situation can happen at the SDR104 and HS200 tuning procedure when the PHY is requested to change a phase of sampling clock when moving to the next tuning iteration. Workarounds ----------- The following are valid workarounds to resolve the issue: 1. In eMMC mode, software sends tune request twice instead of once at each iteration. This means that the clock phase is not changed on the second request so there is no potential for clock instability. 2. In SD mode, software must not use the hardware tuning and instead perform an almost identical procedure to eMMC, using the HRS34 Tune Force register. Signed-off-by: Masahiro Yamada Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-cadence.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index 0f589e26ee63..bc30d1637246 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -253,6 +253,7 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS06; u32 tmp; + int i, ret; if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val))) return -EINVAL; @@ -260,11 +261,24 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) tmp = readl(reg); tmp &= ~SDHCI_CDNS_HRS06_TUNE; tmp |= FIELD_PREP(SDHCI_CDNS_HRS06_TUNE, val); - tmp |= SDHCI_CDNS_HRS06_TUNE_UP; - writel(tmp, reg); - return readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), - 0, 1); + /* + * Workaround for IP errata: + * The IP6116 SD/eMMC PHY design has a timing issue on receive data + * path. Send tune request twice. + */ + for (i = 0; i < 2; i++) { + tmp |= SDHCI_CDNS_HRS06_TUNE_UP; + writel(tmp, reg); + + ret = readl_poll_timeout(reg, tmp, + !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), + 0, 1); + + return ret; + } + + return 0; } static int sdhci_cdns_execute_tuning(struct mmc_host *mmc, u32 opcode) -- cgit v1.2.3 From d39b1b2abf9fcae3001b868186faa1ae889aecf5 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 5 Apr 2018 18:31:42 +0800 Subject: mmc: dw_mmc: fix misleading comment in dw_mci_rk3288_set_ios DDR52 with 8-bit mode should be handled in a different way when requesting ciu_clk. However DDR50 is used for SDMMC/SDIO and could never be possible with 8-bit mode. It's trival but misleading. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-rockchip.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 40d7de2eea12..8c86a800a8fd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -44,9 +44,8 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * bus_hz = cclkin / RK3288_CLKGEN_DIV * ios->clock = (div == 0) ? bus_hz : (bus_hz / (2 * div)) * - * Note: div can only be 0 or 1 - * if DDR50 8bit mode(only emmc work in 8bit mode), - * div must be set 1 + * Note: div can only be 0 or 1, but div must be set to 1 for eMMC + * DDR52 8-bit mode. */ if (ios->bus_width == MMC_BUS_WIDTH_8 && ios->timing == MMC_TIMING_MMC_DDR52) -- cgit v1.2.3 From ebc5a1bf4f2afc2f2b348320dcfb45a8c0ac3de5 Mon Sep 17 00:00:00 2001 From: "harish_kandiga@mentor.com" Date: Tue, 10 Apr 2018 12:30:31 +0530 Subject: mmc: core: Add a new quirk for limiting clock rate This patch adds a quirk to limit clock rate which can be used to reduce the SDIO clock rate for some chips with broken UHS. Signed-off-by: Harish Jenny K N Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/card.h | 6 ++++++ drivers/mmc/core/sdio.c | 6 +++++- include/linux/mmc/card.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index 9c821eedd156..1170feb8f969 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -149,6 +149,12 @@ static inline void __maybe_unused remove_quirk(struct mmc_card *card, int data) card->quirks &= ~data; } +static inline void __maybe_unused add_limit_rate_quirk(struct mmc_card *card, + int data) +{ + card->quirk_max_rate = data; +} + /* * Quirk add/remove for MMC products. */ diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index c599a628a387..24b510b743da 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -444,6 +444,7 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) unsigned int bus_speed, timing; int err; unsigned char speed; + unsigned int max_rate; /* * If the host doesn't support any of the UHS-I modes, fallback on @@ -500,9 +501,12 @@ static int sdio_set_bus_speed_mode(struct mmc_card *card) if (err) return err; + max_rate = min_not_zero(card->quirk_max_rate, + card->sw_caps.uhs_max_dtr); + if (bus_speed) { mmc_set_timing(card->host, timing); - mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr); + mmc_set_clock(card->host, max_rate); } return 0; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 279b39008a33..5ebc47855721 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -252,6 +252,7 @@ struct mmc_card { #define MMC_TYPE_SD_COMBO 3 /* SD combo (IO+mem) card */ unsigned int state; /* (our) card state */ unsigned int quirks; /* card quirks */ + unsigned int quirk_max_rate; /* max rate set by quirks */ #define MMC_QUIRK_LENIENT_FN0 (1<<0) /* allow SDIO FN0 writes outside of the VS CCCR range */ #define MMC_QUIRK_BLKSZ_FOR_BYTE_MODE (1<<1) /* use func->cur_blksize */ /* for byte mode */ -- cgit v1.2.3 From 8ccd66f258cd79420102f3735b3bc8d974a22088 Mon Sep 17 00:00:00 2001 From: Diwakar Sharma Date: Tue, 10 Apr 2018 12:30:32 +0530 Subject: mmc: core: sdio: Set SDIO clock of SDR104 to 150MHz for Marvell 8887 chip This patch uses limit clock rate quirk to reduce clock rate for "SDR104" mode on IMX side for Marvell 8887 WiFi + Bluetooth chip side, as Marvell does not recommend to use SDIO at the speed of higher than 150MHz. Signed-off-by: Diwakar Sharma Signed-off-by: Harish Jenny K N Signed-off-by: Ulf Hansson --- drivers/mmc/core/quirks.h | 3 +++ include/linux/mmc/sdio_ids.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index 5153577754f0..dd2f73af8f2c 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -132,6 +132,9 @@ static const struct mmc_fixup sdio_fixup_methods[] = { SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797_F0, add_quirk, MMC_QUIRK_BROKEN_IRQ_POLLING), + SDIO_FIXUP(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887WLAN, + add_limit_rate_quirk, 150000000), + END_FIXUP }; diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index cdd66a5fbd5e..2836a96e014a 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -55,6 +55,7 @@ #define SDIO_DEVICE_ID_MARVELL_8688WLAN 0x9104 #define SDIO_DEVICE_ID_MARVELL_8688BT 0x9105 #define SDIO_DEVICE_ID_MARVELL_8797_F0 0x9128 +#define SDIO_DEVICE_ID_MARVELL_8887WLAN 0x9134 #define SDIO_VENDOR_ID_SIANO 0x039a #define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201 -- cgit v1.2.3 From c63cde7d6a313a0203733fe31a76dab4e56d61d3 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Wed, 28 Mar 2018 18:00:57 -0300 Subject: MIPS: configs: ci20: Enable ext4 Now that we have MMC support, enable ext2/3/4 support in the CI20 defconfig. Signed-off-by: Ezequiel Garcia Acked-by: James Hogan Signed-off-by: Ulf Hansson --- arch/mips/configs/ci20_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/mips/configs/ci20_defconfig b/arch/mips/configs/ci20_defconfig index f88b05fd3077..be23fd25eeaa 100644 --- a/arch/mips/configs/ci20_defconfig +++ b/arch/mips/configs/ci20_defconfig @@ -111,6 +111,7 @@ CONFIG_DMADEVICES=y CONFIG_DMA_JZ4780=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_MEMORY=y +CONFIG_EXT4_FS=y # CONFIG_DNOTIFY is not set CONFIG_PROC_KCORE=y # CONFIG_PROC_PAGE_MONITOR is not set -- cgit v1.2.3 From 743b819e4178935e3f098e5f13db301f532fa9e0 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 16:22:59 +0200 Subject: mmc: sunxi: Reorder the headers Our headers sort algorithm has had pretty chaotic results. Let's fix that. Signed-off-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 20cfb20418f3..91feed8045fd 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -13,36 +13,33 @@ * the License, or (at your option) any later version. */ -#include -#include -#include -#include -#include -#include -#include - #include #include -#include -#include -#include -#include +#include +#include #include -#include -#include -#include - -#include -#include -#include - +#include +#include +#include +#include +#include +#include +#include #include +#include #include #include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* register offset definitions */ #define SDXC_REG_GCTRL (0x00) /* SMC Global Control Register */ -- cgit v1.2.3 From 0fc4c61fff02bea1a82caf05884fefc0ceb14c5f Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 16:23:00 +0200 Subject: mmc: sunxi: Change sunxi_mmc_init_host argument type All the other functions in the driver take a struct sunxi_mmc_host pointer. Let's make it consistent. Signed-off-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 91feed8045fd..9fdaea37aa74 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -319,10 +319,9 @@ static int sunxi_mmc_reset_host(struct sunxi_mmc_host *host) return 0; } -static int sunxi_mmc_init_host(struct mmc_host *mmc) +static int sunxi_mmc_init_host(struct sunxi_mmc_host *host) { u32 rval; - struct sunxi_mmc_host *host = mmc_priv(mmc); if (sunxi_mmc_reset_host(host)) return -EIO; @@ -885,7 +884,7 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->vqmmc_enabled = true; } - host->ferror = sunxi_mmc_init_host(mmc); + host->ferror = sunxi_mmc_init_host(host); if (host->ferror) return; -- cgit v1.2.3 From 3f6c808e524700cb4f45123f9390ff83289c31e5 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 16:23:01 +0200 Subject: mmc: sunxi: Move bus width configuration to a function In order to improve readibility and reusability, let's move the bus width setup to a small function called by our .set_ios hook. Signed-off-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 9fdaea37aa74..c0bb75e17a44 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -855,6 +855,22 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host, return 0; } +static void sunxi_mmc_set_bus_width(struct sunxi_mmc_host *host, + unsigned char width) +{ + switch (width) { + case MMC_BUS_WIDTH_1: + mmc_writel(host, REG_WIDTH, SDXC_WIDTH1); + break; + case MMC_BUS_WIDTH_4: + mmc_writel(host, REG_WIDTH, SDXC_WIDTH4); + break; + case MMC_BUS_WIDTH_8: + mmc_writel(host, REG_WIDTH, SDXC_WIDTH8); + break; + } +} + static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sunxi_mmc_host *host = mmc_priv(mmc); @@ -903,18 +919,7 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) break; } - /* set bus width */ - switch (ios->bus_width) { - case MMC_BUS_WIDTH_1: - mmc_writel(host, REG_WIDTH, SDXC_WIDTH1); - break; - case MMC_BUS_WIDTH_4: - mmc_writel(host, REG_WIDTH, SDXC_WIDTH4); - break; - case MMC_BUS_WIDTH_8: - mmc_writel(host, REG_WIDTH, SDXC_WIDTH8); - break; - } + sunxi_mmc_set_bus_width(host, ios->bus_width); /* set ddr mode */ rval = mmc_readl(host, REG_GCTRL); -- cgit v1.2.3 From ad04d9555da02c719de25b7d1e81ea8d0d2c4838 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 16:23:02 +0200 Subject: mmc: sunxi: Move clock configuration to a function In order to improve readibility and reusability, let's move the clock setup to a small function called by our .set_ios hook. Signed-off-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index c0bb75e17a44..3c1ab46cc60a 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -871,6 +871,23 @@ static void sunxi_mmc_set_bus_width(struct sunxi_mmc_host *host, } } +static void sunxi_mmc_set_clk(struct sunxi_mmc_host *host, struct mmc_ios *ios) +{ + u32 rval; + + /* set ddr mode */ + rval = mmc_readl(host, REG_GCTRL); + if (ios->timing == MMC_TIMING_UHS_DDR50 || + ios->timing == MMC_TIMING_MMC_DDR52) + rval |= SDXC_DDR_MODE; + else + rval &= ~SDXC_DDR_MODE; + mmc_writel(host, REG_GCTRL, rval); + + host->ferror = sunxi_mmc_clk_set_rate(host, ios); + /* Android code had a usleep_range(50000, 55000); here */ +} + static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sunxi_mmc_host *host = mmc_priv(mmc); @@ -920,21 +937,7 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } sunxi_mmc_set_bus_width(host, ios->bus_width); - - /* set ddr mode */ - rval = mmc_readl(host, REG_GCTRL); - if (ios->timing == MMC_TIMING_UHS_DDR50 || - ios->timing == MMC_TIMING_MMC_DDR52) - rval |= SDXC_DDR_MODE; - else - rval &= ~SDXC_DDR_MODE; - mmc_writel(host, REG_GCTRL, rval); - - /* set up clock */ - if (ios->power_mode) { - host->ferror = sunxi_mmc_clk_set_rate(host, ios); - /* Android code had a usleep_range(50000, 55000); here */ - } + sunxi_mmc_set_clk(host, ios); } static int sunxi_mmc_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) -- cgit v1.2.3 From e27e1f3d04061ccc3735361554088cd7aa286e31 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 16:23:03 +0200 Subject: mmc: sunxi: Move the card power configuration to a function In order to improve readibility and reusability, let's move the card setup to a small function called by our .set_ios hook. Signed-off-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 3c1ab46cc60a..0165da0d022a 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -888,17 +888,15 @@ static void sunxi_mmc_set_clk(struct sunxi_mmc_host *host, struct mmc_ios *ios) /* Android code had a usleep_range(50000, 55000); here */ } -static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +static void sunxi_mmc_card_power(struct sunxi_mmc_host *host, + struct mmc_ios *ios) { - struct sunxi_mmc_host *host = mmc_priv(mmc); - u32 rval; + struct mmc_host *mmc = host->mmc; - /* Set the power state */ switch (ios->power_mode) { - case MMC_POWER_ON: - break; - case MMC_POWER_UP: + dev_dbg(mmc_dev(mmc), "Powering card up\n"); + if (!IS_ERR(mmc->supply.vmmc)) { host->ferror = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, @@ -916,25 +914,37 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } host->vqmmc_enabled = true; } - - host->ferror = sunxi_mmc_init_host(host); - if (host->ferror) - return; - - dev_dbg(mmc_dev(mmc), "power on!\n"); break; case MMC_POWER_OFF: - dev_dbg(mmc_dev(mmc), "power off!\n"); - sunxi_mmc_reset_host(host); + dev_dbg(mmc_dev(mmc), "Powering card off\n"); + 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: + dev_dbg(mmc_dev(mmc), "Ignoring unknown card power state\n"); + break; } +} + +static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sunxi_mmc_host *host = mmc_priv(mmc); + + if (ios->power_mode == MMC_POWER_OFF) + sunxi_mmc_reset_host(host); + + sunxi_mmc_card_power(host, ios); + + if (ios->power_mode == MMC_POWER_UP) + sunxi_mmc_init_host(host); sunxi_mmc_set_bus_width(host, ios->bus_width); sunxi_mmc_set_clk(host, ios); -- cgit v1.2.3 From 9a8e1e8cc2c02c57c4e941651a8481a633506c91 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 16:23:04 +0200 Subject: mmc: sunxi: Add runtime_pm support So far, even if our card was not in use, we didn't shut down our MMC controller, which meant that it was still active and clocking the bus. While this obviously means that we could save some power there, it also creates issues when it comes to EMC control since we'll have a perfect peak at the card clock rate. Let's implement runtime_pm with autosuspend so that we will shut down the controller when it's not been in use for quite some time. Signed-off-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 0165da0d022a..0253deb153a4 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -969,6 +970,9 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) unsigned long flags; u32 imask; + if (enable) + pm_runtime_get_noresume(host->dev); + spin_lock_irqsave(&host->lock, flags); imask = mmc_readl(host, REG_IMASK); @@ -981,6 +985,9 @@ static void sunxi_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) } mmc_writel(host, REG_IMASK, imask); spin_unlock_irqrestore(&host->lock, flags); + + if (!enable) + pm_runtime_put_noidle(host->mmc->parent); } static void sunxi_mmc_hw_reset(struct mmc_host *mmc) @@ -1394,6 +1401,11 @@ static int sunxi_mmc_probe(struct platform_device *pdev) if (ret) goto error_free_dma; + pm_runtime_set_active(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ret = mmc_add_host(mmc); if (ret) goto error_free_dma; @@ -1414,6 +1426,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev) struct sunxi_mmc_host *host = mmc_priv(mmc); mmc_remove_host(mmc); + pm_runtime_force_suspend(&pdev->dev); disable_irq(host->irq); sunxi_mmc_disable(host); dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); @@ -1422,10 +1435,45 @@ static int sunxi_mmc_remove(struct platform_device *pdev) return 0; } +static int sunxi_mmc_runtime_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct sunxi_mmc_host *host = mmc_priv(mmc); + int ret; + + ret = sunxi_mmc_enable(host); + if (ret) + return ret; + + sunxi_mmc_init_host(host); + sunxi_mmc_set_bus_width(host, mmc->ios.bus_width); + sunxi_mmc_set_clk(host, &mmc->ios); + + return 0; +} + +static int sunxi_mmc_runtime_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct sunxi_mmc_host *host = mmc_priv(mmc); + + sunxi_mmc_reset_host(host); + sunxi_mmc_disable(host); + + return 0; +} + +static const struct dev_pm_ops sunxi_mmc_pm_ops = { + SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, + sunxi_mmc_runtime_resume, + NULL) +}; + static struct platform_driver sunxi_mmc_driver = { .driver = { .name = "sunxi-mmc", .of_match_table = of_match_ptr(sunxi_mmc_of_match), + .pm = &sunxi_mmc_pm_ops, }, .probe = sunxi_mmc_probe, .remove = sunxi_mmc_remove, -- cgit v1.2.3 From eef797ac13c08fae0f0ce7d2215d0951e884fa2d Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 16 Apr 2018 16:23:05 +0200 Subject: mmc: sunxi: Drop the init / reset of the controller from set_ios Our set_ios hook is, when the card is power up or down, either doing a full init or put our controller back into a reset mode. Since we're also doing that in our runtime_pm hooks, and at possibly much more often, we can drop it from the set_ios, and either rely on our runtime_pm hooks or our probe to do it. Signed-off-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 0253deb153a4..97c6b79b7d6f 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -939,14 +939,7 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct sunxi_mmc_host *host = mmc_priv(mmc); - if (ios->power_mode == MMC_POWER_OFF) - sunxi_mmc_reset_host(host); - sunxi_mmc_card_power(host, ios); - - if (ios->power_mode == MMC_POWER_UP) - sunxi_mmc_init_host(host); - sunxi_mmc_set_bus_width(host, ios->bus_width); sunxi_mmc_set_clk(host, ios); } @@ -1401,6 +1394,10 @@ static int sunxi_mmc_probe(struct platform_device *pdev) if (ret) goto error_free_dma; + ret = sunxi_mmc_init_host(host); + if (ret) + goto error_free_dma; + pm_runtime_set_active(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 50); pm_runtime_use_autosuspend(&pdev->dev); -- cgit v1.2.3 From 1d6efe08c80d84b1f39845707288ffd4df239c3b Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 17 Apr 2018 19:49:13 +0200 Subject: mmc: Remove depends on HAS_DMA in case of platform dependency Remove dependencies on HAS_DMA where a Kconfig symbol depends on another symbol that implies HAS_DMA, and, optionally, on "|| COMPILE_TEST". In most cases this other symbol is an architecture or platform specific symbol, or PCI. Generic symbols and drivers without platform dependencies keep their dependencies on HAS_DMA, to prevent compiling subsystems or drivers that cannot work anyway. This simplifies the dependencies, and allows to improve compile-testing. Signed-off-by: Geert Uytterhoeven Reviewed-by: Mark Brown Acked-by: Robin Murphy Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index fa02faf03d81..d70d11d0abab 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -358,7 +358,6 @@ config MMC_MESON_MX_SDIO tristate "Amlogic Meson6/Meson8/Meson8b SD/MMC Host Controller support" depends on ARCH_MESON || COMPILE_TEST depends on COMMON_CLK - depends on HAS_DMA depends on OF help This selects support for the SD/MMC Host Controller on @@ -401,7 +400,6 @@ config MMC_OMAP config MMC_OMAP_HS tristate "TI OMAP High Speed Multimedia Card Interface support" - depends on HAS_DMA depends on ARCH_OMAP2PLUS || ARCH_KEYSTONE || COMPILE_TEST help This selects the TI OMAP High Speed Multimedia card Interface. @@ -511,7 +509,6 @@ config MMC_DAVINCI config MMC_GOLDFISH tristate "goldfish qemu Multimedia Card Interface support" - depends on HAS_DMA depends on GOLDFISH || COMPILE_TEST help This selects the Goldfish Multimedia card Interface emulation @@ -605,7 +602,7 @@ config MMC_SDHI config MMC_SDHI_SYS_DMAC tristate "DMA for SDHI SD/SDIO controllers using SYS-DMAC" - depends on MMC_SDHI && HAS_DMA + depends on MMC_SDHI default MMC_SDHI if (SUPERH || ARM) help This provides DMA support for SDHI SD/SDIO controllers @@ -615,7 +612,7 @@ config MMC_SDHI_SYS_DMAC config MMC_SDHI_INTERNAL_DMAC tristate "DMA for SDHI SD/SDIO controllers using on-chip bus mastering" depends on ARM64 || COMPILE_TEST - depends on MMC_SDHI && HAS_DMA + depends on MMC_SDHI default MMC_SDHI if ARM64 help This provides DMA support for SDHI SD/SDIO controllers @@ -669,7 +666,6 @@ config MMC_CAVIUM_THUNDERX config MMC_DW tristate "Synopsys DesignWare Memory Card Interface" - depends on HAS_DMA depends on ARC || ARM || ARM64 || MIPS || COMPILE_TEST help This selects support for the Synopsys DesignWare Mobile Storage IP @@ -748,7 +744,6 @@ config MMC_DW_ZX config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" - depends on HAS_DMA depends on SUPERH || ARCH_RENESAS || COMPILE_TEST help This selects the MMC Host Interface controller (MMCIF) found in various @@ -869,7 +864,6 @@ config MMC_TOSHIBA_PCI config MMC_BCM2835 tristate "Broadcom BCM2835 SDHOST MMC Controller support" depends on ARCH_BCM2835 || COMPILE_TEST - depends on HAS_DMA help This selects the BCM2835 SDHOST MMC controller. If you have a BCM2835 platform with SD or MMC devices, say Y or M here. -- cgit v1.2.3 From 247cfe53557524a94dd1001d19e5aa50bd5aca81 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Fri, 13 Apr 2018 16:54:57 -0500 Subject: mmc: core: Add capability to avoid 3.3V signaling Some SD host controllers cannot handle extended use of 3.3V signaling. To accommodate these controllers, add a capability that requires us to negotiate the voltage down from 3.3V during card initialization. Signed-off-by: Kyle Roeschley Signed-off-by: Jennifer Dahm Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 8 ++++++++ drivers/mmc/core/sd.c | 8 ++++++++ drivers/mmc/core/sdio.c | 8 ++++++++ include/linux/mmc/host.h | 1 + 4 files changed, 25 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 6f8ebd6caa4c..915b58f9bfc5 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1830,6 +1830,14 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, } } + if (host->caps2 & MMC_CAP2_AVOID_3_3V && + host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + pr_err("%s: Host failed to negotiate down from 3.3V\n", + mmc_hostname(host)); + err = -EINVAL; + goto free_card; + } + if (!oldcard) host->card = card; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index baf3d5da4ccb..7f87a5354f9f 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1058,6 +1058,14 @@ retry: mmc_set_bus_width(host, MMC_BUS_WIDTH_4); } } + + if (host->caps2 & MMC_CAP2_AVOID_3_3V && + host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + pr_err("%s: Host failed to negotiate down from 3.3V\n", + mmc_hostname(host)); + err = -EINVAL; + goto free_card; + } done: host->card = card; return 0; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 24b510b743da..87e53a721a10 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -792,6 +792,14 @@ try_again: if (err) goto remove; } + + if (host->caps2 & MMC_CAP2_AVOID_3_3V && + host->ios.signal_voltage == MMC_SIGNAL_VOLTAGE_330) { + pr_err("%s: Host failed to negotiate down from 3.3V\n", + mmc_hostname(host)); + err = -EINVAL; + goto remove; + } finish: if (!oldcard) host->card = card; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 85146235231e..7c6eaf63f5ce 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -354,6 +354,7 @@ struct mmc_host { #define MMC_CAP2_NO_MMC (1 << 22) /* Do not send (e)MMC commands during initialization */ #define MMC_CAP2_CQE (1 << 23) /* Has eMMC command queue engine */ #define MMC_CAP2_CQE_DCMD (1 << 24) /* CQE can issue a direct command */ +#define MMC_CAP2_AVOID_3_3V (1 << 25) /* Host must negotiate down from 3.3V */ int fixed_drv_type; /* fixed driver type for non-removable media */ -- cgit v1.2.3 From bb26b8412dec694a99f3b1b1dd1eed1f185f04f6 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Fri, 13 Apr 2018 16:54:58 -0500 Subject: mmc: sdhci-pci: Avoid 3.3V signaling on some NI 904x On some NI 904x devices, using 3.3V signaling for extended periods of time will physically damage the pads connected to the SDHC, eventually causing complete failure of the controller. To work around this, require that we avoid 3.3V signaling. Signed-off-by: Kyle Roeschley Signed-off-by: Jennifer Dahm Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 4 ++++ drivers/mmc/host/sdhci-pci.h | 1 + 2 files changed, 5 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 78c25ad35fd2..9dfbd7f7e9fa 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -832,6 +832,10 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_SD) slot->host->mmc_host_ops.get_cd = bxt_get_cd; + if (slot->chip->pdev->subsystem_vendor == PCI_VENDOR_ID_NI && + slot->chip->pdev->subsystem_device == PCI_SUBDEVICE_ID_NI_78E3) + slot->host->mmc->caps2 |= MMC_CAP2_AVOID_3_3V; + return 0; } diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 5cbcdc448f98..db9cb54ef700 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -54,6 +54,7 @@ #define PCI_DEVICE_ID_REALTEK_5250 0x5250 #define PCI_SUBDEVICE_ID_NI_7884 0x7884 +#define PCI_SUBDEVICE_ID_NI_78E3 0x78e3 #define PCI_VENDOR_ID_ARASAN 0x16e6 #define PCI_DEVICE_ID_ARASAN_PHY_EMMC 0x0670 -- cgit v1.2.3 From a028b435bf37fa091f701ee44e45cc152740fb8f Mon Sep 17 00:00:00 2001 From: Niklas Söderlund Date: Wed, 18 Apr 2018 20:20:58 +0200 Subject: mmc: renesas_sdhi: use helpers to access struct scatterlist members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of directly accessing the members of struct scatterlist use the helpers mmc_get_dma_dir() and sg_dma_address() in renesas_sdhi_internal_dmac_start_dma(). Based on previous work by Masaharu Hayakawa. Signed-off-by: Niklas Söderlund [rebased to mmc/next] Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 6af946d16d24..6a86619d3f85 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -157,7 +157,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, { struct scatterlist *sg = host->sg_ptr; u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE; - enum dma_data_direction dir; int ret; /* This DMAC cannot handle if sg_len is not 1 */ @@ -169,16 +168,15 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, if (data->flags & MMC_DATA_READ) { dtran_mode |= DTRAN_MODE_CH_NUM_CH1; - dir = DMA_FROM_DEVICE; if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) && test_and_set_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags)) goto force_pio; } else { dtran_mode |= DTRAN_MODE_CH_NUM_CH0; - dir = DMA_TO_DEVICE; } - ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, dir); + ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, + mmc_get_dma_dir(data)); if (ret == 0) goto force_pio; @@ -188,7 +186,7 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, renesas_sdhi_internal_dmac_dm_write(host, DM_CM_DTRAN_MODE, dtran_mode); renesas_sdhi_internal_dmac_dm_write(host, DM_DTRAN_ADDR, - sg->dma_address); + sg_dma_address(sg)); return; -- cgit v1.2.3 From ae275b9d606f92227f43603919dc3576380efdc1 Mon Sep 17 00:00:00 2001 From: Masaharu Hayakawa Date: Wed, 18 Apr 2018 20:20:59 +0200 Subject: mmc: renesas_sdhi: Fix alignment check of sg buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes sg->offset is not used for buffer addresses allocated by dma_map_sg(), so alignment checks should be done on the allocated buffer addresses. Delete the alignment check for sg->offset that is done before dma_map_sg(). Instead, it performs the alignment check for sg->dma_address after dma_map_sg(). Signed-off-by: Masaharu Hayakawa [Niklas: broke this commit in two and tidied small style issue] Signed-off-by: Niklas Söderlund [rebased to mmc/next] Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 6a86619d3f85..b36e174a4b0c 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -157,14 +157,20 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, { struct scatterlist *sg = host->sg_ptr; u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE; - int ret; /* This DMAC cannot handle if sg_len is not 1 */ WARN_ON(host->sg_len > 1); + if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len, + mmc_get_dma_dir(data))) + goto force_pio; + /* This DMAC cannot handle if buffer is not 8-bytes alignment */ - if (!IS_ALIGNED(sg->offset, 8)) + if (!IS_ALIGNED(sg_dma_address(sg), 8)) { + dma_unmap_sg(&host->pdev->dev, sg, host->sg_len, + mmc_get_dma_dir(data)); goto force_pio; + } if (data->flags & MMC_DATA_READ) { dtran_mode |= DTRAN_MODE_CH_NUM_CH1; @@ -175,11 +181,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, dtran_mode |= DTRAN_MODE_CH_NUM_CH0; } - ret = dma_map_sg(&host->pdev->dev, sg, host->sg_len, - mmc_get_dma_dir(data)); - if (ret == 0) - goto force_pio; - renesas_sdhi_internal_dmac_enable_dma(host, true); /* set dma parameters */ -- cgit v1.2.3 From 1abf9e52ea502afb44700098d1cf49a102bd259a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 18 Apr 2018 20:21:00 +0200 Subject: mmc: renesas_sdhi_internal_dmac: use more generic whitelisting Whitelisting every ES version does not scale. So, we whitelist whole SoCs independent of ES version. If we need specific handling for an ES version, we put it to the front, so it will be matched first. Signed-off-by: Wolfram Sang Reviewed-by: Yoshihiro Shimoda Reviewed-by: Simon Horman Tested-by: Nguyen Viet Dung Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index b36e174a4b0c..240e8a168aa1 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -271,12 +271,15 @@ static const struct tmio_mmc_dma_ops renesas_sdhi_internal_dmac_dma_ops = { * implementation as others may use a different implementation. */ static const struct soc_device_attribute gen3_soc_whitelist[] = { + /* specific ones */ { .soc_id = "r8a7795", .revision = "ES1.*", .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, - { .soc_id = "r8a7795", .revision = "ES2.0" }, { .soc_id = "r8a7796", .revision = "ES1.0", .data = (void *)BIT(SDHI_INTERNAL_DMAC_ONE_RX_ONLY) }, - { .soc_id = "r8a77995", .revision = "ES1.0" }, + /* generic ones */ + { .soc_id = "r8a7795" }, + { .soc_id = "r8a7796" }, + { .soc_id = "r8a77995" }, { /* sentinel */ } }; -- cgit v1.2.3 From ebca50dfae525341c48c2f69798667352318549e Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 18 Apr 2018 20:21:01 +0200 Subject: mmc: renesas_sdhi_internal_dmac: remove superfluous WARN The WARN can never trigger because we limited the max_seg number in renesas_sdhi_of_data already. Remove it and update the comment. Signed-off-by: Wolfram Sang Reviewed-by: Simon Horman Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 240e8a168aa1..2935dd9315d3 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -91,7 +91,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { .scc_offset = 0x1000, .taps = rcar_gen3_scc_taps, .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps), - /* Gen3 SDHI DMAC can handle 0xffffffff blk count, but seg = 1 */ + /* DMAC can handle 0xffffffff blk count but only 1 segment */ .max_blk_count = 0xffffffff, .max_segs = 1, }; @@ -158,9 +158,6 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, struct scatterlist *sg = host->sg_ptr; u32 dtran_mode = DTRAN_MODE_BUS_WID_TH | DTRAN_MODE_ADDR_MODE; - /* This DMAC cannot handle if sg_len is not 1 */ - WARN_ON(host->sg_len > 1); - if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len, mmc_get_dma_dir(data))) goto force_pio; -- cgit v1.2.3 From b638602eae7851918d2abed823ab3bed0c269e59 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Mon, 16 Apr 2018 21:30:02 +0300 Subject: mmc: dt: tmio_mmc: document R8A77980 bindings Document the R-Car V3H (R8A77980) SoC in the Renesas SDHI bindings. Signed-off-by: Sergei Shtylyov Reviewed-by: Wolfram Sang Reviewed-by: Rob Herring Signed-off-by: Ulf Hansson Reviewed-by: Simon Horman --- Documentation/devicetree/bindings/mmc/tmio_mmc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt index 2d5287eeed95..ba3825245889 100644 --- a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt +++ b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt @@ -26,6 +26,7 @@ Required properties: "renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC "renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC "renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC + "renesas,sdhi-r8a77980" - SDHI IP on R8A77980 SoC "renesas,sdhi-r8a77995" - SDHI IP on R8A77995 SoC "renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller "renesas,rcar-gen1-sdhi" - a generic R-Car Gen1 SDHI controller -- cgit v1.2.3 From be17355a7472e8466fdb29dca09eaa6a9ab6468d Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 13 Apr 2018 16:18:27 +0300 Subject: mmc: sdhci-pci: Fix 3.3V voltage switch for some BYT-based Intel controllers Fix 3.3V voltage switch for some BYT-based Intel controllers by making use of the ACPI DSM. Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 9dfbd7f7e9fa..77dd3521daae 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -453,6 +453,7 @@ static const struct sdhci_pci_fixes sdhci_intel_pch_sdio = { enum { INTEL_DSM_FNS = 0, INTEL_DSM_V18_SWITCH = 3, + INTEL_DSM_V33_SWITCH = 4, INTEL_DSM_DRV_STRENGTH = 9, INTEL_DSM_D3_RETUNE = 10, }; @@ -620,17 +621,37 @@ static void intel_hs400_enhanced_strobe(struct mmc_host *mmc, sdhci_writel(host, val, INTEL_HS400_ES_REG); } -static void sdhci_intel_voltage_switch(struct sdhci_host *host) +static int intel_start_signal_voltage_switch(struct mmc_host *mmc, + struct mmc_ios *ios) { + struct device *dev = mmc_dev(mmc); + struct sdhci_host *host = mmc_priv(mmc); struct sdhci_pci_slot *slot = sdhci_priv(host); struct intel_host *intel_host = sdhci_pci_priv(slot); - struct device *dev = &slot->chip->pdev->dev; + unsigned int fn; u32 result = 0; int err; - err = intel_dsm(intel_host, dev, INTEL_DSM_V18_SWITCH, &result); - pr_debug("%s: %s DSM error %d result %u\n", - mmc_hostname(host->mmc), __func__, err, result); + err = sdhci_start_signal_voltage_switch(mmc, ios); + if (err) + return err; + + switch (ios->signal_voltage) { + case MMC_SIGNAL_VOLTAGE_330: + fn = INTEL_DSM_V33_SWITCH; + break; + case MMC_SIGNAL_VOLTAGE_180: + fn = INTEL_DSM_V18_SWITCH; + break; + default: + return 0; + } + + err = intel_dsm(intel_host, dev, fn, &result); + pr_debug("%s: %s DSM fn %u error %d result %u\n", + mmc_hostname(mmc), __func__, fn, err, result); + + return 0; } static const struct sdhci_ops sdhci_intel_byt_ops = { @@ -641,7 +662,6 @@ static const struct sdhci_ops sdhci_intel_byt_ops = { .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, - .voltage_switch = sdhci_intel_voltage_switch, }; static const struct sdhci_ops sdhci_intel_glk_ops = { @@ -652,7 +672,6 @@ static const struct sdhci_ops sdhci_intel_glk_ops = { .reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, .hw_reset = sdhci_pci_hw_reset, - .voltage_switch = sdhci_intel_voltage_switch, .irq = sdhci_cqhci_irq, }; @@ -691,6 +710,7 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot) byt_read_dsm(slot); ops->execute_tuning = intel_execute_tuning; + ops->start_signal_voltage_switch = intel_start_signal_voltage_switch; } static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) -- cgit v1.2.3 From 909b345665158323b44e9c8b70234dea3067685d Mon Sep 17 00:00:00 2001 From: Ryder Lee Date: Mon, 16 Apr 2018 10:33:47 +0800 Subject: mmc: mediatek: use of_device_get_match_data() The usage of of_device_get_match_data() reduce the code size a bit. Also, the only way to call msdc_drv_probe() is to match an entry in msdc_of_ids[], so of_id cannot be NULL. Signed-off-by: Ryder Lee Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index cb274e822293..d642e11b75d4 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1820,7 +1821,6 @@ static int msdc_drv_probe(struct platform_device *pdev) struct mmc_host *mmc; struct msdc_host *host; struct resource *res; - const struct of_device_id *of_id; int ret; if (!pdev->dev.of_node) { @@ -1828,9 +1828,6 @@ static int msdc_drv_probe(struct platform_device *pdev) return -EINVAL; } - of_id = of_match_node(msdc_of_ids, pdev->dev.of_node); - if (!of_id) - return -EINVAL; /* Allocate MMC host for this device */ mmc = mmc_alloc_host(sizeof(struct msdc_host), &pdev->dev); if (!mmc) @@ -1899,7 +1896,7 @@ static int msdc_drv_probe(struct platform_device *pdev) msdc_of_property_parse(pdev, host); host->dev = &pdev->dev; - host->dev_comp = of_id->data; + host->dev_comp = of_device_get_match_data(&pdev->dev); host->mmc = mmc; host->src_clk_freq = clk_get_rate(host->src_clk); /* Set host parameters to mmc */ -- cgit v1.2.3 From 970f2d90be108352125c993333b0bf7ad0fb35d1 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 19 Apr 2018 16:05:58 +0200 Subject: mmc: host: simplify getting .drvdata We should get drvdata from struct device directly. Going via platform_device is an unneeded step back and forth. Signed-off-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/host/davinci_mmc.c | 6 ++---- drivers/mmc/host/sdhci-of-arasan.c | 6 ++---- drivers/mmc/host/wmt-sdmmc.c | 6 ++---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 8e363174f9d6..9e68c3645e22 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1377,8 +1377,7 @@ static int __exit davinci_mmcsd_remove(struct platform_device *pdev) #ifdef CONFIG_PM static int davinci_mmcsd_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mmc_davinci_host *host = platform_get_drvdata(pdev); + struct mmc_davinci_host *host = dev_get_drvdata(dev); writel(0, host->base + DAVINCI_MMCIM); mmc_davinci_reset_ctrl(host, 1); @@ -1389,8 +1388,7 @@ static int davinci_mmcsd_suspend(struct device *dev) static int davinci_mmcsd_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct mmc_davinci_host *host = platform_get_drvdata(pdev); + struct mmc_davinci_host *host = dev_get_drvdata(dev); clk_enable(host->clk); mmc_davinci_reset_ctrl(host, 0); diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index ab66e323bcb8..e3332a522a5d 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -360,8 +360,7 @@ static const struct sdhci_pltfm_data sdhci_arasan_cqe_pdata = { */ static int sdhci_arasan_suspend(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); int ret; @@ -404,8 +403,7 @@ static int sdhci_arasan_suspend(struct device *dev) */ static int sdhci_arasan_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); int ret; diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index fd30ac7da5e5..3ba42f508014 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -928,8 +928,7 @@ static int wmt_mci_remove(struct platform_device *pdev) static int wmt_mci_suspend(struct device *dev) { u32 reg_tmp; - struct platform_device *pdev = to_platform_device(dev); - struct mmc_host *mmc = platform_get_drvdata(pdev); + struct mmc_host *mmc = dev_get_drvdata(dev); struct wmt_mci_priv *priv; if (!mmc) @@ -953,8 +952,7 @@ static int wmt_mci_suspend(struct device *dev) static int wmt_mci_resume(struct device *dev) { u32 reg_tmp; - struct platform_device *pdev = to_platform_device(dev); - struct mmc_host *mmc = platform_get_drvdata(pdev); + struct mmc_host *mmc = dev_get_drvdata(dev); struct wmt_mci_priv *priv; if (mmc) { -- cgit v1.2.3 From 3f1109d1324405eaeb6c4a586084bd1d933d8b19 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 19 Apr 2018 10:59:58 -0500 Subject: mmc: sdhci-cadence: fix logically and structurally dead code Currently, the code block inside the for loop will never execute more than once, because the function returns immediately after the first iteration, hence the execution of the code at the second iteration is structurally dead and, code at line 281: return 0; is never reached. Fix this by checking _ret_ before return. Addresses-Coverity-ID: 1468009 ("Logically dead code") Addresses-Coverity-ID: 1468002 ("Structurally dead code") Suggested-by: Masahiro Yamada Signed-off-by: Gustavo A. R. Silva Acked-by: Masahiro Yamada Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-cadence.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence.c index bc30d1637246..7a343b87b5e5 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence.c @@ -274,8 +274,8 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) ret = readl_poll_timeout(reg, tmp, !(tmp & SDHCI_CDNS_HRS06_TUNE_UP), 0, 1); - - return ret; + if (ret) + return ret; } return 0; -- cgit v1.2.3 From e419768f3242e0e91ee6c691b2e871ff6afd6995 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Thu, 19 Apr 2018 23:07:44 +0300 Subject: mmc: renesas_sdhi_internal_dmac: add R8A77980 to whitelist I've successfully tested eMMC on R8A77980/Condor. R8A77980 has a single SDHI core anyway, so can't be a subject of the known RX DMA errata... Signed-off-by: Sergei Shtylyov Reviewed-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 2935dd9315d3..a6bf12348bdc 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -276,6 +276,7 @@ static const struct soc_device_attribute gen3_soc_whitelist[] = { /* generic ones */ { .soc_id = "r8a7795" }, { .soc_id = "r8a7796" }, + { .soc_id = "r8a77980" }, { .soc_id = "r8a77995" }, { /* sentinel */ } }; -- cgit v1.2.3 From ac06fba1de8fb2685bf6e185ce211def3bf6d16e Mon Sep 17 00:00:00 2001 From: Vijay Viswanath Date: Fri, 20 Apr 2018 17:45:28 +0530 Subject: mmc: sdhci-msm: Add support to store supported vdd-io voltages During probe check whether the vdd-io regulator of sdhc platform device can support 1.8V and 3V and store this information as a capability of platform device. Signed-off-by: Vijay Viswanath Reviewed-by: Douglas Anderson Acked-by: Bjorn Andersson Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index c283291db705..edd30a23d36b 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "sdhci-pltfm.h" @@ -81,6 +82,9 @@ #define CORE_HC_SELECT_IN_HS400 (6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) +#define CORE_3_0V_SUPPORT (1 << 25) +#define CORE_1_8V_SUPPORT (1 << 26) + #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIB BIT(16) #define CORE_HW_AUTOCAL_ENA BIT(17) @@ -148,6 +152,7 @@ struct sdhci_msm_host { u32 curr_io_level; wait_queue_head_t pwr_irq_wait; bool pwr_irq_flag; + u32 caps_0; }; static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -1103,7 +1108,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); u32 irq_status, irq_ack = 0; int retry = 10; - int pwr_state = 0, io_level = 0; + u32 pwr_state = 0, io_level = 0; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); @@ -1313,6 +1318,27 @@ static void sdhci_msm_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_msm_check_power_status(host, req_type); } +static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) +{ + struct mmc_host *mmc = msm_host->mmc; + struct regulator *supply = mmc->supply.vqmmc; + u32 caps = 0; + + if (!IS_ERR(mmc->supply.vqmmc)) { + if (regulator_is_supported_voltage(supply, 1700000, 1950000)) + caps |= CORE_1_8V_SUPPORT; + if (regulator_is_supported_voltage(supply, 2700000, 3600000)) + caps |= CORE_3_0V_SUPPORT; + + if (!caps) + pr_warn("%s: 1.8/3V not supported for vqmmc\n", + mmc_hostname(mmc)); + } + + msm_host->caps_0 |= caps; + pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); +} + static const struct of_device_id sdhci_msm_dt_match[] = { { .compatible = "qcom,sdhci-msm-v4" }, {}, @@ -1530,6 +1556,7 @@ static int sdhci_msm_probe(struct platform_device *pdev) ret = sdhci_add_host(host); if (ret) goto pm_runtime_disable; + sdhci_msm_set_regulator_caps(msm_host); pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); -- cgit v1.2.3 From 5c132323c218624f2e0850ba33b0c630ea997aa9 Mon Sep 17 00:00:00 2001 From: Vijay Viswanath Date: Fri, 20 Apr 2018 17:45:29 +0530 Subject: mmc: sdhci-msm: support voltage pad switching The PADs for SD card are dual-voltage that support 3v/1.8v. Those PADs have a control signal (io_pad_pwr_switch/mode18 ) that indicates whether the PAD works in 3v or 1.8v. SDHC core on msm platforms should have IO_PAD_PWR_SWITCH bit set/unset based on actual voltage used for IO lines. So when power irq is triggered for io high or io low, the driver should check the voltages supported and set the pad accordingly. Signed-off-by: Krishna Konda Signed-off-by: Venkat Gopalakrishnan Signed-off-by: Vijay Viswanath Reviewed-by: Douglas Anderson Acked-by: Bjorn Andersson Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 57 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index edd30a23d36b..bb11916c58a1 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -78,12 +78,15 @@ #define CORE_HC_MCLK_SEL_DFLT (2 << 8) #define CORE_HC_MCLK_SEL_HS400 (3 << 8) #define CORE_HC_MCLK_SEL_MASK (3 << 8) +#define CORE_IO_PAD_PWR_SWITCH_EN (1 << 15) +#define CORE_IO_PAD_PWR_SWITCH (1 << 16) #define CORE_HC_SELECT_IN_EN BIT(18) #define CORE_HC_SELECT_IN_HS400 (6 << 19) #define CORE_HC_SELECT_IN_MASK (7 << 19) #define CORE_3_0V_SUPPORT (1 << 25) #define CORE_1_8V_SUPPORT (1 << 26) +#define CORE_VOLT_SUPPORT (CORE_3_0V_SUPPORT | CORE_1_8V_SUPPORT) #define CORE_CSR_CDC_CTLR_CFG0 0x130 #define CORE_SW_TRIG_FULL_CALIB BIT(16) @@ -1109,7 +1112,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) u32 irq_status, irq_ack = 0; int retry = 10; u32 pwr_state = 0, io_level = 0; - + u32 config; irq_status = readl_relaxed(msm_host->core_mem + CORE_PWRCTL_STATUS); irq_status &= INT_MASK; @@ -1166,6 +1169,38 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) */ writel_relaxed(irq_ack, msm_host->core_mem + CORE_PWRCTL_CTL); + /* + * If we don't have info regarding the voltage levels supported by + * regulators, don't change the IO PAD PWR SWITCH. + */ + if (msm_host->caps_0 & CORE_VOLT_SUPPORT) { + u32 new_config; + /* + * We should unset IO PAD PWR switch only if the register write + * can set IO lines high and the regulator also switches to 3 V. + * Else, we should keep the IO PAD PWR switch set. + * This is applicable to certain targets where eMMC vccq supply + * is only 1.8V. In such targets, even during REQ_IO_HIGH, the + * IO PAD PWR switch must be kept set to reflect actual + * regulator voltage. This way, during initialization of + * controllers with only 1.8V, we will set the IO PAD bit + * without waiting for a REQ_IO_LOW. + */ + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + new_config = config; + + if ((io_level & REQ_IO_HIGH) && + (msm_host->caps_0 & CORE_3_0V_SUPPORT)) + new_config &= ~CORE_IO_PAD_PWR_SWITCH; + else if ((io_level & REQ_IO_LOW) || + (msm_host->caps_0 & CORE_1_8V_SUPPORT)) + new_config |= CORE_IO_PAD_PWR_SWITCH; + + if (config ^ new_config) + writel_relaxed(new_config, + host->ioaddr + CORE_VENDOR_SPEC); + } + if (pwr_state) msm_host->curr_pwr_state = pwr_state; if (io_level) @@ -1322,7 +1357,8 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) { struct mmc_host *mmc = msm_host->mmc; struct regulator *supply = mmc->supply.vqmmc; - u32 caps = 0; + u32 caps = 0, config; + struct sdhci_host *host = mmc_priv(mmc); if (!IS_ERR(mmc->supply.vqmmc)) { if (regulator_is_supported_voltage(supply, 1700000, 1950000)) @@ -1335,6 +1371,23 @@ static void sdhci_msm_set_regulator_caps(struct sdhci_msm_host *msm_host) mmc_hostname(mmc)); } + if (caps) { + /* + * Set the PAD_PWR_SWITCH_EN bit so that the PAD_PWR_SWITCH + * bit can be used as required later on. + */ + u32 io_level = msm_host->curr_io_level; + + config = readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC); + config |= CORE_IO_PAD_PWR_SWITCH_EN; + + if ((io_level & REQ_IO_HIGH) && (caps & CORE_3_0V_SUPPORT)) + config &= ~CORE_IO_PAD_PWR_SWITCH; + else if ((io_level & REQ_IO_LOW) || (caps & CORE_1_8V_SUPPORT)) + config |= CORE_IO_PAD_PWR_SWITCH; + + writel_relaxed(config, host->ioaddr + CORE_VENDOR_SPEC); + } msm_host->caps_0 |= caps; pr_debug("%s: supported caps: 0x%08x\n", mmc_hostname(mmc), caps); } -- cgit v1.2.3 From 3fbd432231d7486e4b12c02daea7fd748f79d58c Mon Sep 17 00:00:00 2001 From: Andrew Gabbasov Date: Tue, 17 Apr 2018 18:45:12 +0530 Subject: mmc: sdhci-esdhc-imx: Set maximum watermark levels for PIO access While performing R/W access in PIO mode, the common SDHCI driver checks the buffer ready status once per whole block processing. That is, after getting an appropriate interrupt, or checking an appropriate status bit, the driver makes buffer accesses for the whole block size (e.g. 128 reads for 512 bytes block). This is done in accordance with SD Host Controller Specification. At the same time, the Ultra Secured Digital Host Controller (uSDHC), used in i.MX6 (and, probably, earlier i.MX series too), uses a separate Watermark Levels register, controlling the amount of data or space available when raising status bit or interrupt. For default watermark setting of 16 words, the controller expects (and guarantees) no more than 16 buffer accesses after raising buffer ready status bit and generating an appropriate interrupt. If the driver tries to access the whole block size, it will get incorrect data at the end, and a new interrupt will appear later, when the driver already doesn't expect it. This happens sometimes, more likely on low frequencies, e.g. when reading EXT_CSD at MMC card initialization phase (which makes that initialization fail). Such behavior of i.MX uSDHC seems to be non-compliant to SDHCI Specification, but this is the way it works now. In order not to rewrite the SDHCI driver PIO mode access logic, the IMX specific driver can just set the watermark level to default block size (128 words or 512 bytes), so that the controller behavior will be consistent to generic specification. This patch does this for PIO mode accesses only, restoring default values for DMA accesses to avoid any possible side effects from performance point of view. Signed-off-by: Andrew Gabbasov Signed-off-by: Harish Jenny K N Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index cd2b5f643a15..d6aef70d34fa 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -41,6 +41,12 @@ #define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8) #define ESDHC_WTMK_LVL 0x44 #define ESDHC_WTMK_DEFAULT_VAL 0x10401040 +#define ESDHC_WTMK_LVL_RD_WML_MASK 0x000000FF +#define ESDHC_WTMK_LVL_RD_WML_SHIFT 0 +#define ESDHC_WTMK_LVL_WR_WML_MASK 0x00FF0000 +#define ESDHC_WTMK_LVL_WR_WML_SHIFT 16 +#define ESDHC_WTMK_LVL_WML_VAL_DEF 64 +#define ESDHC_WTMK_LVL_WML_VAL_MAX 128 #define ESDHC_MIX_CTRL 0x48 #define ESDHC_MIX_CTRL_DDREN (1 << 3) #define ESDHC_MIX_CTRL_AC23EN (1 << 7) @@ -516,6 +522,7 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } if (esdhc_is_usdhc(imx_data)) { + u32 wml; u32 m = readl(host->ioaddr + ESDHC_MIX_CTRL); /* Swap AC23 bit */ if (val & SDHCI_TRNS_AUTO_CMD23) { @@ -524,6 +531,21 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } m = val | (m & ~ESDHC_MIX_CTRL_SDHCI_MASK); writel(m, host->ioaddr + ESDHC_MIX_CTRL); + + /* Set watermark levels for PIO access to maximum value + * (128 words) to accommodate full 512 bytes buffer. + * For DMA access restore the levels to default value. + */ + m = readl(host->ioaddr + ESDHC_WTMK_LVL); + if (val & SDHCI_TRNS_DMA) + wml = ESDHC_WTMK_LVL_WML_VAL_DEF; + else + wml = ESDHC_WTMK_LVL_WML_VAL_MAX; + m &= ~(ESDHC_WTMK_LVL_RD_WML_MASK | + ESDHC_WTMK_LVL_WR_WML_MASK); + m |= (wml << ESDHC_WTMK_LVL_RD_WML_SHIFT) | + (wml << ESDHC_WTMK_LVL_WR_WML_SHIFT); + writel(m, host->ioaddr + ESDHC_WTMK_LVL); } else { /* * Postpone this write, we must do it together with a -- cgit v1.2.3 From 99d02d6cd5610711d91f286bb67a57142028e9e6 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 24 Apr 2018 18:40:49 +0200 Subject: mmc: mmci: Remove bogus local_irq_save() On !RT interrupt runs with interrupts disabled. On RT it's in a thread, so no need to disable interrupts at all. Remove the local_irq_save() invocation. Signed-off-by: Thomas Gleixner Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 70b0df8b9c78..f1849775e47e 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -1253,15 +1253,12 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) struct sg_mapping_iter *sg_miter = &host->sg_miter; struct variant_data *variant = host->variant; void __iomem *base = host->base; - unsigned long flags; u32 status; status = readl(base + MMCISTATUS); dev_dbg(mmc_dev(host->mmc), "irq1 (pio) %08x\n", status); - local_irq_save(flags); - do { unsigned int remain, len; char *buffer; @@ -1301,8 +1298,6 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id) sg_miter_stop(sg_miter); - local_irq_restore(flags); - /* * If we have less than the fifo 'half-full' threshold to transfer, * trigger a PIO interrupt as soon as any data is available. -- cgit v1.2.3 From 2a9bde19e04149a526f5d9e8dd37e81562b47ab2 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Wed, 25 Apr 2018 15:19:03 +0800 Subject: mmc: mediatek: add 64G DRAM DMA support MT2712 MSDC supports 64G DRAM DMA access, it needs update gpd/bd structure. Signed-off-by: Chaotian Jing Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 56 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index d642e11b75d4..04841386b65d 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -71,6 +71,7 @@ #define SDC_ADV_CFG0 0x64 #define EMMC_IOCON 0x7c #define SDC_ACMD_RESP 0x80 +#define DMA_SA_H4BIT 0x8c #define MSDC_DMA_SA 0x90 #define MSDC_DMA_CTRL 0x98 #define MSDC_DMA_CFG 0x9c @@ -195,6 +196,9 @@ /* SDC_ADV_CFG0 mask */ #define SDC_RX_ENHANCE_EN (0x1 << 20) /* RW */ +/* DMA_SA_H4BIT mask */ +#define DMA_ADDR_HIGH_4BIT (0xf << 0) /* RW */ + /* MSDC_DMA_CTRL mask */ #define MSDC_DMA_CTRL_START (0x1 << 0) /* W */ #define MSDC_DMA_CTRL_STOP (0x1 << 1) /* W */ @@ -228,6 +232,7 @@ #define MSDC_PATCH_BIT2_CFGRESP (0x1 << 15) /* RW */ #define MSDC_PATCH_BIT2_CFGCRCSTS (0x1 << 28) /* RW */ +#define MSDC_PB2_SUPPORT_64G (0x1 << 1) /* RW */ #define MSDC_PB2_RESPWAIT (0x3 << 2) /* RW */ #define MSDC_PB2_RESPSTSENSEL (0x7 << 16) /* RW */ #define MSDC_PB2_CRCSTSENSEL (0x7 << 29) /* RW */ @@ -281,6 +286,8 @@ struct mt_gpdma_desc { #define GPDMA_DESC_BDP (0x1 << 1) #define GPDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */ #define GPDMA_DESC_INT (0x1 << 16) +#define GPDMA_DESC_NEXT_H4 (0xf << 24) +#define GPDMA_DESC_PTR_H4 (0xf << 28) u32 next; u32 ptr; u32 gpd_data_len; @@ -297,6 +304,8 @@ struct mt_bdma_desc { #define BDMA_DESC_CHECKSUM (0xff << 8) /* bit8 ~ bit15 */ #define BDMA_DESC_BLKPAD (0x1 << 17) #define BDMA_DESC_DWPAD (0x1 << 18) +#define BDMA_DESC_NEXT_H4 (0xf << 24) +#define BDMA_DESC_PTR_H4 (0xf << 28) u32 next; u32 ptr; u32 bd_data_len; @@ -335,6 +344,7 @@ struct mtk_mmc_compatible { bool busy_check; bool stop_clk_fix; bool enhance_rx; + bool support_64g; }; struct msdc_tune_para { @@ -404,6 +414,7 @@ static const struct mtk_mmc_compatible mt8135_compat = { .busy_check = false, .stop_clk_fix = false, .enhance_rx = false, + .support_64g = false, }; static const struct mtk_mmc_compatible mt8173_compat = { @@ -415,6 +426,7 @@ static const struct mtk_mmc_compatible mt8173_compat = { .busy_check = false, .stop_clk_fix = false, .enhance_rx = false, + .support_64g = false, }; static const struct mtk_mmc_compatible mt2701_compat = { @@ -426,6 +438,7 @@ static const struct mtk_mmc_compatible mt2701_compat = { .busy_check = false, .stop_clk_fix = false, .enhance_rx = false, + .support_64g = false, }; static const struct mtk_mmc_compatible mt2712_compat = { @@ -437,6 +450,7 @@ static const struct mtk_mmc_compatible mt2712_compat = { .busy_check = true, .stop_clk_fix = true, .enhance_rx = true, + .support_64g = true, }; static const struct mtk_mmc_compatible mt7622_compat = { @@ -448,6 +462,7 @@ static const struct mtk_mmc_compatible mt7622_compat = { .busy_check = true, .stop_clk_fix = true, .enhance_rx = true, + .support_64g = false, }; static const struct of_device_id msdc_of_ids[] = { @@ -557,7 +572,12 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma, /* init bd */ bd[j].bd_info &= ~BDMA_DESC_BLKPAD; bd[j].bd_info &= ~BDMA_DESC_DWPAD; - bd[j].ptr = (u32)dma_address; + bd[j].ptr = lower_32_bits(dma_address); + if (host->dev_comp->support_64g) { + bd[j].bd_info &= ~BDMA_DESC_PTR_H4; + bd[j].bd_info |= (upper_32_bits(dma_address) & 0xf) + << 28; + } bd[j].bd_data_len &= ~BDMA_DESC_BUFLEN; bd[j].bd_data_len |= (dma_len & BDMA_DESC_BUFLEN); @@ -576,7 +596,10 @@ static inline void msdc_dma_setup(struct msdc_host *host, struct msdc_dma *dma, 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); + if (host->dev_comp->support_64g) + sdr_set_field(host->base + DMA_SA_H4BIT, DMA_ADDR_HIGH_4BIT, + upper_32_bits(dma->gpd_addr) & 0xf); + writel(lower_32_bits(dma->gpd_addr), host->base + MSDC_DMA_SA); } static void msdc_prepare_data(struct msdc_host *host, struct mmc_request *mrq) @@ -1367,6 +1390,9 @@ static void msdc_init_hw(struct msdc_host *host) MSDC_PATCH_BIT2_CFGCRCSTS); } + if (host->dev_comp->support_64g) + sdr_set_bits(host->base + MSDC_PATCH_BIT2, + MSDC_PB2_SUPPORT_64G); if (host->dev_comp->data_tune) { sdr_set_bits(host->base + tune_reg, MSDC_PAD_TUNE_RD_SEL | MSDC_PAD_TUNE_CMD_SEL); @@ -1408,19 +1434,32 @@ 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; + dma_addr_t dma_addr; int i; memset(gpd, 0, sizeof(struct mt_gpdma_desc) * 2); + dma_addr = dma->gpd_addr + sizeof(struct mt_gpdma_desc); gpd->gpd_info = GPDMA_DESC_BDP; /* hwo, cs, bd pointer */ - gpd->ptr = (u32)dma->bd_addr; /* physical address */ /* gpd->next is must set for desc DMA * That's why must alloc 2 gpd structure. */ - gpd->next = (u32)dma->gpd_addr + sizeof(struct mt_gpdma_desc); + gpd->next = lower_32_bits(dma_addr); + if (host->dev_comp->support_64g) + gpd->gpd_info |= (upper_32_bits(dma_addr) & 0xf) << 24; + + dma_addr = dma->bd_addr; + gpd->ptr = lower_32_bits(dma->bd_addr); /* physical address */ + if (host->dev_comp->support_64g) + gpd->gpd_info |= (upper_32_bits(dma_addr) & 0xf) << 28; + 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); + for (i = 0; i < (MAX_BD_NUM - 1); i++) { + dma_addr = dma->bd_addr + sizeof(*bd) * (i + 1); + bd[i].next = lower_32_bits(dma_addr); + if (host->dev_comp->support_64g) + bd[i].bd_info |= (upper_32_bits(dma_addr) & 0xf) << 24; + } } static void msdc_ops_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -1913,7 +1952,10 @@ static int msdc_drv_probe(struct platform_device *pdev) 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); + if (host->dev_comp->support_64g) + host->dma_mask = DMA_BIT_MASK(36); + else + host->dma_mask = DMA_BIT_MASK(32); mmc_dev(mmc)->dma_mask = &host->dma_mask; host->timeout_clks = 3 * 1048576; -- cgit v1.2.3 From 0ec4ee3c9b9755b28fd2e89b2ceaae6f15403368 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:10 +0530 Subject: mmc: sdhci-omap: Fix when capabilities are obtained from SDHCI_CAPABILITIES reg sdhci_omap_config_iodelay_pinctrl_state() requires caps and caps2 to be initialized (speed mode capabilities like UHS/HS200) before it is invoked. While mmc_of_parse() initializes caps/caps2 if capabilities is populated in device tree, it will remain uninitialized for capabilities obtained from SDHCI_CAPABILITIES register. Fix sdhci_omap_config_iodelay_pinctrl_state() to be used even while getting the capabilities from SDHCI_CAPABILITIES register by invoking sdhci_setup_host() before sdhci_omap_config_iodelay_pinctrl_state(). Signed-off-by: Kishon Vijay Abraham I Acked-by: Tony Lindgren Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-omap.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index 1456abd5eeb9..e7e43f2ae224 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -916,10 +916,6 @@ static int sdhci_omap_probe(struct platform_device *pdev) goto err_put_sync; } - ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host); - if (ret) - goto err_put_sync; - host->mmc_host_ops.get_ro = mmc_gpio_get_ro; host->mmc_host_ops.start_signal_voltage_switch = sdhci_omap_start_signal_voltage_switch; @@ -930,12 +926,23 @@ static int sdhci_omap_probe(struct platform_device *pdev) sdhci_read_caps(host); host->caps |= SDHCI_CAN_DO_ADMA2; - ret = sdhci_add_host(host); + ret = sdhci_setup_host(host); if (ret) goto err_put_sync; + ret = sdhci_omap_config_iodelay_pinctrl_state(omap_host); + if (ret) + goto err_cleanup_host; + + ret = __sdhci_add_host(host); + if (ret) + goto err_cleanup_host; + return 0; +err_cleanup_host: + sdhci_cleanup_host(host); + err_put_sync: pm_runtime_put_sync(dev); -- cgit v1.2.3 From 5bc2bda54d49cb2dbf9387b47feb25c32d20e216 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:11 +0530 Subject: mmc: sdhci-omap: Remove setting ADMA capability in driver sdhci can directly get ADMA capability from MMCHS_CAPA register. Remove explicitly setting ADMA here as some instances might not have ADMA enabled. (sdhci_read_caps() is also removed from here since sdhci_setup_host() invokes it). Signed-off-by: Kishon Vijay Abraham I Acked-by: Adrian Hunter Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-omap.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index e7e43f2ae224..78f3ceea570e 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -923,9 +923,6 @@ static int sdhci_omap_probe(struct platform_device *pdev) host->mmc_host_ops.card_busy = sdhci_omap_card_busy; host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning; - sdhci_read_caps(host); - host->caps |= SDHCI_CAN_DO_ADMA2; - ret = sdhci_setup_host(host); if (ret) goto err_put_sync; -- cgit v1.2.3 From 212f4f8a44909a5f2db187b2ff7cfba46c626528 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:12 +0530 Subject: mmc: sdhci-omap: Workaround for Errata i843 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Errata i843 in AM572x Sitara Processors Silicon Revision 2.0, 1.1 (SPRZ429K July 2014–Revised March 2017 [1]) mentions PG 1.0/1.1 silicon has limitations w.r.t frequencies at which MMC1/2/3 can operate. Use soc_device_match() to identify rev 1.0/1.1 silicon and override mmc->f_max according to the errata workaround. "max-frequency" dt property cannot be used since the device tree is added for rev 2.0 silicon. soc_device_match() is also used in order to get the IODelay values for rev 1.0/1.1 silicon. [1] -> http://www.ti.com/lit/er/sprz429k/sprz429k.pdf Signed-off-by: Kishon Vijay Abraham I Acked-by: Adrian Hunter Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-omap.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index 78f3ceea570e..b3dc7f1466e5 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "sdhci-pltfm.h" @@ -100,6 +101,7 @@ struct sdhci_omap_data { }; struct sdhci_omap_host { + char *version; void __iomem *base; struct device *dev; struct regulator *pbias; @@ -733,12 +735,21 @@ static struct pinctrl_state u32 *caps, u32 capmask) { struct device *dev = omap_host->dev; + char *version = omap_host->version; struct pinctrl_state *pinctrl_state = ERR_PTR(-ENODEV); + char str[20]; if (!(*caps & capmask)) goto ret; - pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode); + if (version) { + snprintf(str, 20, "%s-%s", mode, version); + pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, str); + } + + if (IS_ERR(pinctrl_state)) + pinctrl_state = pinctrl_lookup_state(omap_host->pinctrl, mode); + if (IS_ERR(pinctrl_state)) { dev_err(dev, "no pinctrl state for %s mode", mode); *caps &= ~capmask; @@ -830,6 +841,16 @@ static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host return 0; } +static const struct soc_device_attribute sdhci_omap_soc_devices[] = { + { + .machine = "DRA7[45]*", + .revision = "ES1.[01]", + }, + { + /* sentinel */ + } +}; + static int sdhci_omap_probe(struct platform_device *pdev) { int ret; @@ -841,6 +862,7 @@ static int sdhci_omap_probe(struct platform_device *pdev) struct mmc_host *mmc; const struct of_device_id *match; struct sdhci_omap_data *data; + const struct soc_device_attribute *soc; match = of_match_device(omap_sdhci_match, dev); if (!match) @@ -875,6 +897,17 @@ static int sdhci_omap_probe(struct platform_device *pdev) if (ret) goto err_pltfm_free; + soc = soc_device_match(sdhci_omap_soc_devices); + if (soc) { + omap_host->version = "rev11"; + if (!strcmp(dev_name(dev), "4809c000.mmc")) + mmc->f_max = 96000000; + if (!strcmp(dev_name(dev), "480b4000.mmc")) + mmc->f_max = 48000000; + if (!strcmp(dev_name(dev), "480ad000.mmc")) + mmc->f_max = 48000000; + } + pltfm_host->clk = devm_clk_get(dev, "fck"); if (IS_ERR(pltfm_host->clk)) { ret = PTR_ERR(pltfm_host->clk); -- cgit v1.2.3 From 1d3a2220c2a1ab864caf4a64dee745d287089238 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:13 +0530 Subject: mmc: sdhci-omap: Invoke sdhci_get_of_property to read sdhci dt properties Invoke sdhci_get_of_property defined in sdhci-pltfm.c to read sdhci specific properties from dt node. Signed-off-by: Kishon Vijay Abraham I Acked-by: Adrian Hunter Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-omap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index b3dc7f1466e5..d5c1a9c71f95 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -893,6 +893,7 @@ static int sdhci_omap_probe(struct platform_device *pdev) host->ioaddr += offset; mmc = host->mmc; + sdhci_get_of_property(pdev); ret = mmc_of_parse(mmc); if (ret) goto err_pltfm_free; -- cgit v1.2.3 From c16bc9a7678a27ece028dcbfea8df2049a65dbec Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:14 +0530 Subject: mmc: sdhci: Disable 1.8v modes (HS200/HS400/UHS) if controller can't support 1.8v The SDHCI controller in a SoC might support HS200/HS400 (indicated using mmc-hs200-1_8v/mmc-hs400-1_8v dt property), but if the board is modeled such that the IO lines are not connected to 1.8v then HS200/HS400 cannot be supported. Disable HS200/HS400 if the board does not have 1.8v connected to the IO lines. Also Disable DDR/UHS in 1.8v if the IO lines are not connected to 1.8v. Signed-off-by: Kishon Vijay Abraham I Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 10 ++++++++++ include/linux/mmc/host.h | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2ededa7f43df..0f3cdca3e769 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -3672,6 +3672,16 @@ int sdhci_setup_host(struct sdhci_host *host) if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) { host->caps1 &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); + /* + * The SDHCI controller in a SoC might support HS200/HS400 + * (indicated using mmc-hs200-1_8v/mmc-hs400-1_8v dt property), + * but if the board is modeled such that the IO lines are not + * connected to 1.8v then HS200/HS400 cannot be supported. + * Disable HS200/HS400 if the board does not have 1.8v connected + * to the IO lines. (Applicable for other modes in 1.8v) + */ + mmc->caps2 &= ~(MMC_CAP2_HSX00_1_8V | MMC_CAP2_HS400_ES); + mmc->caps &= ~(MMC_CAP_1_8V_DDR | MMC_CAP_UHS); } /* Any UHS-I mode in caps implies SDR12 and SDR25 support. */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 85146235231e..4e80c199c9c2 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -320,6 +320,9 @@ struct mmc_host { #define MMC_CAP_UHS_SDR50 (1 << 18) /* Host supports UHS SDR50 mode */ #define MMC_CAP_UHS_SDR104 (1 << 19) /* Host supports UHS SDR104 mode */ #define MMC_CAP_UHS_DDR50 (1 << 20) /* Host supports UHS DDR50 mode */ +#define MMC_CAP_UHS (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | \ + MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | \ + MMC_CAP_UHS_DDR50) /* (1 << 21) is free for reuse */ #define MMC_CAP_DRIVER_TYPE_A (1 << 23) /* Host supports Driver Type A */ #define MMC_CAP_DRIVER_TYPE_C (1 << 24) /* Host supports Driver Type C */ @@ -345,6 +348,7 @@ struct mmc_host { #define MMC_CAP2_HS400_1_2V (1 << 16) /* Can support HS400 1.2V */ #define MMC_CAP2_HS400 (MMC_CAP2_HS400_1_8V | \ MMC_CAP2_HS400_1_2V) +#define MMC_CAP2_HSX00_1_8V (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V) #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 */ -- cgit v1.2.3 From a999fd9392310ff44b684f2011a60de7c7e73891 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 27 Apr 2018 17:17:15 +0530 Subject: mmc: sdhci: Add quirk to disable HW timeout Add quirk to disable HW timeout if the requested timeout is more than the maximum obtainable timeout. Also, if the quirk is set and ->get_max_timeout_count() is not implemented, max_busy_timeout is set to zero. Based-on-patch-by: Kishon Vijay Abraham I Signed-off-by: Adrian Hunter Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 38 ++++++++++++++++++++++++++++++++++---- drivers/mmc/host/sdhci.h | 5 +++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0f3cdca3e769..8da118d0d2c4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -709,12 +709,15 @@ static u32 sdhci_sdma_address(struct sdhci_host *host) return sg_dma_address(host->data->sg); } -static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) +static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd, + bool *too_big) { u8 count; struct mmc_data *data = cmd->data; unsigned target_timeout, current_timeout; + *too_big = true; + /* * If the host controller provides us with an incorrect timeout * value, just skip the check and use 0xE. The hardware may take @@ -768,9 +771,12 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd) } if (count >= 0xF) { - DBG("Too large timeout 0x%x requested for CMD%d!\n", - count, cmd->opcode); + if (!(host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT)) + DBG("Too large timeout 0x%x requested for CMD%d!\n", + count, cmd->opcode); count = 0xE; + } else { + *too_big = false; } return count; @@ -790,6 +796,16 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host) sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); } +static void sdhci_set_data_timeout_irq(struct sdhci_host *host, bool enable) +{ + if (enable) + host->ier |= SDHCI_INT_DATA_TIMEOUT; + else + host->ier &= ~SDHCI_INT_DATA_TIMEOUT; + sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); + sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); +} + static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) { u8 count; @@ -797,7 +813,17 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) if (host->ops->set_timeout) { host->ops->set_timeout(host, cmd); } else { - count = sdhci_calc_timeout(host, cmd); + bool too_big = false; + + count = sdhci_calc_timeout(host, cmd, &too_big); + + if (too_big && + host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) { + sdhci_set_data_timeout_irq(host, false); + } else if (!(host->ier & SDHCI_INT_DATA_TIMEOUT)) { + sdhci_set_data_timeout_irq(host, true); + } + sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); } } @@ -3616,6 +3642,10 @@ int sdhci_setup_host(struct sdhci_host *host) mmc->max_busy_timeout /= host->timeout_clk; } + if (host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT && + !host->ops->get_max_timeout_count) + mmc->max_busy_timeout = 0; + mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23; mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index c95b0a4a7594..f6555c0f4ad3 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -437,6 +437,11 @@ struct sdhci_host { #define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1<<15) /* Controller has CRC in 136 bit Command Response */ #define SDHCI_QUIRK2_RSP_136_HAS_CRC (1<<16) +/* + * Disable HW timeout if the requested timeout is more than the maximum + * obtainable timeout. + */ +#define SDHCI_QUIRK2_DISABLE_HW_TIMEOUT (1<<17) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ -- cgit v1.2.3 From 0bb28d738fb2066b314d0fe2eccffd1cab59517b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 27 Apr 2018 17:17:16 +0530 Subject: mmc: sdhci: Factor out target_timeout calculation Factor out the target_timeout calculation so it can be re-used. Signed-off-by: Adrian Hunter Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8da118d0d2c4..b229354e5b88 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -709,6 +709,35 @@ static u32 sdhci_sdma_address(struct sdhci_host *host) return sg_dma_address(host->data->sg); } +static unsigned int sdhci_target_timeout(struct sdhci_host *host, + struct mmc_command *cmd, + struct mmc_data *data) +{ + unsigned int target_timeout; + + /* timeout in us */ + if (!data) { + target_timeout = cmd->busy_timeout * 1000; + } else { + target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000); + if (host->clock && data->timeout_clks) { + unsigned long long val; + + /* + * data->timeout_clks is in units of clock cycles. + * host->clock is in Hz. target_timeout is in us. + * Hence, us = 1000000 * cycles / Hz. Round up. + */ + val = 1000000ULL * data->timeout_clks; + if (do_div(val, host->clock)) + target_timeout++; + target_timeout += val; + } + } + + return target_timeout; +} + static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd, bool *too_big) { @@ -732,24 +761,7 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd, return 0xE; /* timeout in us */ - if (!data) - target_timeout = cmd->busy_timeout * 1000; - else { - target_timeout = DIV_ROUND_UP(data->timeout_ns, 1000); - if (host->clock && data->timeout_clks) { - unsigned long long val; - - /* - * data->timeout_clks is in units of clock cycles. - * host->clock is in Hz. target_timeout is in us. - * Hence, us = 1000000 * cycles / Hz. Round up. - */ - val = 1000000ULL * data->timeout_clks; - if (do_div(val, host->clock)) - target_timeout++; - target_timeout += val; - } - } + target_timeout = sdhci_target_timeout(host, cmd, data); /* * Figure out needed cycles. -- cgit v1.2.3 From fc1fa1b7db27546803905feef403cedf40ca027a Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:17 +0530 Subject: mmc: sdhci: Program a relatively accurate SW timeout value sdhci has a 10 second timeout to catch devices that stop responding. In the case of quirk SDHCI_QUIRK2_DISABLE_HW_TIMEOUT, instead of programming 10 second arbitrary value, calculate the total time it would take for the entire transfer to happen and program the timeout value accordingly. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 52 +++++++++++++++++++++++++++++++++++++++++------- drivers/mmc/host/sdhci.h | 10 ++++++++++ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index b229354e5b88..1c828e0e9905 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -738,6 +738,39 @@ static unsigned int sdhci_target_timeout(struct sdhci_host *host, return target_timeout; } +static void sdhci_calc_sw_timeout(struct sdhci_host *host, + struct mmc_command *cmd) +{ + struct mmc_data *data = cmd->data; + struct mmc_host *mmc = host->mmc; + struct mmc_ios *ios = &mmc->ios; + unsigned char bus_width = 1 << ios->bus_width; + unsigned int blksz; + unsigned int freq; + u64 target_timeout; + u64 transfer_time; + + target_timeout = sdhci_target_timeout(host, cmd, data); + target_timeout *= NSEC_PER_USEC; + + if (data) { + blksz = data->blksz; + freq = host->mmc->actual_clock ? : host->clock; + transfer_time = (u64)blksz * NSEC_PER_SEC * (8 / bus_width); + do_div(transfer_time, freq); + /* multiply by '2' to account for any unknowns */ + transfer_time = transfer_time * 2; + /* calculate timeout for the entire data */ + host->data_timeout = data->blocks * target_timeout + + transfer_time; + } else { + host->data_timeout = target_timeout; + } + + if (host->data_timeout) + host->data_timeout += MMC_CMD_TRANSFER_TIME; +} + static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd, bool *too_big) { @@ -831,6 +864,7 @@ static void sdhci_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) if (too_big && host->quirks2 & SDHCI_QUIRK2_DISABLE_HW_TIMEOUT) { + sdhci_calc_sw_timeout(host, cmd); sdhci_set_data_timeout_irq(host, false); } else if (!(host->ier & SDHCI_INT_DATA_TIMEOUT)) { sdhci_set_data_timeout_irq(host, true); @@ -845,6 +879,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd) u8 ctrl; struct mmc_data *data = cmd->data; + host->data_timeout = 0; + if (sdhci_data_line_cmd(cmd)) sdhci_set_timeout(host, cmd); @@ -1198,13 +1234,6 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) mdelay(1); } - timeout = jiffies; - if (!cmd->data && cmd->busy_timeout > 9000) - timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; - else - timeout += 10 * HZ; - sdhci_mod_timer(host, cmd->mrq, timeout); - host->cmd = cmd; if (sdhci_data_line_cmd(cmd)) { WARN_ON(host->data_cmd); @@ -1244,6 +1273,15 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200) flags |= SDHCI_CMD_DATA; + timeout = jiffies; + if (host->data_timeout) + timeout += nsecs_to_jiffies(host->data_timeout); + else if (!cmd->data && cmd->busy_timeout > 9000) + timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ; + else + timeout += 10 * HZ; + sdhci_mod_timer(host, cmd->mrq, timeout); + sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); } EXPORT_SYMBOL_GPL(sdhci_send_command); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index f6555c0f4ad3..23966f887da6 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -332,6 +332,14 @@ struct sdhci_adma2_64_desc { /* Allow for a a command request and a data request at the same time */ #define SDHCI_MAX_MRQS 2 +/* + * 48bit command and 136 bit response in 100KHz clock could take upto 2.48ms. + * However since the start time of the command, the time between + * command and response, and the time between response and start of data is + * not known, set the command transfer time to 10ms. + */ +#define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */ + enum sdhci_cookie { COOKIE_UNMAPPED, COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */ @@ -555,6 +563,8 @@ struct sdhci_host { /* Host SDMA buffer boundary. */ u32 sdma_boundary; + u64 data_timeout; + unsigned long private[0] ____cacheline_aligned; }; -- cgit v1.2.3 From 25f80d86059c4b78eeec8cb42db158ad2a29eb02 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:18 +0530 Subject: mmc: sdhci-omap: Workaround for Errata i834 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Errata i834 in AM572x Sitara Processors Silicon Revision 2.0, 1.1 (SPRZ429K July 2014–Revised March 2017 [1]) mentions the maximum obtainable timeout through MMC host controller is 700ms. And for commands taking longer than 700ms, hardware timeout should be disabled and software timeout should be used. The workaround for Errata i834 can be achieved by adding SDHCI_QUIRK2_DISABLE_HW_TIMEOUT quirk in sdhci-omap. [1] -> http://www.ti.com/lit/er/sprz429k/sprz429k.pdf Signed-off-by: Kishon Vijay Abraham I Acked-by: Tony Lindgren Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-omap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index d5c1a9c71f95..2232a9974b21 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -715,7 +715,8 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = { SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN | SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_RSP_136_HAS_CRC, + SDHCI_QUIRK2_RSP_136_HAS_CRC | + SDHCI_QUIRK2_DISABLE_HW_TIMEOUT, .ops = &sdhci_omap_ops, }; -- cgit v1.2.3 From 3031ec961d0d5d9502b56c86e9cbdc680012d7cc Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:19 +0530 Subject: dt-bindings: sdhci-omap: Add K2G specific binding Add binding for the TI's sdhci-omap controller present in K2G. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Rob Herring Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-omap.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-omap.txt b/Documentation/devicetree/bindings/mmc/sdhci-omap.txt index 51775a372c06..8d09b837e350 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-omap.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-omap.txt @@ -4,7 +4,9 @@ Refer to mmc.txt for standard MMC bindings. Required properties: - compatible: Should be "ti,dra7-sdhci" for DRA7 and DRA72 controllers + Should be "ti,k2g-sdhci" for K2G - ti,hwmods: Must be "mmc", is controller instance starting 1 + (Not required for K2G). Example: mmc1: mmc@4809c000 { -- cgit v1.2.3 From 6d75df75abb15109d6305f5cb22793752b3b7ab8 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:20 +0530 Subject: mmc: sdhci-omap: Add support for MMC/SD controller in k2g SoC Add support for the new compatible added specifically to support k2g's MMC/SD controller. Signed-off-by: Kishon Vijay Abraham I Acked-by: Adrian Hunter Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-omap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index 2232a9974b21..b11c846dd8a1 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -720,6 +720,10 @@ static const struct sdhci_pltfm_data sdhci_omap_pdata = { .ops = &sdhci_omap_ops, }; +static const struct sdhci_omap_data k2g_data = { + .offset = 0x200, +}; + static const struct sdhci_omap_data dra7_data = { .offset = 0x200, .flags = SDHCI_OMAP_REQUIRE_IODELAY, @@ -727,6 +731,7 @@ static const struct sdhci_omap_data dra7_data = { static const struct of_device_id omap_sdhci_match[] = { { .compatible = "ti,dra7-sdhci", .data = &dra7_data }, + { .compatible = "ti,k2g-sdhci", .data = &k2g_data }, {}, }; MODULE_DEVICE_TABLE(of, omap_sdhci_match); -- cgit v1.2.3 From efde12b246d7704a5697cdaea4512fd97030bb36 Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:21 +0530 Subject: mmc: sdhci-omap: Add sdhci_omap specific ops for enable_sdio_irq Add sdhci_omap_enable_sdio_irq to set CTPL and CLKEXTFREE bits in MMCHS_CON register required to detect asynchronous card interrupt on DAT[1]. Signed-off-by: Kishon Vijay Abraham I Acked-by: Adrian Hunter Acked-by: Tony Lindgren Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-omap.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index b11c846dd8a1..bd5e03d177e0 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -36,6 +36,7 @@ #define CON_DDR BIT(19) #define CON_CLKEXTFREE BIT(16) #define CON_PADEN BIT(15) +#define CON_CTPL BIT(11) #define CON_INIT BIT(1) #define CON_OD BIT(0) @@ -226,6 +227,23 @@ static void sdhci_omap_conf_bus_power(struct sdhci_omap_host *omap_host, } } +static void sdhci_omap_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_omap_host *omap_host = sdhci_pltfm_priv(pltfm_host); + u32 reg; + + reg = sdhci_omap_readl(omap_host, SDHCI_OMAP_CON); + if (enable) + reg |= (CON_CTPL | CON_CLKEXTFREE); + else + reg &= ~(CON_CTPL | CON_CLKEXTFREE); + sdhci_omap_writel(omap_host, SDHCI_OMAP_CON, reg); + + sdhci_enable_sdio_irq(mmc, enable); +} + static inline void sdhci_omap_set_dll(struct sdhci_omap_host *omap_host, int count) { @@ -962,6 +980,7 @@ static int sdhci_omap_probe(struct platform_device *pdev) host->mmc_host_ops.set_ios = sdhci_omap_set_ios; host->mmc_host_ops.card_busy = sdhci_omap_card_busy; host->mmc_host_ops.execute_tuning = sdhci_omap_execute_tuning; + host->mmc_host_ops.enable_sdio_irq = sdhci_omap_enable_sdio_irq; ret = sdhci_setup_host(host); if (ret) -- cgit v1.2.3 From 75b732d570ac7a6bd53fa770adcb64f9afabcdda Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:22 +0530 Subject: dt-bindings: sdhci-omap: Add pinctrl bindings Add pinctrl binding rquired to get the mux mode and IODelay values from devicetree. Signed-off-by: Kishon Vijay Abraham I Reviewed-by: Rob Herring Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-omap.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-omap.txt b/Documentation/devicetree/bindings/mmc/sdhci-omap.txt index 8d09b837e350..393848c2138e 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-omap.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-omap.txt @@ -7,6 +7,11 @@ Required properties: Should be "ti,k2g-sdhci" for K2G - ti,hwmods: Must be "mmc", is controller instance starting 1 (Not required for K2G). +- pinctrl-names: Should be subset of "default", "hs", "sdr12", "sdr25", "sdr50", + "ddr50-rev11", "sdr104-rev11", "ddr50", "sdr104", + "ddr_1_8v-rev11", "ddr_1_8v" or "ddr_3_3v", "hs200_1_8v-rev11", + "hs200_1_8v", +- pinctrl- : Pinctrl states as described in bindings/pinctrl/pinctrl-bindings.txt Example: mmc1: mmc@4809c000 { -- cgit v1.2.3 From 3f4028780287ff5a7308f40e10bbba9a42b993aa Mon Sep 17 00:00:00 2001 From: Kishon Vijay Abraham I Date: Fri, 27 Apr 2018 17:17:23 +0530 Subject: mmc: sdhci-omap: Get IODelay values for 3.3v DDR mode commit 8d20b2eae6c47b095523 ("mmc: sdhci_omap: Add support to set IODELAY values") stored IODelay values for all MM/SD modes in pinctrl_state structure member of sdhci_omap_host. However for DDR mode it gets IODelay values only for 1.8v DDR mode. Since some of the platforms which uses sdhci-omap has IO lines connected to 3.3v, get IODelay values for 3.3v DDR mode. Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-omap.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index bd5e03d177e0..f3a7c8ece4be 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -842,8 +842,15 @@ static int sdhci_omap_config_iodelay_pinctrl_state(struct sdhci_omap_host state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_1_8v", caps, MMC_CAP_1_8V_DDR); - if (!IS_ERR(state)) + if (!IS_ERR(state)) { pinctrl_state[MMC_TIMING_MMC_DDR52] = state; + } else { + state = sdhci_omap_iodelay_pinctrl_state(omap_host, "ddr_3_3v", + caps, + MMC_CAP_3_3V_DDR); + if (!IS_ERR(state)) + pinctrl_state[MMC_TIMING_MMC_DDR52] = state; + } state = sdhci_omap_iodelay_pinctrl_state(omap_host, "hs", caps, MMC_CAP_SD_HIGHSPEED); -- cgit v1.2.3 From 52a70098ffb9a1cb1ec377f5e63b137db5d1ccbd Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 24 Apr 2018 08:42:56 +0800 Subject: mmc: dt-bindings: Add optional cd-debounce-delay-ms cd-gpios uses a fixed delay, 200ms, before detecting card after the card is inserted. 200ms doesn't work for some platforms, so some host drivers added their own properties for parsing that from DT, for instance, dw_mmc and pxamci. That being said, it should also be tunable when using cd-gpios. Signed-off-by: Shawn Lin Reviewed-by: Rob Herring Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 467cd7b147ce..dbe23b12ea1c 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -19,6 +19,8 @@ Optional properties: - wp-gpios: Specify GPIOs for write protection, see gpio binding - cd-inverted: when present, polarity on the CD line is inverted. See the note below for the case, when a GPIO is used for the CD line +- cd-debounce-delay-ms: Set delay time before detecting card after card insert interrupt. + It's only valid when cd-gpios is present. - 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 -- cgit v1.2.3 From bfd694d5e21c2f0d344db6afeaf993bb0f299545 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 24 Apr 2018 08:42:57 +0800 Subject: mmc: core: Add tunable delay before detecting card after card is inserted Allow to use tunable delay before detecting card after card is inserted, which either comes from firmware node, or comes from debounce value passed on to mmc_gpiod_request_cd(). If the platform doesn't support debounce, then we fall back to use the debounce period as the delay, otherwise, it behaves the same as before that a HW debounce(if set) plus a 200ms hardcode delay before detecting the card. Signed-off-by: Shawn Lin Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 9 +++++++-- drivers/mmc/core/slot-gpio.c | 7 +++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 64b03d6eaf18..da08a17fbf6c 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -179,7 +179,7 @@ static void mmc_retune_timer(struct timer_list *t) int mmc_of_parse(struct mmc_host *host) { struct device *dev = host->parent; - u32 bus_width, drv_type; + u32 bus_width, drv_type, cd_debounce_delay_ms; int ret; bool cd_cap_invert, cd_gpio_invert = false; bool ro_cap_invert, ro_gpio_invert = false; @@ -230,11 +230,16 @@ int mmc_of_parse(struct mmc_host *host) } else { cd_cap_invert = device_property_read_bool(dev, "cd-inverted"); + if (device_property_read_u32(dev, "cd-debounce-delay-ms", + &cd_debounce_delay_ms)) + cd_debounce_delay_ms = 200; + if (device_property_read_bool(dev, "broken-cd")) host->caps |= MMC_CAP_NEEDS_POLL; ret = mmc_gpiod_request_cd(host, "cd", 0, true, - 0, &cd_gpio_invert); + cd_debounce_delay_ms, + &cd_gpio_invert); if (!ret) dev_info(host->parent, "Got CD GPIO\n"); else if (ret != -ENOENT && ret != -ENOSYS) diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 31f7dbb15668..56559351d2e1 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -28,15 +28,17 @@ struct mmc_gpio { irqreturn_t (*cd_gpio_isr)(int irq, void *dev_id); char *ro_label; char cd_label[0]; + u32 cd_debounce_delay_ms; }; static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) { /* Schedule a card detection after a debounce timeout */ struct mmc_host *host = dev_id; + struct mmc_gpio *ctx = host->slot.handler_priv; host->trigger_card_event = true; - mmc_detect_change(host, msecs_to_jiffies(200)); + mmc_detect_change(host, msecs_to_jiffies(ctx->cd_debounce_delay_ms)); return IRQ_HANDLED; } @@ -49,6 +51,7 @@ int mmc_gpio_alloc(struct mmc_host *host) if (ctx) { ctx->ro_label = ctx->cd_label + len; + ctx->cd_debounce_delay_ms = 200; snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent)); snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent)); host->slot.handler_priv = ctx; @@ -261,7 +264,7 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, if (debounce) { ret = gpiod_set_debounce(desc, debounce); if (ret < 0) - return ret; + ctx->cd_debounce_delay_ms = debounce; } if (gpio_invert) -- cgit v1.2.3 From fb09f44e290b1775fbeadb4289dbcfc2f954b0d2 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 11 May 2015 16:46:47 +0200 Subject: mmc: core: Re-factor some code for SDIO re-initialization The mmc_sdio_init_card() function has a couple of callers. In the re-initialization cases, some additional reset commands are issued before mmc_sdio_init_card() is called. As these additional reset commands are the same, let's move these into a new static function, mmc_sdio_reinit_card() and call mmc_sdio_init_card() from there. In this way we avoid the open coding. Signed-off-by: Ulf Hansson Tested-by: Quentin Schulz Reviewed-by: Shawn Lin --- drivers/mmc/core/sdio.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 87e53a721a10..34d69d0b5bf8 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -813,6 +813,22 @@ err: return err; } +static int mmc_sdio_reinit_card(struct mmc_host *host, bool powered_resume) +{ + int ret; + + sdio_reset(host); + mmc_go_idle(host); + mmc_send_if_cond(host, host->card->ocr); + + ret = mmc_send_io_op_cond(host, 0, NULL); + if (ret) + return ret; + + return mmc_sdio_init_card(host, host->card->ocr, host->card, + powered_resume); +} + /* * Host is being removed. Free up the current card. */ @@ -960,14 +976,7 @@ static int mmc_sdio_resume(struct mmc_host *host) /* No need to reinitialize powered-resumed nonremovable cards */ if (mmc_card_is_removable(host) || !mmc_card_keep_power(host)) { - sdio_reset(host); - mmc_go_idle(host); - mmc_send_if_cond(host, host->card->ocr); - err = mmc_send_io_op_cond(host, 0, NULL); - if (!err) - err = mmc_sdio_init_card(host, host->card->ocr, - host->card, - mmc_card_keep_power(host)); + err = mmc_sdio_reinit_card(host, mmc_card_keep_power(host)); } else if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); @@ -990,8 +999,6 @@ static int mmc_sdio_power_restore(struct mmc_host *host) { int ret; - mmc_claim_host(host); - /* * Reset the card by performing the same steps that are taken by * mmc_rescan_try_freq() and mmc_attach_sdio() during a "normal" probe. @@ -1009,20 +1016,12 @@ static int mmc_sdio_power_restore(struct mmc_host *host) * */ - sdio_reset(host); - mmc_go_idle(host); - mmc_send_if_cond(host, host->card->ocr); - - ret = mmc_send_io_op_cond(host, 0, NULL); - if (ret) - goto out; + mmc_claim_host(host); - ret = mmc_sdio_init_card(host, host->card->ocr, host->card, - mmc_card_keep_power(host)); + ret = mmc_sdio_reinit_card(host, mmc_card_keep_power(host)); if (!ret && host->sdio_irqs) mmc_signal_sdio_irq(host); -out: mmc_release_host(host); return ret; -- cgit v1.2.3 From 3a3db6030b64ceb4b4e41a6811168c5d90a9f7f8 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 5 Apr 2018 13:24:43 +0200 Subject: mmc: core: Rename ->reset() bus ops to ->hw_reset() The bus ops ->reset() executes a full HW reset of the card, as the calling function mmc_hw_reset() also indicates by its name. Let's convert to follow the similar names, for both the bus ops callback and for the corresponding bus ops functions, as to clarify the purpose of code. Signed-off-by: Ulf Hansson Tested-by: Quentin Schulz Reviewed-by: Shawn Lin --- drivers/mmc/core/core.c | 6 +++--- drivers/mmc/core/core.h | 2 +- drivers/mmc/core/mmc.c | 4 ++-- drivers/mmc/core/sd.c | 4 ++-- drivers/mmc/core/sdio.c | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 121ce50b6d5e..3e17c62eea86 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2435,16 +2435,16 @@ int mmc_hw_reset(struct mmc_host *host) return -EINVAL; mmc_bus_get(host); - if (!host->bus_ops || host->bus_dead || !host->bus_ops->reset) { + if (!host->bus_ops || host->bus_dead || !host->bus_ops->hw_reset) { mmc_bus_put(host); return -EOPNOTSUPP; } - ret = host->bus_ops->reset(host); + ret = host->bus_ops->hw_reset(host); mmc_bus_put(host); if (ret) - pr_warn("%s: tried to reset card, got error %d\n", + pr_warn("%s: tried to HW reset card, got error %d\n", mmc_hostname(host), ret); return ret; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index d6303d69071b..367ed1127be2 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -32,7 +32,7 @@ struct mmc_bus_ops { int (*power_restore)(struct mmc_host *); int (*alive)(struct mmc_host *); int (*shutdown)(struct mmc_host *); - int (*reset)(struct mmc_host *); + int (*hw_reset)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 915b58f9bfc5..57a8bd39cdde 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -2125,7 +2125,7 @@ static int mmc_can_reset(struct mmc_card *card) return 1; } -static int mmc_reset(struct mmc_host *host) +static int _mmc_hw_reset(struct mmc_host *host) { struct mmc_card *card = host->card; @@ -2159,7 +2159,7 @@ static const struct mmc_bus_ops mmc_ops = { .runtime_resume = mmc_runtime_resume, .alive = mmc_alive, .shutdown = mmc_shutdown, - .reset = mmc_reset, + .hw_reset = _mmc_hw_reset, }; /* diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 7f87a5354f9f..d0d9f90e7cdf 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1222,7 +1222,7 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) return 0; } -static int mmc_sd_reset(struct mmc_host *host) +static int mmc_sd_hw_reset(struct mmc_host *host) { mmc_power_cycle(host, host->card->ocr); return mmc_sd_init_card(host, host->card->ocr, host->card); @@ -1237,7 +1237,7 @@ static const struct mmc_bus_ops mmc_sd_ops = { .resume = mmc_sd_resume, .alive = mmc_sd_alive, .shutdown = mmc_sd_suspend, - .reset = mmc_sd_reset, + .hw_reset = mmc_sd_hw_reset, }; /* diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 34d69d0b5bf8..362737052f84 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1050,7 +1050,7 @@ static int mmc_sdio_runtime_resume(struct mmc_host *host) return ret; } -static int mmc_sdio_reset(struct mmc_host *host) +static int mmc_sdio_hw_reset(struct mmc_host *host) { mmc_power_cycle(host, host->card->ocr); return mmc_sdio_power_restore(host); @@ -1066,7 +1066,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, + .hw_reset = mmc_sdio_hw_reset, }; -- cgit v1.2.3 From 1433269c4d2461be1f36db5dbb453976b38996ff Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 5 Apr 2018 13:42:00 +0200 Subject: mmc: core: Export a function mmc_sw_reset() to allow soft reset of cards It's rather common that a firmware is loaded into an SDIO func device memory, by the corresponding SDIO func driver during ->probe() time. However, to actually start running the new firmware, sometimes a soft reset (no power cycle) and a re-initialization of the card is needed. This is for example the case with the Espressif ESP8089 WiFi chips, when connected to an SDIO interface. To cope with this scenario, let's add a new exported function, mmc_sw_reset(), which may be called when a soft reset and re-initialization of the card are needed. The mmc_sw_reset() is implemented on top of a new bus ops callback, similar to how the mmc_hw_reset() has been implemented. Signed-off-by: Ulf Hansson Tested-by: Quentin Schulz Reviewed-by: Shawn Lin --- drivers/mmc/core/core.c | 24 ++++++++++++++++++++++++ drivers/mmc/core/core.h | 1 + include/linux/mmc/core.h | 1 + 3 files changed, 26 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 3e17c62eea86..7a79dc1599e5 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2451,6 +2451,30 @@ int mmc_hw_reset(struct mmc_host *host) } EXPORT_SYMBOL(mmc_hw_reset); +int mmc_sw_reset(struct mmc_host *host) +{ + int ret; + + if (!host->card) + return -EINVAL; + + mmc_bus_get(host); + if (!host->bus_ops || host->bus_dead || !host->bus_ops->sw_reset) { + mmc_bus_put(host); + return -EOPNOTSUPP; + } + + ret = host->bus_ops->sw_reset(host); + mmc_bus_put(host); + + if (ret) + pr_warn("%s: tried to SW reset card, got error %d\n", + mmc_hostname(host), ret); + + return ret; +} +EXPORT_SYMBOL(mmc_sw_reset); + static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq) { host->f_init = freq; diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 367ed1127be2..a141ec0e1ccc 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -33,6 +33,7 @@ struct mmc_bus_ops { int (*alive)(struct mmc_host *); int (*shutdown)(struct mmc_host *); int (*hw_reset)(struct mmc_host *); + int (*sw_reset)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 927519385482..134a6483347a 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -177,6 +177,7 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries); int mmc_hw_reset(struct mmc_host *host); +int mmc_sw_reset(struct mmc_host *host); void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card); #endif /* LINUX_MMC_CORE_H */ -- cgit v1.2.3 From 508c9864ccede5dd4b8a7220b3fe6998763e4407 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 5 Apr 2018 21:24:15 +0200 Subject: mmc: core: Share internal function to set initial signal voltage Move the corresponding code for setting the initial signal voltage, from mmc_power_up() into a new function, mmc_set_initial_signal_voltage(). Make the function internally available to the mmc core, as to allow the following changes to make use of it. Signed-off-by: Ulf Hansson Tested-by: Quentin Schulz Reviewed-by: Shawn Lin --- drivers/mmc/core/core.c | 19 ++++++++++++------- drivers/mmc/core/core.h | 1 + 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7a79dc1599e5..2c1a220531c4 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1484,6 +1484,17 @@ int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage) } +void mmc_set_initial_signal_voltage(struct mmc_host *host) +{ + /* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */ + if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330)) + dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n"); + else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) + dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n"); + else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120)) + dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n"); +} + int mmc_host_set_uhs_voltage(struct mmc_host *host) { u32 clock; @@ -1646,13 +1657,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) /* Set initial state and call mmc_set_ios */ mmc_set_initial_state(host); - /* Try to set signal voltage to 3.3V but fall back to 1.8v or 1.2v */ - if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_330)) - dev_dbg(mmc_dev(host), "Initial signal voltage of 3.3v\n"); - else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180)) - dev_dbg(mmc_dev(host), "Initial signal voltage of 1.8v\n"); - else if (!mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_120)) - dev_dbg(mmc_dev(host), "Initial signal voltage of 1.2v\n"); + mmc_set_initial_signal_voltage(host); /* * This delay should be sufficient to allow the power supply diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index a141ec0e1ccc..9d8f09ac0821 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -52,6 +52,7 @@ u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); int mmc_set_uhs_voltage(struct mmc_host *host, u32 ocr); int mmc_host_set_uhs_voltage(struct mmc_host *host); int mmc_set_signal_voltage(struct mmc_host *host, int signal_voltage); +void mmc_set_initial_signal_voltage(struct mmc_host *host); 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, -- cgit v1.2.3 From 7405df4c79cd7b4204c5b4b5af2b1ad362ea85ad Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 5 Apr 2018 16:31:25 +0200 Subject: mmc: core: Implement ->sw_reset bus ops for SDIO Let's implement the ->sw_reset() bus ops to allow SDIO func drivers, in particular, to make a SW reset without doing a full power cycle of the SDIO card. Signed-off-by: Ulf Hansson Tested-by: Quentin Schulz Reviewed-by: Shawn Lin --- drivers/mmc/core/sdio.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 362737052f84..a86490dbca70 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -1056,6 +1056,18 @@ static int mmc_sdio_hw_reset(struct mmc_host *host) return mmc_sdio_power_restore(host); } +static int mmc_sdio_sw_reset(struct mmc_host *host) +{ + mmc_set_clock(host, host->f_init); + sdio_reset(host); + mmc_go_idle(host); + + mmc_set_initial_state(host); + mmc_set_initial_signal_voltage(host); + + return mmc_sdio_reinit_card(host, 0); +} + static const struct mmc_bus_ops mmc_sdio_ops = { .remove = mmc_sdio_remove, .detect = mmc_sdio_detect, @@ -1067,6 +1079,7 @@ static const struct mmc_bus_ops mmc_sdio_ops = { .power_restore = mmc_sdio_power_restore, .alive = mmc_sdio_alive, .hw_reset = mmc_sdio_hw_reset, + .sw_reset = mmc_sdio_sw_reset, }; -- cgit v1.2.3 From 4b7d45451dff133b6453cb9571ea5349be1ce14f Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 May 2018 13:31:37 +0200 Subject: mmc: rtsx_usb: Use MMC_CAP2_NO_SDIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having to return -EINVAL when requested to send SDIO specific commands, let's set MMC_CAP2_NO_SDIO as it completely prevents them. Signed-off-by: Ulf Hansson Tested-by: Michał Pecio --- drivers/mmc/host/rtsx_usb_sdmmc.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 78422079ecfa..f9faa98c5465 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -839,17 +838,6 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) goto finish_detect_card; } - /* - * Reject SDIO CMDs to speed up card identification - * since unsupported - */ - if (cmd->opcode == SD_IO_SEND_OP_COND || - cmd->opcode == SD_IO_RW_DIRECT || - cmd->opcode == SD_IO_RW_EXTENDED) { - cmd->error = -EINVAL; - goto finish; - } - mutex_lock(&ucr->dev_mutex); mutex_lock(&host->host_mutex); @@ -1333,7 +1321,8 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host) MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_NEEDS_POLL; - mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE; + mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE | + MMC_CAP2_NO_SDIO; mmc->max_current_330 = 400; mmc->max_current_180 = 800; -- cgit v1.2.3 From ec30f11e821f2dcf707d0c66e01e87fbe8b91a66 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 May 2018 13:55:17 +0200 Subject: mmc: rtsx_usb: Use the provided busy timeout from the mmc core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using a fixed 3s timeout for commands with R1B responses, convert to use the per request calculated busy timeout from the mmc core. This is needed to cope with requests that requires longer timeout, for example erase/discard commands. Signed-off-by: Ulf Hansson Tested-by: Michał Pecio --- drivers/mmc/host/rtsx_usb_sdmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index f9faa98c5465..560614c5934e 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -342,7 +342,7 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host, } if (rsp_type == SD_RSP_TYPE_R1b) - timeout = 3000; + timeout = cmd->busy_timeout ? cmd->busy_timeout : 3000; if (cmd->opcode == SD_SWITCH_VOLTAGE) { err = rtsx_usb_write_register(ucr, SD_BUS_STAT, -- cgit v1.2.3 From 400fdb25c87431dd23194b5f6ec62ffae1b224ce Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 May 2018 14:02:32 +0200 Subject: mmc: rtsx_usb: Enable MMC_CAP_ERASE to allow erase/discard/trim requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ulf Hansson Tested-by: Michał Pecio --- drivers/mmc/host/rtsx_usb_sdmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index 560614c5934e..9a3ff22dd0fe 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -1320,7 +1320,7 @@ static void rtsx_usb_init_host(struct rtsx_usb_sdmmc *host) mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | - MMC_CAP_NEEDS_POLL; + MMC_CAP_NEEDS_POLL | MMC_CAP_ERASE; mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE | MMC_CAP2_NO_SDIO; -- cgit v1.2.3 From 3ce7f76ff9033cbf966c1121ea59051b533f95ef Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 2 May 2018 15:13:06 +0200 Subject: mmc: core: Drop unused define for timeout MMC_CORE_TIMEOUT_MS isn't being used no more, let's drop it. Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 2c1a220531c4..42cfcb64abe0 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -50,9 +50,6 @@ #include "sd_ops.h" #include "sdio_ops.h" -/* If the device is not responding */ -#define MMC_CORE_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */ - /* The max erase timeout, used when host->max_busy_timeout isn't specified */ #define MMC_ERASE_TIMEOUT_MS (60 * 1000) /* 60 s */ -- cgit v1.2.3 From cf56c819c851a542a43b3b9d4330ed6821a62da1 Mon Sep 17 00:00:00 2001 From: Aapo Vienamo Date: Fri, 4 May 2018 12:20:53 +0300 Subject: mmc: tegra: remove redundant return statement A redundant return statement is removed from tegra_sdhci_set_uhs_signaling(). The function returns void and the return does not affect the control flow of the function. Signed-off-by: Aapo Vienamo Acked-by: Thierry Reding Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index b877c13184c2..970d38f68939 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -231,7 +231,7 @@ static void tegra_sdhci_set_uhs_signaling(struct sdhci_host *host, if (timing == MMC_TIMING_UHS_DDR50) tegra_host->ddr_signaling = true; - return sdhci_set_uhs_signaling(host, timing); + sdhci_set_uhs_signaling(host, timing); } static unsigned int tegra_sdhci_get_max_clock(struct sdhci_host *host) -- cgit v1.2.3 From caeffcf1b26bac57676fbb15df81bff1b79123ea Mon Sep 17 00:00:00 2001 From: Masaharu Hayakawa Date: Wed, 9 May 2018 21:38:48 +0900 Subject: mmc: renesas_sdhi: Add r8a77965 support This patch adds r8a77965 support in SDHI. Signed-off-by: Masaharu Hayakawa Signed-off-by: Yoshihiro Kaneko Tested-by: Simon Horman Tested-by: Wolfram Sang Reviewed-by: Wolfram Sang Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/tmio_mmc.txt | 1 + drivers/mmc/host/renesas_sdhi_internal_dmac.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt index ba3825245889..ee978c95189d 100644 --- a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt +++ b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt @@ -26,6 +26,7 @@ Required properties: "renesas,sdhi-r8a7794" - SDHI IP on R8A7794 SoC "renesas,sdhi-r8a7795" - SDHI IP on R8A7795 SoC "renesas,sdhi-r8a7796" - SDHI IP on R8A7796 SoC + "renesas,sdhi-r8a77965" - SDHI IP on R8A77965 SoC "renesas,sdhi-r8a77980" - SDHI IP on R8A77980 SoC "renesas,sdhi-r8a77995" - SDHI IP on R8A77995 SoC "renesas,sdhi-shmobile" - a generic sh-mobile SDHI controller diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index a6bf12348bdc..b6edb7a695b5 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -276,6 +276,7 @@ static const struct soc_device_attribute gen3_soc_whitelist[] = { /* generic ones */ { .soc_id = "r8a7795" }, { .soc_id = "r8a7796" }, + { .soc_id = "r8a77965" }, { .soc_id = "r8a77980" }, { .soc_id = "r8a77995" }, { /* sentinel */ } -- cgit v1.2.3 From 10a0ed7a945054c2c4951e7a938b5f5498964820 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 8 May 2018 09:04:19 +0800 Subject: Documentation: mmc: addtional description for post-power-on-delay-ms post-power-on-delay-ms woule be reused to substitute the hard-coded 10ms delay waiting for power supply to be stable, specificed by individual platform/board. Default to 10ms as before, if no available. Signed-off-by: Shawn Lin Reviewed-by: Rob Herring Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index dbe23b12ea1c..f5a0923b34ca 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -58,6 +58,10 @@ Optional properties: - fixed-emmc-driver-type: for non-removable eMMC, enforce this driver type. The value is the driver type as specified in the eMMC specification (table 206 in spec version 5.1). +- post-power-on-delay-ms : It was invented for MMC pwrseq-simple which could + be referred to mmc-pwrseq-simple.txt. But now it's reused as a tunable delay + waiting for I/O signalling and card power supply to be stable, regardless of + whether pwrseq-simple is used. Default to 10ms if no available. *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line polarity properties, we have to fix the meaning of the "normal" and "inverted" -- cgit v1.2.3 From 6d796c68cd15234a33a4bd2ef7231125fea2dc6c Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 8 May 2018 09:04:20 +0800 Subject: mmc: core: add tunable delay waiting for power to be stable The hard-coded 10ms delay in mmc_power_up came from commit 79bccc5aefb4 ("mmc: increase power up delay"), which said "The TI controller on Toshiba Tecra M5 needs more time to power up or the cards will init incorrectly or not at all." But it's too engineering solution for a special board but force all platforms to wait for that long time, especially painful for mmc_power_up for eMMC when booting. However, it's added since 2009, and we can't tell if other platforms benefit from it. But in practise, the modern hardware are most likely to have a stable power supply with 1ms after setting it for no matter PMIC or discrete power. And more importnatly, most regulators implement the callback of ->set_voltage_time_sel() for regulator core to wait for specific period of time for the power supply to be stable, which means once regulator_set_voltage_* return, the power should reach the the minimum voltage that works for initialization. Of course, if there are some other ways for host to power the card, we should allow them to argue a suitable delay as well. With this patch, we could assign the delay from firmware, or we could assigne it via ->set_ios() callback from host drivers. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 4 ++-- drivers/mmc/core/host.c | 4 ++++ include/linux/mmc/host.h | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 42cfcb64abe0..9a769edbabe0 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1660,7 +1660,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) * This delay should be sufficient to allow the power supply * to reach the minimum voltage. */ - mmc_delay(10); + mmc_delay(host->ios.power_delay_ms); mmc_pwrseq_post_power_on(host); @@ -1673,7 +1673,7 @@ void mmc_power_up(struct mmc_host *host, u32 ocr) * This delay must be at least 74 clock sizes, or 1 ms, or the * time required to reach a stable voltage. */ - mmc_delay(10); + mmc_delay(host->ios.power_delay_ms); } void mmc_power_off(struct mmc_host *host) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index da08a17fbf6c..c57ffff18e37 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -343,6 +343,9 @@ int mmc_of_parse(struct mmc_host *host) host->dsr_req = 0; } + device_property_read_u32(dev, "post-power-on-delay-ms", + &host->ios.power_delay_ms); + return mmc_pwrseq_alloc(host); } @@ -408,6 +411,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->max_blk_count = PAGE_SIZE / 512; host->fixed_drv_type = -EINVAL; + host->ios.power_delay_ms = 10; return host; } diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 8f1859044db1..64300a48dcce 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -22,6 +22,7 @@ struct mmc_ios { unsigned int clock; /* clock rate */ unsigned short vdd; + unsigned int power_delay_ms; /* waiting for stable power */ /* vdd stores the bit number of the selected voltage range from below. */ -- cgit v1.2.3 From 86958dcc5ad706e8e0a3e647df01f6a5c509ff5f Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Tue, 8 May 2018 14:46:48 -0400 Subject: mmc: dw_mmc-bluefield: Add driver extension This commit adds extension to the dw_mmc driver for Mellanox BlueField SoC. It updates the UHS_REG_EXT register to bring up the eMMC card on this SoC. Signed-off-by: Liming Sun Reviewed-by: David Woods Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 9 +++++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-bluefield.c | 81 +++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 drivers/mmc/host/dw_mmc-bluefield.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index d70d11d0abab..0581c199c996 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -686,6 +686,15 @@ config MMC_DW_PLTFM If unsure, say Y. +config MMC_DW_BLUEFIELD + tristate "BlueField specific extensions for Synopsys DW Memory Card Interface" + depends on MMC_DW + select MMC_DW_PLTFM + help + This selects support for Mellanox BlueField SoC specific extensions to + the Synopsys DesignWare Memory Card Interface driver. Select this + option for platforms based on Mellanox BlueField SoC's. + config MMC_DW_EXYNOS tristate "Exynos specific extensions for Synopsys DW Memory Card Interface" depends on MMC_DW diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 6aead24879b4..85dc1322c3de 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -49,6 +49,7 @@ thunderx-mmc-objs := cavium.o cavium-thunderx.o obj-$(CONFIG_MMC_CAVIUM_THUNDERX) += thunderx-mmc.o obj-$(CONFIG_MMC_DW) += dw_mmc.o obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o +obj-$(CONFIG_MMC_DW_BLUEFIELD) += dw_mmc-bluefield.o obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c new file mode 100644 index 000000000000..54c3fbb4a391 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-bluefield.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Mellanox Technologies. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" + +#define UHS_REG_EXT_SAMPLE_MASK GENMASK(22, 16) +#define UHS_REG_EXT_DRIVE_MASK GENMASK(29, 23) +#define BLUEFIELD_UHS_REG_EXT_SAMPLE 2 +#define BLUEFIELD_UHS_REG_EXT_DRIVE 4 + +static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + u32 reg; + + /* Update the Drive and Sample fields in register UHS_REG_EXT. */ + reg = mci_readl(host, UHS_REG_EXT); + reg &= ~UHS_REG_EXT_SAMPLE_MASK; + reg |= FIELD_PREP(UHS_REG_EXT_SAMPLE_MASK, + BLUEFIELD_UHS_REG_EXT_SAMPLE); + reg &= ~UHS_REG_EXT_DRIVE_MASK; + reg |= FIELD_PREP(UHS_REG_EXT_DRIVE_MASK, BLUEFIELD_UHS_REG_EXT_DRIVE); + mci_writel(host, UHS_REG_EXT, reg); +} + +static const struct dw_mci_drv_data bluefield_drv_data = { + .set_ios = dw_mci_bluefield_set_ios +}; + +static const struct of_device_id dw_mci_bluefield_match[] = { + { .compatible = "mellanox,bluefield-dw-mshc", + .data = &bluefield_drv_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_mci_bluefield_match); + +static int dw_mci_bluefield_probe(struct platform_device *pdev) +{ + const struct dw_mci_drv_data *drv_data = NULL; + const struct of_device_id *match; + + if (pdev->dev.of_node) { + match = of_match_node(dw_mci_bluefield_match, + pdev->dev.of_node); + drv_data = match->data; + } + + return dw_mci_pltfm_register(pdev, drv_data); +} + +static struct platform_driver dw_mci_bluefield_pltfm_driver = { + .probe = dw_mci_bluefield_probe, + .remove = dw_mci_pltfm_remove, + .driver = { + .name = "dwmmc_bluefield", + .of_match_table = dw_mci_bluefield_match, + .pm = &dw_mci_pltfm_pmops, + }, +}; + +module_platform_driver(dw_mci_bluefield_pltfm_driver); + +MODULE_DESCRIPTION("BlueField DW Multimedia Card driver"); +MODULE_AUTHOR("Mellanox Technologies"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 0e190fd071ccda260d7b54d2f40c379f28a1237e Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Tue, 8 May 2018 14:46:49 -0400 Subject: dt-bindings: mmc: Add binding for BlueField SoC This commit adds "mellanox,bluefield-dw-mshc" for dwmmc driver extension on Mellanox BlueField SoC platform. Signed-off-by: Liming Sun Reviewed-by: David Woods Reviewed-by: Rob Herring Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/bluefield-dw-mshc.txt | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt diff --git a/Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt new file mode 100644 index 000000000000..b0f0999ea1a9 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/bluefield-dw-mshc.txt @@ -0,0 +1,29 @@ +* Mellanox Bluefield SoC specific extensions to the Synopsys Designware + Mobile Storage Host Controller + +Read synopsys-dw-mshc.txt for more details + +The Synopsys designware mobile storage host controller is used to interface +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents +differences between the core Synopsys dw mshc controller properties described +by synopsys-dw-mshc.txt and the properties used by the Mellanox Bluefield SoC +specific extensions to the Synopsys Designware Mobile Storage Host Controller. + +Required Properties: + +* compatible: should be one of the following. + - "mellanox,bluefield-dw-mshc": for controllers with Mellanox Bluefield SoC + specific extensions. + +Example: + + /* Mellanox Bluefield SoC MMC */ + mmc@6008000 { + compatible = "mellanox,bluefield-dw-mshc"; + reg = <0x6008000 0x400>; + interrupts = <32>; + fifo-depth = <0x100>; + clock-frequency = <24000000>; + bus-width = <8>; + cap-mmc-highspeed; + }; -- cgit v1.2.3 From c5f9ae6cf2a6556399e8e3e1e28fc2af7065c672 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 15 May 2018 11:57:47 +0200 Subject: dt-bindings: mmc: meson-gx: add reset Add the reset to the documentation of the meson-gx mmc controller bindings. Reviewed-by: Kevin Hilman Signed-off-by: Jerome Brunet Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt index 5add8d7d855f..13e70409e8ac 100644 --- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt @@ -20,6 +20,7 @@ Required properties: "clkin1" - Other parent clock of internal mux The driver has an internal mux clock which switches between clkin0 and clkin1 depending on the clock rate requested by the MMC core. +- resets : phandle of the internal reset line Example: @@ -30,4 +31,5 @@ Example: clocks = <&clkc CLKID_SD_EMMC_A>, <&xtal>, <&clkc CLKID_FCLK_DIV2>; clock-names = "core", "clkin0", "clkin1"; pinctrl-0 = <&emmc_pins>; + resets = <&reset RESET_SD_EMMC_A>; }; -- cgit v1.2.3 From 19c6beaa064c4b198e7d774feab7a0851cf606a0 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 15 May 2018 11:57:48 +0200 Subject: mmc: meson-gx: add device reset Trigger the reset line of the mmc controller while probing, if available. The reset should be optional for now, at least until all related DT nodes have the reset property. Reviewed-by: Kevin Hilman Signed-off-by: Jerome Brunet Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-gx-mmc.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 55bbd67177df..c201c378537e 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -1212,6 +1213,14 @@ static int meson_mmc_probe(struct platform_device *pdev) goto free_host; } + ret = device_reset_optional(&pdev->dev); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "device reset failed: %d\n", ret); + + return ret; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->regs = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(host->regs)) { -- cgit v1.2.3 From 65f9e20e0d34902e442818760217501f05d17b6c Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 17 May 2018 15:47:42 +0800 Subject: mmc: block: Don't switch to the same partition type in mmc_blk_remove() It's pointless to switch and trace partition type if the current selected device partition is the same with that one. Moreover, cycled claiming host associated with mmc_blk_part_switch() could make mmc_blk_remove() end up waiting for grabbing the context if it's occupied, which lead requests could still hit the low-level drivers, if an asynchronous unbind for host drivers happened, as the card hasn't been set removed in the remove path. So a simple dd in background: dd if=/dev/mmcblk0 of=/dev/null bs=512k count=100000 & and doing unbind then: echo fe320000.dwmmc > /sys/bus/platform/drivers/dwmmc_rockchip/unbind could make the console stuck for quite a while depending on the numbers of requests. Suggested-by: Adrian Hunter Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 03e3d48b083e..54457d8745cd 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -2968,9 +2968,11 @@ static void mmc_blk_remove(struct mmc_card *card) mmc_blk_remove_debugfs(card, md); mmc_blk_remove_parts(card, md); pm_runtime_get_sync(&card->dev); - mmc_claim_host(card->host); - mmc_blk_part_switch(card, md->part_type); - mmc_release_host(card->host); + if (md->part_curr != md->part_type) { + mmc_claim_host(card->host); + mmc_blk_part_switch(card, md->part_type); + mmc_release_host(card->host); + } if (card->type != MMC_TYPE_SD_COMBO) pm_runtime_disable(&card->dev); pm_runtime_put_noidle(&card->dev); -- cgit v1.2.3 From 53d7e098ba085584e9ce083e8a1041284b351878 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 18 May 2018 19:18:41 +0200 Subject: mmc: android-goldfish: use sg_copy_{from,to}_buffer This handles highmem pages, and also cleans up the code. Signed-off-by: Christoph Hellwig Signed-off-by: Ulf Hansson --- drivers/mmc/host/android-goldfish.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c index 63d27589cd89..294de177632c 100644 --- a/drivers/mmc/host/android-goldfish.c +++ b/drivers/mmc/host/android-goldfish.c @@ -217,8 +217,8 @@ static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host, * We don't really have DMA, so we need * to copy from our platform driver buffer */ - uint8_t *dest = (uint8_t *)sg_virt(data->sg); - memcpy(dest, host->virt_base, data->sg->length); + sg_copy_to_buffer(data->sg, 1, host->virt_base, + data->sg->length); } host->data->bytes_xfered += data->sg->length; dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, @@ -393,8 +393,8 @@ static void goldfish_mmc_prepare_data(struct goldfish_mmc_host *host, * We don't really have DMA, so we need to copy to our * platform driver buffer */ - const uint8_t *src = (uint8_t *)sg_virt(data->sg); - memcpy(host->virt_base, src, data->sg->length); + sg_copy_from_buffer(data->sg, 1, host->virt_base, + data->sg->length); } } -- cgit v1.2.3 From 5b4277814e3fd91b60f9b4fb4a702050eed27c87 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 18 May 2018 19:18:42 +0200 Subject: mmc: atmel-mci: use sg_copy_{from,to}_buffer This handles highmem pages, and also cleans up the code. Signed-off-by: Christoph Hellwig Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index e55f3932d580..5aa2c9404e92 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1967,7 +1967,6 @@ static void atmci_tasklet_func(unsigned long priv) static void atmci_read_data_pio(struct atmel_mci *host) { struct scatterlist *sg = host->sg; - void *buf = sg_virt(sg); unsigned int offset = host->pio_offset; struct mmc_data *data = host->data; u32 value; @@ -1977,7 +1976,7 @@ static void atmci_read_data_pio(struct atmel_mci *host) do { value = atmci_readl(host, ATMCI_RDR); if (likely(offset + 4 <= sg->length)) { - put_unaligned(value, (u32 *)(buf + offset)); + sg_pcopy_to_buffer(sg, 1, &value, sizeof(u32), offset); offset += 4; nbytes += 4; @@ -1990,11 +1989,11 @@ static void atmci_read_data_pio(struct atmel_mci *host) goto done; offset = 0; - buf = sg_virt(sg); } } else { unsigned int remaining = sg->length - offset; - memcpy(buf + offset, &value, remaining); + + sg_pcopy_to_buffer(sg, 1, &value, remaining, offset); nbytes += remaining; flush_dcache_page(sg_page(sg)); @@ -2004,8 +2003,8 @@ static void atmci_read_data_pio(struct atmel_mci *host) goto done; offset = 4 - remaining; - buf = sg_virt(sg); - memcpy(buf, (u8 *)&value + remaining, offset); + sg_pcopy_to_buffer(sg, 1, (u8 *)&value + remaining, + offset, 0); nbytes += offset; } @@ -2035,7 +2034,6 @@ done: static void atmci_write_data_pio(struct atmel_mci *host) { struct scatterlist *sg = host->sg; - void *buf = sg_virt(sg); unsigned int offset = host->pio_offset; struct mmc_data *data = host->data; u32 value; @@ -2044,7 +2042,7 @@ static void atmci_write_data_pio(struct atmel_mci *host) do { if (likely(offset + 4 <= sg->length)) { - value = get_unaligned((u32 *)(buf + offset)); + sg_pcopy_from_buffer(sg, 1, &value, sizeof(u32), offset); atmci_writel(host, ATMCI_TDR, value); offset += 4; @@ -2056,13 +2054,12 @@ static void atmci_write_data_pio(struct atmel_mci *host) goto done; offset = 0; - buf = sg_virt(sg); } } else { unsigned int remaining = sg->length - offset; value = 0; - memcpy(&value, buf + offset, remaining); + sg_pcopy_from_buffer(sg, 1, &value, remaining, offset); nbytes += remaining; host->sg = sg = sg_next(sg); @@ -2073,8 +2070,8 @@ static void atmci_write_data_pio(struct atmel_mci *host) } offset = 4 - remaining; - buf = sg_virt(sg); - memcpy((u8 *)&value + remaining, buf, offset); + sg_pcopy_from_buffer(sg, 1, (u8 *)&value + remaining, + offset, 0); atmci_writel(host, ATMCI_TDR, value); nbytes += offset; } -- cgit v1.2.3 From b189e7589f6d3411e85c6b7ae6eef158f08f388f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 18 May 2018 19:18:44 +0200 Subject: mmc: mxcmmc: handle highmem pages Use kmap_atomic to map the scatterlist entry before using it. Signed-off-by: Christoph Hellwig Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxcmmc.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 5ff8ef7223cc..150fbdba777d 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -291,8 +291,10 @@ static void mxcmci_swap_buffers(struct mmc_data *data) struct scatterlist *sg; int i; - for_each_sg(data->sg, sg, data->sg_len, i) - buffer_swap32(sg_virt(sg), sg->length); + for_each_sg(data->sg, sg, data->sg_len, i) { + void *buf = kmap_atomic(sg_page(sg) + sg->offset; + buffer_swap32(buf, sg->length); + kunmap_atomic(buf); } #else static inline void mxcmci_swap_buffers(struct mmc_data *data) {} @@ -609,6 +611,7 @@ static int mxcmci_transfer_data(struct mxcmci_host *host) { struct mmc_data *data = host->req->data; struct scatterlist *sg; + void *buf; int stat, i; host->data = data; @@ -616,14 +619,18 @@ static int mxcmci_transfer_data(struct mxcmci_host *host) if (data->flags & MMC_DATA_READ) { for_each_sg(data->sg, sg, data->sg_len, i) { - stat = mxcmci_pull(host, sg_virt(sg), sg->length); + buf = kmap_atomic(sg_page(sg) + sg->offset); + stat = mxcmci_pull(host, buf, sg->length); + kunmap(buf); if (stat) return stat; host->datasize += sg->length; } } else { for_each_sg(data->sg, sg, data->sg_len, i) { - stat = mxcmci_push(host, sg_virt(sg), sg->length); + buf = kmap_atomic(sg_page(sg) + sg->offset); + stat = mxcmci_push(host, buf, sg->length); + kunmap(buf); if (stat) return stat; host->datasize += sg->length; -- cgit v1.2.3 From a4034173822e6a9092245f546739946489f8f30d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 18 May 2018 19:18:45 +0200 Subject: mmc: ushc: handle highmem pages Pass the scatterlist on to the USB subsystem instead of expecting a kernel virtual address. Signed-off-by: Christoph Hellwig Signed-off-by: Ulf Hansson --- drivers/mmc/host/ushc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c index 81dac17064d7..b2b379b10dfa 100644 --- a/drivers/mmc/host/ushc.c +++ b/drivers/mmc/host/ushc.c @@ -300,8 +300,10 @@ static void ushc_request(struct mmc_host *mmc, struct mmc_request *req) pipe = usb_sndbulkpipe(ushc->usb_dev, 2); usb_fill_bulk_urb(ushc->data_urb, ushc->usb_dev, pipe, - sg_virt(data->sg), data->sg->length, + NULL, data->sg->length, data_callback, ushc); + ushc->data_urb->num_sgs = 1; + ushc->data_urb->sg = data->sg; ret = usb_submit_urb(ushc->data_urb, GFP_ATOMIC); if (ret < 0) goto out; -- cgit v1.2.3 From 9181ece38c93679ec413580f9ddf05b5cdb214db Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 18 May 2018 19:18:46 +0200 Subject: mmc: wbsd: handle highmem pages Use sg_copy_{from,to}_buffer to bounce buffer and kmap_atomic to map the scatterlist entry before using it. Signed-off-by: Christoph Hellwig Signed-off-by: Ulf Hansson --- drivers/mmc/host/wbsd.c | 68 ++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index f4233576153b..1e54bbf13d75 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -268,43 +268,29 @@ static inline int wbsd_next_sg(struct wbsd_host *host) return host->num_sg; } -static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) +static inline char *wbsd_map_sg(struct wbsd_host *host) { - return sg_virt(host->cur_sg); + return kmap_atomic(sg_page(host->cur_sg)) + host->cur_sg->offset; } static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) { - unsigned int len, i; - struct scatterlist *sg; - char *dmabuf = host->dma_buffer; - char *sgbuf; - - sg = data->sg; - len = data->sg_len; - - for (i = 0; i < len; i++) { - sgbuf = sg_virt(&sg[i]); - memcpy(dmabuf, sgbuf, sg[i].length); - dmabuf += sg[i].length; - } + size_t len = 0; + int i; + + for (i = 0; i < data->sg_len; i++) + len += data->sg[i].length; + sg_copy_to_buffer(data->sg, data->sg_len, host->dma_buffer, len); } static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) { - unsigned int len, i; - struct scatterlist *sg; - char *dmabuf = host->dma_buffer; - char *sgbuf; - - sg = data->sg; - len = data->sg_len; - - for (i = 0; i < len; i++) { - sgbuf = sg_virt(&sg[i]); - memcpy(sgbuf, dmabuf, sg[i].length); - dmabuf += sg[i].length; - } + size_t len = 0; + int i; + + for (i = 0; i < data->sg_len; i++) + len += data->sg[i].length; + sg_copy_from_buffer(data->sg, data->sg_len, host->dma_buffer, len); } /* @@ -418,7 +404,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) { struct mmc_data *data = host->mrq->cmd->data; char *buffer; - int i, fsr, fifo; + int i, idx, fsr, fifo; /* * Handle excessive data. @@ -426,7 +412,8 @@ static void wbsd_empty_fifo(struct wbsd_host *host) if (host->num_sg == 0) return; - buffer = wbsd_sg_to_buffer(host) + host->offset; + buffer = wbsd_map_sg(host) + host->offset; + idx = 0; /* * Drain the fifo. This has a tendency to loop longer @@ -445,8 +432,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) fifo = 1; for (i = 0; i < fifo; i++) { - *buffer = inb(host->base + WBSD_DFR); - buffer++; + buffer[idx++] = inb(host->base + WBSD_DFR); host->offset++; host->remain--; @@ -456,16 +442,19 @@ static void wbsd_empty_fifo(struct wbsd_host *host) * End of scatter list entry? */ if (host->remain == 0) { + kunmap_atomic(buffer); /* * Get next entry. Check if last. */ if (!wbsd_next_sg(host)) return; - buffer = wbsd_sg_to_buffer(host); + buffer = wbsd_map_sg(host); + idx = 0; } } } + kunmap_atomic(buffer); /* * This is a very dirty hack to solve a @@ -480,7 +469,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) { struct mmc_data *data = host->mrq->cmd->data; char *buffer; - int i, fsr, fifo; + int i, idx, fsr, fifo; /* * Check that we aren't being called after the @@ -489,7 +478,8 @@ static void wbsd_fill_fifo(struct wbsd_host *host) if (host->num_sg == 0) return; - buffer = wbsd_sg_to_buffer(host) + host->offset; + buffer = wbsd_map_sg(host) + host->offset; + idx = 0; /* * Fill the fifo. This has a tendency to loop longer @@ -508,8 +498,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) fifo = 15; for (i = 16; i > fifo; i--) { - outb(*buffer, host->base + WBSD_DFR); - buffer++; + outb(buffer[idx], host->base + WBSD_DFR); host->offset++; host->remain--; @@ -519,16 +508,19 @@ static void wbsd_fill_fifo(struct wbsd_host *host) * End of scatter list entry? */ if (host->remain == 0) { + kunmap_atomic(buffer); /* * Get next entry. Check if last. */ if (!wbsd_next_sg(host)) return; - buffer = wbsd_sg_to_buffer(host); + buffer = wbsd_map_sg(host); + idx = 0; } } } + kunmap_atomic(buffer); /* * The controller stops sending interrupts for -- cgit v1.2.3 From e1cb88ad4655dd7484e56c01a05a2986496f56a8 Mon Sep 17 00:00:00 2001 From: Xie Yisheng Date: Mon, 21 May 2018 19:57:53 +0800 Subject: mmc: sdhci-xenon: use match_string() helper match_string() returns the index of an array for a matching string, which can be used intead of open coded variant. Cc: Adrian Hunter Cc: Hu Ziji Cc: Ulf Hansson Cc: linux-mmc@vger.kernel.org Signed-off-by: Yisheng Xie Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-xenon-phy.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-xenon-phy.c b/drivers/mmc/host/sdhci-xenon-phy.c index ec8794335241..a35804b203a7 100644 --- a/drivers/mmc/host/sdhci-xenon-phy.c +++ b/drivers/mmc/host/sdhci-xenon-phy.c @@ -814,15 +814,10 @@ static int xenon_add_phy(struct device_node *np, struct sdhci_host *host, { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct xenon_priv *priv = sdhci_pltfm_priv(pltfm_host); - int i, ret; + int ret; - for (i = 0; i < NR_PHY_TYPES; i++) { - if (!strcmp(phy_name, phy_types[i])) { - priv->phy_type = i; - break; - } - } - if (i == NR_PHY_TYPES) { + priv->phy_type = match_string(phy_types, NR_PHY_TYPES, phy_name); + if (priv->phy_type < 0) { dev_err(mmc_dev(host->mmc), "Unable to determine PHY name %s. Use default eMMC 5.1 PHY\n", phy_name); -- cgit v1.2.3 From c8a4e30d9ac0a7d405c4d26e313af7464ee00929 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 22 May 2018 16:26:26 +0200 Subject: mmc: core: Move calls to ->prepare_hs400_tuning() closer to mmc code Move the calls to ->prepare_hs400_tuning(), from mmc_retune() into mmc_hs400_to_hs200(), as it better belongs there, rather than being generic to all type of cards. Signed-off-by: Ulf Hansson Reviewed-by: Simon Horman --- drivers/mmc/core/host.c | 3 --- drivers/mmc/core/mmc.c | 4 ++++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index c57ffff18e37..abf9e884386c 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -143,9 +143,6 @@ int mmc_retune(struct mmc_host *host) 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); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 57a8bd39cdde..4466f5de54d4 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1282,6 +1282,10 @@ int mmc_hs400_to_hs200(struct mmc_card *card) mmc_set_bus_speed(card); + /* Prepare tuning for HS400 mode. */ + if (host->ops->prepare_hs400_tuning) + host->ops->prepare_hs400_tuning(host, &host->ios); + return 0; out_err: -- cgit v1.2.3 From a0d476654a2b222567710ab13f3660a39c5641c8 Mon Sep 17 00:00:00 2001 From: "yinbo.zhu" Date: Thu, 24 May 2018 11:38:25 +0800 Subject: mmc: sd: Define name for default speed dtr Add a new define for the sd default speed 25MHz case Signed-off-by: Yinbo Zhu Signed-off-by: Ulf Hansson --- include/linux/mmc/card.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 5ebc47855721..de7377815b6b 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -156,6 +156,7 @@ struct sd_switch_caps { #define UHS_DDR50_MAX_DTR 50000000 #define UHS_SDR25_MAX_DTR UHS_DDR50_MAX_DTR #define UHS_SDR12_MAX_DTR 25000000 +#define DEFAULT_SPEED_MAX_DTR UHS_SDR12_MAX_DTR unsigned int sd3_bus_mode; #define UHS_SDR12_BUS_SPEED 0 #define HIGH_SPEED_BUS_SPEED 1 -- cgit v1.2.3 From fb8617e1ee4d4057c76730e2f8376ece45663c34 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Fri, 25 May 2018 15:15:09 +0800 Subject: mmc: sdhci-*: Don't emit error msg if sdhci_add_host() fails I noticed below error msg with sdhci-pxav3 on some berlin platforms: [.....] sdhci-pxav3 f7ab0000.sdhci failed to add host It is due to getting related vmmc or vqmmc regulator returns -EPROBE_DEFER. It doesn't matter at all but it's confusing. >From another side, if driver probing fails and the error number isn't -EPROBE_DEFER, the core will tell us something as below: [.....] sdhci-pxav3: probe of f7ab0000.sdhci failed with error -EXX So it's not necessary to emit error msg if sdhci_add_host() fails. And some other sdhci host drivers also have this issue, let's fix them together. Signed-off-by: Jisheng Zhang Acked-by: Adrian Hunter Acked-by: Viresh Kumar Acked-by: Patrice Chotard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-bcm-kona.c | 4 +--- drivers/mmc/host/sdhci-pic32.c | 4 +--- drivers/mmc/host/sdhci-pxav2.c | 4 +--- drivers/mmc/host/sdhci-pxav3.c | 4 +--- drivers/mmc/host/sdhci-s3c.c | 4 +--- drivers/mmc/host/sdhci-spear.c | 4 +--- drivers/mmc/host/sdhci-st.c | 4 +--- 7 files changed, 7 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 11ca95c60bcf..bdbd4897c0f7 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -284,10 +284,8 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev) sdhci_bcm_kona_sd_init(host); ret = sdhci_add_host(host); - if (ret) { - dev_err(dev, "Failed sdhci_add_host\n"); + if (ret) goto err_reset; - } /* if device is eMMC, emulate card insert right here */ if (!mmc_card_is_removable(host->mmc)) { diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c index a6caa49ca25a..a11e6397d4ff 100644 --- a/drivers/mmc/host/sdhci-pic32.c +++ b/drivers/mmc/host/sdhci-pic32.c @@ -200,10 +200,8 @@ static int pic32_sdhci_probe(struct platform_device *pdev) } ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "error adding host\n"); + if (ret) goto err_base_clk; - } dev_info(&pdev->dev, "Successfully added sdhci host\n"); return 0; diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 8986f9d9cf98..2c3827f54927 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -221,10 +221,8 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) host->ops = &pxav2_sdhci_ops; ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "failed to add host\n"); + if (ret) goto disable_clk; - } return 0; diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index a34434166ca7..b8e96f392428 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -472,10 +472,8 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) pm_suspend_ignore_children(&pdev->dev, 1); ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "failed to add host\n"); + if (ret) goto err_add_host; - } if (host->mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) device_init_wakeup(&pdev->dev, 1); diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index cda83ccb2702..9ef89d00970e 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -655,10 +655,8 @@ static int sdhci_s3c_probe(struct platform_device *pdev) goto err_req_regs; ret = sdhci_add_host(host); - if (ret) { - dev_err(dev, "sdhci_add_host() failed\n"); + if (ret) goto err_req_regs; - } #ifdef CONFIG_PM if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL) diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 14511526a3a8..9247d51f2eed 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -126,10 +126,8 @@ static int sdhci_probe(struct platform_device *pdev) } ret = sdhci_add_host(host); - if (ret) { - dev_dbg(&pdev->dev, "error adding host\n"); + if (ret) goto disable_clk; - } platform_set_drvdata(pdev, host); diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index c32daed0d418..8f95647195d9 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -422,10 +422,8 @@ static int sdhci_st_probe(struct platform_device *pdev) st_mmcss_cconfig(np, host); ret = sdhci_add_host(host); - if (ret) { - dev_err(&pdev->dev, "Failed sdhci_add_host\n"); + if (ret) goto err_out; - } host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); -- cgit v1.2.3 From 52af318c93e970d26c5522eab23ef84fdcb417e1 Mon Sep 17 00:00:00 2001 From: Evan Green Date: Fri, 25 May 2018 12:25:23 -0700 Subject: mmc: Allow non-sleeping GPIO cd This change uses the appropriate _cansleep or non-sleeping API for reading GPIO card detect state. This allows users with GPIOs that never sleep to avoid a warning when certain quirks are present. The sdhci controller has an SDHCI_QUIRK_NO_CARD_NO_RESET, which indicates that a controller will not reset properly if no card is inserted. With this quirk enabled, mmc_get_cd_gpio is called in several places with a spinlock held and interrupts disabled. gpiod_get_raw_value_cansleep is not happy with this situation, and throws out a warning. For boards that a) use controllers that have this quirk, and b) wire card detect up to a GPIO that doesn't sleep, this is a spurious warning. This change silences that warning, at the cost of pushing this problem down to users that have sleeping GPIOs and controllers with this quirk. Signed-off-by: Evan Green Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 56559351d2e1..ef05e0039378 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -79,15 +79,22 @@ EXPORT_SYMBOL(mmc_gpio_get_ro); int mmc_gpio_get_cd(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; + int cansleep; if (!ctx || !ctx->cd_gpio) return -ENOSYS; - if (ctx->override_cd_active_level) - return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^ - !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); + cansleep = gpiod_cansleep(ctx->cd_gpio); + if (ctx->override_cd_active_level) { + int value = cansleep ? + gpiod_get_raw_value_cansleep(ctx->cd_gpio) : + gpiod_get_raw_value(ctx->cd_gpio); + return !value ^ !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); + } - return gpiod_get_value_cansleep(ctx->cd_gpio); + return cansleep ? + gpiod_get_value_cansleep(ctx->cd_gpio) : + gpiod_get_value(ctx->cd_gpio); } EXPORT_SYMBOL(mmc_gpio_get_cd); -- cgit v1.2.3 From a6720c023af24220f3f26459c0aa156260a84aa0 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 28 May 2018 08:14:21 +0200 Subject: mmc: au1xmmc: handle highmem pages Use kmap_atomic to map the scatterlist entry before using it. Signed-off-by: Christoph Hellwig Signed-off-by: Ulf Hansson --- drivers/mmc/host/au1xmmc.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index ed77fbfa4774..9b4be67330dd 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -405,7 +406,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host) /* This is the pointer to the data buffer */ sg = &data->sg[host->pio.index]; - sg_ptr = sg_virt(sg) + host->pio.offset; + sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset; /* This is the space left inside the buffer */ sg_len = data->sg[host->pio.index].length - host->pio.offset; @@ -421,11 +422,12 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host) if (!(status & SD_STATUS_TH)) break; - val = *sg_ptr++; + val = sg_ptr[count]; __raw_writel((unsigned long)val, HOST_TXPORT(host)); wmb(); /* drain writebuffer */ } + kunmap_atomic(sg_ptr); host->pio.len -= count; host->pio.offset += count; @@ -462,7 +464,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host) if (host->pio.index < host->dma.len) { sg = &data->sg[host->pio.index]; - sg_ptr = sg_virt(sg) + host->pio.offset; + sg_ptr = kmap_atomic(sg_page(sg)) + sg->offset + host->pio.offset; /* This is the space left inside the buffer */ sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; @@ -501,8 +503,10 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host) val = __raw_readl(HOST_RXPORT(host)); if (sg_ptr) - *sg_ptr++ = (unsigned char)(val & 0xFF); + sg_ptr[count] = (unsigned char)(val & 0xFF); } + if (sg_ptr) + kunmap_atomic(sg_ptr); host->pio.len -= count; host->pio.offset += count; -- cgit v1.2.3 From 833b51170feeee1718990480f792bb05cb0ca17c Mon Sep 17 00:00:00 2001 From: Martin Hicks Date: Mon, 28 May 2018 13:23:04 +0200 Subject: mmc: Throttle calls to MMC_SEND_STATUS during mmc_do_erase() This drastically reduces the rate at which the MMC_SEND_STATUS cmd polls for completion of the MMC Erase operation. The patch does this by adding a backoff sleep that starts by sleeping for short intervals (128-256us), and ramps up to sleeping for 32-64ms. Even on very quickly completing erase operations, the loop iterates a few times, so not too much extra latency is added to these commands. For long running discard operarations, like a full-device secure discard, this change drops the interrupt rates on my single-core NXP I.MX6UL from 45000/s to about 20/s, and greatly improves system responsiveness. Signed-off-by: Martin Hicks Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 9a769edbabe0..281826d1fcca 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1969,6 +1969,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, unsigned int qty = 0, busy_timeout = 0; bool use_r1b_resp = false; unsigned long timeout; + int loop_udelay=64, udelay_max=32768; int err; mmc_retune_hold(card->host); @@ -2093,9 +2094,15 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from, err = -EIO; goto out; } + if ((cmd.resp[0] & R1_READY_FOR_DATA) && + R1_CURRENT_STATE(cmd.resp[0]) != R1_STATE_PRG) + break; + + usleep_range(loop_udelay, loop_udelay*2); + if (loop_udelay < udelay_max) + loop_udelay *= 2; + } while (1); - } 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 45ee50461c249666e1aa050bb8a7d0785f1c79f5 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 25 May 2018 23:07:42 +0200 Subject: mmc: sunxi: mark PM functions as __maybe_unused The newly added runtime-pm functions cause a harmless warning when CONFIG_PM is disabled: drivers/mmc/host/sunxi-mmc.c:1452:12: error: 'sunxi_mmc_runtime_suspend' defined but not used [-Werror=unused-function] static int sunxi_mmc_runtime_suspend(struct device *dev) ^~~~~~~~~~~~~~~~~~~~~~~~~ drivers/mmc/host/sunxi-mmc.c:1435:12: error: 'sunxi_mmc_runtime_resume' defined but not used [-Werror=unused-function] static int sunxi_mmc_runtime_resume(struct device *dev) This marks them as __maybe_unused to shut up the warning. Fixes: 9a8e1e8cc2c0 ("mmc: sunxi: Add runtime_pm support") Signed-off-by: Arnd Bergmann Acked-by: Maxime Ripard Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 97c6b79b7d6f..837888b96bd3 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1432,7 +1432,7 @@ static int sunxi_mmc_remove(struct platform_device *pdev) return 0; } -static int sunxi_mmc_runtime_resume(struct device *dev) +static int __maybe_unused sunxi_mmc_runtime_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct sunxi_mmc_host *host = mmc_priv(mmc); @@ -1449,7 +1449,7 @@ static int sunxi_mmc_runtime_resume(struct device *dev) return 0; } -static int sunxi_mmc_runtime_suspend(struct device *dev) +static int __maybe_unused sunxi_mmc_runtime_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct sunxi_mmc_host *host = mmc_priv(mmc); -- cgit v1.2.3 From a639bb72c26f1175f56fcd3477a18381ae284ddb Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 29 May 2018 12:30:35 +0200 Subject: mmc: mxmmc: include linux/highmem.h The highmem conversion caused a build error in some configurations: drivers/mmc/host/mxcmmc.c: In function 'mxcmci_transfer_data': drivers/mmc/host/mxcmmc.c:622:10: error: implicit declaration of function 'kmap_atomic'; did you mean 'in_atomic'? [-Werror=implicit-function-declaration] This includes the correct header file. Fixes: b189e7589f6d ("mmc: mxcmmc: handle highmem pages") Signed-off-by: Arnd Bergmann Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxcmmc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 150fbdba777d..d62bf7efce13 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From d8edfc4edd2ecaf7848a5f7533cac1178fac5bcc Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 30 May 2018 14:56:45 +0200 Subject: mmc: mxmmc: Use ifdef rather than __maybe_unused To be consistent with code in other mmc host drivers, convert to check the correct PM config #ifdef in favor of using __maybe_unused. Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxcmmc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index d62bf7efce13..75f781c11e89 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -1214,7 +1214,8 @@ static int mxcmci_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused mxcmci_suspend(struct device *dev) +#ifdef CONFIG_PM_SLEEP +static int mxcmci_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); @@ -1224,7 +1225,7 @@ static int __maybe_unused mxcmci_suspend(struct device *dev) return 0; } -static int __maybe_unused mxcmci_resume(struct device *dev) +static int mxcmci_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct mxcmci_host *host = mmc_priv(mmc); @@ -1240,6 +1241,7 @@ static int __maybe_unused mxcmci_resume(struct device *dev) return ret; } +#endif static SIMPLE_DEV_PM_OPS(mxcmci_pm_ops, mxcmci_suspend, mxcmci_resume); -- cgit v1.2.3 From af6b8ff4bce4ea43e89236339f84fc0a26044c42 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 30 May 2018 15:02:16 +0200 Subject: mmc: sunxi: Use ifdef rather than __maybe_unused To be consistent with code in other mmc host drivers, convert to check the correct PM config #ifdef in favor of using __maybe_unused. Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 837888b96bd3..e7472590f2ed 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1432,7 +1432,8 @@ static int sunxi_mmc_remove(struct platform_device *pdev) return 0; } -static int __maybe_unused sunxi_mmc_runtime_resume(struct device *dev) +#ifdef CONFIG_PM +static int sunxi_mmc_runtime_resume(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct sunxi_mmc_host *host = mmc_priv(mmc); @@ -1449,7 +1450,7 @@ static int __maybe_unused sunxi_mmc_runtime_resume(struct device *dev) return 0; } -static int __maybe_unused sunxi_mmc_runtime_suspend(struct device *dev) +static int sunxi_mmc_runtime_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); struct sunxi_mmc_host *host = mmc_priv(mmc); @@ -1459,6 +1460,7 @@ static int __maybe_unused sunxi_mmc_runtime_suspend(struct device *dev) return 0; } +#endif static const struct dev_pm_ops sunxi_mmc_pm_ops = { SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, -- cgit v1.2.3 From 4ba9bf98b8996b70d73381e700384ac75b82f745 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Wed, 30 May 2018 17:43:35 +0300 Subject: mmc: sdhci-msm: Remove NO_CARD_NO_RESET quirk Now we have a proper implementation for the power irq handling and this quirk is not needed anymore. In fact, it is causing card detection delays on apq8096 platforms and the following error is displayed: sdhci_msm 74a4900.sdhci: mmc0: pwr_irq for req: (4) timed out The quirk is forcing the controller to retain 1.8V signalling on the slot even when a new card is inserted, which is not correct. The proper behavior would be to reset the controller in order to start with 3.3V signaling. Fixes: c0309b3803fe ("mmc: sdhci-msm: Add sdhci msm register write APIs which wait for pwr irq") Suggested-by: Vijay Viswanath Signed-off-by: Georgi Djakov Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index bb11916c58a1..646bf377ba77 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1412,7 +1412,6 @@ static const struct sdhci_ops sdhci_msm_ops = { static const struct sdhci_pltfm_data sdhci_msm_pdata = { .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | - SDHCI_QUIRK_NO_CARD_NO_RESET | SDHCI_QUIRK_SINGLE_POWER_WRITE | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, -- cgit v1.2.3 From 0ef89ec24e7bfe17fb81ec222f3cd39bd5090efc Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 31 May 2018 12:16:04 +0200 Subject: mmc: mvsdio: Respect card busy time out from mmc core Instead of using a hardcoded timeout of 5 * HZ jiffies, let's respect the command busy timeout provided by the mmc core. This make the used timeout more reliable. Cc: Damien Thebault Signed-off-by: Ulf Hansson Tested-by: Damien Thebault --- drivers/mmc/host/mvsdio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 210247b3d11a..4c70829cffd7 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -143,6 +143,7 @@ static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_command *cmd = mrq->cmd; u32 cmdreg = 0, xfer = 0, intr = 0; unsigned long flags; + unsigned int timeout; BUG_ON(host->mrq != NULL); host->mrq = mrq; @@ -234,7 +235,8 @@ static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq) mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); mvsd_write(MVSD_ERR_INTR_EN, 0xffff); - mod_timer(&host->timer, jiffies + 5 * HZ); + timeout = cmd->busy_timeout ? cmd->busy_timeout : 5000; + mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout)); spin_unlock_irqrestore(&host->lock, flags); } -- cgit v1.2.3 From 77252da7cd2cd6d6eed31aaa8d69b05e271bf455 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 31 May 2018 12:23:28 +0200 Subject: mmc: mvsdio: Enable MMC_CAP_ERASE There is no obvious reasons to why mvsdio shouldn't be able to support erase/trim/discard operations, hence let's set MMC_CAP_ERASE for it. Cc: Damien Thebault Signed-off-by: Ulf Hansson Tested-by: Damien Thebault --- drivers/mmc/host/mvsdio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index 4c70829cffd7..e22bbff89c8d 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -757,6 +757,8 @@ static int mvsd_probe(struct platform_device *pdev) if (maxfreq) mmc->f_max = maxfreq; + mmc->caps |= MMC_CAP_ERASE; + spin_lock_init(&host->lock); host->base = devm_ioremap_resource(&pdev->dev, r); -- cgit v1.2.3 From ef5332c10d4f332a2ac79e9ad5452f4e89d1815a Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 1 Jun 2018 13:00:37 +0200 Subject: mmc: renesas_sdhi: really fix WP logic regressions This reverts commit e060d376cc61 ("mmc: renesas_sdhi: fix WP detection") and adds some code to really fix the regressions. It was missed so far that Renesas R-Car instantiations of SDHI chose to disable internal WP and used the existence of "wp-gpios" to en/disable WP at all. With the first refactoring by Yamada-san with commit 2ad1db059b9a ("mmc: renesas_sdhi: use MMC_CAP2_NO_WRITE_PROTECT instead of TMIO own flag"), WP was always disabled even when GPIOs were present. With Wolfram's first fix which gets now reverted, GPIOs were honored. But when not available, the fallback was to internal WP and not to disabled WP. This caused wrong WP status on uSD card slots. Restore the old behaviour now. By default, WP is disabled. When a GPIO is found, the GPIO re-enables WP. We will think about possible better ways to handle this in the future. Tested on a previously regressing Renesas Lager board (H2) and a still working Renesas Salvator-X board (M3-W). Reported-by: Yoshihiro Shimoda Signed-off-by: Wolfram Sang Cc: stable@vger.kernel.org # v4.17+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 5 +++++ drivers/mmc/host/renesas_sdhi_internal_dmac.c | 1 + drivers/mmc/host/renesas_sdhi_sys_dmac.c | 3 +++ 3 files changed, 9 insertions(+) diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 51e01f03fb99..45c015da2e75 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -534,6 +535,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, host->multi_io_quirk = renesas_sdhi_multi_io_quirk; host->dma_ops = dma_ops; + /* For some SoC, we disable internal WP. GPIO may override this */ + if (mmc_can_gpio_ro(host->mmc)) + mmc_data->capabilities2 &= ~MMC_CAP2_NO_WRITE_PROTECT; + /* SDR speeds are only available on Gen2+ */ if (mmc_data->flags & TMIO_MMC_MIN_RCAR2) { /* card_busy caused issues on r8a73a4 (pre-Gen2) CD-less SDHI */ diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index b6edb7a695b5..f7f9773d161f 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -87,6 +87,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, .bus_shift = 2, .scc_offset = 0x1000, .taps = rcar_gen3_scc_taps, diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c index 848e50c1638a..4bb46c489d71 100644 --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -42,6 +42,7 @@ static const struct renesas_sdhi_of_data of_rz_compatible = { static const struct renesas_sdhi_of_data of_rcar_gen1_compatible = { .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_CLK_ACTUAL, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, }; /* Definitions for sampling clocks */ @@ -61,6 +62,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen2_compatible = { TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, .dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES, .dma_rx_offset = 0x2000, .scc_offset = 0x0300, @@ -81,6 +83,7 @@ static const struct renesas_sdhi_of_data of_rcar_gen3_compatible = { TMIO_MMC_HAVE_CBSY | TMIO_MMC_MIN_RCAR2, .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_CMD23, + .capabilities2 = MMC_CAP2_NO_WRITE_PROTECT, .bus_shift = 2, .scc_offset = 0x1000, .taps = rcar_gen3_scc_taps, -- cgit v1.2.3