From 0bb7e560f821c7770973a94e346654c4bdccd42c Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Tue, 8 Dec 2020 21:35:27 +0100 Subject: mmc: mxs-mmc: Fix a resource leak in an error handling path in 'mxs_mmc_probe()' If 'mmc_of_parse()' fails, we must undo the previous 'dma_request_chan()' call. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/20201208203527.49262-1-christophe.jaillet@wanadoo.fr Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxs-mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 56bbc6cd9c84..947581de7860 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -628,7 +628,7 @@ static int mxs_mmc_probe(struct platform_device *pdev) ret = mmc_of_parse(mmc); if (ret) - goto out_clk_disable; + goto out_free_dma; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; -- cgit v1.2.3 From 5d15cbf63515c6183d2ed7c9dd0586b4db23ffb1 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 9 Dec 2020 20:42:02 +0100 Subject: mmc: owl-mmc: Fix a resource leak in an error handling path and in the remove function 'dma_request_chan()' calls should be balanced by a corresponding 'dma_release_channel()' call. Add the missing call both in the error handling path of the probe function and in the remove function. Fixes: ff65ffe46d28 ("mmc: Add Actions Semi Owl SoCs SD/MMC driver") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/20201209194202.54099-1-christophe.jaillet@wanadoo.fr Signed-off-by: Ulf Hansson --- drivers/mmc/host/owl-mmc.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/owl-mmc.c b/drivers/mmc/host/owl-mmc.c index 53b81582f1af..5490962dc8e5 100644 --- a/drivers/mmc/host/owl-mmc.c +++ b/drivers/mmc/host/owl-mmc.c @@ -640,7 +640,7 @@ static int owl_mmc_probe(struct platform_device *pdev) owl_host->irq = platform_get_irq(pdev, 0); if (owl_host->irq < 0) { ret = -EINVAL; - goto err_free_host; + goto err_release_channel; } ret = devm_request_irq(&pdev->dev, owl_host->irq, owl_irq_handler, @@ -648,19 +648,21 @@ static int owl_mmc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Failed to request irq %d\n", owl_host->irq); - goto err_free_host; + goto err_release_channel; } ret = mmc_add_host(mmc); if (ret) { dev_err(&pdev->dev, "Failed to add host\n"); - goto err_free_host; + goto err_release_channel; } dev_dbg(&pdev->dev, "Owl MMC Controller Initialized\n"); return 0; +err_release_channel: + dma_release_channel(owl_host->dma); err_free_host: mmc_free_host(mmc); @@ -674,6 +676,7 @@ static int owl_mmc_remove(struct platform_device *pdev) mmc_remove_host(mmc); disable_irq(owl_host->irq); + dma_release_channel(owl_host->dma); mmc_free_host(mmc); return 0; -- cgit v1.2.3 From c9c256a8b0dc09c305c409d6264cc016af2ba38d Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 17 Dec 2020 21:42:36 +0100 Subject: mmc: sdhci-sprd: Fix some resource leaks in the remove function 'sdhci_remove_host()' and 'sdhci_pltfm_free()' should be used in place of 'mmc_remove_host()' and 'mmc_free_host()'. This avoids some resource leaks, is more in line with the error handling path of the probe function, and is more consistent with other drivers. Fixes: fb8bd90f83c4 ("mmc: sdhci-sprd: Add Spreadtrum's initial host controller") Signed-off-by: Christophe JAILLET Acked-by: Orson Zhai Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20201217204236.163446-1-christophe.jaillet@wanadoo.fr Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-sprd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index f85171edabeb..5dc36efff47f 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -708,14 +708,14 @@ static int sdhci_sprd_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_sprd_host *sprd_host = TO_SPRD_HOST(host); - struct mmc_host *mmc = host->mmc; - mmc_remove_host(mmc); + sdhci_remove_host(host, 0); + clk_disable_unprepare(sprd_host->clk_sdio); clk_disable_unprepare(sprd_host->clk_enable); clk_disable_unprepare(sprd_host->clk_2x_enable); - mmc_free_host(mmc); + sdhci_pltfm_free(pdev); return 0; } -- cgit v1.2.3 From 6052b3c370fb82dec28bcfff6d7ec0da84ac087a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 17 Dec 2020 22:09:22 +0100 Subject: mmc: usdhi6rol0: Fix a resource leak in the error handling path of the probe A call to 'ausdhi6_dma_release()' to undo a previous call to 'usdhi6_dma_request()' is missing in the error handling path of the probe function. It is already present in the remove function. Fixes: 75fa9ea6e3c0 ("mmc: add a driver for the Renesas usdhi6rol0 SD/SDIO host controller") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/20201217210922.165340-1-christophe.jaillet@wanadoo.fr Signed-off-by: Ulf Hansson --- drivers/mmc/host/usdhi6rol0.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/usdhi6rol0.c b/drivers/mmc/host/usdhi6rol0.c index e2d5112d809d..615f3d008af1 100644 --- a/drivers/mmc/host/usdhi6rol0.c +++ b/drivers/mmc/host/usdhi6rol0.c @@ -1858,10 +1858,12 @@ static int usdhi6_probe(struct platform_device *pdev) ret = mmc_add_host(mmc); if (ret < 0) - goto e_clk_off; + goto e_release_dma; return 0; +e_release_dma: + usdhi6_dma_release(host); e_clk_off: clk_disable_unprepare(host->clk); e_free_mmc: -- cgit v1.2.3 From 5f7dfda4f2cec580c135fd81d96a05006651c128 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Thu, 10 Dec 2020 16:55:10 +0800 Subject: mmc: sdhci-of-dwcmshc: set SDHCI_QUIRK2_PRESET_VALUE_BROKEN The SDHCI_PRESET_FOR_* registers are not set(all read as zeros), so set the quirk. Signed-off-by: Jisheng Zhang Link: https://lore.kernel.org/r/20201210165510.76b917e5@xhacker.debian Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index d90020ed3622..59d8d96ce206 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -112,6 +112,7 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = { static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { .ops = &sdhci_dwcmshc_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }; static int dwcmshc_probe(struct platform_device *pdev) -- cgit v1.2.3 From b79ee3852c364c62e126f1e9031e46ffccaeeaac Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Fri, 11 Dec 2020 01:19:21 +0000 Subject: dt-bindings: mmc: sunxi: Add Allwinner A100 and H616 compatibles Add binding for A100's and H616's mmc and emmc controller. Signed-off-by: Yangtao Li Signed-off-by: Andre Przywara Link: https://lore.kernel.org/r/20201211011934.6171-9-andre.przywara@arm.com Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml b/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml index e82c9a07b6fb..e75b3a8ba816 100644 --- a/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml @@ -26,6 +26,8 @@ properties: - const: allwinner,sun9i-a80-mmc - const: allwinner,sun50i-a64-emmc - const: allwinner,sun50i-a64-mmc + - const: allwinner,sun50i-a100-emmc + - const: allwinner,sun50i-a100-mmc - items: - const: allwinner,sun8i-a83t-mmc - const: allwinner,sun7i-a20-mmc @@ -47,6 +49,12 @@ properties: - items: - const: allwinner,sun50i-h6-mmc - const: allwinner,sun50i-a64-mmc + - items: + - const: allwinner,sun50i-h616-emmc + - const: allwinner,sun50i-a100-emmc + - items: + - const: allwinner,sun50i-h616-mmc + - const: allwinner,sun50i-a100-mmc reg: maxItems: 1 -- cgit v1.2.3 From 3536b82e5853ec302c1803896c126725afb6d57c Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Fri, 11 Dec 2020 01:19:22 +0000 Subject: mmc: sunxi: add support for A100 mmc controller This patch adds support for A100 MMC controller, which use word address for internal dma. Signed-off-by: Yangtao Li Signed-off-by: Andre Przywara Link: https://lore.kernel.org/r/20201211011934.6171-10-andre.przywara@arm.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 6310693f2ac0..e46bb4e404a8 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -245,6 +245,7 @@ struct sunxi_idma_des { struct sunxi_mmc_cfg { u32 idma_des_size_bits; + u32 idma_des_shift; const struct sunxi_mmc_clk_delay *clk_delays; /* does the IP block support autocalibration? */ @@ -344,7 +345,7 @@ static int sunxi_mmc_init_host(struct sunxi_mmc_host *host) /* Enable CEATA support */ mmc_writel(host, REG_FUNS, SDXC_CEATA_ON); /* Set DMA descriptor list base address */ - mmc_writel(host, REG_DLBA, host->sg_dma); + mmc_writel(host, REG_DLBA, host->sg_dma >> host->cfg->idma_des_shift); rval = mmc_readl(host, REG_GCTRL); rval |= SDXC_INTERRUPT_ENABLE_BIT; @@ -374,8 +375,10 @@ static void sunxi_mmc_init_idma_des(struct sunxi_mmc_host *host, next_desc += sizeof(struct sunxi_idma_des); pdes[i].buf_addr_ptr1 = - cpu_to_le32(sg_dma_address(&data->sg[i])); - pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc); + cpu_to_le32(sg_dma_address(&data->sg[i]) >> + host->cfg->idma_des_shift); + pdes[i].buf_addr_ptr2 = cpu_to_le32((u32)next_desc >> + host->cfg->idma_des_shift); } pdes[0].config |= cpu_to_le32(SDXC_IDMAC_DES0_FD); @@ -1179,6 +1182,23 @@ static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = { .needs_new_timings = true, }; +static const struct sunxi_mmc_cfg sun50i_a100_cfg = { + .idma_des_size_bits = 16, + .idma_des_shift = 2, + .clk_delays = NULL, + .can_calibrate = true, + .mask_data0 = true, + .needs_new_timings = true, +}; + +static const struct sunxi_mmc_cfg sun50i_a100_emmc_cfg = { + .idma_des_size_bits = 13, + .idma_des_shift = 2, + .clk_delays = NULL, + .can_calibrate = true, + .needs_new_timings = true, +}; + static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun4i-a10-mmc", .data = &sun4i_a10_cfg }, { .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg }, @@ -1187,6 +1207,8 @@ static const struct of_device_id sunxi_mmc_of_match[] = { { .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg }, { .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg }, { .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg }, + { .compatible = "allwinner,sun50i-a100-mmc", .data = &sun50i_a100_cfg }, + { .compatible = "allwinner,sun50i-a100-emmc", .data = &sun50i_a100_emmc_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match); -- cgit v1.2.3 From a8cd989e1a57dff3994cd113650afb0223c44ec6 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 14 Dec 2020 09:21:14 -0800 Subject: mmc: sdhci-msm: Warn about overclocking SD/MMC As talked about in commit 5e4b7e82d497 ("clk: qcom: gcc-sdm845: Use floor ops for sdcc clks"), most clocks handled by the Qualcomm clock drivers are rounded _up_ by default instead of down. We should make sure SD/MMC clocks are always rounded down in the clock drivers. Let's add a warning in the Qualcomm SDHCI driver to help catch the problem. This would have saved a bunch of time [1]. NOTE: this doesn't actually fix any problems, it just makes it obvious to devs that there is a problem and that should be an indication to fix the clock driver. [1] http://lore.kernel.org/r/20201210102234.1.I096779f219625148900fc984dd0084ed1ba87c7f@changeid Suggested-by: Stephen Boyd Signed-off-by: Douglas Anderson Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20201214092048.v5.1.Iec3430c7d3c2a29262695edef7b82a14aaa567e5@changeid Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 9c7927b03253..4d8dd706007d 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -352,6 +352,7 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); struct mmc_ios curr_ios = host->mmc->ios; struct clk *core_clk = msm_host->bulk_clks[0].clk; + unsigned long achieved_rate; int rc; clock = msm_get_clock_rate_for_bus_mode(host, clock); @@ -362,10 +363,20 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, curr_ios.timing); return; } + + /* + * Qualcomm clock drivers by default round clock _up_ if they can't + * make the requested rate. This is not good for SD. Yell if we + * encounter it. + */ + achieved_rate = clk_get_rate(core_clk); + if (achieved_rate > clock) + pr_warn("%s: Card appears overclocked; req %u Hz, actual %lu Hz\n", + mmc_hostname(host->mmc), clock, achieved_rate); + msm_host->clk_rate = clock; pr_debug("%s: Setting clock at rate %lu at timing %d\n", - mmc_hostname(host->mmc), clk_get_rate(core_clk), - curr_ios.timing); + mmc_hostname(host->mmc), achieved_rate, curr_ios.timing); } /* Platform specific tuning */ -- cgit v1.2.3 From f16c8fd4449efb4441272af6102e55523b15a7ad Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 14 Dec 2020 09:21:15 -0800 Subject: mmc: sdhci-msm: Actually set the actual clock The MSM SDHCI driver always set the "actual_clock" field to 0. It had a comment about it not being needed because we weren't using the standard SDHCI divider mechanism and we'd just fallback to "host->clock". However, it's still better to provide the actual clock. Why? 1. It will make timeout calculations slightly better. On one system I have, the eMMC requets 200 MHz (for HS400-ES) but actually gets 192 MHz. These are close, but why not get the more accurate one. 2. If things are seriously off in the clock driver and it's missing rates or picking the wrong rate (maybe it's rounding up instead of down), this will make it much more obvious what's going on. NOTE: we have to be a little careful here because the "actual_clock" field shouldn't include the multiplier that sdhci-msm needs internally. Suggested-by: Adrian Hunter Signed-off-by: Douglas Anderson Reviewed-by: Bjorn Andersson Acked-by: Adrian Hunter Reviewed-by: Veerabhadrarao Badiganti Link: https://lore.kernel.org/r/20201214092048.v5.2.I7564620993acd4baa63fa0e3925ca879a86d3ee3@changeid Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4d8dd706007d..97902616a695 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -327,8 +327,7 @@ static void sdhci_msm_v5_variant_writel_relaxed(u32 val, writel_relaxed(val, host->ioaddr + offset); } -static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, - unsigned int clock) +static unsigned int msm_get_clock_mult_for_bus_mode(struct sdhci_host *host) { struct mmc_ios ios = host->mmc->ios; /* @@ -341,8 +340,8 @@ static unsigned int msm_get_clock_rate_for_bus_mode(struct sdhci_host *host, ios.timing == MMC_TIMING_MMC_DDR52 || ios.timing == MMC_TIMING_MMC_HS400 || host->flags & SDHCI_HS400_TUNING) - clock *= 2; - return clock; + return 2; + return 1; } static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, @@ -353,14 +352,16 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, struct mmc_ios curr_ios = host->mmc->ios; struct clk *core_clk = msm_host->bulk_clks[0].clk; unsigned long achieved_rate; + unsigned int desired_rate; + unsigned int mult; int rc; - clock = msm_get_clock_rate_for_bus_mode(host, clock); - rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), clock); + mult = msm_get_clock_mult_for_bus_mode(host); + desired_rate = clock * mult; + rc = dev_pm_opp_set_rate(mmc_dev(host->mmc), desired_rate); if (rc) { pr_err("%s: Failed to set clock at rate %u at timing %d\n", - mmc_hostname(host->mmc), clock, - curr_ios.timing); + mmc_hostname(host->mmc), desired_rate, curr_ios.timing); return; } @@ -370,11 +371,14 @@ static void msm_set_clock_rate_for_bus_mode(struct sdhci_host *host, * encounter it. */ achieved_rate = clk_get_rate(core_clk); - if (achieved_rate > clock) + if (achieved_rate > desired_rate) pr_warn("%s: Card appears overclocked; req %u Hz, actual %lu Hz\n", - mmc_hostname(host->mmc), clock, achieved_rate); + mmc_hostname(host->mmc), desired_rate, achieved_rate); + host->mmc->actual_clock = achieved_rate / mult; + + /* Stash the rate we requested to use in sdhci_msm_runtime_resume() */ + msm_host->clk_rate = desired_rate; - msm_host->clk_rate = clock; pr_debug("%s: Setting clock at rate %lu at timing %d\n", mmc_hostname(host->mmc), achieved_rate, curr_ios.timing); } @@ -1755,13 +1759,6 @@ static unsigned int sdhci_msm_get_min_clock(struct sdhci_host *host) static void __sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) { u16 clk; - /* - * Keep actual_clock as zero - - * - since there is no divider used so no need of having actual_clock. - * - MSM controller uses SDCLK for data timeout calculation. If - * actual_clock is zero, host->clock is taken for calculation. - */ - host->mmc->actual_clock = 0; sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); @@ -1784,7 +1781,7 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock) struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); if (!clock) { - msm_host->clk_rate = clock; + host->mmc->actual_clock = msm_host->clk_rate = 0; goto out; } -- cgit v1.2.3 From d7aefb2887601cf1fc3f86f55d43b2c9aece5e8f Mon Sep 17 00:00:00 2001 From: Takeshi Saito Date: Wed, 16 Dec 2020 19:29:31 +0900 Subject: mmc: renesas_sdhi_internal_dmac: Fix DMA buffer alignment from 8 to 128-bytes According to the latest datasheet, the internal DMAC buffer alignment R-Car Gen3 SDHI HW should be 128-bytes. So, fix it. Signed-off-by: Takeshi Saito [shimoda: revise commit description, rebase] Fixes: 2a68ea7896e3 ("mmc: renesas-sdhi: add support for R-Car Gen3 SDHI DMAC") Signed-off-by: Yoshihiro Shimoda Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Link: https://lore.kernel.org/r/1608114572-1892-2-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index fe13e1ea22dc..f3e76d6b3e3f 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -186,8 +186,8 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, mmc_get_dma_dir(data))) goto force_pio; - /* This DMAC cannot handle if buffer is not 8-bytes alignment */ - if (!IS_ALIGNED(sg_dma_address(sg), 8)) + /* This DMAC cannot handle if buffer is not 128-bytes alignment */ + if (!IS_ALIGNED(sg_dma_address(sg), 128)) goto force_pio_with_unmap; if (data->flags & MMC_DATA_READ) { -- cgit v1.2.3 From 69e7d76afdb54243df957351804c0f1afca46d0f Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 16 Dec 2020 19:29:32 +0900 Subject: mmc: renesas_internal_dmac: add pre_req and post_req support Add pre_req and post_req support to improve performance. Inspired by a patch in the BSP by Masaharu Hayakawa. Signed-off-by: Yoshihiro Shimoda Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Link: https://lore.kernel.org/r/1608114572-1892-3-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 91 ++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index f3e76d6b3e3f..ff97f15e317c 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -56,6 +56,12 @@ #define INFO2_DTRANERR1 BIT(17) #define INFO2_DTRANERR0 BIT(16) +enum renesas_sdhi_dma_cookie { + COOKIE_UNMAPPED, + COOKIE_PRE_MAPPED, + COOKIE_MAPPED, +}; + /* * Specification of this driver: * - host->chan_{rx,tx} will be used as a flag of enabling/disabling the dma @@ -172,6 +178,50 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host) { tasklet_schedule(&priv->dma_priv.dma_complete); } +/* + * renesas_sdhi_internal_dmac_map() will be called with two difference + * sg pointers in two mmc_data by .pre_req(), but tmio host can have a single + * sg_ptr only. So, renesas_sdhi_internal_dmac_{un}map() should use a sg + * pointer in a mmc_data instead of host->sg_ptr. + */ +static void +renesas_sdhi_internal_dmac_unmap(struct tmio_mmc_host *host, + struct mmc_data *data, + enum renesas_sdhi_dma_cookie cookie) +{ + bool unmap = cookie == COOKIE_UNMAPPED ? (data->host_cookie != cookie) : + (data->host_cookie == cookie); + + if (unmap) { + dma_unmap_sg(&host->pdev->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data)); + data->host_cookie = COOKIE_UNMAPPED; + } +} + +static bool +renesas_sdhi_internal_dmac_map(struct tmio_mmc_host *host, + struct mmc_data *data, + enum renesas_sdhi_dma_cookie cookie) +{ + if (data->host_cookie == COOKIE_PRE_MAPPED) + return true; + + if (!dma_map_sg(&host->pdev->dev, data->sg, data->sg_len, + mmc_get_dma_dir(data))) + return false; + + data->host_cookie = cookie; + + /* This DMAC cannot handle if buffer is not 128-bytes alignment */ + if (!IS_ALIGNED(sg_dma_address(data->sg), 128)) { + renesas_sdhi_internal_dmac_unmap(host, data, cookie); + return false; + } + + return true; +} + static void renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, struct mmc_data *data) @@ -182,14 +232,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, if (!test_bit(SDHI_INTERNAL_DMAC_ADDR_MODE_FIXED_ONLY, &global_flags)) dtran_mode |= DTRAN_MODE_ADDR_MODE; - if (!dma_map_sg(&host->pdev->dev, sg, host->sg_len, - mmc_get_dma_dir(data))) + if (!renesas_sdhi_internal_dmac_map(host, data, COOKIE_MAPPED)) goto force_pio; - /* This DMAC cannot handle if buffer is not 128-bytes alignment */ - if (!IS_ALIGNED(sg_dma_address(sg), 128)) - goto force_pio_with_unmap; - if (data->flags & MMC_DATA_READ) { dtran_mode |= DTRAN_MODE_CH_NUM_CH1; if (test_bit(SDHI_INTERNAL_DMAC_ONE_RX_ONLY, &global_flags) && @@ -212,7 +257,7 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, return; force_pio_with_unmap: - dma_unmap_sg(&host->pdev->dev, sg, host->sg_len, mmc_get_dma_dir(data)); + renesas_sdhi_internal_dmac_unmap(host, data, COOKIE_UNMAPPED); force_pio: renesas_sdhi_internal_dmac_enable_dma(host, false); @@ -245,7 +290,7 @@ static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host) dir = DMA_TO_DEVICE; renesas_sdhi_internal_dmac_enable_dma(host, false); - dma_unmap_sg(&host->pdev->dev, host->sg_ptr, host->sg_len, dir); + renesas_sdhi_internal_dmac_unmap(host, host->data, COOKIE_MAPPED); if (dir == DMA_FROM_DEVICE) clear_bit(SDHI_INTERNAL_DMAC_RX_IN_USE, &global_flags); @@ -274,6 +319,32 @@ static void renesas_sdhi_internal_dmac_end_dma(struct tmio_mmc_host *host) renesas_sdhi_internal_dmac_complete(host); } +static void renesas_sdhi_internal_dmac_post_req(struct mmc_host *mmc, + struct mmc_request *mrq, + int err) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data) + return; + + renesas_sdhi_internal_dmac_unmap(host, data, COOKIE_UNMAPPED); +} + +static void renesas_sdhi_internal_dmac_pre_req(struct mmc_host *mmc, + struct mmc_request *mrq) +{ + struct tmio_mmc_host *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + + if (!data) + return; + + data->host_cookie = COOKIE_UNMAPPED; + renesas_sdhi_internal_dmac_map(host, data, COOKIE_PRE_MAPPED); +} + static void renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdata) @@ -295,6 +366,10 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host, tasklet_init(&host->dma_issue, renesas_sdhi_internal_dmac_issue_tasklet_fn, (unsigned long)host); + + /* Add pre_req and post_req */ + host->ops.pre_req = renesas_sdhi_internal_dmac_pre_req; + host->ops.post_req = renesas_sdhi_internal_dmac_post_req; } static void -- cgit v1.2.3 From 0354ca6edd464a2cf332f390581977b8699ed081 Mon Sep 17 00:00:00 2001 From: Chaotian Jing Date: Fri, 18 Dec 2020 15:16:11 +0800 Subject: mmc: mediatek: fix race condition between msdc_request_timeout and irq when get request SW timeout, if CMD/DAT xfer done irq coming right now, then there is race between the msdc_request_timeout work and irq handler, and the host->cmd and host->data may set to NULL in irq handler. also, current flow ensure that only one path can go to msdc_request_done(), so no need check the return value of cancel_delayed_work(). Signed-off-by: Chaotian Jing Link: https://lore.kernel.org/r/20201218071611.12276-1-chaotian.jing@mediatek.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index de09c6347524..898ed1b023df 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -1127,13 +1127,13 @@ static void msdc_track_cmd_data(struct msdc_host *host, static void msdc_request_done(struct msdc_host *host, struct mmc_request *mrq) { unsigned long flags; - bool ret; - ret = cancel_delayed_work(&host->req_timeout); - if (!ret) { - /* delay work already running */ - return; - } + /* + * No need check the return value of cancel_delayed_work, as only ONE + * path will go here! + */ + cancel_delayed_work(&host->req_timeout); + spin_lock_irqsave(&host->lock, flags); host->mrq = NULL; spin_unlock_irqrestore(&host->lock, flags); @@ -1155,7 +1155,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, bool done = false; bool sbc_error; unsigned long flags; - u32 *rsp = cmd->resp; + u32 *rsp; if (mrq->sbc && cmd == mrq->cmd && (events & (MSDC_INT_ACMDRDY | MSDC_INT_ACMDCRCERR @@ -1176,6 +1176,7 @@ static bool msdc_cmd_done(struct msdc_host *host, int events, if (done) return true; + rsp = cmd->resp; sdr_clr_bits(host->base + MSDC_INTEN, cmd_ints_mask); @@ -1363,7 +1364,7 @@ static void msdc_data_xfer_next(struct msdc_host *host, static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, struct mmc_request *mrq, struct mmc_data *data) { - struct mmc_command *stop = data->stop; + struct mmc_command *stop; unsigned long flags; bool done; unsigned int check_data = events & @@ -1379,6 +1380,7 @@ static bool msdc_data_xfer_done(struct msdc_host *host, u32 events, if (done) return true; + stop = data->stop; if (check_data || (stop && stop->error)) { dev_dbg(host->dev, "DMA status: 0x%8X\n", -- cgit v1.2.3 From e085b51c74cc99334c290f7c67d039c95f788239 Mon Sep 17 00:00:00 2001 From: Dmitry Lebed Date: Thu, 17 Dec 2020 23:53:12 -0800 Subject: mmc: meson-gx: check for scatterlist size alignment in block mode Enable SGDMA support for SD_IO_RW_EXTENDED and add proper check for scatterlist size alignment in block mode. According to documentation, in SDIO block mode meson-gx DMA could only handle buffers with sizes that are multiples of SDIO block size. Some SDIO drivers like brcmfmac use scatterlist API, but do not enforce proper scatterlist buffer size alignemnt, this looks like a root cause of non-working CMD53. Some minor style fixes. Signed-off-by: Dmitry Lebed Reviewed-by: Jerome Brunet Link: https://lore.kernel.org/r/20201218075312.67338-1-lebed.dmitry@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-gx-mmc.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 13f6a2c0ed04..eb6c02bc4a02 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -227,7 +227,6 @@ static void meson_mmc_get_transfer_mode(struct mmc_host *mmc, struct mmc_data *data = mrq->data; struct scatterlist *sg; int i; - bool use_desc_chain_mode = true; /* * When Controller DMA cannot directly access DDR memory, disable @@ -237,25 +236,33 @@ static void meson_mmc_get_transfer_mode(struct mmc_host *mmc, if (host->dram_access_quirk) return; - /* - * Broken SDIO with AP6255-based WiFi on Khadas VIM Pro has been - * reported. For some strange reason this occurs in descriptor - * chain mode only. So let's fall back to bounce buffer mode - * for command SD_IO_RW_EXTENDED. - */ - if (mrq->cmd->opcode == SD_IO_RW_EXTENDED) - return; + if (data->blocks > 1) { + /* + * In block mode DMA descriptor format, "length" field indicates + * number of blocks and there is no way to pass DMA size that + * is not multiple of SDIO block size, making it impossible to + * tie more than one memory buffer with single SDIO block. + * Block mode sg buffer size should be aligned with SDIO block + * size, otherwise chain mode could not be used. + */ + for_each_sg(data->sg, sg, data->sg_len, i) { + if (sg->length % data->blksz) { + WARN_ONCE(1, "unaligned sg len %u blksize %u\n", + sg->length, data->blksz); + return; + } + } + } - for_each_sg(data->sg, sg, data->sg_len, i) + for_each_sg(data->sg, sg, data->sg_len, i) { /* check for 8 byte alignment */ - if (sg->offset & 7) { + if (sg->offset % 8) { WARN_ONCE(1, "unaligned scatterlist buffer\n"); - use_desc_chain_mode = false; - break; + return; } + } - if (use_desc_chain_mode) - data->host_cookie |= SD_EMMC_DESC_CHAIN_MODE; + data->host_cookie |= SD_EMMC_DESC_CHAIN_MODE; } static inline bool meson_mmc_desc_chain_mode(const struct mmc_data *data) -- cgit v1.2.3 From 35cdcd1268f654f55e2308767349c44bd25d8009 Mon Sep 17 00:00:00 2001 From: Masaharu Hayakawa Date: Wed, 23 Dec 2020 16:30:21 +0900 Subject: mmc: tmio: Add data timeout error detection Currently, busy timeout is not checked for data transfer command. But, if the temperature condition changes, the data cannot be acquired correctly and timeout may occur. Also, we could reproduce an issue by using mmc_test driver (e.g. "Correct xfer_size at write (start failure)"). Therefore, this adds timeout error check. Signed-off-by: Masaharu Hayakawa [saito: rework commit message.] Signed-off-by: Takeshi Saito [shimoda: rebase, add commit description] Signed-off-by: Yoshihiro Shimoda Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Link: https://lore.kernel.org/r/1608708622-29668-2-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 942b8375179c..21492b56b253 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -477,8 +477,10 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host, unsigned int stat) if (!data) goto out; - if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR || - stat & TMIO_STAT_TXUNDERRUN) + if (stat & TMIO_STAT_DATATIMEOUT) + data->error = -ETIMEDOUT; + else if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR || + stat & TMIO_STAT_TXUNDERRUN) data->error = -EILSEQ; if (host->dma_on && (data->flags & MMC_DATA_WRITE)) { u32 status = sd_ctrl_read16_and_16_as_32(host, CTL_STATUS); -- cgit v1.2.3 From ed2fab9a8229cc70fe03032e48d0ec375df6013e Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 23 Dec 2020 16:30:22 +0900 Subject: mmc: renesas_sdhi: Add a condition of cmd/data timeout for retune According to the datasheet, this controller needs retune when cmd or data timeout happens. So, add a condition into .check_retune(). Signed-off-by: Yoshihiro Shimoda Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/1608708622-29668-3-git-send-email-yoshihiro.shimoda.uh@renesas.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 16 +++++++++++++--- drivers/mmc/host/tmio_mmc.h | 2 +- drivers/mmc/host/tmio_mmc_core.c | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 38f028e70633..158c21e5a942 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -768,10 +768,12 @@ static bool renesas_sdhi_auto_correction(struct tmio_mmc_host *host) return false; } -static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host) +static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host, + struct mmc_request *mrq) { struct renesas_sdhi *priv = host_to_priv(host); bool use_4tap = priv->quirks && priv->quirks->hs400_4taps; + bool ret = false; /* * Skip checking SCC errors when running on 4 taps in HS400 mode as @@ -785,11 +787,19 @@ static bool renesas_sdhi_check_scc_error(struct tmio_mmc_host *host) if (mmc_doing_tune(host->mmc)) return false; + if (((mrq->cmd->error == -ETIMEDOUT) || + (mrq->data && mrq->data->error == -ETIMEDOUT)) && + ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || + (host->ops.get_cd && host->ops.get_cd(host->mmc)))) + ret |= true; + if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) & SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN) - return renesas_sdhi_auto_correction(host); + ret |= renesas_sdhi_auto_correction(host); + else + ret |= renesas_sdhi_manual_correction(host, use_4tap); - return renesas_sdhi_manual_correction(host, use_4tap); + return ret; } static int renesas_sdhi_wait_idle(struct tmio_mmc_host *host, u32 bit) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 784fa6ed5843..2d1db9396d4a 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -181,7 +181,7 @@ struct tmio_mmc_host { unsigned int direction, int blk_size); int (*write16_hook)(struct tmio_mmc_host *host, int addr); void (*reset)(struct tmio_mmc_host *host); - bool (*check_retune)(struct tmio_mmc_host *host); + bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq); void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq); unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host); diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 21492b56b253..49c2d406c48e 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -804,7 +804,7 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host) } /* Error means retune, but executed command was still successful */ - if (host->check_retune && host->check_retune(host)) + if (host->check_retune && host->check_retune(host, mrq)) mmc_retune_needed(host->mmc); /* If SET_BLOCK_COUNT, continue with main command */ -- cgit v1.2.3 From c7129b703f0431aa68cad69f7c8c506a0c98030f Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Wed, 23 Dec 2020 10:01:50 +0100 Subject: mmc: jz4740: remove unused struct component card_detect_irq I have not found any user for this struct component. Signed-off-by: H. Nikolaus Schaller Reviewed-by: Paul Cercueil Link: https://lore.kernel.org/r/415ea1cb85eedc12f0c2f70da9a1ae3885bdd091.1608714110.git.hns@goldelico.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index a1f92fed2a55..b3c636edbb46 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -152,7 +152,6 @@ struct jz4740_mmc_host { enum jz4740_mmc_version version; int irq; - int card_detect_irq; void __iomem *base; struct resource *mem_res; -- cgit v1.2.3 From 67a73230fbaed9ff4902389d4f040abe57c5783c Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Wed, 23 Dec 2020 10:01:51 +0100 Subject: mmc: omap: remove unused struct component card_detect_irq I have not found any user for this struct component. Signed-off-by: H. Nikolaus Schaller Link: https://lore.kernel.org/r/b6f2168b863e4273c6bca5a22fbd4a3a8ddf68d6.1608714110.git.hns@goldelico.com Signed-off-by: Ulf Hansson --- include/linux/platform_data/mmc-omap.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h index f0b8947e6b07..91051e9907f3 100644 --- a/include/linux/platform_data/mmc-omap.h +++ b/include/linux/platform_data/mmc-omap.h @@ -108,8 +108,7 @@ struct omap_mmc_platform_data { const char *name; u32 ocr_mask; - /* Card detection IRQs */ - int card_detect_irq; + /* Card detection */ int (*card_detect)(struct device *dev, int slot); unsigned int ban_openended:1; -- cgit v1.2.3 From a5ca4c32121297e2306438ef0b2c08f98bafa3f3 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sun, 27 Dec 2020 18:41:55 +0100 Subject: dt-bindings: mmc: renesas,sdhi: Add r8a779a0 support Signed-off-by: Wolfram Sang Acked-by: Rob Herring Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20201227174202.40834-2-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index 6bbf29b5c239..5908a717d2e8 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -59,6 +59,7 @@ properties: - renesas,sdhi-r8a77980 # R-Car V3H - renesas,sdhi-r8a77990 # R-Car E3 - renesas,sdhi-r8a77995 # R-Car D3 + - renesas,sdhi-r8a779a0 # R-Car V3U - const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2 reg: -- cgit v1.2.3 From 8b2805647acf3da1f0a2e5c37a103c6853052730 Mon Sep 17 00:00:00 2001 From: Ricky Wu Date: Wed, 30 Dec 2020 17:04:01 +0800 Subject: mmc: rtsx: Add MMC_CAP2_NO_SDIO flag Added flag MMC_CAP2_NO_SDIO to mmc->caps2 Card Reader not support SDIO Signed-off-by: Ricky Wu Link: https://lore.kernel.org/r/20201230090401.12627-2-ricky_wu@realtek.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index e6f5bbce5685..446efbb9e099 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1425,7 +1425,8 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host) MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; if (pcr->rtd3_en) mmc->caps = mmc->caps | MMC_CAP_AGGRESSIVE_PM; - 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; mmc->ops = &realtek_pci_sdmmc_ops; -- cgit v1.2.3 From 15f908fafc0e43899e92528e044511a7b3d4aeb3 Mon Sep 17 00:00:00 2001 From: Renius Chen Date: Wed, 6 Jan 2021 16:53:32 +0800 Subject: mmc: sdhci-pci-gli: Add a switch to enable/disable CQHCI support Add a vendor-specific bit named GLI_9763E_MB_ERP_ON at the bit7 of register 888h to decide whether to enhance random R/W performance of GL9763E. CQHCI support will be enabled if and only if the bit is set and the GLI_9763E_MB_CMQ_OFF bit is not set. Signed-off-by: Renius Chen Link: https://lore.kernel.org/r/20210106085332.5494-1-reniuschengl@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-gli.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c index b85edd62e7f0..c6a107d7c742 100644 --- a/drivers/mmc/host/sdhci-pci-gli.c +++ b/drivers/mmc/host/sdhci-pci-gli.c @@ -84,6 +84,7 @@ #define GLI_9763E_VHS_REV_W 0x2 #define PCIE_GLI_9763E_MB 0x888 #define GLI_9763E_MB_CMDQ_OFF BIT(19) +#define GLI_9763E_MB_ERP_ON BIT(7) #define PCIE_GLI_9763E_SCR 0x8E0 #define GLI_9763E_SCR_AXI_REQ BIT(9) @@ -814,7 +815,8 @@ static int gli_probe_slot_gl9763e(struct sdhci_pci_slot *slot) pci_read_config_dword(pdev, PCIE_GLI_9763E_MB, &value); if (!(value & GLI_9763E_MB_CMDQ_OFF)) - host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; + if (value & GLI_9763E_MB_ERP_ON) + host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; gli_pcie_enable_msi(slot); host->mmc_host_ops.hs400_enhanced_strobe = -- cgit v1.2.3 From 9476e27bb5ce224c37b5d614336f3bec8dc3a956 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 7 Jan 2021 20:01:17 +0530 Subject: dt-bindings: mmc: sdhci-msm: Document the SDX55 compatible The SDHCI controller on SDX55 is based on MSM SDHCI v5 IP. Hence, document the compatible with "qcom,sdhci-msm-v5" as the fallback. While at it, let's also sort the compatibles in ascending order. Cc: Ulf Hansson Cc: linux-mmc@vger.kernel.org Signed-off-by: Manivannan Sadhasivam Reviewed-by: Vinod Koul Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210107143118.2386-1-manivannan.sadhasivam@linaro.org Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-msm.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index 3b602fd6180b..9fa8a24fbc97 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -17,10 +17,11 @@ Required properties: "qcom,msm8916-sdhci", "qcom,sdhci-msm-v4" "qcom,msm8992-sdhci", "qcom,sdhci-msm-v4" "qcom,msm8996-sdhci", "qcom,sdhci-msm-v4" - "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5" - "qcom,sdm845-sdhci", "qcom,sdhci-msm-v5" "qcom,qcs404-sdhci", "qcom,sdhci-msm-v5" "qcom,sc7180-sdhci", "qcom,sdhci-msm-v5"; + "qcom,sdm845-sdhci", "qcom,sdhci-msm-v5" + "qcom,sdx55-sdhci", "qcom,sdhci-msm-v5"; + "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5" NOTE that some old device tree files may be floating around that only have the string "qcom,sdhci-msm-v4" without the SoC compatible string but doing that should be considered a deprecated practice. -- cgit v1.2.3 From 973c7c994b720ec5db66d968bb27fbc716ac027e Mon Sep 17 00:00:00 2001 From: Muhammad Husaini Zulkifli Date: Mon, 11 Jan 2021 23:50:00 +0800 Subject: mmc: sdhci-of-arasan: Fixed kernel-doc format warning Commit 36c6aadaae86 ("mmc: sdhci-of-arasan: Add support for Intel Keem Bay") introduces syscon map sdhci_arasan_update_support64b(). Unfortunately, kernel-doc format script tool has not been run before and causing warning on the documentation style. Below is the output when running the kernel-doc script tool: drivers/mmc/host/sdhci-of-arasan.c:1388: warning: contents before sections Let's fix this by using standard conventions kernel-doc format. Fixes: 36c6aadaae86 ("mmc: sdhci-of-arasan: Add support for Intel Keem Bay") Signed-off-by: Muhammad Husaini Zulkifli Acked-by: Andy Shevchenko Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20210111155001.12307-2-muhammad.husaini.zulkifli@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 4f3774bcda94..643313687b5e 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1380,17 +1380,16 @@ static void sdhci_arasan_unregister_sdclk(struct device *dev) /** * sdhci_arasan_update_support64b - Set SUPPORT_64B (64-bit System Bus Support) + * @host: The sdhci_host + * @value: The value to write * * This should be set based on the System Address Bus. * 0: the Core supports only 32-bit System Address Bus. * 1: the Core supports 64-bit System Address Bus. * - * NOTES: - * - For Keem Bay, it is required to clear this bit. Its default value is 1'b1. - * Keem Bay does not support 64-bit access. - * - * @host: The sdhci_host - * @value: The value to write + * NOTE: + * For Keem Bay, it is required to clear this bit. Its default value is 1'b1. + * Keem Bay does not support 64-bit access. */ static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value) { -- cgit v1.2.3 From db84509359bb8827cc923f42940496fa2f906157 Mon Sep 17 00:00:00 2001 From: Muhammad Husaini Zulkifli Date: Mon, 11 Jan 2021 23:50:01 +0800 Subject: mmc: sdhci-of-arasan: Change code style of mapping the syscon field Change code style of mapping the syscon in sdhci_arasan_update_support64b() for readability and consistency. Signed-off-by: Muhammad Husaini Zulkifli Acked-by: Andy Shevchenko Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20210111155001.12307-3-muhammad.husaini.zulkifli@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 643313687b5e..8a406b62c6ad 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1395,10 +1395,10 @@ static void sdhci_arasan_update_support64b(struct sdhci_host *host, u32 value) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); - const struct sdhci_arasan_soc_ctl_map *soc_ctl_map = - sdhci_arasan->soc_ctl_map; + const struct sdhci_arasan_soc_ctl_map *soc_ctl_map; /* Having a map is optional */ + soc_ctl_map = sdhci_arasan->soc_ctl_map; if (!soc_ctl_map) return; -- cgit v1.2.3 From 783be176eb2775447b6f6254ecf2e3499334941a Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Tue, 12 Jan 2021 11:46:52 +0200 Subject: dt-bindings: mmc: xenon: add AP807 compatible string This patch adds DT binding description for the Xenon SD/MMC controller included in AP807 north bridge. Signed-off-by: Marcin Wojtas Signed-off-by: Konstantin Porotchkin Link: https://lore.kernel.org/r/20210112094655.12274-2-kostap@marvell.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.txt b/Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.txt index ed1456f5c94d..c51a62d751dc 100644 --- a/Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.txt +++ b/Documentation/devicetree/bindings/mmc/marvell,xenon-sdhci.txt @@ -12,6 +12,7 @@ Required Properties: - "marvell,armada-3700-sdhci": For controllers on Armada-3700 SoC. Must provide a second register area and marvell,pad-type. - "marvell,armada-ap806-sdhci": For controllers on Armada AP806. + - "marvell,armada-ap807-sdhci": For controllers on Armada AP807. - "marvell,armada-cp110-sdhci": For controllers on Armada CP110. - clocks: -- cgit v1.2.3 From 7214d79237df4d4e3b40e5c1763698e21393da16 Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Tue, 12 Jan 2021 11:46:53 +0200 Subject: mmc: xenon: add AP807 compatible string This patch adds new compatible string to differentiate between the controllers versions included in the AP807 north bridge. Thanks to this change, the SoCs which use AP807 will not run unintentionally in the 'slow mode' with disabled UHS modes. Signed-off-by: Marcin Wojtas Signed-off-by: Konstantin Porotchkin Link: https://lore.kernel.org/r/20210112094655.12274-3-kostap@marvell.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-xenon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c index d19eef5f725f..666cee4c7f7c 100644 --- a/drivers/mmc/host/sdhci-xenon.c +++ b/drivers/mmc/host/sdhci-xenon.c @@ -689,6 +689,7 @@ static const struct dev_pm_ops sdhci_xenon_dev_pm_ops = { static const struct of_device_id sdhci_xenon_dt_ids[] = { { .compatible = "marvell,armada-ap806-sdhci", .data = (void *)XENON_AP806}, + { .compatible = "marvell,armada-ap807-sdhci", .data = (void *)XENON_AP807}, { .compatible = "marvell,armada-cp110-sdhci", .data = (void *)XENON_CP110}, { .compatible = "marvell,armada-3700-sdhci", .data = (void *)XENON_A3700}, {} -- cgit v1.2.3 From 3882917da8c3a0ce67b052d94e7f591bcb5275a0 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 12 Jan 2021 23:24:21 -0600 Subject: mmc: sunxi-mmc: Ensure host is suspended during system sleep If the device suspend process begins before the mmc host's autosuspend timeout, the host will continue running during system sleep. Avoid this by forcing runtime suspend during a global suspend transition. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20210113052421.36553-1-samuel@sholland.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sunxi-mmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index e46bb4e404a8..2702736a1c57 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -1529,6 +1529,8 @@ static int sunxi_mmc_runtime_suspend(struct device *dev) #endif static const struct dev_pm_ops sunxi_mmc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, sunxi_mmc_runtime_resume, NULL) -- cgit v1.2.3 From 12b1c5edc3a96824d4e2aaf882b5aaa6f2cb4b3b Mon Sep 17 00:00:00 2001 From: Ricky Wu Date: Wed, 13 Jan 2021 18:57:23 +0800 Subject: mmc: rtsx: add delay before power on To make sure voltage is below 0.5V before power on during a power cycle, add a delay of at least 100ms. The delays in the mmc core is not sufficient for the rtsx pci device. Signed-off-by: Ricky Wu Link: https://lore.kernel.org/r/20210113105723.7916-1-ricky_wu@realtek.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 446efbb9e099..4ca937415734 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -906,6 +906,8 @@ static int sd_power_on(struct realtek_pci_sdmmc *host) if (host->power_state == SDMMC_POWER_ON) return 0; + msleep(100); + rtsx_pci_init_cmd(pcr); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE, -- cgit v1.2.3 From 81aa9876f9439287eab4ddc6b70377d3774cb2e6 Mon Sep 17 00:00:00 2001 From: Aswath Govindraju Date: Wed, 13 Jan 2021 17:29:07 +0530 Subject: dt-bindings: mmc: sdhci-am654: Add compatible string for AM64 SoC Add compatible string for AM64 SoC in device tree binding of AM654 SDHCI module as the same IP is used. Signed-off-by: Aswath Govindraju Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210113115908.3882-2-a-govindraju@ti.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-am654.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml index 1ae945434c53..34e53db29428 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml @@ -21,6 +21,8 @@ properties: - ti,j721e-sdhci-4bit - ti,j7200-sdhci-8bit - ti,j721e-sdhci-4bit + - ti,am64-sdhci-8bit + - ti,am64-sdhci-4bit reg: maxItems: 2 -- cgit v1.2.3 From 754b7f2f7d2acda729619c6cfed9bc8124ca17cb Mon Sep 17 00:00:00 2001 From: Faiz Abbas Date: Wed, 13 Jan 2021 17:29:08 +0530 Subject: mmc: sdhci_am654: Add Support for TI's AM64 SoC Add support for the controller present on the AM64x SoC. There are instances: sdhci0: 8bit bus width, max 400 MBps sdhci1: 4bit bus width, max 100 MBps Signed-off-by: Faiz Abbas Signed-off-by: Vignesh Raghavendra Signed-off-by: Kishon Vijay Abraham I Signed-off-by: Aswath Govindraju Link: https://lore.kernel.org/r/20210113115908.3882-3-a-govindraju@ti.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index a64ea143d185..7a34649b0754 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -514,6 +514,26 @@ static const struct sdhci_am654_driver_data sdhci_j721e_4bit_drvdata = { .flags = IOMUX_PRESENT, }; +static const struct sdhci_pltfm_data sdhci_am64_8bit_pdata = { + .ops = &sdhci_j721e_8bit_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + +static const struct sdhci_am654_driver_data sdhci_am64_8bit_drvdata = { + .pdata = &sdhci_am64_8bit_pdata, + .flags = DLL_PRESENT | DLL_CALIB, +}; + +static const struct sdhci_pltfm_data sdhci_am64_4bit_pdata = { + .ops = &sdhci_j721e_4bit_ops, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + +static const struct sdhci_am654_driver_data sdhci_am64_4bit_drvdata = { + .pdata = &sdhci_am64_4bit_pdata, + .flags = IOMUX_PRESENT, +}; + static const struct soc_device_attribute sdhci_am654_devices[] = { { .family = "AM65X", .revision = "SR1.0", @@ -737,6 +757,14 @@ static const struct of_device_id sdhci_am654_of_match[] = { .compatible = "ti,j721e-sdhci-4bit", .data = &sdhci_j721e_4bit_drvdata, }, + { + .compatible = "ti,am64-sdhci-8bit", + .data = &sdhci_am64_8bit_drvdata, + }, + { + .compatible = "ti,am64-sdhci-4bit", + .data = &sdhci_am64_4bit_drvdata, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sdhci_am654_of_match); -- cgit v1.2.3 From 1c148853e8eb433ec88cf0b9482376ec7814b599 Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Wed, 13 Jan 2021 19:34:31 +0800 Subject: mmc: core: remove needless err = 0 in mmc_init_card() Since they will always being in successful path to return 0 directly, no need to set err = 0. Signed-off-by: Yue Hu Link: https://lore.kernel.org/r/20210113113431.522-1-zbestahu@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index ff3063ce2acd..54ab1c7405e5 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1697,7 +1697,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, goto free_card; if (err) { - err = 0; /* * Just disable enhanced area off & sz * will try to enable ERASE_GROUP_DEF @@ -1802,7 +1801,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, pr_warn("%s: Enabling HPI failed\n", mmc_hostname(card->host)); card->ext_csd.hpi_en = 0; - err = 0; } else { card->ext_csd.hpi_en = 1; } @@ -1831,7 +1829,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, pr_warn("%s: Cache is supported, but failed to turn on (%d)\n", mmc_hostname(card->host), err); card->ext_csd.cache_ctrl = 0; - err = 0; } else { card->ext_csd.cache_ctrl = 1; } @@ -1851,7 +1848,6 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr, mmc_hostname(card->host)); card->ext_csd.cmdq_support = false; card->ext_csd.cmdq_depth = 0; - err = 0; } } /* -- cgit v1.2.3 From 783667ccb5b57cc81af2646b46f56ada05f0f0ae Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Fri, 15 Jan 2021 11:45:06 +0800 Subject: mmc: core: remove redundant card null check to mmc_can_sleep() Note that only _mmc_suspend() will call mmc_can_sleep(). And card is checked before in mmc_can_poweroff_notify(). Signed-off-by: Yue Hu Link: https://lore.kernel.org/r/20210115034506.646-1-zbestahu@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 54ab1c7405e5..0d80b72ddde8 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1895,7 +1895,7 @@ err: static int mmc_can_sleep(struct mmc_card *card) { - return (card && card->ext_csd.rev >= 3); + return card->ext_csd.rev >= 3; } static int mmc_sleep(struct mmc_host *host) -- cgit v1.2.3 From edee82f79bab6c109c07d6a99cb564cc8fd1d0cf Mon Sep 17 00:00:00 2001 From: Renius Chen Date: Fri, 15 Jan 2021 13:47:36 +0800 Subject: mmc: sdhci-pci-gli: Enlarge ASPM L1 entry delay of GL9763E GL9763E enters ASPM L1 state after a very short idle in default, even during a burst of request. So the R/W performance of GL9763E is low with some platforms, which support ASPM mechanism, due to entering ASPM L1 state very frequently in R/W process. Set the L1 entry delay bits in vendor-specific register to 0x3FF to enlarge the idle period to 260us for improving the R/W performance of GL9763E. Signed-off-by: Renius Chen Link: https://lore.kernel.org/r/20210115054736.27769-1-reniuschengl@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-gli.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c index c6a107d7c742..fb14f70cb9a0 100644 --- a/drivers/mmc/host/sdhci-pci-gli.c +++ b/drivers/mmc/host/sdhci-pci-gli.c @@ -88,6 +88,10 @@ #define PCIE_GLI_9763E_SCR 0x8E0 #define GLI_9763E_SCR_AXI_REQ BIT(9) +#define PCIE_GLI_9763E_CFG2 0x8A4 +#define GLI_9763E_CFG2_L1DLY GENMASK(28, 19) +#define GLI_9763E_CFG2_L1DLY_MAX 0x3FF + #define PCIE_GLI_9763E_MMC_CTRL 0x960 #define GLI_9763E_HS400_SLOW BIT(3) @@ -792,6 +796,12 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot) value &= ~GLI_9763E_HS400_SLOW; pci_write_config_dword(pdev, PCIE_GLI_9763E_MMC_CTRL, value); + pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG2, &value); + value &= ~GLI_9763E_CFG2_L1DLY; + /* set ASPM L1 entry delay to 260us */ + value |= FIELD_PREP(GLI_9763E_CFG2_L1DLY, GLI_9763E_CFG2_L1DLY_MAX); + pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG2, value); + pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); value &= ~GLI_9763E_VHS_REV; value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R); -- cgit v1.2.3 From eb085875243db0afdd1e11f0f410a08a64568ed8 Mon Sep 17 00:00:00 2001 From: Muhammad Husaini Zulkifli Date: Thu, 14 Jan 2021 23:26:52 +0800 Subject: mmc: sdhci-of-arasan: use of_device_get_match_data() Use of_device_get_match_data() helper to get the match-data. Signed-off-by: Muhammad Husaini Zulkifli Suggested-by: Andy Shevchenko Acked-by: Adrian Hunter Acked-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210114152700.21916-2-muhammad.husaini.zulkifli@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 8a406b62c6ad..444181772191 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1507,7 +1507,6 @@ cleanup: static int sdhci_arasan_probe(struct platform_device *pdev) { int ret; - const struct of_device_id *match; struct device_node *node; struct clk *clk_xin; struct sdhci_host *host; @@ -1516,8 +1515,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; const struct sdhci_arasan_of_data *data; - match = of_match_node(sdhci_arasan_of_match, pdev->dev.of_node); - data = match->data; + data = of_device_get_match_data(&pdev->dev); host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan)); if (IS_ERR(host)) -- cgit v1.2.3 From 80d41efe2e5fd321005a6ecce1a46583f0bbe1e5 Mon Sep 17 00:00:00 2001 From: Muhammad Husaini Zulkifli Date: Thu, 14 Jan 2021 23:26:53 +0800 Subject: mmc: sdhci-of-arasan: Convert to use np instead of pdev->dev.of_node Use np pointer to simplify code and improve readability. Signed-off-by: Muhammad Husaini Zulkifli Suggested-by: Andy Shevchenko Acked-by: Adrian Hunter Acked-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210114152700.21916-3-muhammad.husaini.zulkifli@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 444181772191..7621624b6ad7 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1528,7 +1528,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) sdhci_arasan->soc_ctl_map = data->soc_ctl_map; sdhci_arasan->clk_ops = data->clk_ops; - node = of_parse_phandle(pdev->dev.of_node, "arasan,soc-ctl-syscon", 0); + node = of_parse_phandle(np, "arasan,soc-ctl-syscon", 0); if (node) { sdhci_arasan->soc_ctl_base = syscon_node_to_regmap(node); of_node_put(node); @@ -1577,8 +1577,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) pltfm_host->clk = clk_xin; - if (of_device_is_compatible(pdev->dev.of_node, - "rockchip,rk3399-sdhci-5.1")) + if (of_device_is_compatible(np, "rockchip,rk3399-sdhci-5.1")) sdhci_arasan_update_clockmultiplier(host, 0x0); if (of_device_is_compatible(np, "intel,keembay-sdhci-5.1-emmc") || @@ -1611,8 +1610,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) } sdhci_arasan->phy = ERR_PTR(-ENODEV); - if (of_device_is_compatible(pdev->dev.of_node, - "arasan,sdhci-5.1")) { + if (of_device_is_compatible(np, "arasan,sdhci-5.1")) { sdhci_arasan->phy = devm_phy_get(&pdev->dev, "phy_arasan"); if (IS_ERR(sdhci_arasan->phy)) { -- cgit v1.2.3 From 2ff0b85d627f6a3b96b35abb0be303bd1a690fe6 Mon Sep 17 00:00:00 2001 From: Muhammad Husaini Zulkifli Date: Thu, 14 Jan 2021 23:26:54 +0800 Subject: mmc: sdhci-of-arasan: Add structure device pointer in probe function Add struct device *dev in probe func() so that it can widely use in probe to make code more readable. Signed-off-by: Muhammad Husaini Zulkifli Acked-by: Adrian Hunter Acked-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210114152700.21916-4-muhammad.husaini.zulkifli@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 7621624b6ad7..1080195e487d 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1511,11 +1511,12 @@ static int sdhci_arasan_probe(struct platform_device *pdev) struct clk *clk_xin; struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; struct sdhci_arasan_data *sdhci_arasan; - struct device_node *np = pdev->dev.of_node; const struct sdhci_arasan_of_data *data; - data = of_device_get_match_data(&pdev->dev); + data = of_device_get_match_data(dev); host = sdhci_pltfm_init(pdev, data->pdata, sizeof(*sdhci_arasan)); if (IS_ERR(host)) @@ -1534,36 +1535,36 @@ static int sdhci_arasan_probe(struct platform_device *pdev) of_node_put(node); if (IS_ERR(sdhci_arasan->soc_ctl_base)) { - ret = dev_err_probe(&pdev->dev, + ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->soc_ctl_base), "Can't get syscon\n"); goto err_pltfm_free; } } - sdhci_arasan->clk_ahb = devm_clk_get(&pdev->dev, "clk_ahb"); + sdhci_arasan->clk_ahb = devm_clk_get(dev, "clk_ahb"); if (IS_ERR(sdhci_arasan->clk_ahb)) { - dev_err(&pdev->dev, "clk_ahb clock not found.\n"); + dev_err(dev, "clk_ahb clock not found.\n"); ret = PTR_ERR(sdhci_arasan->clk_ahb); goto err_pltfm_free; } - clk_xin = devm_clk_get(&pdev->dev, "clk_xin"); + clk_xin = devm_clk_get(dev, "clk_xin"); if (IS_ERR(clk_xin)) { - dev_err(&pdev->dev, "clk_xin clock not found.\n"); + dev_err(dev, "clk_xin clock not found.\n"); ret = PTR_ERR(clk_xin); goto err_pltfm_free; } ret = clk_prepare_enable(sdhci_arasan->clk_ahb); if (ret) { - dev_err(&pdev->dev, "Unable to enable AHB clock.\n"); + dev_err(dev, "Unable to enable AHB clock.\n"); goto err_pltfm_free; } ret = clk_prepare_enable(clk_xin); if (ret) { - dev_err(&pdev->dev, "Unable to enable SD clock.\n"); + dev_err(dev, "Unable to enable SD clock.\n"); goto clk_dis_ahb; } @@ -1591,7 +1592,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) sdhci_arasan_update_baseclkfreq(host); - ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, &pdev->dev); + ret = sdhci_arasan_register_sdclk(sdhci_arasan, clk_xin, dev); if (ret) goto clk_disable_all; @@ -1600,28 +1601,27 @@ static int sdhci_arasan_probe(struct platform_device *pdev) arasan_zynqmp_execute_tuning; } - arasan_dt_parse_clk_phases(&pdev->dev, &sdhci_arasan->clk_data); + arasan_dt_parse_clk_phases(dev, &sdhci_arasan->clk_data); ret = mmc_of_parse(host->mmc); if (ret) { if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "parsing dt failed (%d)\n", ret); + dev_err(dev, "parsing dt failed (%d)\n", ret); goto unreg_clk; } sdhci_arasan->phy = ERR_PTR(-ENODEV); if (of_device_is_compatible(np, "arasan,sdhci-5.1")) { - sdhci_arasan->phy = devm_phy_get(&pdev->dev, - "phy_arasan"); + sdhci_arasan->phy = devm_phy_get(dev, "phy_arasan"); if (IS_ERR(sdhci_arasan->phy)) { ret = PTR_ERR(sdhci_arasan->phy); - dev_err(&pdev->dev, "No phy for arasan,sdhci-5.1.\n"); + dev_err(dev, "No phy for arasan,sdhci-5.1.\n"); goto unreg_clk; } ret = phy_init(sdhci_arasan->phy); if (ret < 0) { - dev_err(&pdev->dev, "phy_init err.\n"); + dev_err(dev, "phy_init err.\n"); goto unreg_clk; } @@ -1646,7 +1646,7 @@ err_add_host: if (!IS_ERR(sdhci_arasan->phy)) phy_exit(sdhci_arasan->phy); unreg_clk: - sdhci_arasan_unregister_sdclk(&pdev->dev); + sdhci_arasan_unregister_sdclk(dev); clk_disable_all: clk_disable_unprepare(clk_xin); clk_dis_ahb: -- cgit v1.2.3 From ffd68f356e411f79d5d448908a1f60e374c95cb2 Mon Sep 17 00:00:00 2001 From: Muhammad Husaini Zulkifli Date: Thu, 14 Jan 2021 23:26:55 +0800 Subject: mmc: sdhci-of-arasan: Use dev_err_probe() to avoid spamming logs Using dev_err_probe() can avoid spamming logs when probe is deferred. This function can also help to reduce code the size, uniform error handling and simplify the code. Signed-off-by: Muhammad Husaini Zulkifli Acked-by: Adrian Hunter Acked-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210114152700.21916-5-muhammad.husaini.zulkifli@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 1080195e487d..839965f7c717 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1544,15 +1544,14 @@ static int sdhci_arasan_probe(struct platform_device *pdev) sdhci_arasan->clk_ahb = devm_clk_get(dev, "clk_ahb"); if (IS_ERR(sdhci_arasan->clk_ahb)) { - dev_err(dev, "clk_ahb clock not found.\n"); - ret = PTR_ERR(sdhci_arasan->clk_ahb); + ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->clk_ahb), + "clk_ahb clock not found.\n"); goto err_pltfm_free; } clk_xin = devm_clk_get(dev, "clk_xin"); if (IS_ERR(clk_xin)) { - dev_err(dev, "clk_xin clock not found.\n"); - ret = PTR_ERR(clk_xin); + ret = dev_err_probe(dev, PTR_ERR(clk_xin), "clk_xin clock not found.\n"); goto err_pltfm_free; } @@ -1605,8 +1604,7 @@ static int sdhci_arasan_probe(struct platform_device *pdev) ret = mmc_of_parse(host->mmc); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "parsing dt failed (%d)\n", ret); + ret = dev_err_probe(dev, ret, "parsing dt failed.\n"); goto unreg_clk; } @@ -1614,8 +1612,8 @@ static int sdhci_arasan_probe(struct platform_device *pdev) if (of_device_is_compatible(np, "arasan,sdhci-5.1")) { sdhci_arasan->phy = devm_phy_get(dev, "phy_arasan"); if (IS_ERR(sdhci_arasan->phy)) { - ret = PTR_ERR(sdhci_arasan->phy); - dev_err(dev, "No phy for arasan,sdhci-5.1.\n"); + ret = dev_err_probe(dev, PTR_ERR(sdhci_arasan->phy), + "No phy for arasan,sdhci-5.1.\n"); goto unreg_clk; } -- cgit v1.2.3 From c58c5950b87a5ba8270c15a86b4261800e4079c1 Mon Sep 17 00:00:00 2001 From: Renius Chen Date: Mon, 11 Jan 2021 16:22:49 +0800 Subject: mmc: sdhci-pci-gli: Finetune HS400 RX delay for GL9763E To improve the compatibility of GL9763E with HS400 eMMC cards, finetune the RX delay of HS400 mode. Signed-off-by: Renius Chen Link: https://lore.kernel.org/r/20210111082249.17092-1-reniuschengl@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-gli.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c index fb14f70cb9a0..4a0f69b97a78 100644 --- a/drivers/mmc/host/sdhci-pci-gli.c +++ b/drivers/mmc/host/sdhci-pci-gli.c @@ -95,6 +95,10 @@ #define PCIE_GLI_9763E_MMC_CTRL 0x960 #define GLI_9763E_HS400_SLOW BIT(3) +#define PCIE_GLI_9763E_CLKRXDLY 0x934 +#define GLI_9763E_HS400_RXDLY GENMASK(31, 28) +#define GLI_9763E_HS400_RXDLY_5 0x5 + #define SDHCI_GLI_9763E_CQE_BASE_ADDR 0x200 #define GLI_9763E_CQE_TRNS_MODE (SDHCI_TRNS_MULTI | \ SDHCI_TRNS_BLK_CNT_EN | \ @@ -802,6 +806,11 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot) value |= FIELD_PREP(GLI_9763E_CFG2_L1DLY, GLI_9763E_CFG2_L1DLY_MAX); pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG2, value); + pci_read_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, &value); + value &= ~GLI_9763E_HS400_RXDLY; + value |= FIELD_PREP(GLI_9763E_HS400_RXDLY, GLI_9763E_HS400_RXDLY_5); + pci_write_config_dword(pdev, PCIE_GLI_9763E_CLKRXDLY, value); + pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); value &= ~GLI_9763E_VHS_REV; value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R); -- cgit v1.2.3 From 6b1dc6229aecbcb45e8901576684a8c09e25ad7b Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Wed, 16 Dec 2020 21:17:37 +0800 Subject: mmc: core: convert comma to semicolon Replace a comma between expression statements by a semicolon. Signed-off-by: Zheng Yongjun Link: https://lore.kernel.org/r/20201216131737.14883-1-zhengyongjun3@huawei.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 19f1ee57fb34..49d9117550af 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -547,10 +547,10 @@ int mmc_cqe_recovery(struct mmc_host *host) host->cqe_ops->cqe_recovery_start(host); memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = MMC_STOP_TRANSMISSION, - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC, + cmd.opcode = MMC_STOP_TRANSMISSION; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */ - cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT, + cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT; mmc_wait_for_cmd(host, &cmd, 0); memset(&cmd, 0, sizeof(cmd)); @@ -558,7 +558,7 @@ int mmc_cqe_recovery(struct mmc_host *host) cmd.arg = 1; /* Discard entire queue */ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; cmd.flags &= ~MMC_RSP_CRC; /* Ignore CRC */ - cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT, + cmd.busy_timeout = MMC_CQE_RECOVERY_TIMEOUT; err = mmc_wait_for_cmd(host, &cmd, 0); host->cqe_ops->cqe_recovery_finish(host); -- cgit v1.2.3 From eab234fce3334fd876558b6027ef2830ea559ad7 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Fri, 15 Jan 2021 17:51:21 +0800 Subject: mmc: omap_hsmmc: Simplify bool comparison and conversion Fix the following coccicheck warning: ./drivers/mmc/host/omap_hsmmc.c:297:6-25: WARNING: Comparison of 0/1 to bool variable According to the context, vqmmc_enabled is more suitable for bool type. Reported-by: Abaci Robot Signed-off-by: Yang Li Link: https://lore.kernel.org/r/1610704281-11036-1-git-send-email-abaci-bugfix@linux.alibaba.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index aa9cc49206d1..2f8038d69f67 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -177,7 +177,7 @@ struct omap_hsmmc_host { struct regulator *pbias; bool pbias_enabled; void __iomem *base; - int vqmmc_enabled; + bool vqmmc_enabled; resource_size_t mapbase; spinlock_t irq_lock; /* Prevent races with irq handler */ unsigned int dma_len; @@ -232,7 +232,7 @@ static int omap_hsmmc_enable_supply(struct mmc_host *mmc) dev_err(mmc_dev(mmc), "vmmc_aux reg enable failed\n"); goto err_vqmmc; } - host->vqmmc_enabled = 1; + host->vqmmc_enabled = true; } return 0; @@ -256,7 +256,7 @@ static int omap_hsmmc_disable_supply(struct mmc_host *mmc) dev_err(mmc_dev(mmc), "vmmc_aux reg disable failed\n"); return ret; } - host->vqmmc_enabled = 0; + host->vqmmc_enabled = false; } if (!IS_ERR(mmc->supply.vmmc)) { @@ -285,22 +285,22 @@ static int omap_hsmmc_set_pbias(struct omap_hsmmc_host *host, bool power_on) return 0; if (power_on) { - if (host->pbias_enabled == 0) { + if (!host->pbias_enabled) { ret = regulator_enable(host->pbias); if (ret) { dev_err(host->dev, "pbias reg enable fail\n"); return ret; } - host->pbias_enabled = 1; + host->pbias_enabled = true; } } else { - if (host->pbias_enabled == 1) { + if (host->pbias_enabled) { ret = regulator_disable(host->pbias); if (ret) { dev_err(host->dev, "pbias reg disable fail\n"); return ret; } - host->pbias_enabled = 0; + host->pbias_enabled = false; } } @@ -1861,8 +1861,8 @@ static int omap_hsmmc_probe(struct platform_device *pdev) host->base = base + pdata->reg_offset; host->power_mode = MMC_POWER_OFF; host->next_data.cookie = 1; - host->pbias_enabled = 0; - host->vqmmc_enabled = 0; + host->pbias_enabled = false; + host->vqmmc_enabled = false; platform_set_drvdata(pdev, host); -- cgit v1.2.3 From 7c7905df68c5ca5b3b20f41ef3f0d245ae6f32c3 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Fri, 15 Jan 2021 21:32:18 +0200 Subject: dt-bindings: mmc: sdhci-am654: fix compatible for j7200 On TI J7200 SoC the SDHCI controller compatible defined as "ti,j7200-sdhci-8bit", "ti,j721e-sdhci-8bit" or "ti,j7200-sdhci-4bit", "ti,j721e-sdhci-4bit" which causes dtbs_check warnings: mmc@4f80000: compatible: ['ti,j7200-sdhci-8bit', 'ti,j721e-sdhci-8bit'] is too long mmc@4f80000: compatible: Additional items are not allowed ('ti,j721e-sdhci-8bit' was unexpected) mmc@4fb0000: compatible:0: 'ti,j7200-sdhci-4bit' is not one of ['ti,am654-sdhci-5.1', 'ti,j721e-sdhci-8bit', 'ti,j721e-sdhci-4bit', 'ti,j7200-sdhci-8bit', 'ti,j721e-sdhci-4bit', 'ti,am64-sdhci-8bit', 'ti,am64-sdhci-4bit'] mmc@4fb0000: compatible: ['ti,j7200-sdhci-4bit', 'ti,j721e-sdhci-4bit'] is too long mmc@4fb0000: compatible: Additional items are not allowed ('ti,j721e-sdhci-4bit' was unexpected) Fix it by adding missing compatible strings and their combinations. Fixes: 407d0c2cdd12 ("dt-bindings: mmc: sdhci-am654: Convert sdhci-am654 controller documentation to json schema") Signed-off-by: Grygorii Strashko Link: https://lore.kernel.org/r/20210115193218.5809-1-grygorii.strashko@ti.com Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/sdhci-am654.yaml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml index 34e53db29428..3a79e39253d2 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-am654.yaml @@ -15,14 +15,19 @@ allOf: properties: compatible: - enum: - - ti,am654-sdhci-5.1 - - ti,j721e-sdhci-8bit - - ti,j721e-sdhci-4bit - - ti,j7200-sdhci-8bit - - ti,j721e-sdhci-4bit - - ti,am64-sdhci-8bit - - ti,am64-sdhci-4bit + oneOf: + - const: ti,am654-sdhci-5.1 + - const: ti,j721e-sdhci-8bit + - const: ti,j721e-sdhci-4bit + - const: ti,j721e-sdhci-4bit + - const: ti,am64-sdhci-8bit + - const: ti,am64-sdhci-4bit + - items: + - const: ti,j7200-sdhci-8bit + - const: ti,j721e-sdhci-8bit + - items: + - const: ti,j7200-sdhci-4bit + - const: ti,j721e-sdhci-4bit reg: maxItems: 2 -- cgit v1.2.3 From 3561afa02605b398d1b98e1ce913ea6411cdc5dd Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Thu, 14 Jan 2021 13:44:28 +1030 Subject: mmc: core: Add helper for parsing clock phase properties Drivers for MMC hosts that accept phase corrections can take advantage of the helper by embedding an instance of struct mmc_clk_phase_map in their private data and invoking mmc_of_parse_clk_phase() to extract phase parameters. It is the responsibility of the host driver to translate and apply the extracted values to hardware as required. Signed-off-by: Andrew Jeffery Link: https://lore.kernel.org/r/20210114031433.2388532-2-andrew@aj.id.au Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mmc/host.h | 13 +++++++++++++ 2 files changed, 57 insertions(+) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 96b2ca1f1b06..74e853bb6948 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -163,6 +163,50 @@ static void mmc_retune_timer(struct timer_list *t) mmc_retune_needed(host); } +static void mmc_of_parse_timing_phase(struct device *dev, const char *prop, + struct mmc_clk_phase *phase) +{ + int degrees[2] = {0}; + int rc; + + rc = device_property_read_u32_array(dev, prop, degrees, 2); + phase->valid = !rc; + if (phase->valid) { + phase->in_deg = degrees[0]; + phase->out_deg = degrees[1]; + } +} + +void +mmc_of_parse_clk_phase(struct mmc_host *host, struct mmc_clk_phase_map *map) +{ + struct device *dev = host->parent; + + mmc_of_parse_timing_phase(dev, "clk-phase-legacy", + &map->phase[MMC_TIMING_LEGACY]); + mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs", + &map->phase[MMC_TIMING_MMC_HS]); + mmc_of_parse_timing_phase(dev, "clk-phase-sd-hs", + &map->phase[MMC_TIMING_SD_HS]); + mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr12", + &map->phase[MMC_TIMING_UHS_SDR12]); + mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr25", + &map->phase[MMC_TIMING_UHS_SDR25]); + mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr50", + &map->phase[MMC_TIMING_UHS_SDR50]); + mmc_of_parse_timing_phase(dev, "clk-phase-uhs-sdr104", + &map->phase[MMC_TIMING_UHS_SDR104]); + mmc_of_parse_timing_phase(dev, "clk-phase-uhs-ddr50", + &map->phase[MMC_TIMING_UHS_DDR50]); + mmc_of_parse_timing_phase(dev, "clk-phase-mmc-ddr52", + &map->phase[MMC_TIMING_MMC_DDR52]); + mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs200", + &map->phase[MMC_TIMING_MMC_HS200]); + mmc_of_parse_timing_phase(dev, "clk-phase-mmc-hs400", + &map->phase[MMC_TIMING_MMC_HS400]); +} +EXPORT_SYMBOL(mmc_of_parse_clk_phase); + /** * mmc_of_parse() - parse host's device-tree node * @host: host whose node should be parsed. diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 01bba36545c5..8cf686d98a68 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -79,6 +79,17 @@ struct mmc_ios { bool enhanced_strobe; /* hs400es selection */ }; +struct mmc_clk_phase { + bool valid; + u16 in_deg; + u16 out_deg; +}; + +#define MMC_NUM_CLK_PHASES (MMC_TIMING_MMC_HS400 + 1) +struct mmc_clk_phase_map { + struct mmc_clk_phase phase[MMC_NUM_CLK_PHASES]; +}; + struct mmc_host; struct mmc_host_ops { @@ -490,6 +501,8 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *); int mmc_add_host(struct mmc_host *); void mmc_remove_host(struct mmc_host *); void mmc_free_host(struct mmc_host *); +void mmc_of_parse_clk_phase(struct mmc_host *host, + struct mmc_clk_phase_map *map); int mmc_of_parse(struct mmc_host *host); int mmc_of_parse_voltage(struct device_node *np, u32 *mask); -- cgit v1.2.3 From 2fc88f92359df753fc892f3b3d0e1d69ef6c620c Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Thu, 14 Jan 2021 13:44:29 +1030 Subject: mmc: sdhci-of-aspeed: Expose clock phase controls The Aspeed SD/eMMC controllers expose configurable clock phase correction by inserting delays of up to 15 logic elements in length into the bus clock path. The hardware supports independent configuration for both bus directions on a per-slot basis. The timing delay per element encoded in the driver was experimentally determined by scope measurements. The phase controls for both slots are grouped together in a single register of the global register block of the SD/MMC controller(s), which drives the use of a locking scheme between the SDHCIs and the global register set. Signed-off-by: Andrew Jeffery Link: https://lore.kernel.org/r/20210114031433.2388532-3-andrew@aj.id.au Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-aspeed.c | 216 +++++++++++++++++++++++++++++++++++-- 1 file changed, 208 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index 4f008ba3280e..b1a14e7dda82 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -16,9 +17,19 @@ #include "sdhci-pltfm.h" -#define ASPEED_SDC_INFO 0x00 -#define ASPEED_SDC_S1MMC8 BIT(25) -#define ASPEED_SDC_S0MMC8 BIT(24) +#define ASPEED_SDC_INFO 0x00 +#define ASPEED_SDC_S1_MMC8 BIT(25) +#define ASPEED_SDC_S0_MMC8 BIT(24) +#define ASPEED_SDC_PHASE 0xf4 +#define ASPEED_SDC_S1_PHASE_IN GENMASK(25, 21) +#define ASPEED_SDC_S0_PHASE_IN GENMASK(20, 16) +#define ASPEED_SDC_S1_PHASE_OUT GENMASK(15, 11) +#define ASPEED_SDC_S1_PHASE_IN_EN BIT(10) +#define ASPEED_SDC_S1_PHASE_OUT_EN GENMASK(9, 8) +#define ASPEED_SDC_S0_PHASE_OUT GENMASK(7, 3) +#define ASPEED_SDC_S0_PHASE_IN_EN BIT(2) +#define ASPEED_SDC_S0_PHASE_OUT_EN GENMASK(1, 0) +#define ASPEED_SDC_PHASE_MAX 31 struct aspeed_sdc { struct clk *clk; @@ -28,9 +39,36 @@ struct aspeed_sdc { void __iomem *regs; }; +struct aspeed_sdhci_tap_param { + bool valid; + +#define ASPEED_SDHCI_TAP_PARAM_INVERT_CLK BIT(4) + u8 in; + u8 out; +}; + +struct aspeed_sdhci_tap_desc { + u32 tap_mask; + u32 enable_mask; + u8 enable_value; +}; + +struct aspeed_sdhci_phase_desc { + struct aspeed_sdhci_tap_desc in; + struct aspeed_sdhci_tap_desc out; +}; + +struct aspeed_sdhci_pdata { + const struct aspeed_sdhci_phase_desc *phase_desc; + size_t nr_phase_descs; +}; + struct aspeed_sdhci { + const struct aspeed_sdhci_pdata *pdata; struct aspeed_sdc *parent; u32 width_mask; + struct mmc_clk_phase_map phase_map; + const struct aspeed_sdhci_phase_desc *phase_desc; }; static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc, @@ -50,10 +88,118 @@ static void aspeed_sdc_configure_8bit_mode(struct aspeed_sdc *sdc, spin_unlock(&sdc->lock); } +static u32 +aspeed_sdc_set_phase_tap(const struct aspeed_sdhci_tap_desc *desc, + u8 tap, bool enable, u32 reg) +{ + reg &= ~(desc->enable_mask | desc->tap_mask); + if (enable) { + reg |= tap << __ffs(desc->tap_mask); + reg |= desc->enable_value << __ffs(desc->enable_mask); + } + + return reg; +} + +static void +aspeed_sdc_set_phase_taps(struct aspeed_sdc *sdc, + const struct aspeed_sdhci_phase_desc *desc, + const struct aspeed_sdhci_tap_param *taps) +{ + u32 reg; + + spin_lock(&sdc->lock); + reg = readl(sdc->regs + ASPEED_SDC_PHASE); + + reg = aspeed_sdc_set_phase_tap(&desc->in, taps->in, taps->valid, reg); + reg = aspeed_sdc_set_phase_tap(&desc->out, taps->out, taps->valid, reg); + + writel(reg, sdc->regs + ASPEED_SDC_PHASE); + spin_unlock(&sdc->lock); +} + +#define PICOSECONDS_PER_SECOND 1000000000000ULL +#define ASPEED_SDHCI_NR_TAPS 15 +/* Measured value with *handwave* environmentals and static loading */ +#define ASPEED_SDHCI_MAX_TAP_DELAY_PS 1253 +static int aspeed_sdhci_phase_to_tap(struct device *dev, unsigned long rate_hz, + int phase_deg) +{ + u64 phase_period_ps; + u64 prop_delay_ps; + u64 clk_period_ps; + unsigned int tap; + u8 inverted; + + phase_deg %= 360; + + if (phase_deg >= 180) { + inverted = ASPEED_SDHCI_TAP_PARAM_INVERT_CLK; + phase_deg -= 180; + dev_dbg(dev, + "Inverting clock to reduce phase correction from %d to %d degrees\n", + phase_deg + 180, phase_deg); + } else { + inverted = 0; + } + + prop_delay_ps = ASPEED_SDHCI_MAX_TAP_DELAY_PS / ASPEED_SDHCI_NR_TAPS; + clk_period_ps = div_u64(PICOSECONDS_PER_SECOND, (u64)rate_hz); + phase_period_ps = div_u64((u64)phase_deg * clk_period_ps, 360ULL); + + tap = div_u64(phase_period_ps, prop_delay_ps); + if (tap > ASPEED_SDHCI_NR_TAPS) { + dev_warn(dev, + "Requested out of range phase tap %d for %d degrees of phase compensation at %luHz, clamping to tap %d\n", + tap, phase_deg, rate_hz, ASPEED_SDHCI_NR_TAPS); + tap = ASPEED_SDHCI_NR_TAPS; + } + + return inverted | tap; +} + +static void +aspeed_sdhci_phases_to_taps(struct device *dev, unsigned long rate, + const struct mmc_clk_phase *phases, + struct aspeed_sdhci_tap_param *taps) +{ + taps->valid = phases->valid; + + if (!phases->valid) + return; + + taps->in = aspeed_sdhci_phase_to_tap(dev, rate, phases->in_deg); + taps->out = aspeed_sdhci_phase_to_tap(dev, rate, phases->out_deg); +} + +static void +aspeed_sdhci_configure_phase(struct sdhci_host *host, unsigned long rate) +{ + struct aspeed_sdhci_tap_param _taps = {0}, *taps = &_taps; + struct mmc_clk_phase *params; + struct aspeed_sdhci *sdhci; + struct device *dev; + + dev = host->mmc->parent; + sdhci = sdhci_pltfm_priv(sdhci_priv(host)); + + if (!sdhci->phase_desc) + return; + + params = &sdhci->phase_map.phase[host->timing]; + aspeed_sdhci_phases_to_taps(dev, rate, params, taps); + aspeed_sdc_set_phase_taps(sdhci->parent, sdhci->phase_desc, taps); + dev_dbg(dev, + "Using taps [%d, %d] for [%d, %d] degrees of phase correction at %luHz (%d)\n", + taps->in & ASPEED_SDHCI_NR_TAPS, + taps->out & ASPEED_SDHCI_NR_TAPS, + params->in_deg, params->out_deg, rate, host->timing); +} + static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host; - unsigned long parent; + unsigned long parent, bus; int div; u16 clk; @@ -69,13 +215,17 @@ static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) clock = host->max_clk; for (div = 2; div < 256; div *= 2) { - if ((parent / div) <= clock) + bus = parent / div; + if (bus <= clock) break; } + div >>= 1; clk = div << SDHCI_DIVIDER_SHIFT; + aspeed_sdhci_configure_phase(host, bus); + sdhci_enable_clk(host, clk); } @@ -157,6 +307,7 @@ static inline int aspeed_sdhci_calculate_slot(struct aspeed_sdhci *dev, static int aspeed_sdhci_probe(struct platform_device *pdev) { + const struct aspeed_sdhci_pdata *aspeed_pdata; struct sdhci_pltfm_host *pltfm_host; struct aspeed_sdhci *dev; struct sdhci_host *host; @@ -164,12 +315,15 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) int slot; int ret; + aspeed_pdata = of_device_get_match_data(&pdev->dev); + host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev)); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); dev = sdhci_pltfm_priv(pltfm_host); + dev->pdata = aspeed_pdata; dev->parent = dev_get_drvdata(pdev->dev.parent); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -180,8 +334,17 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) else if (slot >= 2) return -EINVAL; - dev_info(&pdev->dev, "Configuring for slot %d\n", slot); - dev->width_mask = !slot ? ASPEED_SDC_S0MMC8 : ASPEED_SDC_S1MMC8; + if (dev->pdata && slot < dev->pdata->nr_phase_descs) { + dev->phase_desc = &dev->pdata->phase_desc[slot]; + } else { + dev_info(&pdev->dev, + "Phase control not supported for slot %d\n", slot); + dev->phase_desc = NULL; + } + + dev->width_mask = !slot ? ASPEED_SDC_S0_MMC8 : ASPEED_SDC_S1_MMC8; + + dev_info(&pdev->dev, "Configured for slot %d\n", slot); sdhci_get_of_property(pdev); @@ -199,6 +362,9 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) if (ret) goto err_sdhci_add; + if (dev->phase_desc) + mmc_of_parse_clk_phase(host->mmc, &dev->phase_map); + ret = sdhci_add_host(host); if (ret) goto err_sdhci_add; @@ -230,10 +396,44 @@ static int aspeed_sdhci_remove(struct platform_device *pdev) return 0; } +static const struct aspeed_sdhci_phase_desc ast2600_sdhci_phase[] = { + /* SDHCI/Slot 0 */ + [0] = { + .in = { + .tap_mask = ASPEED_SDC_S0_PHASE_IN, + .enable_mask = ASPEED_SDC_S0_PHASE_IN_EN, + .enable_value = 1, + }, + .out = { + .tap_mask = ASPEED_SDC_S0_PHASE_OUT, + .enable_mask = ASPEED_SDC_S0_PHASE_OUT_EN, + .enable_value = 3, + }, + }, + /* SDHCI/Slot 1 */ + [1] = { + .in = { + .tap_mask = ASPEED_SDC_S1_PHASE_IN, + .enable_mask = ASPEED_SDC_S1_PHASE_IN_EN, + .enable_value = 1, + }, + .out = { + .tap_mask = ASPEED_SDC_S1_PHASE_OUT, + .enable_mask = ASPEED_SDC_S1_PHASE_OUT_EN, + .enable_value = 3, + }, + }, +}; + +static const struct aspeed_sdhci_pdata ast2600_sdhci_pdata = { + .phase_desc = ast2600_sdhci_phase, + .nr_phase_descs = ARRAY_SIZE(ast2600_sdhci_phase), +}; + static const struct of_device_id aspeed_sdhci_of_match[] = { { .compatible = "aspeed,ast2400-sdhci", }, { .compatible = "aspeed,ast2500-sdhci", }, - { .compatible = "aspeed,ast2600-sdhci", }, + { .compatible = "aspeed,ast2600-sdhci", .data = &ast2600_sdhci_pdata, }, { } }; -- cgit v1.2.3 From 0c53dc321a507c78fdd15a682f42175a131b1763 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Thu, 14 Jan 2021 13:44:30 +1030 Subject: mmc: sdhci-of-aspeed: Add AST2600 bus clock support The AST2600 can achieve HS200 speeds with a change to the bus clock divisor behaviour. The divisor can also be more accurate with respect to the requested clock rate, but keep the one-hot behaviour for backwards compatibility with the AST2400 and AST2500. Signed-off-by: Andrew Jeffery Link: https://lore.kernel.org/r/20210114031433.2388532-4-andrew@aj.id.au Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-aspeed.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index b1a14e7dda82..4b30f6a1178e 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -59,6 +59,7 @@ struct aspeed_sdhci_phase_desc { }; struct aspeed_sdhci_pdata { + unsigned int clk_div_start; const struct aspeed_sdhci_phase_desc *phase_desc; size_t nr_phase_descs; }; @@ -200,10 +201,13 @@ static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host; unsigned long parent, bus; + struct aspeed_sdhci *sdhci; int div; u16 clk; pltfm_host = sdhci_priv(host); + sdhci = sdhci_pltfm_priv(pltfm_host); + parent = clk_get_rate(pltfm_host->clk); sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); @@ -214,7 +218,23 @@ static void aspeed_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if (WARN_ON(clock > host->max_clk)) clock = host->max_clk; - for (div = 2; div < 256; div *= 2) { + /* + * Regarding the AST2600: + * + * If (EMMC12C[7:6], EMMC12C[15:8] == 0) then + * period of SDCLK = period of SDMCLK. + * + * If (EMMC12C[7:6], EMMC12C[15:8] != 0) then + * period of SDCLK = period of SDMCLK * 2 * (EMMC12C[7:6], EMMC[15:8]) + * + * If you keep EMMC12C[7:6] = 0 and EMMC12C[15:8] as one-hot, + * 0x1/0x2/0x4/etc, you will find it is compatible to AST2400 or AST2500 + * + * Keep the one-hot behaviour for backwards compatibility except for + * supporting the value 0 in (EMMC12C[7:6], EMMC12C[15:8]), and capture + * the 0-value capability in clk_div_start. + */ + for (div = sdhci->pdata->clk_div_start; div < 256; div *= 2) { bus = parent / div; if (bus <= clock) break; @@ -316,6 +336,10 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) int ret; aspeed_pdata = of_device_get_match_data(&pdev->dev); + if (!aspeed_pdata) { + dev_err(&pdev->dev, "Missing platform configuration data\n"); + return -EINVAL; + } host = sdhci_pltfm_init(pdev, &aspeed_sdhci_pdata, sizeof(*dev)); if (IS_ERR(host)) @@ -334,7 +358,7 @@ static int aspeed_sdhci_probe(struct platform_device *pdev) else if (slot >= 2) return -EINVAL; - if (dev->pdata && slot < dev->pdata->nr_phase_descs) { + if (slot < dev->pdata->nr_phase_descs) { dev->phase_desc = &dev->pdata->phase_desc[slot]; } else { dev_info(&pdev->dev, @@ -396,6 +420,10 @@ static int aspeed_sdhci_remove(struct platform_device *pdev) return 0; } +static const struct aspeed_sdhci_pdata ast2400_sdhci_pdata = { + .clk_div_start = 2, +}; + static const struct aspeed_sdhci_phase_desc ast2600_sdhci_phase[] = { /* SDHCI/Slot 0 */ [0] = { @@ -426,13 +454,14 @@ static const struct aspeed_sdhci_phase_desc ast2600_sdhci_phase[] = { }; static const struct aspeed_sdhci_pdata ast2600_sdhci_pdata = { + .clk_div_start = 1, .phase_desc = ast2600_sdhci_phase, .nr_phase_descs = ARRAY_SIZE(ast2600_sdhci_phase), }; static const struct of_device_id aspeed_sdhci_of_match[] = { - { .compatible = "aspeed,ast2400-sdhci", }, - { .compatible = "aspeed,ast2500-sdhci", }, + { .compatible = "aspeed,ast2400-sdhci", .data = &ast2400_sdhci_pdata, }, + { .compatible = "aspeed,ast2500-sdhci", .data = &ast2400_sdhci_pdata, }, { .compatible = "aspeed,ast2600-sdhci", .data = &ast2600_sdhci_pdata, }, { } }; -- cgit v1.2.3 From 0bbcd22556ef203b29e39a6ce1bd7e9523b6032e Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Thu, 14 Jan 2021 13:44:31 +1030 Subject: mmc: sdhci-of-aspeed: Add KUnit tests for phase calculations Converting degrees of phase to logic delays is irritating to test on hardware, so lets exercise the function using KUnit. Signed-off-by: Andrew Jeffery Link: https://lore.kernel.org/r/20210114031433.2388532-5-andrew@aj.id.au Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 14 +++++ drivers/mmc/host/sdhci-of-aspeed-test.c | 98 +++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-of-aspeed.c | 4 ++ 3 files changed, 116 insertions(+) create mode 100644 drivers/mmc/host/sdhci-of-aspeed-test.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 596f32637315..d6f00d1d6251 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -168,6 +168,20 @@ config MMC_SDHCI_OF_ASPEED If unsure, say N. +config MMC_SDHCI_OF_ASPEED_TEST + bool "Tests for the ASPEED SDHCI driver" + depends on MMC_SDHCI_OF_ASPEED && KUNIT=y + help + Enable KUnit tests for the ASPEED SDHCI driver. Select this + option only if you will boot the kernel for the purpose of running + unit tests (e.g. under UML or qemu). + + The KUnit tests generally exercise parts of the driver that do not + directly touch the hardware, for example, the phase correction + calculations. + + If unsure, say N. + config MMC_SDHCI_OF_AT91 tristate "SDHCI OF support for the Atmel SDMMC controller" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/sdhci-of-aspeed-test.c b/drivers/mmc/host/sdhci-of-aspeed-test.c new file mode 100644 index 000000000000..34070605b28b --- /dev/null +++ b/drivers/mmc/host/sdhci-of-aspeed-test.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* Copyright (C) 2020 IBM Corp. */ + +#include + +static void aspeed_sdhci_phase_ddr52(struct kunit *test) +{ + int rate = 52000000; + + KUNIT_EXPECT_EQ(test, 0, + aspeed_sdhci_phase_to_tap(NULL, rate, 0)); + KUNIT_EXPECT_EQ(test, 0, + aspeed_sdhci_phase_to_tap(NULL, rate, 1)); + KUNIT_EXPECT_EQ(test, 1, + aspeed_sdhci_phase_to_tap(NULL, rate, 2)); + KUNIT_EXPECT_EQ(test, 1, + aspeed_sdhci_phase_to_tap(NULL, rate, 3)); + KUNIT_EXPECT_EQ(test, 2, + aspeed_sdhci_phase_to_tap(NULL, rate, 4)); + KUNIT_EXPECT_EQ(test, 3, + aspeed_sdhci_phase_to_tap(NULL, rate, 5)); + KUNIT_EXPECT_EQ(test, 14, + aspeed_sdhci_phase_to_tap(NULL, rate, 23)); + KUNIT_EXPECT_EQ(test, 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 24)); + KUNIT_EXPECT_EQ(test, 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 25)); + + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 0, + aspeed_sdhci_phase_to_tap(NULL, rate, 180)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 0, + aspeed_sdhci_phase_to_tap(NULL, rate, 181)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1, + aspeed_sdhci_phase_to_tap(NULL, rate, 182)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1, + aspeed_sdhci_phase_to_tap(NULL, rate, 183)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 2, + aspeed_sdhci_phase_to_tap(NULL, rate, 184)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 3, + aspeed_sdhci_phase_to_tap(NULL, rate, 185)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 14, + aspeed_sdhci_phase_to_tap(NULL, rate, 203)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 204)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 205)); +} + +static void aspeed_sdhci_phase_hs200(struct kunit *test) +{ + int rate = 200000000; + + KUNIT_EXPECT_EQ(test, 0, + aspeed_sdhci_phase_to_tap(NULL, rate, 0)); + KUNIT_EXPECT_EQ(test, 0, + aspeed_sdhci_phase_to_tap(NULL, rate, 5)); + KUNIT_EXPECT_EQ(test, 1, + aspeed_sdhci_phase_to_tap(NULL, rate, 6)); + KUNIT_EXPECT_EQ(test, 1, + aspeed_sdhci_phase_to_tap(NULL, rate, 7)); + KUNIT_EXPECT_EQ(test, 14, + aspeed_sdhci_phase_to_tap(NULL, rate, 89)); + KUNIT_EXPECT_EQ(test, 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 90)); + KUNIT_EXPECT_EQ(test, 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 91)); + KUNIT_EXPECT_EQ(test, 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 96)); + + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK, + aspeed_sdhci_phase_to_tap(NULL, rate, 180)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK, + aspeed_sdhci_phase_to_tap(NULL, rate, 185)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1, + aspeed_sdhci_phase_to_tap(NULL, rate, 186)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 1, + aspeed_sdhci_phase_to_tap(NULL, rate, 187)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 14, + aspeed_sdhci_phase_to_tap(NULL, rate, 269)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 270)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 271)); + KUNIT_EXPECT_EQ(test, (int)ASPEED_SDHCI_TAP_PARAM_INVERT_CLK | 15, + aspeed_sdhci_phase_to_tap(NULL, rate, 276)); +} + +static struct kunit_case aspeed_sdhci_test_cases[] = { + KUNIT_CASE(aspeed_sdhci_phase_ddr52), + KUNIT_CASE(aspeed_sdhci_phase_hs200), + {} +}; + +static struct kunit_suite aspeed_sdhci_test_suite = { + .name = "sdhci-of-aspeed", + .test_cases = aspeed_sdhci_test_cases, +}; +kunit_test_suite(aspeed_sdhci_test_suite); diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index 4b30f6a1178e..3b0d381e1215 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -579,6 +579,10 @@ static void __exit aspeed_sdc_exit(void) } module_exit(aspeed_sdc_exit); +#if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST) +#include "sdhci-of-aspeed-test.c" +#endif + MODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers"); MODULE_AUTHOR("Ryan Chen "); MODULE_AUTHOR("Andrew Jeffery "); -- cgit v1.2.3 From 37af07d91d0a28f75e45d52ac8959e6bd587a4aa Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Thu, 14 Jan 2021 13:44:32 +1030 Subject: MAINTAINERS: Add entry for the ASPEED SD/MMC driver Add myself as the maintainer. Signed-off-by: Andrew Jeffery Link: https://lore.kernel.org/r/20210114031433.2388532-6-andrew@aj.id.au Signed-off-by: Ulf Hansson --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d3e847f7f3dc..f34714c2356c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2832,6 +2832,15 @@ F: Documentation/devicetree/bindings/interrupt-controller/aspeed,ast2xxx-scu-ic. F: drivers/irqchip/irq-aspeed-scu-ic.c F: include/dt-bindings/interrupt-controller/aspeed-scu-ic.h +ASPEED SD/MMC DRIVER +M: Andrew Jeffery +L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) +L: openbmc@lists.ozlabs.org (moderated for non-subscribers) +L: linux-mmc@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml +F: drivers/mmc/host/sdhci-of-aspeed* + ASPEED VIDEO ENGINE DRIVER M: Eddie James L: linux-media@vger.kernel.org -- cgit v1.2.3 From bd7342fea7f88863605fabce103ff0dc5d488276 Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Mon, 18 Jan 2021 16:45:20 +0800 Subject: mmc: core: remove unused host parameter of mmc_sd_get_csd() The host parameter is not used in the body of mmc_sd_get_csd(), so let's remove it. Update related code at the same time. Signed-off-by: Yue Hu Link: https://lore.kernel.org/r/20210118084520.241-1-zbestahu@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd.c | 4 ++-- drivers/mmc/core/sd.h | 2 +- drivers/mmc/core/sdio.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 6f054c449d46..6fa51a6ed058 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -860,7 +860,7 @@ try_again: return err; } -int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card) +int mmc_sd_get_csd(struct mmc_card *card) { int err; @@ -1046,7 +1046,7 @@ retry: } if (!oldcard) { - err = mmc_sd_get_csd(host, card); + err = mmc_sd_get_csd(card); if (err) goto free_card; diff --git a/drivers/mmc/core/sd.h b/drivers/mmc/core/sd.h index 497c026a5c5a..1af5a038bae9 100644 --- a/drivers/mmc/core/sd.h +++ b/drivers/mmc/core/sd.h @@ -10,7 +10,7 @@ struct mmc_host; struct mmc_card; int mmc_sd_get_cid(struct mmc_host *host, u32 ocr, u32 *cid, u32 *rocr); -int mmc_sd_get_csd(struct mmc_host *host, struct mmc_card *card); +int mmc_sd_get_csd(struct mmc_card *card); void mmc_decode_cid(struct mmc_card *card); int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, bool reinit); diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 694a212cbe25..0fda7784cab2 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -751,7 +751,7 @@ try_again: * Read CSD, before selecting the card */ if (!oldcard && card->type == MMC_TYPE_SD_COMBO) { - err = mmc_sd_get_csd(host, card); + err = mmc_sd_get_csd(card); if (err) goto remove; -- cgit v1.2.3 From 6d7ffe169e281b5724abd42843f13882f0526fd5 Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Tue, 19 Jan 2021 13:14:25 +0800 Subject: mmc: test: remove the shutdown function Since it's doing nothing for shutdown behavior. And the callback will be checked firstly in mmc_bus_shutdown(). Signed-off-by: Yue Hu Link: https://lore.kernel.org/r/20210119051425.305-1-zbestahu@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_test.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index 152e7525ed33..f999b483549d 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -3267,17 +3267,12 @@ static void mmc_test_remove(struct mmc_card *card) mmc_test_free_dbgfs_file(card); } -static void mmc_test_shutdown(struct mmc_card *card) -{ -} - static struct mmc_driver mmc_driver = { .drv = { .name = "mmc_test", }, .probe = mmc_test_probe, .remove = mmc_test_remove, - .shutdown = mmc_test_shutdown, }; static int __init mmc_test_init(void) -- cgit v1.2.3 From 707662d59d42c995bbcd3c5bf8c5b4f9169e765c Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Tue, 19 Jan 2021 15:37:05 +0800 Subject: mmc: test: clean up mmc_test_cleanup() mmc_test_cleanup() has same body as __mmc_test_prepare() with write except the character to memset(). Signed-off-by: Yue Hu Link: https://lore.kernel.org/r/20210119073705.375-1-zbestahu@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_test.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index f999b483549d..39a478874ca3 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -624,7 +624,7 @@ static unsigned int mmc_test_capacity(struct mmc_card *card) * Fill the first couple of sectors of the card with known data * so that bad reads/writes can be detected */ -static int __mmc_test_prepare(struct mmc_test_card *test, int write) +static int __mmc_test_prepare(struct mmc_test_card *test, int write, int val) { int ret, i; @@ -633,7 +633,7 @@ static int __mmc_test_prepare(struct mmc_test_card *test, int write) return ret; if (write) - memset(test->buffer, 0xDF, 512); + memset(test->buffer, val, 512); else { for (i = 0; i < 512; i++) test->buffer[i] = i; @@ -650,31 +650,17 @@ static int __mmc_test_prepare(struct mmc_test_card *test, int write) static int mmc_test_prepare_write(struct mmc_test_card *test) { - return __mmc_test_prepare(test, 1); + return __mmc_test_prepare(test, 1, 0xDF); } static int mmc_test_prepare_read(struct mmc_test_card *test) { - return __mmc_test_prepare(test, 0); + return __mmc_test_prepare(test, 0, 0); } static int mmc_test_cleanup(struct mmc_test_card *test) { - int ret, i; - - ret = mmc_test_set_blksize(test, 512); - if (ret) - return ret; - - memset(test->buffer, 0, 512); - - for (i = 0; i < BUFFER_SIZE / 512; i++) { - ret = mmc_test_buffer_transfer(test, test->buffer, i, 512, 1); - if (ret) - return ret; - } - - return 0; + return __mmc_test_prepare(test, 1, 0); } /*******************************************************************/ -- cgit v1.2.3 From 1a9705936aff95fe2bf03684a586c0c83b71f71f Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 19 Jan 2021 11:06:55 +0100 Subject: mmc: mmci: Convert bindings to DT schema This converts the MMCI bindings from simple text to a proper schema. Cc: devicetree@vger.kernel.org Cc: Marek Vasut Signed-off-by: Linus Walleij Tested-by: Marek Vasut Link: https://lore.kernel.org/r/20210119100655.2755164-1-linus.walleij@linaro.org Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/arm,pl18x.yaml | 203 +++++++++++++++++++++ Documentation/devicetree/bindings/mmc/mmci.txt | 74 -------- 2 files changed, 203 insertions(+), 74 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/arm,pl18x.yaml delete mode 100644 Documentation/devicetree/bindings/mmc/mmci.txt diff --git a/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml new file mode 100644 index 000000000000..eddc1f6bdbe5 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml @@ -0,0 +1,203 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/arm,pl18x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM PrimeCell MultiMedia Card Interface (MMCI) PL180 and PL181 + +maintainers: + - Linus Walleij + - Ulf Hansson + +description: + The ARM PrimeCells MMCI PL180 and PL181 provides an interface for + reading and writing to MultiMedia and SD cards alike. Over the years + vendors have use the VHDL code from ARM to create derivative MMC/SD/SDIO + host controllers with very similar characteristics. + +allOf: + - $ref: /schemas/arm/primecell.yaml# + - $ref: mmc-controller.yaml# + +# We need a select here so we don't match all nodes with 'arm,primecell' +select: + properties: + compatible: + contains: + enum: + - arm,pl180 + - arm,pl181 + - arm,pl18x + required: + - compatible + +properties: + compatible: + oneOf: + - description: The first version of the block, simply called + PL180 and found in the ARM Integrator IM/PD1 logic module. + items: + - const: arm,pl180 + - const: arm,primecell + - description: The improved version of the block, found in the + ARM Versatile and later reference designs. Further revisions + exist but get detected at runtime by reading some magic numbers + in the PrimeCell ID registers. + items: + - const: arm,pl181 + - const: arm,primecell + - description: Wildcard entry that will let the operating system + inspect the PrimeCell ID registers to determine which hardware + variant of PL180 or PL181 this is. + items: + - const: arm,pl18x + - const: arm,primecell + + clocks: + description: One or two clocks, the "apb_pclk" and the "MCLK" + which is the core block clock. The names are not compulsory. + minItems: 1 + maxItems: 2 + + power-domains: true + + resets: + maxItems: 1 + + reg: + description: the MMIO memory window must be exactly 4KB (0x1000) and the + layout should provide the PrimeCell ID registers so that the device can + be discovered. On ST Micro variants, a second register window may be + defined if a delay block is present and used for tuning. + + interrupts: + description: The first interrupt is the command interrupt and corresponds + to the event at the end of a command. The second interrupt is the + PIO (polled I/O) interrupt and occurs when the FIFO needs to be + emptied as part of a bulk read from the card. Some variants have these + two interrupts wired into the same line (logic OR) and in that case + only one interrupt may be provided. + minItems: 1 + maxItems: 2 + + st,sig-dir-dat0: + $ref: /schemas/types.yaml#/definitions/flag + description: ST Micro-specific property, bus signal direction pins used for + DAT[0]. + + st,sig-dir-dat2: + $ref: /schemas/types.yaml#/definitions/flag + description: ST Micro-specific property, bus signal direction pins used for + DAT[2]. + + st,sig-dir-dat31: + $ref: /schemas/types.yaml#/definitions/flag + description: ST Micro-specific property, bus signal direction pins used for + DAT[3] and DAT[1]. + + st,sig-dir-dat74: + $ref: /schemas/types.yaml#/definitions/flag + description: ST Micro-specific property, bus signal direction pins used for + DAT[7] and DAT[4]. + + st,sig-dir-cmd: + $ref: /schemas/types.yaml#/definitions/flag + description: ST Micro-specific property, CMD signal direction used for + pin CMD. + + st,sig-pin-fbclk: + $ref: /schemas/types.yaml#/definitions/flag + description: ST Micro-specific property, feedback clock FBCLK signal pin + in use. + + st,sig-dir: + $ref: /schemas/types.yaml#/definitions/flag + description: ST Micro-specific property, signal direction polarity used for + pins CMD, DAT[0], DAT[1], DAT[2] and DAT[3]. + + st,neg-edge: + $ref: /schemas/types.yaml#/definitions/flag + description: ST Micro-specific property, data and command phase relation, + generated on the sd clock falling edge. + + st,use-ckin: + $ref: /schemas/types.yaml#/definitions/flag + description: ST Micro-specific property, use CKIN pin from an external + driver to sample the receive data (for example with a voltage switch + transceiver). + +unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + +examples: + - | + #include + #include + + mmc@5000 { + compatible = "arm,pl180", "arm,primecell"; + reg = <0x5000 0x1000>; + interrupts-extended = <&vic 22 &sic 1>; + clocks = <&xtal24mhz>, <&pclk>; + clock-names = "mclk", "apb_pclk"; + }; + + mmc@80126000 { + compatible = "arm,pl18x", "arm,primecell"; + reg = <0x80126000 0x1000>; + interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dma 29 0 0x2>, <&dma 29 0 0x0>; + dma-names = "rx", "tx"; + clocks = <&prcc_kclk 1 5>, <&prcc_pclk 1 5>; + clock-names = "sdi", "apb_pclk"; + max-frequency = <100000000>; + bus-width = <4>; + cap-sd-highspeed; + cap-mmc-highspeed; + cd-gpios = <&gpio2 31 0x4>; + st,sig-dir-dat0; + st,sig-dir-dat2; + st,sig-dir-cmd; + st,sig-pin-fbclk; + vmmc-supply = <&ab8500_ldo_aux3_reg>; + vqmmc-supply = <&vmmci>; + }; + + mmc@101f6000 { + compatible = "arm,pl18x", "arm,primecell"; + reg = <0x101f6000 0x1000>; + clocks = <&sdiclk>, <&pclksdi>; + clock-names = "mclk", "apb_pclk"; + interrupt-parent = <&vica>; + interrupts = <22>; + max-frequency = <400000>; + bus-width = <4>; + cap-mmc-highspeed; + cap-sd-highspeed; + full-pwr-cycle; + st,sig-dir-dat0; + st,sig-dir-dat2; + st,sig-dir-dat31; + st,sig-dir-cmd; + st,sig-pin-fbclk; + vmmc-supply = <&vmmc_regulator>; + }; + + mmc@52007000 { + compatible = "arm,pl18x", "arm,primecell"; + arm,primecell-periphid = <0x10153180>; + reg = <0x52007000 0x1000>; + interrupts = <49>; + interrupt-names = "cmd_irq"; + clocks = <&rcc 0>; + clock-names = "apb_pclk"; + resets = <&rcc 1>; + cap-sd-highspeed; + cap-mmc-highspeed; + max-frequency = <120000000>; + }; diff --git a/Documentation/devicetree/bindings/mmc/mmci.txt b/Documentation/devicetree/bindings/mmc/mmci.txt deleted file mode 100644 index 4ec921e4bf34..000000000000 --- a/Documentation/devicetree/bindings/mmc/mmci.txt +++ /dev/null @@ -1,74 +0,0 @@ -* ARM PrimeCell MultiMedia Card Interface (MMCI) PL180/1 - -The ARM PrimeCell MMCI PL180 and PL181 provides an interface for -reading and writing to MultiMedia and SD cards alike. - -This file documents differences between the core properties described -by mmc.txt and the properties used by the mmci driver. Using "st" as -the prefix for a property, indicates support by the ST Micro variant. - -Required properties: -- compatible : contains "arm,pl18x", "arm,primecell". -- vmmc-supply : phandle to the regulator device tree node, mentioned - as the VCC/VDD supply in the eMMC/SD specs. - -Optional properties: -- arm,primecell-periphid : contains the PrimeCell Peripheral ID, it overrides - the ID provided by the HW -- resets : phandle to internal reset line. - Should be defined for sdmmc variant. -- vqmmc-supply : phandle to the regulator device tree node, mentioned - as the VCCQ/VDD_IO supply in the eMMC/SD specs. -specific for ux500 variant: -- st,sig-dir-dat0 : bus signal direction pin used for DAT[0]. -- st,sig-dir-dat2 : bus signal direction pin used for DAT[2]. -- st,sig-dir-dat31 : bus signal direction pin used for DAT[3] and DAT[1]. -- st,sig-dir-dat74 : bus signal direction pin used for DAT[4] to DAT[7]. -- st,sig-dir-cmd : cmd signal direction pin used for CMD. -- st,sig-pin-fbclk : feedback clock signal pin used. - -specific for sdmmc variant: -- reg : a second base register may be defined if a delay - block is present and used for tuning. -- st,sig-dir : signal direction polarity used for cmd, dat0 dat123. -- st,neg-edge : data & command phase relation, generated on - sd clock falling edge. -- st,use-ckin : use ckin pin from an external driver to sample - the receive data (example: with voltage - switch transceiver). - -Deprecated properties: -- mmc-cap-mmc-highspeed : indicates whether MMC is high speed capable. -- mmc-cap-sd-highspeed : indicates whether SD is high speed capable. - -Example: - -sdi0_per1@80126000 { - compatible = "arm,pl18x", "arm,primecell"; - reg = <0x80126000 0x1000>; - interrupts = <0 60 IRQ_TYPE_LEVEL_HIGH>; - - dmas = <&dma 29 0 0x2>, /* Logical - DevToMem */ - <&dma 29 0 0x0>; /* Logical - MemToDev */ - dma-names = "rx", "tx"; - - clocks = <&prcc_kclk 1 5>, <&prcc_pclk 1 5>; - clock-names = "sdi", "apb_pclk"; - - max-frequency = <100000000>; - bus-width = <4>; - cap-sd-highspeed; - cap-mmc-highspeed; - cd-gpios = <&gpio2 31 0x4>; // 95 - st,sig-dir-dat0; - st,sig-dir-dat2; - st,sig-dir-cmd; - st,sig-pin-fbclk; - - vmmc-supply = <&ab8500_ldo_aux3_reg>; - vqmmc-supply = <&vmmci>; - - pinctrl-names = "default", "sleep"; - pinctrl-0 = <&sdi0_default_mode>; - pinctrl-1 = <&sdi0_sleep_mode>; -}; -- cgit v1.2.3 From 4f9833d3ec8da34861cd0680b00c73e653877eb9 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Tue, 19 Jan 2021 18:04:06 -0600 Subject: mmc: sdhci-iproc: Add ACPI bindings for the RPi The RPi4 has an Arasan controller it carries over from the RPi3 and a newer eMMC2 controller. Because of a couple of quirks, it seems wiser to bind these controllers to the same driver that DT is using on this platform rather than the generic sdhci_acpi driver with PNP0D40. So, BCM2847 describes the older Arasan and BRCME88C describes the newer eMMC2. The older Arasan is reusing an existing ACPI _HID used by other OSes booting these tables on the RPi. With this change, Linux is capable of utilizing the SD card slot, and the Wi-Fi when booted with UEFI+ACPI on the RPi4. Signed-off-by: Jeremy Linton Acked-by: Florian Fainelli Link: https://lore.kernel.org/r/20210120000406.1843400-2-jeremy.linton@arm.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-iproc.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index c9434b461aab..ddeaf8e1f72f 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -296,9 +296,27 @@ static const struct of_device_id sdhci_iproc_of_match[] = { MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match); #ifdef CONFIG_ACPI +/* + * This is a duplicate of bcm2835_(pltfrm_)data without caps quirks + * which are provided by the ACPI table. + */ +static const struct sdhci_pltfm_data sdhci_bcm_arasan_data = { + .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_NO_HISPD_BIT, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + .ops = &sdhci_iproc_32only_ops, +}; + +static const struct sdhci_iproc_data bcm_arasan_data = { + .pdata = &sdhci_bcm_arasan_data, +}; + static const struct acpi_device_id sdhci_iproc_acpi_ids[] = { { .id = "BRCM5871", .driver_data = (kernel_ulong_t)&iproc_cygnus_data }, { .id = "BRCM5872", .driver_data = (kernel_ulong_t)&iproc_data }, + { .id = "BCM2847", .driver_data = (kernel_ulong_t)&bcm_arasan_data }, + { .id = "BRCME88C", .driver_data = (kernel_ulong_t)&bcm2711_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(acpi, sdhci_iproc_acpi_ids); -- cgit v1.2.3 From fe6e1471785a714220d1289e831fc0e74cb1f232 Mon Sep 17 00:00:00 2001 From: Jiapeng Zhong Date: Wed, 20 Jan 2021 15:39:37 +0800 Subject: mmc: atmel-mci: Assign boolean values to a bool variable Fix the following coccicheck warnings: ./drivers/mmc/host/atmel-mci.c:2436:2-34: WARNING: Assignment of 0/1 to bool variable. ./drivers/mmc/host/atmel-mci.c:2425:2-20: WARNING: Assignment of 0/1 to bool variable. Reported-by: Abaci Robot Signed-off-by: Jiapeng Zhong Acked-by: Ludovic Desroches Link: https://lore.kernel.org/r/1611128377-66216-1-git-send-email-abaci-bugfix@linux.alibaba.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 46 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 444bd3a0a922..632412066eda 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -2401,45 +2401,45 @@ static void atmci_get_cap(struct atmel_mci *host) dev_info(&host->pdev->dev, "version: 0x%x\n", version); - host->caps.has_dma_conf_reg = 0; - host->caps.has_pdc = 1; - host->caps.has_cfg_reg = 0; - host->caps.has_cstor_reg = 0; - host->caps.has_highspeed = 0; - host->caps.has_rwproof = 0; - host->caps.has_odd_clk_div = 0; - host->caps.has_bad_data_ordering = 1; - host->caps.need_reset_after_xfer = 1; - host->caps.need_blksz_mul_4 = 1; - host->caps.need_notbusy_for_read_ops = 0; + host->caps.has_dma_conf_reg = false; + host->caps.has_pdc = true; + host->caps.has_cfg_reg = false; + host->caps.has_cstor_reg = false; + host->caps.has_highspeed = false; + host->caps.has_rwproof = false; + host->caps.has_odd_clk_div = false; + host->caps.has_bad_data_ordering = true; + host->caps.need_reset_after_xfer = true; + host->caps.need_blksz_mul_4 = true; + host->caps.need_notbusy_for_read_ops = false; /* keep only major version number */ switch (version & 0xf00) { case 0x600: case 0x500: - host->caps.has_odd_clk_div = 1; + host->caps.has_odd_clk_div = true; fallthrough; case 0x400: case 0x300: - host->caps.has_dma_conf_reg = 1; - host->caps.has_pdc = 0; - host->caps.has_cfg_reg = 1; - host->caps.has_cstor_reg = 1; - host->caps.has_highspeed = 1; + host->caps.has_dma_conf_reg = true; + host->caps.has_pdc = false; + host->caps.has_cfg_reg = true; + host->caps.has_cstor_reg = true; + host->caps.has_highspeed = true; fallthrough; case 0x200: - host->caps.has_rwproof = 1; - host->caps.need_blksz_mul_4 = 0; - host->caps.need_notbusy_for_read_ops = 1; + host->caps.has_rwproof = true; + host->caps.need_blksz_mul_4 = false; + host->caps.need_notbusy_for_read_ops = true; fallthrough; case 0x100: - host->caps.has_bad_data_ordering = 0; - host->caps.need_reset_after_xfer = 0; + host->caps.has_bad_data_ordering = false; + host->caps.need_reset_after_xfer = false; fallthrough; case 0x0: break; default: - host->caps.has_pdc = 0; + host->caps.has_pdc = false; dev_warn(&host->pdev->dev, "Unmanaged mci version, set minimum capabilities\n"); break; -- cgit v1.2.3 From e55f2cf512591dc0051a32e14b8866668d02706a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 20 Jan 2021 15:28:00 +0100 Subject: mmc: remove dw_mmc-zx driver The zte zx platform is getting removed, so this driver is no longer needed. Cc: Jun Nie Cc: Shawn Guo Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20210120142801.334550-2-arnd@kernel.org Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/zx-dw-mshc.txt | 31 --- drivers/mmc/host/Kconfig | 9 - drivers/mmc/host/Makefile | 1 - drivers/mmc/host/dw_mmc-zx.c | 234 --------------------- drivers/mmc/host/dw_mmc-zx.h | 32 --- 5 files changed, 307 deletions(-) delete mode 100644 Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt delete mode 100644 drivers/mmc/host/dw_mmc-zx.c delete mode 100644 drivers/mmc/host/dw_mmc-zx.h diff --git a/Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt deleted file mode 100644 index 0f59bd5361f5..000000000000 --- a/Documentation/devicetree/bindings/mmc/zx-dw-mshc.txt +++ /dev/null @@ -1,31 +0,0 @@ -* ZTE specific extensions to the Synopsys Designware Mobile Storage - Host Controller - -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 ZTE specific -extensions to the Synopsys Designware Mobile Storage Host Controller. - -Required Properties: - -* compatible: should be - - "zte,zx296718-dw-mshc": for ZX SoCs - -Example: - - mmc1: mmc@1110000 { - compatible = "zte,zx296718-dw-mshc"; - reg = <0x01110000 0x1000>; - interrupts = ; - fifo-depth = <32>; - data-addr = <0x200>; - fifo-watermark-aligned; - bus-width = <4>; - clock-frequency = <50000000>; - clocks = <&topcrm SD0_AHB>, <&topcrm SD0_WCLK>; - clock-names = "biu", "ciu"; - max-frequency = <50000000>; - cap-sdio-irq; - cap-sd-highspeed; - }; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index d6f00d1d6251..1c87ffe9192d 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -882,15 +882,6 @@ config MMC_DW_ROCKCHIP Synopsys DesignWare Memory Card Interface driver. Select this option for platforms based on RK3066, RK3188 and RK3288 SoC's. -config MMC_DW_ZX - tristate "ZTE specific extensions for Synopsys DW Memory Card Interface" - depends on MMC_DW && ARCH_ZX - select MMC_DW_PLTFM - help - This selects support for ZTE SoC specific extensions to the - Synopsys DesignWare Memory Card Interface driver. Select this option - for platforms based on ZX296718 SoC's. - config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" depends on SUPERH || ARCH_RENESAS || COMPILE_TEST diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 451c25fc2c69..43136d382d5f 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -61,7 +61,6 @@ obj-$(CONFIG_MMC_DW_HI3798CV200) += dw_mmc-hi3798cv200.o obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o -obj-$(CONFIG_MMC_DW_ZX) += dw_mmc-zx.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o diff --git a/drivers/mmc/host/dw_mmc-zx.c b/drivers/mmc/host/dw_mmc-zx.c deleted file mode 100644 index 51bcc6332f3a..000000000000 --- a/drivers/mmc/host/dw_mmc-zx.c +++ /dev/null @@ -1,234 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * ZX Specific Extensions for Synopsys DW Multimedia Card Interface driver - * - * Copyright (C) 2016, Linaro Ltd. - * Copyright (C) 2016, ZTE Corp. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "dw_mmc.h" -#include "dw_mmc-pltfm.h" -#include "dw_mmc-zx.h" - -struct dw_mci_zx_priv_data { - struct regmap *sysc_base; -}; - -enum delay_type { - DELAY_TYPE_READ, /* read dqs delay */ - DELAY_TYPE_CLK, /* clk sample delay */ -}; - -static int dw_mci_zx_emmc_set_delay(struct dw_mci *host, unsigned int delay, - enum delay_type dflag) -{ - struct dw_mci_zx_priv_data *priv = host->priv; - struct regmap *sysc_base = priv->sysc_base; - unsigned int clksel; - unsigned int loop = 1000; - int ret; - - if (!sysc_base) - return -EINVAL; - - ret = regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0, - PARA_HALF_CLK_MODE | PARA_DLL_BYPASS_MODE | - PARA_PHASE_DET_SEL_MASK | - PARA_DLL_LOCK_NUM_MASK | - DLL_REG_SET | PARA_DLL_START_MASK, - PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4)); - if (ret) - return ret; - - ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG1, &clksel); - if (ret) - return ret; - - if (dflag == DELAY_TYPE_CLK) { - clksel &= ~CLK_SAMP_DELAY_MASK; - clksel |= CLK_SAMP_DELAY(delay); - } else { - clksel &= ~READ_DQS_DELAY_MASK; - clksel |= READ_DQS_DELAY(delay); - } - - regmap_write(sysc_base, LB_AON_EMMC_CFG_REG1, clksel); - regmap_update_bits(sysc_base, LB_AON_EMMC_CFG_REG0, - PARA_DLL_START_MASK | PARA_DLL_LOCK_NUM_MASK | - DLL_REG_SET, - PARA_DLL_START(4) | PARA_DLL_LOCK_NUM(4) | - DLL_REG_SET); - - do { - ret = regmap_read(sysc_base, LB_AON_EMMC_CFG_REG2, &clksel); - if (ret) - return ret; - - } while (--loop && !(clksel & ZX_DLL_LOCKED)); - - if (!loop) { - dev_err(host->dev, "Error: %s dll lock fail\n", __func__); - return -EIO; - } - - return 0; -} - -static int dw_mci_zx_emmc_execute_tuning(struct dw_mci_slot *slot, u32 opcode) -{ - struct dw_mci *host = slot->host; - struct mmc_host *mmc = slot->mmc; - int ret, len = 0, start = 0, end = 0, delay, best = 0; - - for (delay = 1; delay < 128; delay++) { - ret = dw_mci_zx_emmc_set_delay(host, delay, DELAY_TYPE_CLK); - if (!ret && mmc_send_tuning(mmc, opcode, NULL)) { - if (start >= 0) { - end = delay - 1; - /* check and update longest good range */ - if ((end - start) > len) { - best = (start + end) >> 1; - len = end - start; - } - } - start = -1; - end = 0; - continue; - } - if (start < 0) - start = delay; - } - - if (start >= 0) { - end = delay - 1; - if ((end - start) > len) { - best = (start + end) >> 1; - len = end - start; - } - } - if (best < 0) - return -EIO; - - dev_info(host->dev, "%s best range: start %d end %d\n", __func__, - start, end); - return dw_mci_zx_emmc_set_delay(host, best, DELAY_TYPE_CLK); -} - -static int dw_mci_zx_prepare_hs400_tuning(struct dw_mci *host, - struct mmc_ios *ios) -{ - int ret; - - /* config phase shift as 90 degree */ - ret = dw_mci_zx_emmc_set_delay(host, 32, DELAY_TYPE_READ); - if (ret < 0) - return -EIO; - - return 0; -} - -static int dw_mci_zx_execute_tuning(struct dw_mci_slot *slot, u32 opcode) -{ - struct dw_mci *host = slot->host; - - if (host->verid == 0x290a) /* only for emmc */ - return dw_mci_zx_emmc_execute_tuning(slot, opcode); - /* TODO: Add 0x210a dedicated tuning for sd/sdio */ - - return 0; -} - -static int dw_mci_zx_parse_dt(struct dw_mci *host) -{ - struct device_node *np = host->dev->of_node; - struct device_node *node; - struct dw_mci_zx_priv_data *priv; - struct regmap *sysc_base; - - /* syscon is needed only by emmc */ - node = of_parse_phandle(np, "zte,aon-syscon", 0); - if (node) { - sysc_base = syscon_node_to_regmap(node); - of_node_put(node); - - if (IS_ERR(sysc_base)) - return dev_err_probe(host->dev, PTR_ERR(sysc_base), - "Can't get syscon\n"); - } else { - return 0; - } - - priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - priv->sysc_base = sysc_base; - host->priv = priv; - - return 0; -} - -static unsigned long zx_dwmmc_caps[3] = { - MMC_CAP_CMD23, - MMC_CAP_CMD23, - MMC_CAP_CMD23, -}; - -static const struct dw_mci_drv_data zx_drv_data = { - .caps = zx_dwmmc_caps, - .num_caps = ARRAY_SIZE(zx_dwmmc_caps), - .execute_tuning = dw_mci_zx_execute_tuning, - .prepare_hs400_tuning = dw_mci_zx_prepare_hs400_tuning, - .parse_dt = dw_mci_zx_parse_dt, -}; - -static const struct of_device_id dw_mci_zx_match[] = { - { .compatible = "zte,zx296718-dw-mshc", .data = &zx_drv_data}, - {}, -}; -MODULE_DEVICE_TABLE(of, dw_mci_zx_match); - -static int dw_mci_zx_probe(struct platform_device *pdev) -{ - const struct dw_mci_drv_data *drv_data; - const struct of_device_id *match; - - match = of_match_node(dw_mci_zx_match, pdev->dev.of_node); - drv_data = match->data; - - return dw_mci_pltfm_register(pdev, drv_data); -} - -static const struct dev_pm_ops dw_mci_zx_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, - dw_mci_runtime_resume, - NULL) -}; - -static struct platform_driver dw_mci_zx_pltfm_driver = { - .probe = dw_mci_zx_probe, - .remove = dw_mci_pltfm_remove, - .driver = { - .name = "dwmmc_zx", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .of_match_table = dw_mci_zx_match, - .pm = &dw_mci_zx_dev_pm_ops, - }, -}; - -module_platform_driver(dw_mci_zx_pltfm_driver); - -MODULE_DESCRIPTION("ZTE emmc/sd driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/dw_mmc-zx.h b/drivers/mmc/host/dw_mmc-zx.h deleted file mode 100644 index 09ac52766f14..000000000000 --- a/drivers/mmc/host/dw_mmc-zx.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _DW_MMC_ZX_H_ -#define _DW_MMC_ZX_H_ - -/* ZX296718 SoC specific DLL register offset. */ -#define LB_AON_EMMC_CFG_REG0 0x1B0 -#define LB_AON_EMMC_CFG_REG1 0x1B4 -#define LB_AON_EMMC_CFG_REG2 0x1B8 - -/* LB_AON_EMMC_CFG_REG0 register defines */ -#define PARA_DLL_START(x) ((x) & 0xFF) -#define PARA_DLL_START_MASK 0xFF -#define DLL_REG_SET BIT(8) -#define PARA_DLL_LOCK_NUM(x) (((x) & 7) << 16) -#define PARA_DLL_LOCK_NUM_MASK (7 << 16) -#define PARA_PHASE_DET_SEL(x) (((x) & 7) << 20) -#define PARA_PHASE_DET_SEL_MASK (7 << 20) -#define PARA_DLL_BYPASS_MODE BIT(23) -#define PARA_HALF_CLK_MODE BIT(24) - -/* LB_AON_EMMC_CFG_REG1 register defines */ -#define READ_DQS_DELAY(x) ((x) & 0x7F) -#define READ_DQS_DELAY_MASK (0x7F) -#define READ_DQS_BYPASS_MODE BIT(7) -#define CLK_SAMP_DELAY(x) (((x) & 0x7F) << 8) -#define CLK_SAMP_DELAY_MASK (0x7F << 8) -#define CLK_SAMP_BYPASS_MODE BIT(15) - -/* LB_AON_EMMC_CFG_REG2 register defines */ -#define ZX_DLL_LOCKED BIT(2) - -#endif /* _DW_MMC_ZX_H_ */ -- cgit v1.2.3 From c7b9f01fd12a46333c917f416b0df6ee39e7c774 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 20 Jan 2021 15:28:01 +0100 Subject: mmc: remove sirf prima/atlas driver The CSR SiRF prima2/atlas platforms are getting removed, so this driver is no longer needed. Cc: Barry Song Signed-off-by: Arnd Bergmann Acked-by: Barry Song Link: https://lore.kernel.org/r/20210120142801.334550-3-arnd@kernel.org Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/sdhci-sirf.txt | 18 -- drivers/mmc/host/Kconfig | 12 -- drivers/mmc/host/Makefile | 1 - drivers/mmc/host/sdhci-sirf.c | 235 --------------------- 4 files changed, 266 deletions(-) delete mode 100644 Documentation/devicetree/bindings/mmc/sdhci-sirf.txt delete mode 100644 drivers/mmc/host/sdhci-sirf.c diff --git a/Documentation/devicetree/bindings/mmc/sdhci-sirf.txt b/Documentation/devicetree/bindings/mmc/sdhci-sirf.txt deleted file mode 100644 index dd6ed464bcb8..000000000000 --- a/Documentation/devicetree/bindings/mmc/sdhci-sirf.txt +++ /dev/null @@ -1,18 +0,0 @@ -* SiRFprimII/marco/atlas6 SDHCI Controller - -This file documents differences between the core properties in mmc.txt -and the properties used by the sdhci-sirf driver. - -Required properties: -- compatible: sirf,prima2-sdhc - -Optional properties: -- cd-gpios: card detect gpio, with zero flags. - -Example: - - sd0: sdhci@56000000 { - compatible = "sirf,prima2-sdhc"; - reg = <0xcd000000 0x100000>; - cd-gpios = <&gpio 6 0>; - }; diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 1c87ffe9192d..bbf6989e3638 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -326,18 +326,6 @@ config MMC_SDHCI_S3C If unsure, say N. -config MMC_SDHCI_SIRF - tristate "SDHCI support on CSR SiRFprimaII and SiRFmarco SoCs" - depends on ARCH_SIRF || COMPILE_TEST - depends on MMC_SDHCI_PLTFM - select MMC_SDHCI_IO_ACCESSORS - help - This selects the SDHCI support for SiRF System-on-Chip devices. - - If you have a controller with this interface, say Y or M here. - - If unsure, say N. - config MMC_SDHCI_PXAV3 tristate "Marvell MMP2 SD Host Controller support (PXAV3)" depends on CLKDEV_LOOKUP diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 43136d382d5f..d2ec428cc808 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o -obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o obj-$(CONFIG_MMC_SDHCI_F_SDH30) += sdhci_f_sdh30.o obj-$(CONFIG_MMC_SDHCI_MILBEAUT) += sdhci-milbeaut.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o diff --git a/drivers/mmc/host/sdhci-sirf.c b/drivers/mmc/host/sdhci-sirf.c deleted file mode 100644 index e9b347b3af7e..000000000000 --- a/drivers/mmc/host/sdhci-sirf.c +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * SDHCI support for SiRF primaII and marco SoCs - * - * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. - */ - -#include -#include -#include -#include -#include -#include -#include "sdhci-pltfm.h" - -#define SDHCI_CLK_DELAY_SETTING 0x4C -#define SDHCI_SIRF_8BITBUS BIT(3) -#define SIRF_TUNING_COUNT 16384 - -static void sdhci_sirf_set_bus_width(struct sdhci_host *host, int width) -{ - u8 ctrl; - - ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); - ctrl &= ~(SDHCI_CTRL_4BITBUS | SDHCI_SIRF_8BITBUS); - - /* - * CSR atlas7 and prima2 SD host version is not 3.0 - * 8bit-width enable bit of CSR SD hosts is 3, - * while stardard hosts use bit 5 - */ - if (width == MMC_BUS_WIDTH_8) - ctrl |= SDHCI_SIRF_8BITBUS; - else if (width == MMC_BUS_WIDTH_4) - ctrl |= SDHCI_CTRL_4BITBUS; - - sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); -} - -static u32 sdhci_sirf_readl_le(struct sdhci_host *host, int reg) -{ - u32 val = readl(host->ioaddr + reg); - - if (unlikely((reg == SDHCI_CAPABILITIES_1) && - (host->mmc->caps & MMC_CAP_UHS_SDR50))) { - /* fake CAP_1 register */ - val = SDHCI_SUPPORT_DDR50 | - SDHCI_SUPPORT_SDR50 | SDHCI_USE_SDR50_TUNING; - } - - if (unlikely(reg == SDHCI_SLOT_INT_STATUS)) { - u32 prss = val; - /* fake chips as V3.0 host conreoller */ - prss &= ~(0xFF << 16); - val = prss | (SDHCI_SPEC_300 << 16); - } - return val; -} - -static u16 sdhci_sirf_readw_le(struct sdhci_host *host, int reg) -{ - u16 ret = 0; - - ret = readw(host->ioaddr + reg); - - if (unlikely(reg == SDHCI_HOST_VERSION)) { - ret = readw(host->ioaddr + SDHCI_HOST_VERSION); - ret |= SDHCI_SPEC_300; - } - - return ret; -} - -static int sdhci_sirf_execute_tuning(struct sdhci_host *host, u32 opcode) -{ - int tuning_seq_cnt = 3; - int phase; - u8 tuned_phase_cnt = 0; - int rc = 0, longest_range = 0; - int start = -1, end = 0, tuning_value = -1, range = 0; - u16 clock_setting; - struct mmc_host *mmc = host->mmc; - - clock_setting = sdhci_readw(host, SDHCI_CLK_DELAY_SETTING); - clock_setting &= ~0x3fff; - -retry: - phase = 0; - tuned_phase_cnt = 0; - do { - sdhci_writel(host, - clock_setting | phase, - SDHCI_CLK_DELAY_SETTING); - - if (!mmc_send_tuning(mmc, opcode, NULL)) { - /* Tuning is successful at this tuning point */ - tuned_phase_cnt++; - dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n", - mmc_hostname(mmc), phase); - if (start == -1) - start = phase; - end = phase; - range++; - if (phase == (SIRF_TUNING_COUNT - 1) - && range > longest_range) - tuning_value = (start + end) / 2; - } else { - dev_dbg(mmc_dev(mmc), "%s: Found bad phase = %d\n", - mmc_hostname(mmc), phase); - if (range > longest_range) { - tuning_value = (start + end) / 2; - longest_range = range; - } - start = -1; - end = range = 0; - } - } while (++phase < SIRF_TUNING_COUNT); - - if (tuned_phase_cnt && tuning_value > 0) { - /* - * Finally set the selected phase in delay - * line hw block. - */ - phase = tuning_value; - sdhci_writel(host, - clock_setting | phase, - SDHCI_CLK_DELAY_SETTING); - - dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n", - mmc_hostname(mmc), phase); - } else { - if (--tuning_seq_cnt) - goto retry; - /* Tuning failed */ - dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n", - mmc_hostname(mmc)); - rc = -EIO; - } - - return rc; -} - -static const struct sdhci_ops sdhci_sirf_ops = { - .read_l = sdhci_sirf_readl_le, - .read_w = sdhci_sirf_readw_le, - .platform_execute_tuning = sdhci_sirf_execute_tuning, - .set_clock = sdhci_set_clock, - .get_max_clock = sdhci_pltfm_clk_get_max_clock, - .set_bus_width = sdhci_sirf_set_bus_width, - .reset = sdhci_reset, - .set_uhs_signaling = sdhci_set_uhs_signaling, -}; - -static const struct sdhci_pltfm_data sdhci_sirf_pdata = { - .ops = &sdhci_sirf_ops, - .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | - SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_RESET_CMD_DATA_ON_IOS, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, -}; - -static int sdhci_sirf_probe(struct platform_device *pdev) -{ - struct sdhci_host *host; - struct sdhci_pltfm_host *pltfm_host; - struct clk *clk; - int ret; - - clk = devm_clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "unable to get clock"); - return PTR_ERR(clk); - } - - host = sdhci_pltfm_init(pdev, &sdhci_sirf_pdata, 0); - if (IS_ERR(host)) - return PTR_ERR(host); - - pltfm_host = sdhci_priv(host); - pltfm_host->clk = clk; - - sdhci_get_of_property(pdev); - - ret = clk_prepare_enable(pltfm_host->clk); - if (ret) - goto err_clk_prepare; - - ret = sdhci_add_host(host); - if (ret) - goto err_sdhci_add; - - /* - * We must request the IRQ after sdhci_add_host(), as the tasklet only - * gets setup in sdhci_add_host() and we oops. - */ - ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0); - if (ret == -EPROBE_DEFER) - goto err_request_cd; - if (!ret) - mmc_gpiod_request_cd_irq(host->mmc); - - return 0; - -err_request_cd: - sdhci_remove_host(host, 0); -err_sdhci_add: - clk_disable_unprepare(pltfm_host->clk); -err_clk_prepare: - sdhci_pltfm_free(pdev); - return ret; -} - -static const struct of_device_id sdhci_sirf_of_match[] = { - { .compatible = "sirf,prima2-sdhc" }, - { } -}; -MODULE_DEVICE_TABLE(of, sdhci_sirf_of_match); - -static struct platform_driver sdhci_sirf_driver = { - .driver = { - .name = "sdhci-sirf", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .of_match_table = sdhci_sirf_of_match, - .pm = &sdhci_pltfm_pmops, - }, - .probe = sdhci_sirf_probe, - .remove = sdhci_pltfm_unregister, -}; - -module_platform_driver(sdhci_sirf_driver); - -MODULE_DESCRIPTION("SDHCI driver for SiRFprimaII/SiRFmarco"); -MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 4af307f574260cb1b768be84c81ce572da7a84ea Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 22 Jan 2021 22:18:52 +1030 Subject: mmc: sdhci-of-aspeed: Fix kunit-related build error Randy found that with the following Kconfig settings we have duplicate definitions (e.g. __inittest()) in sdhci-of-aspeed due to competing module_init()/module_exit() calls from kunit and driver the itself. ``` CONFIG_MMC_SDHCI_OF_ASPEED=m CONFIG_MMC_SDHCI_OF_ASPEED_TEST=y ``` Conditionally open-code the kunit initialisation to avoid the error. Fixes: 7efa02a981d6 ("mmc: sdhci-of-aspeed: Add KUnit tests for phase calculations") Reported-by: Randy Dunlap Signed-off-by: Andrew Jeffery Acked-by: Randy Dunlap # build-tested Link: https://lore.kernel.org/r/20210122114852.3790565-1-andrew@aj.id.au Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-aspeed-test.c | 9 ++++++- drivers/mmc/host/sdhci-of-aspeed.c | 42 +++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-aspeed-test.c b/drivers/mmc/host/sdhci-of-aspeed-test.c index 34070605b28b..bb67d159b7d8 100644 --- a/drivers/mmc/host/sdhci-of-aspeed-test.c +++ b/drivers/mmc/host/sdhci-of-aspeed-test.c @@ -95,4 +95,11 @@ static struct kunit_suite aspeed_sdhci_test_suite = { .name = "sdhci-of-aspeed", .test_cases = aspeed_sdhci_test_cases, }; -kunit_test_suite(aspeed_sdhci_test_suite); + +static struct kunit_suite *aspeed_sdc_test_suite_array[] = { + &aspeed_sdhci_test_suite, + NULL, +}; + +static struct kunit_suite **aspeed_sdc_test_suites + __used __section(".kunit_test_suites") = aspeed_sdc_test_suite_array; diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index 3b0d381e1215..7d8692e90996 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -556,6 +556,29 @@ static struct platform_driver aspeed_sdc_driver = { .remove = aspeed_sdc_remove, }; +#if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST) +#include "sdhci-of-aspeed-test.c" + +static inline int aspeed_sdc_tests_init(void) +{ + return __kunit_test_suites_init(aspeed_sdc_test_suites); +} + +static inline void aspeed_sdc_tests_exit(void) +{ + __kunit_test_suites_exit(aspeed_sdc_test_suites); +} +#else +static inline int aspeed_sdc_tests_init(void) +{ + return 0; +} + +static inline void aspeed_sdc_tests_exit(void) +{ +} +#endif + static int __init aspeed_sdc_init(void) { int rc; @@ -566,7 +589,18 @@ static int __init aspeed_sdc_init(void) rc = platform_driver_register(&aspeed_sdc_driver); if (rc < 0) - platform_driver_unregister(&aspeed_sdhci_driver); + goto cleanup_sdhci; + + rc = aspeed_sdc_tests_init(); + if (rc < 0) { + platform_driver_unregister(&aspeed_sdc_driver); + goto cleanup_sdhci; + } + + return 0; + +cleanup_sdhci: + platform_driver_unregister(&aspeed_sdhci_driver); return rc; } @@ -574,15 +608,13 @@ module_init(aspeed_sdc_init); static void __exit aspeed_sdc_exit(void) { + aspeed_sdc_tests_exit(); + platform_driver_unregister(&aspeed_sdc_driver); platform_driver_unregister(&aspeed_sdhci_driver); } module_exit(aspeed_sdc_exit); -#if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST) -#include "sdhci-of-aspeed-test.c" -#endif - MODULE_DESCRIPTION("Driver for the ASPEED SD/SDIO/SDHCI Controllers"); MODULE_AUTHOR("Ryan Chen "); MODULE_AUTHOR("Andrew Jeffery "); -- cgit v1.2.3 From 5851d3b042b694839d2241fbb3200ce958135cdf Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 21 Jan 2021 00:21:54 -0800 Subject: block/keyslot-manager: introduce devm_blk_ksm_init() Add a resource-managed variant of blk_ksm_init() so that drivers don't have to worry about calling blk_ksm_destroy(). Note that the implementation uses a custom devres action to call blk_ksm_destroy() rather than switching the two allocations to be directly devres-managed, e.g. with devm_kmalloc(). This is because we need to keep zeroing the memory containing the keyslots when it is freed, and also because we want to continue using kvmalloc() (and there is no devm_kvmalloc()). Signed-off-by: Eric Biggers Reviewed-by: Satya Tangirala Acked-by: Jens Axboe Link: https://lore.kernel.org/r/20210121082155.111333-2-ebiggers@kernel.org Signed-off-by: Ulf Hansson --- Documentation/block/inline-encryption.rst | 12 ++++++------ block/keyslot-manager.c | 29 +++++++++++++++++++++++++++++ include/linux/keyslot-manager.h | 3 +++ 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/Documentation/block/inline-encryption.rst b/Documentation/block/inline-encryption.rst index e75151e467d3..7f9b40d6b416 100644 --- a/Documentation/block/inline-encryption.rst +++ b/Documentation/block/inline-encryption.rst @@ -182,8 +182,9 @@ API presented to device drivers A :c:type:``struct blk_keyslot_manager`` should be set up by device drivers in the ``request_queue`` of the device. The device driver needs to call -``blk_ksm_init`` on the ``blk_keyslot_manager``, which specifying the number of -keyslots supported by the hardware. +``blk_ksm_init`` (or its resource-managed variant ``devm_blk_ksm_init``) on the +``blk_keyslot_manager``, while specifying the number of keyslots supported by +the hardware. The device driver also needs to tell the KSM how to actually manipulate the IE hardware in the device to do things like programming the crypto key into @@ -202,10 +203,9 @@ needs each and every of its keyslots to be reprogrammed with the key it "should have" at the point in time when the function is called. This is useful e.g. if a device loses all its keys on runtime power down/up. -``blk_ksm_destroy`` should be called to free up all resources used by a keyslot -manager upon ``blk_ksm_init``, once the ``blk_keyslot_manager`` is no longer -needed. - +If the driver used ``blk_ksm_init`` instead of ``devm_blk_ksm_init``, then +``blk_ksm_destroy`` should be called to free up all resources used by a +``blk_keyslot_manager`` once it is no longer needed. Layered Devices =============== diff --git a/block/keyslot-manager.c b/block/keyslot-manager.c index 86f8195d8039..324bf4244f5f 100644 --- a/block/keyslot-manager.c +++ b/block/keyslot-manager.c @@ -29,6 +29,7 @@ #define pr_fmt(fmt) "blk-crypto: " fmt #include +#include #include #include #include @@ -127,6 +128,34 @@ err_destroy_ksm: } EXPORT_SYMBOL_GPL(blk_ksm_init); +static void blk_ksm_destroy_callback(void *ksm) +{ + blk_ksm_destroy(ksm); +} + +/** + * devm_blk_ksm_init() - Resource-managed blk_ksm_init() + * @dev: The device which owns the blk_keyslot_manager. + * @ksm: The blk_keyslot_manager to initialize. + * @num_slots: The number of key slots to manage. + * + * Like blk_ksm_init(), but causes blk_ksm_destroy() to be called automatically + * on driver detach. + * + * Return: 0 on success, or else a negative error code. + */ +int devm_blk_ksm_init(struct device *dev, struct blk_keyslot_manager *ksm, + unsigned int num_slots) +{ + int err = blk_ksm_init(ksm, num_slots); + + if (err) + return err; + + return devm_add_action_or_reset(dev, blk_ksm_destroy_callback, ksm); +} +EXPORT_SYMBOL_GPL(devm_blk_ksm_init); + static inline struct hlist_head * blk_ksm_hash_bucket_for_key(struct blk_keyslot_manager *ksm, const struct blk_crypto_key *key) diff --git a/include/linux/keyslot-manager.h b/include/linux/keyslot-manager.h index 18f3f5346843..443ad817c6c5 100644 --- a/include/linux/keyslot-manager.h +++ b/include/linux/keyslot-manager.h @@ -85,6 +85,9 @@ struct blk_keyslot_manager { int blk_ksm_init(struct blk_keyslot_manager *ksm, unsigned int num_slots); +int devm_blk_ksm_init(struct device *dev, struct blk_keyslot_manager *ksm, + unsigned int num_slots); + blk_status_t blk_ksm_get_slot_for_key(struct blk_keyslot_manager *ksm, const struct blk_crypto_key *key, struct blk_ksm_keyslot **slot_ptr); -- cgit v1.2.3 From d76d9d7d1009968dd3a0fc30e5f5ee9fbffc1350 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 21 Jan 2021 00:21:55 -0800 Subject: scsi: ufs: use devm_blk_ksm_init() Use the new resource-managed variant of blk_ksm_init() so that the UFS driver doesn't have to manually call blk_ksm_destroy(). Signed-off-by: Eric Biggers Reviewed-by: Satya Tangirala Link: https://lore.kernel.org/r/20210121082155.111333-3-ebiggers@kernel.org Acked-by: Martin K. Petersen Signed-off-by: Ulf Hansson --- drivers/scsi/ufs/ufshcd-crypto.c | 9 ++------- drivers/scsi/ufs/ufshcd-crypto.h | 5 ----- drivers/scsi/ufs/ufshcd.c | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/ufs/ufshcd-crypto.c b/drivers/scsi/ufs/ufshcd-crypto.c index 07310b12a5dc..153dd5765d9c 100644 --- a/drivers/scsi/ufs/ufshcd-crypto.c +++ b/drivers/scsi/ufs/ufshcd-crypto.c @@ -179,8 +179,8 @@ int ufshcd_hba_init_crypto_capabilities(struct ufs_hba *hba) } /* The actual number of configurations supported is (CFGC+1) */ - err = blk_ksm_init(&hba->ksm, - hba->crypto_capabilities.config_count + 1); + err = devm_blk_ksm_init(hba->dev, &hba->ksm, + hba->crypto_capabilities.config_count + 1); if (err) goto out_free_caps; @@ -238,8 +238,3 @@ void ufshcd_crypto_setup_rq_keyslot_manager(struct ufs_hba *hba, if (hba->caps & UFSHCD_CAP_CRYPTO) blk_ksm_register(&hba->ksm, q); } - -void ufshcd_crypto_destroy_keyslot_manager(struct ufs_hba *hba) -{ - blk_ksm_destroy(&hba->ksm); -} diff --git a/drivers/scsi/ufs/ufshcd-crypto.h b/drivers/scsi/ufs/ufshcd-crypto.h index d53851be5541..78a58e788dff 100644 --- a/drivers/scsi/ufs/ufshcd-crypto.h +++ b/drivers/scsi/ufs/ufshcd-crypto.h @@ -43,8 +43,6 @@ void ufshcd_init_crypto(struct ufs_hba *hba); void ufshcd_crypto_setup_rq_keyslot_manager(struct ufs_hba *hba, struct request_queue *q); -void ufshcd_crypto_destroy_keyslot_manager(struct ufs_hba *hba); - #else /* CONFIG_SCSI_UFS_CRYPTO */ static inline void ufshcd_prepare_lrbp_crypto(struct request *rq, @@ -69,9 +67,6 @@ static inline void ufshcd_init_crypto(struct ufs_hba *hba) { } static inline void ufshcd_crypto_setup_rq_keyslot_manager(struct ufs_hba *hba, struct request_queue *q) { } -static inline void ufshcd_crypto_destroy_keyslot_manager(struct ufs_hba *hba) -{ } - #endif /* CONFIG_SCSI_UFS_CRYPTO */ #endif /* _UFSHCD_CRYPTO_H */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index fb32d122f2e3..4c24eb782835 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -9152,7 +9152,6 @@ EXPORT_SYMBOL_GPL(ufshcd_remove); */ void ufshcd_dealloc_host(struct ufs_hba *hba) { - ufshcd_crypto_destroy_keyslot_manager(hba); scsi_host_put(hba->host); } EXPORT_SYMBOL_GPL(ufshcd_dealloc_host); -- cgit v1.2.3 From 93f1c150cb0d043e1e8985db7824b4e2e1ac653f Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 25 Jan 2021 16:14:48 -0800 Subject: mmc: core: Add basic support for inline encryption In preparation for adding CQHCI crypto engine (inline encryption) support, add the code required to make mmc_core and mmc_block aware of inline encryption. Specifically: - Add a capability flag MMC_CAP2_CRYPTO to struct mmc_host. Drivers will set this if the host and driver support inline encryption. - Embed a blk_keyslot_manager in struct mmc_host. Drivers will initialize this (as a device-managed resource) if the host and driver support inline encryption. mmc_block registers this keyslot manager with the request_queue of any MMC card attached to the host. - Make mmc_block copy the crypto keyslot and crypto data unit number from struct request to struct mmc_request, so that drivers will have access to them. - If the MMC host is reset, reprogram all the keyslots to ensure that the software state stays in sync with the hardware state. Co-developed-by: Satya Tangirala Signed-off-by: Satya Tangirala Acked-by: Adrian Hunter Reviewed-by: Satya Tangirala Reviewed-and-tested-by: Peng Zhou Signed-off-by: Eric Biggers Link: https://lore.kernel.org/r/20210126001456.382989-2-ebiggers@kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/Kconfig | 8 ++++++++ drivers/mmc/core/Makefile | 1 + drivers/mmc/core/block.c | 3 +++ drivers/mmc/core/core.c | 3 +++ drivers/mmc/core/crypto.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/crypto.h | 40 +++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/host.c | 1 + drivers/mmc/core/queue.c | 3 +++ include/linux/mmc/core.h | 6 ++++++ include/linux/mmc/host.h | 11 +++++++++++ 10 files changed, 124 insertions(+) create mode 100644 drivers/mmc/core/crypto.c create mode 100644 drivers/mmc/core/crypto.h diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index c12fe13e4b14..ae8b69aee619 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -81,3 +81,11 @@ config MMC_TEST This driver is only of interest to those developing or testing a host driver. Most people should say N here. +config MMC_CRYPTO + bool "MMC Crypto Engine Support" + depends on BLK_INLINE_ENCRYPTION + help + Enable Crypto Engine Support in MMC. + Enabling this makes it possible for the kernel to use the crypto + capabilities of the MMC device (if present) to perform crypto + operations on data being transferred to/from the device. diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 95ffe008ebdf..6a907736cd7a 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_MMC_BLOCK) += mmc_block.o mmc_block-objs := block.o queue.o obj-$(CONFIG_MMC_TEST) += mmc_test.o obj-$(CONFIG_SDIO_UART) += sdio_uart.o +mmc_core-$(CONFIG_MMC_CRYPTO) += crypto.o diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 42e27a298218..b877f62df366 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -51,6 +51,7 @@ #include "block.h" #include "core.h" #include "card.h" +#include "crypto.h" #include "host.h" #include "bus.h" #include "mmc_ops.h" @@ -1247,6 +1248,8 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq, memset(brq, 0, sizeof(struct mmc_blk_request)); + mmc_crypto_prepare_req(mqrq); + brq->mrq.data = &brq->data; brq->mrq.tag = req->tag; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 49d9117550af..1136b859ddd8 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -37,6 +37,7 @@ #include "core.h" #include "card.h" +#include "crypto.h" #include "bus.h" #include "host.h" #include "sdio_bus.h" @@ -992,6 +993,8 @@ void mmc_set_initial_state(struct mmc_host *host) host->ops->hs400_enhanced_strobe(host, &host->ios); mmc_set_ios(host); + + mmc_crypto_set_initial_state(host); } /** diff --git a/drivers/mmc/core/crypto.c b/drivers/mmc/core/crypto.c new file mode 100644 index 000000000000..419a368f8402 --- /dev/null +++ b/drivers/mmc/core/crypto.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MMC crypto engine (inline encryption) support + * + * Copyright 2020 Google LLC + */ + +#include +#include + +#include "core.h" +#include "crypto.h" +#include "queue.h" + +void mmc_crypto_set_initial_state(struct mmc_host *host) +{ + /* Reset might clear all keys, so reprogram all the keys. */ + if (host->caps2 & MMC_CAP2_CRYPTO) + blk_ksm_reprogram_all_keys(&host->ksm); +} + +void mmc_crypto_setup_queue(struct request_queue *q, struct mmc_host *host) +{ + if (host->caps2 & MMC_CAP2_CRYPTO) + blk_ksm_register(&host->ksm, q); +} +EXPORT_SYMBOL_GPL(mmc_crypto_setup_queue); + +void mmc_crypto_prepare_req(struct mmc_queue_req *mqrq) +{ + struct request *req = mmc_queue_req_to_req(mqrq); + struct mmc_request *mrq = &mqrq->brq.mrq; + + if (!req->crypt_keyslot) + return; + + mrq->crypto_enabled = true; + mrq->crypto_key_slot = blk_ksm_get_slot_idx(req->crypt_keyslot); + + /* + * For now we assume that all MMC drivers set max_dun_bytes_supported=4, + * which is the limit for CQHCI crypto. So all DUNs should be 32-bit. + */ + WARN_ON_ONCE(req->crypt_ctx->bc_dun[0] > U32_MAX); + + mrq->data_unit_num = req->crypt_ctx->bc_dun[0]; +} +EXPORT_SYMBOL_GPL(mmc_crypto_prepare_req); diff --git a/drivers/mmc/core/crypto.h b/drivers/mmc/core/crypto.h new file mode 100644 index 000000000000..fbe9a520bf90 --- /dev/null +++ b/drivers/mmc/core/crypto.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * MMC crypto engine (inline encryption) support + * + * Copyright 2020 Google LLC + */ + +#ifndef _MMC_CORE_CRYPTO_H +#define _MMC_CORE_CRYPTO_H + +struct mmc_host; +struct mmc_queue_req; +struct request_queue; + +#ifdef CONFIG_MMC_CRYPTO + +void mmc_crypto_set_initial_state(struct mmc_host *host); + +void mmc_crypto_setup_queue(struct request_queue *q, struct mmc_host *host); + +void mmc_crypto_prepare_req(struct mmc_queue_req *mqrq); + +#else /* CONFIG_MMC_CRYPTO */ + +static inline void mmc_crypto_set_initial_state(struct mmc_host *host) +{ +} + +static inline void mmc_crypto_setup_queue(struct request_queue *q, + struct mmc_host *host) +{ +} + +static inline void mmc_crypto_prepare_req(struct mmc_queue_req *mqrq) +{ +} + +#endif /* !CONFIG_MMC_CRYPTO */ + +#endif /* _MMC_CORE_CRYPTO_H */ diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 74e853bb6948..9b89a91b6b47 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -25,6 +25,7 @@ #include #include "core.h" +#include "crypto.h" #include "host.h" #include "slot-gpio.h" #include "pwrseq.h" diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index 002426e3cf76..33e7e65b6dde 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -19,6 +19,7 @@ #include "block.h" #include "core.h" #include "card.h" +#include "crypto.h" #include "host.h" #define MMC_DMA_MAP_MERGE_SEGMENTS 512 @@ -407,6 +408,8 @@ static void mmc_setup_queue(struct mmc_queue *mq, struct mmc_card *card) mutex_init(&mq->complete_lock); init_waitqueue_head(&mq->wait); + + mmc_crypto_setup_queue(mq->queue, host); } static inline bool mmc_merge_capable(struct mmc_host *host) diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index 29aa50711626..ab19245e9945 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -162,6 +162,12 @@ struct mmc_request { bool cap_cmd_during_tfr; int tag; + +#ifdef CONFIG_MMC_CRYPTO + bool crypto_enabled; + int crypto_key_slot; + u32 data_unit_num; +#endif }; struct mmc_card; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 8cf686d98a68..927ba7566617 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -15,6 +15,7 @@ #include #include #include +#include struct mmc_ios { unsigned int clock; /* clock rate */ @@ -395,6 +396,11 @@ struct mmc_host { #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 */ #define MMC_CAP2_MERGE_CAPABLE (1 << 26) /* Host can merge a segment over the segment size */ +#ifdef CONFIG_MMC_CRYPTO +#define MMC_CAP2_CRYPTO (1 << 27) /* Host supports inline encryption */ +#else +#define MMC_CAP2_CRYPTO 0 +#endif int fixed_drv_type; /* fixed driver type for non-removable media */ @@ -489,6 +495,11 @@ struct mmc_host { bool cqe_enabled; bool cqe_on; + /* Inline encryption support */ +#ifdef CONFIG_MMC_CRYPTO + struct blk_keyslot_manager ksm; +#endif + /* Host Software Queue support */ bool hsq_enabled; -- cgit v1.2.3 From 0653300224a696719953a19efe13e1c6e9b8cceb Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 25 Jan 2021 16:14:49 -0800 Subject: mmc: cqhci: rename cqhci.c to cqhci-core.c Rename cqhci.c to cqhci-core.c so that another source file can be added to the cqhci module without having to rename the module. Acked-by: Adrian Hunter Reviewed-and-tested-by: Peng Zhou Signed-off-by: Eric Biggers Link: https://lore.kernel.org/r/20210126001456.382989-3-ebiggers@kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/Makefile | 1 + drivers/mmc/host/cqhci-core.c | 1165 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/cqhci.c | 1165 ----------------------------------------- 3 files changed, 1166 insertions(+), 1165 deletions(-) create mode 100644 drivers/mmc/host/cqhci-core.c delete mode 100644 drivers/mmc/host/cqhci.c diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index d2ec428cc808..19687ad42c6b 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -102,6 +102,7 @@ obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o obj-$(CONFIG_MMC_SDHCI_SPRD) += sdhci-sprd.o obj-$(CONFIG_MMC_CQHCI) += cqhci.o +cqhci-y += cqhci-core.o obj-$(CONFIG_MMC_HSQ) += mmc_hsq.o ifeq ($(CONFIG_CB710_DEBUG),y) diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c new file mode 100644 index 000000000000..697fe40756bf --- /dev/null +++ b/drivers/mmc/host/cqhci-core.c @@ -0,0 +1,1165 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2015, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cqhci.h" + +#define DCMD_SLOT 31 +#define NUM_SLOTS 32 + +struct cqhci_slot { + struct mmc_request *mrq; + unsigned int flags; +#define CQHCI_EXTERNAL_TIMEOUT BIT(0) +#define CQHCI_COMPLETED BIT(1) +#define CQHCI_HOST_CRC BIT(2) +#define CQHCI_HOST_TIMEOUT BIT(3) +#define CQHCI_HOST_OTHER BIT(4) +}; + +static inline u8 *get_desc(struct cqhci_host *cq_host, u8 tag) +{ + return cq_host->desc_base + (tag * cq_host->slot_sz); +} + +static inline u8 *get_link_desc(struct cqhci_host *cq_host, u8 tag) +{ + u8 *desc = get_desc(cq_host, tag); + + return desc + cq_host->task_desc_len; +} + +static inline dma_addr_t get_trans_desc_dma(struct cqhci_host *cq_host, u8 tag) +{ + return cq_host->trans_desc_dma_base + + (cq_host->mmc->max_segs * tag * + cq_host->trans_desc_len); +} + +static inline u8 *get_trans_desc(struct cqhci_host *cq_host, u8 tag) +{ + return cq_host->trans_desc_base + + (cq_host->trans_desc_len * cq_host->mmc->max_segs * tag); +} + +static void setup_trans_desc(struct cqhci_host *cq_host, u8 tag) +{ + u8 *link_temp; + dma_addr_t trans_temp; + + link_temp = get_link_desc(cq_host, tag); + trans_temp = get_trans_desc_dma(cq_host, tag); + + memset(link_temp, 0, cq_host->link_desc_len); + if (cq_host->link_desc_len > 8) + *(link_temp + 8) = 0; + + if (tag == DCMD_SLOT && (cq_host->mmc->caps2 & MMC_CAP2_CQE_DCMD)) { + *link_temp = CQHCI_VALID(0) | CQHCI_ACT(0) | CQHCI_END(1); + return; + } + + *link_temp = CQHCI_VALID(1) | CQHCI_ACT(0x6) | CQHCI_END(0); + + if (cq_host->dma64) { + __le64 *data_addr = (__le64 __force *)(link_temp + 4); + + data_addr[0] = cpu_to_le64(trans_temp); + } else { + __le32 *data_addr = (__le32 __force *)(link_temp + 4); + + data_addr[0] = cpu_to_le32(trans_temp); + } +} + +static void cqhci_set_irqs(struct cqhci_host *cq_host, u32 set) +{ + cqhci_writel(cq_host, set, CQHCI_ISTE); + cqhci_writel(cq_host, set, CQHCI_ISGE); +} + +#define DRV_NAME "cqhci" + +#define CQHCI_DUMP(f, x...) \ + pr_err("%s: " DRV_NAME ": " f, mmc_hostname(mmc), ## x) + +static void cqhci_dumpregs(struct cqhci_host *cq_host) +{ + struct mmc_host *mmc = cq_host->mmc; + + CQHCI_DUMP("============ CQHCI REGISTER DUMP ===========\n"); + + CQHCI_DUMP("Caps: 0x%08x | Version: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_CAP), + cqhci_readl(cq_host, CQHCI_VER)); + CQHCI_DUMP("Config: 0x%08x | Control: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_CFG), + cqhci_readl(cq_host, CQHCI_CTL)); + CQHCI_DUMP("Int stat: 0x%08x | Int enab: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_IS), + cqhci_readl(cq_host, CQHCI_ISTE)); + CQHCI_DUMP("Int sig: 0x%08x | Int Coal: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_ISGE), + cqhci_readl(cq_host, CQHCI_IC)); + CQHCI_DUMP("TDL base: 0x%08x | TDL up32: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_TDLBA), + cqhci_readl(cq_host, CQHCI_TDLBAU)); + CQHCI_DUMP("Doorbell: 0x%08x | TCN: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_TDBR), + cqhci_readl(cq_host, CQHCI_TCN)); + CQHCI_DUMP("Dev queue: 0x%08x | Dev Pend: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_DQS), + cqhci_readl(cq_host, CQHCI_DPT)); + CQHCI_DUMP("Task clr: 0x%08x | SSC1: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_TCLR), + cqhci_readl(cq_host, CQHCI_SSC1)); + CQHCI_DUMP("SSC2: 0x%08x | DCMD rsp: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_SSC2), + cqhci_readl(cq_host, CQHCI_CRDCT)); + CQHCI_DUMP("RED mask: 0x%08x | TERRI: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_RMEM), + cqhci_readl(cq_host, CQHCI_TERRI)); + CQHCI_DUMP("Resp idx: 0x%08x | Resp arg: 0x%08x\n", + cqhci_readl(cq_host, CQHCI_CRI), + cqhci_readl(cq_host, CQHCI_CRA)); + + if (cq_host->ops->dumpregs) + cq_host->ops->dumpregs(mmc); + else + CQHCI_DUMP(": ===========================================\n"); +} + +/* + * The allocated descriptor table for task, link & transfer descritors + * looks like: + * |----------| + * |task desc | |->|----------| + * |----------| | |trans desc| + * |link desc-|->| |----------| + * |----------| . + * . . + * no. of slots max-segs + * . |----------| + * |----------| + * The idea here is to create the [task+trans] table and mark & point the + * link desc to the transfer desc table on a per slot basis. + */ +static int cqhci_host_alloc_tdl(struct cqhci_host *cq_host) +{ + int i = 0; + + /* task descriptor can be 64/128 bit irrespective of arch */ + if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) { + cqhci_writel(cq_host, cqhci_readl(cq_host, CQHCI_CFG) | + CQHCI_TASK_DESC_SZ, CQHCI_CFG); + cq_host->task_desc_len = 16; + } else { + cq_host->task_desc_len = 8; + } + + /* + * 96 bits length of transfer desc instead of 128 bits which means + * ADMA would expect next valid descriptor at the 96th bit + * or 128th bit + */ + if (cq_host->dma64) { + if (cq_host->quirks & CQHCI_QUIRK_SHORT_TXFR_DESC_SZ) + cq_host->trans_desc_len = 12; + else + cq_host->trans_desc_len = 16; + cq_host->link_desc_len = 16; + } else { + cq_host->trans_desc_len = 8; + cq_host->link_desc_len = 8; + } + + /* total size of a slot: 1 task & 1 transfer (link) */ + cq_host->slot_sz = cq_host->task_desc_len + cq_host->link_desc_len; + + cq_host->desc_size = cq_host->slot_sz * cq_host->num_slots; + + cq_host->data_size = cq_host->trans_desc_len * cq_host->mmc->max_segs * + cq_host->mmc->cqe_qdepth; + + pr_debug("%s: cqhci: desc_size: %zu data_sz: %zu slot-sz: %d\n", + mmc_hostname(cq_host->mmc), cq_host->desc_size, cq_host->data_size, + cq_host->slot_sz); + + /* + * allocate a dma-mapped chunk of memory for the descriptors + * allocate a dma-mapped chunk of memory for link descriptors + * setup each link-desc memory offset per slot-number to + * the descriptor table. + */ + cq_host->desc_base = dmam_alloc_coherent(mmc_dev(cq_host->mmc), + cq_host->desc_size, + &cq_host->desc_dma_base, + GFP_KERNEL); + if (!cq_host->desc_base) + return -ENOMEM; + + cq_host->trans_desc_base = dmam_alloc_coherent(mmc_dev(cq_host->mmc), + cq_host->data_size, + &cq_host->trans_desc_dma_base, + GFP_KERNEL); + if (!cq_host->trans_desc_base) { + dmam_free_coherent(mmc_dev(cq_host->mmc), cq_host->desc_size, + cq_host->desc_base, + cq_host->desc_dma_base); + cq_host->desc_base = NULL; + cq_host->desc_dma_base = 0; + return -ENOMEM; + } + + pr_debug("%s: cqhci: desc-base: 0x%p trans-base: 0x%p\n desc_dma 0x%llx trans_dma: 0x%llx\n", + mmc_hostname(cq_host->mmc), cq_host->desc_base, cq_host->trans_desc_base, + (unsigned long long)cq_host->desc_dma_base, + (unsigned long long)cq_host->trans_desc_dma_base); + + for (; i < (cq_host->num_slots); i++) + setup_trans_desc(cq_host, i); + + return 0; +} + +static void __cqhci_enable(struct cqhci_host *cq_host) +{ + struct mmc_host *mmc = cq_host->mmc; + u32 cqcfg; + + cqcfg = cqhci_readl(cq_host, CQHCI_CFG); + + /* Configuration must not be changed while enabled */ + if (cqcfg & CQHCI_ENABLE) { + cqcfg &= ~CQHCI_ENABLE; + cqhci_writel(cq_host, cqcfg, CQHCI_CFG); + } + + cqcfg &= ~(CQHCI_DCMD | CQHCI_TASK_DESC_SZ); + + if (mmc->caps2 & MMC_CAP2_CQE_DCMD) + cqcfg |= CQHCI_DCMD; + + if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) + cqcfg |= CQHCI_TASK_DESC_SZ; + + cqhci_writel(cq_host, cqcfg, CQHCI_CFG); + + cqhci_writel(cq_host, lower_32_bits(cq_host->desc_dma_base), + CQHCI_TDLBA); + cqhci_writel(cq_host, upper_32_bits(cq_host->desc_dma_base), + CQHCI_TDLBAU); + + cqhci_writel(cq_host, cq_host->rca, CQHCI_SSC2); + + cqhci_set_irqs(cq_host, 0); + + cqcfg |= CQHCI_ENABLE; + + cqhci_writel(cq_host, cqcfg, CQHCI_CFG); + + mmc->cqe_on = true; + + if (cq_host->ops->enable) + cq_host->ops->enable(mmc); + + /* Ensure all writes are done before interrupts are enabled */ + wmb(); + + cqhci_set_irqs(cq_host, CQHCI_IS_MASK); + + cq_host->activated = true; +} + +static void __cqhci_disable(struct cqhci_host *cq_host) +{ + u32 cqcfg; + + cqcfg = cqhci_readl(cq_host, CQHCI_CFG); + cqcfg &= ~CQHCI_ENABLE; + cqhci_writel(cq_host, cqcfg, CQHCI_CFG); + + cq_host->mmc->cqe_on = false; + + cq_host->activated = false; +} + +int cqhci_deactivate(struct mmc_host *mmc) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + + if (cq_host->enabled && cq_host->activated) + __cqhci_disable(cq_host); + + return 0; +} +EXPORT_SYMBOL(cqhci_deactivate); + +int cqhci_resume(struct mmc_host *mmc) +{ + /* Re-enable is done upon first request */ + return 0; +} +EXPORT_SYMBOL(cqhci_resume); + +static int cqhci_enable(struct mmc_host *mmc, struct mmc_card *card) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + int err; + + if (!card->ext_csd.cmdq_en) + return -EINVAL; + + if (cq_host->enabled) + return 0; + + cq_host->rca = card->rca; + + err = cqhci_host_alloc_tdl(cq_host); + if (err) { + pr_err("%s: Failed to enable CQE, error %d\n", + mmc_hostname(mmc), err); + return err; + } + + __cqhci_enable(cq_host); + + cq_host->enabled = true; + +#ifdef DEBUG + cqhci_dumpregs(cq_host); +#endif + return 0; +} + +/* CQHCI is idle and should halt immediately, so set a small timeout */ +#define CQHCI_OFF_TIMEOUT 100 + +static u32 cqhci_read_ctl(struct cqhci_host *cq_host) +{ + return cqhci_readl(cq_host, CQHCI_CTL); +} + +static void cqhci_off(struct mmc_host *mmc) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + u32 reg; + int err; + + if (!cq_host->enabled || !mmc->cqe_on || cq_host->recovery_halt) + return; + + if (cq_host->ops->disable) + cq_host->ops->disable(mmc, false); + + cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL); + + err = readx_poll_timeout(cqhci_read_ctl, cq_host, reg, + reg & CQHCI_HALT, 0, CQHCI_OFF_TIMEOUT); + if (err < 0) + pr_err("%s: cqhci: CQE stuck on\n", mmc_hostname(mmc)); + else + pr_debug("%s: cqhci: CQE off\n", mmc_hostname(mmc)); + + if (cq_host->ops->post_disable) + cq_host->ops->post_disable(mmc); + + mmc->cqe_on = false; +} + +static void cqhci_disable(struct mmc_host *mmc) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + + if (!cq_host->enabled) + return; + + cqhci_off(mmc); + + __cqhci_disable(cq_host); + + dmam_free_coherent(mmc_dev(mmc), cq_host->data_size, + cq_host->trans_desc_base, + cq_host->trans_desc_dma_base); + + dmam_free_coherent(mmc_dev(mmc), cq_host->desc_size, + cq_host->desc_base, + cq_host->desc_dma_base); + + cq_host->trans_desc_base = NULL; + cq_host->desc_base = NULL; + + cq_host->enabled = false; +} + +static void cqhci_prep_task_desc(struct mmc_request *mrq, + u64 *data, bool intr) +{ + u32 req_flags = mrq->data->flags; + + *data = CQHCI_VALID(1) | + CQHCI_END(1) | + CQHCI_INT(intr) | + CQHCI_ACT(0x5) | + CQHCI_FORCED_PROG(!!(req_flags & MMC_DATA_FORCED_PRG)) | + CQHCI_DATA_TAG(!!(req_flags & MMC_DATA_DAT_TAG)) | + CQHCI_DATA_DIR(!!(req_flags & MMC_DATA_READ)) | + CQHCI_PRIORITY(!!(req_flags & MMC_DATA_PRIO)) | + CQHCI_QBAR(!!(req_flags & MMC_DATA_QBR)) | + CQHCI_REL_WRITE(!!(req_flags & MMC_DATA_REL_WR)) | + CQHCI_BLK_COUNT(mrq->data->blocks) | + CQHCI_BLK_ADDR((u64)mrq->data->blk_addr); + + pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n", + mmc_hostname(mrq->host), mrq->tag, (unsigned long long)*data); +} + +static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq) +{ + int sg_count; + struct mmc_data *data = mrq->data; + + if (!data) + return -EINVAL; + + sg_count = dma_map_sg(mmc_dev(host), data->sg, + data->sg_len, + (data->flags & MMC_DATA_WRITE) ? + DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (!sg_count) { + pr_err("%s: sg-len: %d\n", __func__, data->sg_len); + return -ENOMEM; + } + + return sg_count; +} + +static void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, + bool dma64) +{ + __le32 *attr = (__le32 __force *)desc; + + *attr = (CQHCI_VALID(1) | + CQHCI_END(end ? 1 : 0) | + CQHCI_INT(0) | + CQHCI_ACT(0x4) | + CQHCI_DAT_LENGTH(len)); + + if (dma64) { + __le64 *dataddr = (__le64 __force *)(desc + 4); + + dataddr[0] = cpu_to_le64(addr); + } else { + __le32 *dataddr = (__le32 __force *)(desc + 4); + + dataddr[0] = cpu_to_le32(addr); + } +} + +static int cqhci_prep_tran_desc(struct mmc_request *mrq, + struct cqhci_host *cq_host, int tag) +{ + struct mmc_data *data = mrq->data; + int i, sg_count, len; + bool end = false; + bool dma64 = cq_host->dma64; + dma_addr_t addr; + u8 *desc; + struct scatterlist *sg; + + sg_count = cqhci_dma_map(mrq->host, mrq); + if (sg_count < 0) { + pr_err("%s: %s: unable to map sg lists, %d\n", + mmc_hostname(mrq->host), __func__, sg_count); + return sg_count; + } + + desc = get_trans_desc(cq_host, tag); + + for_each_sg(data->sg, sg, sg_count, i) { + addr = sg_dma_address(sg); + len = sg_dma_len(sg); + + if ((i+1) == sg_count) + end = true; + cqhci_set_tran_desc(desc, addr, len, end, dma64); + desc += cq_host->trans_desc_len; + } + + return 0; +} + +static void cqhci_prep_dcmd_desc(struct mmc_host *mmc, + struct mmc_request *mrq) +{ + u64 *task_desc = NULL; + u64 data = 0; + u8 resp_type; + u8 *desc; + __le64 *dataddr; + struct cqhci_host *cq_host = mmc->cqe_private; + u8 timing; + + if (!(mrq->cmd->flags & MMC_RSP_PRESENT)) { + resp_type = 0x0; + timing = 0x1; + } else { + if (mrq->cmd->flags & MMC_RSP_R1B) { + resp_type = 0x3; + timing = 0x0; + } else { + resp_type = 0x2; + timing = 0x1; + } + } + + task_desc = (__le64 __force *)get_desc(cq_host, cq_host->dcmd_slot); + memset(task_desc, 0, cq_host->task_desc_len); + data |= (CQHCI_VALID(1) | + CQHCI_END(1) | + CQHCI_INT(1) | + CQHCI_QBAR(1) | + CQHCI_ACT(0x5) | + CQHCI_CMD_INDEX(mrq->cmd->opcode) | + CQHCI_CMD_TIMING(timing) | CQHCI_RESP_TYPE(resp_type)); + if (cq_host->ops->update_dcmd_desc) + cq_host->ops->update_dcmd_desc(mmc, mrq, &data); + *task_desc |= data; + desc = (u8 *)task_desc; + pr_debug("%s: cqhci: dcmd: cmd: %d timing: %d resp: %d\n", + mmc_hostname(mmc), mrq->cmd->opcode, timing, resp_type); + dataddr = (__le64 __force *)(desc + 4); + dataddr[0] = cpu_to_le64((u64)mrq->cmd->arg); + +} + +static void cqhci_post_req(struct mmc_host *host, struct mmc_request *mrq) +{ + struct mmc_data *data = mrq->data; + + if (data) { + dma_unmap_sg(mmc_dev(host), data->sg, data->sg_len, + (data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } +} + +static inline int cqhci_tag(struct mmc_request *mrq) +{ + return mrq->cmd ? DCMD_SLOT : mrq->tag; +} + +static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + int err = 0; + u64 data = 0; + u64 *task_desc = NULL; + int tag = cqhci_tag(mrq); + struct cqhci_host *cq_host = mmc->cqe_private; + unsigned long flags; + + if (!cq_host->enabled) { + pr_err("%s: cqhci: not enabled\n", mmc_hostname(mmc)); + return -EINVAL; + } + + /* First request after resume has to re-enable */ + if (!cq_host->activated) + __cqhci_enable(cq_host); + + if (!mmc->cqe_on) { + if (cq_host->ops->pre_enable) + cq_host->ops->pre_enable(mmc); + + cqhci_writel(cq_host, 0, CQHCI_CTL); + mmc->cqe_on = true; + pr_debug("%s: cqhci: CQE on\n", mmc_hostname(mmc)); + if (cqhci_readl(cq_host, CQHCI_CTL) && CQHCI_HALT) { + pr_err("%s: cqhci: CQE failed to exit halt state\n", + mmc_hostname(mmc)); + } + if (cq_host->ops->enable) + cq_host->ops->enable(mmc); + } + + if (mrq->data) { + task_desc = (__le64 __force *)get_desc(cq_host, tag); + cqhci_prep_task_desc(mrq, &data, 1); + *task_desc = cpu_to_le64(data); + err = cqhci_prep_tran_desc(mrq, cq_host, tag); + if (err) { + pr_err("%s: cqhci: failed to setup tx desc: %d\n", + mmc_hostname(mmc), err); + return err; + } + } else { + cqhci_prep_dcmd_desc(mmc, mrq); + } + + spin_lock_irqsave(&cq_host->lock, flags); + + if (cq_host->recovery_halt) { + err = -EBUSY; + goto out_unlock; + } + + cq_host->slot[tag].mrq = mrq; + cq_host->slot[tag].flags = 0; + + cq_host->qcnt += 1; + /* Make sure descriptors are ready before ringing the doorbell */ + wmb(); + cqhci_writel(cq_host, 1 << tag, CQHCI_TDBR); + if (!(cqhci_readl(cq_host, CQHCI_TDBR) & (1 << tag))) + pr_debug("%s: cqhci: doorbell not set for tag %d\n", + mmc_hostname(mmc), tag); +out_unlock: + spin_unlock_irqrestore(&cq_host->lock, flags); + + if (err) + cqhci_post_req(mmc, mrq); + + return err; +} + +static void cqhci_recovery_needed(struct mmc_host *mmc, struct mmc_request *mrq, + bool notify) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + + if (!cq_host->recovery_halt) { + cq_host->recovery_halt = true; + pr_debug("%s: cqhci: recovery needed\n", mmc_hostname(mmc)); + wake_up(&cq_host->wait_queue); + if (notify && mrq->recovery_notifier) + mrq->recovery_notifier(mrq); + } +} + +static unsigned int cqhci_error_flags(int error1, int error2) +{ + int error = error1 ? error1 : error2; + + switch (error) { + case -EILSEQ: + return CQHCI_HOST_CRC; + case -ETIMEDOUT: + return CQHCI_HOST_TIMEOUT; + default: + return CQHCI_HOST_OTHER; + } +} + +static void cqhci_error_irq(struct mmc_host *mmc, u32 status, int cmd_error, + int data_error) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + struct cqhci_slot *slot; + u32 terri; + int tag; + + spin_lock(&cq_host->lock); + + terri = cqhci_readl(cq_host, CQHCI_TERRI); + + pr_debug("%s: cqhci: error IRQ status: 0x%08x cmd error %d data error %d TERRI: 0x%08x\n", + mmc_hostname(mmc), status, cmd_error, data_error, terri); + + /* Forget about errors when recovery has already been triggered */ + if (cq_host->recovery_halt) + goto out_unlock; + + if (!cq_host->qcnt) { + WARN_ONCE(1, "%s: cqhci: error when idle. IRQ status: 0x%08x cmd error %d data error %d TERRI: 0x%08x\n", + mmc_hostname(mmc), status, cmd_error, data_error, + terri); + goto out_unlock; + } + + if (CQHCI_TERRI_C_VALID(terri)) { + tag = CQHCI_TERRI_C_TASK(terri); + slot = &cq_host->slot[tag]; + if (slot->mrq) { + slot->flags = cqhci_error_flags(cmd_error, data_error); + cqhci_recovery_needed(mmc, slot->mrq, true); + } + } + + if (CQHCI_TERRI_D_VALID(terri)) { + tag = CQHCI_TERRI_D_TASK(terri); + slot = &cq_host->slot[tag]; + if (slot->mrq) { + slot->flags = cqhci_error_flags(data_error, cmd_error); + cqhci_recovery_needed(mmc, slot->mrq, true); + } + } + + if (!cq_host->recovery_halt) { + /* + * The only way to guarantee forward progress is to mark at + * least one task in error, so if none is indicated, pick one. + */ + for (tag = 0; tag < NUM_SLOTS; tag++) { + slot = &cq_host->slot[tag]; + if (!slot->mrq) + continue; + slot->flags = cqhci_error_flags(data_error, cmd_error); + cqhci_recovery_needed(mmc, slot->mrq, true); + break; + } + } + +out_unlock: + spin_unlock(&cq_host->lock); +} + +static void cqhci_finish_mrq(struct mmc_host *mmc, unsigned int tag) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + struct cqhci_slot *slot = &cq_host->slot[tag]; + struct mmc_request *mrq = slot->mrq; + struct mmc_data *data; + + if (!mrq) { + WARN_ONCE(1, "%s: cqhci: spurious TCN for tag %d\n", + mmc_hostname(mmc), tag); + return; + } + + /* No completions allowed during recovery */ + if (cq_host->recovery_halt) { + slot->flags |= CQHCI_COMPLETED; + return; + } + + slot->mrq = NULL; + + cq_host->qcnt -= 1; + + data = mrq->data; + if (data) { + if (data->error) + data->bytes_xfered = 0; + else + data->bytes_xfered = data->blksz * data->blocks; + } + + mmc_cqe_request_done(mmc, mrq); +} + +irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, + int data_error) +{ + u32 status; + unsigned long tag = 0, comp_status; + struct cqhci_host *cq_host = mmc->cqe_private; + + status = cqhci_readl(cq_host, CQHCI_IS); + cqhci_writel(cq_host, status, CQHCI_IS); + + pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status); + + if ((status & CQHCI_IS_RED) || cmd_error || data_error) + cqhci_error_irq(mmc, status, cmd_error, data_error); + + if (status & CQHCI_IS_TCC) { + /* read TCN and complete the request */ + comp_status = cqhci_readl(cq_host, CQHCI_TCN); + cqhci_writel(cq_host, comp_status, CQHCI_TCN); + pr_debug("%s: cqhci: TCN: 0x%08lx\n", + mmc_hostname(mmc), comp_status); + + spin_lock(&cq_host->lock); + + for_each_set_bit(tag, &comp_status, cq_host->num_slots) { + /* complete the corresponding mrq */ + pr_debug("%s: cqhci: completing tag %lu\n", + mmc_hostname(mmc), tag); + cqhci_finish_mrq(mmc, tag); + } + + if (cq_host->waiting_for_idle && !cq_host->qcnt) { + cq_host->waiting_for_idle = false; + wake_up(&cq_host->wait_queue); + } + + spin_unlock(&cq_host->lock); + } + + if (status & CQHCI_IS_TCL) + wake_up(&cq_host->wait_queue); + + if (status & CQHCI_IS_HAC) + wake_up(&cq_host->wait_queue); + + return IRQ_HANDLED; +} +EXPORT_SYMBOL(cqhci_irq); + +static bool cqhci_is_idle(struct cqhci_host *cq_host, int *ret) +{ + unsigned long flags; + bool is_idle; + + spin_lock_irqsave(&cq_host->lock, flags); + is_idle = !cq_host->qcnt || cq_host->recovery_halt; + *ret = cq_host->recovery_halt ? -EBUSY : 0; + cq_host->waiting_for_idle = !is_idle; + spin_unlock_irqrestore(&cq_host->lock, flags); + + return is_idle; +} + +static int cqhci_wait_for_idle(struct mmc_host *mmc) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + int ret; + + wait_event(cq_host->wait_queue, cqhci_is_idle(cq_host, &ret)); + + return ret; +} + +static bool cqhci_timeout(struct mmc_host *mmc, struct mmc_request *mrq, + bool *recovery_needed) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + int tag = cqhci_tag(mrq); + struct cqhci_slot *slot = &cq_host->slot[tag]; + unsigned long flags; + bool timed_out; + + spin_lock_irqsave(&cq_host->lock, flags); + timed_out = slot->mrq == mrq; + if (timed_out) { + slot->flags |= CQHCI_EXTERNAL_TIMEOUT; + cqhci_recovery_needed(mmc, mrq, false); + *recovery_needed = cq_host->recovery_halt; + } + spin_unlock_irqrestore(&cq_host->lock, flags); + + if (timed_out) { + pr_err("%s: cqhci: timeout for tag %d\n", + mmc_hostname(mmc), tag); + cqhci_dumpregs(cq_host); + } + + return timed_out; +} + +static bool cqhci_tasks_cleared(struct cqhci_host *cq_host) +{ + return !(cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_CLEAR_ALL_TASKS); +} + +static bool cqhci_clear_all_tasks(struct mmc_host *mmc, unsigned int timeout) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + bool ret; + u32 ctl; + + cqhci_set_irqs(cq_host, CQHCI_IS_TCL); + + ctl = cqhci_readl(cq_host, CQHCI_CTL); + ctl |= CQHCI_CLEAR_ALL_TASKS; + cqhci_writel(cq_host, ctl, CQHCI_CTL); + + wait_event_timeout(cq_host->wait_queue, cqhci_tasks_cleared(cq_host), + msecs_to_jiffies(timeout) + 1); + + cqhci_set_irqs(cq_host, 0); + + ret = cqhci_tasks_cleared(cq_host); + + if (!ret) + pr_debug("%s: cqhci: Failed to clear tasks\n", + mmc_hostname(mmc)); + + return ret; +} + +static bool cqhci_halted(struct cqhci_host *cq_host) +{ + return cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT; +} + +static bool cqhci_halt(struct mmc_host *mmc, unsigned int timeout) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + bool ret; + u32 ctl; + + if (cqhci_halted(cq_host)) + return true; + + cqhci_set_irqs(cq_host, CQHCI_IS_HAC); + + ctl = cqhci_readl(cq_host, CQHCI_CTL); + ctl |= CQHCI_HALT; + cqhci_writel(cq_host, ctl, CQHCI_CTL); + + wait_event_timeout(cq_host->wait_queue, cqhci_halted(cq_host), + msecs_to_jiffies(timeout) + 1); + + cqhci_set_irqs(cq_host, 0); + + ret = cqhci_halted(cq_host); + + if (!ret) + pr_debug("%s: cqhci: Failed to halt\n", mmc_hostname(mmc)); + + return ret; +} + +/* + * After halting we expect to be able to use the command line. We interpret the + * failure to halt to mean the data lines might still be in use (and the upper + * layers will need to send a STOP command), so we set the timeout based on a + * generous command timeout. + */ +#define CQHCI_START_HALT_TIMEOUT 5 + +static void cqhci_recovery_start(struct mmc_host *mmc) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + + pr_debug("%s: cqhci: %s\n", mmc_hostname(mmc), __func__); + + WARN_ON(!cq_host->recovery_halt); + + cqhci_halt(mmc, CQHCI_START_HALT_TIMEOUT); + + if (cq_host->ops->disable) + cq_host->ops->disable(mmc, true); + + mmc->cqe_on = false; +} + +static int cqhci_error_from_flags(unsigned int flags) +{ + if (!flags) + return 0; + + /* CRC errors might indicate re-tuning so prefer to report that */ + if (flags & CQHCI_HOST_CRC) + return -EILSEQ; + + if (flags & (CQHCI_EXTERNAL_TIMEOUT | CQHCI_HOST_TIMEOUT)) + return -ETIMEDOUT; + + return -EIO; +} + +static void cqhci_recover_mrq(struct cqhci_host *cq_host, unsigned int tag) +{ + struct cqhci_slot *slot = &cq_host->slot[tag]; + struct mmc_request *mrq = slot->mrq; + struct mmc_data *data; + + if (!mrq) + return; + + slot->mrq = NULL; + + cq_host->qcnt -= 1; + + data = mrq->data; + if (data) { + data->bytes_xfered = 0; + data->error = cqhci_error_from_flags(slot->flags); + } else { + mrq->cmd->error = cqhci_error_from_flags(slot->flags); + } + + mmc_cqe_request_done(cq_host->mmc, mrq); +} + +static void cqhci_recover_mrqs(struct cqhci_host *cq_host) +{ + int i; + + for (i = 0; i < cq_host->num_slots; i++) + cqhci_recover_mrq(cq_host, i); +} + +/* + * By now the command and data lines should be unused so there is no reason for + * CQHCI to take a long time to halt, but if it doesn't halt there could be + * problems clearing tasks, so be generous. + */ +#define CQHCI_FINISH_HALT_TIMEOUT 20 + +/* CQHCI could be expected to clear it's internal state pretty quickly */ +#define CQHCI_CLEAR_TIMEOUT 20 + +static void cqhci_recovery_finish(struct mmc_host *mmc) +{ + struct cqhci_host *cq_host = mmc->cqe_private; + unsigned long flags; + u32 cqcfg; + bool ok; + + pr_debug("%s: cqhci: %s\n", mmc_hostname(mmc), __func__); + + WARN_ON(!cq_host->recovery_halt); + + ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT); + + if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT)) + ok = false; + + /* + * The specification contradicts itself, by saying that tasks cannot be + * cleared if CQHCI does not halt, but if CQHCI does not halt, it should + * be disabled/re-enabled, but not to disable before clearing tasks. + * Have a go anyway. + */ + if (!ok) { + pr_debug("%s: cqhci: disable / re-enable\n", mmc_hostname(mmc)); + cqcfg = cqhci_readl(cq_host, CQHCI_CFG); + cqcfg &= ~CQHCI_ENABLE; + cqhci_writel(cq_host, cqcfg, CQHCI_CFG); + cqcfg |= CQHCI_ENABLE; + cqhci_writel(cq_host, cqcfg, CQHCI_CFG); + /* Be sure that there are no tasks */ + ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT); + if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT)) + ok = false; + WARN_ON(!ok); + } + + cqhci_recover_mrqs(cq_host); + + WARN_ON(cq_host->qcnt); + + spin_lock_irqsave(&cq_host->lock, flags); + cq_host->qcnt = 0; + cq_host->recovery_halt = false; + mmc->cqe_on = false; + spin_unlock_irqrestore(&cq_host->lock, flags); + + /* Ensure all writes are done before interrupts are re-enabled */ + wmb(); + + cqhci_writel(cq_host, CQHCI_IS_HAC | CQHCI_IS_TCL, CQHCI_IS); + + cqhci_set_irqs(cq_host, CQHCI_IS_MASK); + + pr_debug("%s: cqhci: recovery done\n", mmc_hostname(mmc)); +} + +static const struct mmc_cqe_ops cqhci_cqe_ops = { + .cqe_enable = cqhci_enable, + .cqe_disable = cqhci_disable, + .cqe_request = cqhci_request, + .cqe_post_req = cqhci_post_req, + .cqe_off = cqhci_off, + .cqe_wait_for_idle = cqhci_wait_for_idle, + .cqe_timeout = cqhci_timeout, + .cqe_recovery_start = cqhci_recovery_start, + .cqe_recovery_finish = cqhci_recovery_finish, +}; + +struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev) +{ + struct cqhci_host *cq_host; + struct resource *cqhci_memres = NULL; + + /* check and setup CMDQ interface */ + cqhci_memres = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "cqhci"); + if (!cqhci_memres) { + dev_dbg(&pdev->dev, "CMDQ not supported\n"); + return ERR_PTR(-EINVAL); + } + + cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL); + if (!cq_host) + return ERR_PTR(-ENOMEM); + cq_host->mmio = devm_ioremap(&pdev->dev, + cqhci_memres->start, + resource_size(cqhci_memres)); + if (!cq_host->mmio) { + dev_err(&pdev->dev, "failed to remap cqhci regs\n"); + return ERR_PTR(-EBUSY); + } + dev_dbg(&pdev->dev, "CMDQ ioremap: done\n"); + + return cq_host; +} +EXPORT_SYMBOL(cqhci_pltfm_init); + +static unsigned int cqhci_ver_major(struct cqhci_host *cq_host) +{ + return CQHCI_VER_MAJOR(cqhci_readl(cq_host, CQHCI_VER)); +} + +static unsigned int cqhci_ver_minor(struct cqhci_host *cq_host) +{ + u32 ver = cqhci_readl(cq_host, CQHCI_VER); + + return CQHCI_VER_MINOR1(ver) * 10 + CQHCI_VER_MINOR2(ver); +} + +int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, + bool dma64) +{ + int err; + + cq_host->dma64 = dma64; + cq_host->mmc = mmc; + cq_host->mmc->cqe_private = cq_host; + + cq_host->num_slots = NUM_SLOTS; + cq_host->dcmd_slot = DCMD_SLOT; + + mmc->cqe_ops = &cqhci_cqe_ops; + + mmc->cqe_qdepth = NUM_SLOTS; + if (mmc->caps2 & MMC_CAP2_CQE_DCMD) + mmc->cqe_qdepth -= 1; + + cq_host->slot = devm_kcalloc(mmc_dev(mmc), cq_host->num_slots, + sizeof(*cq_host->slot), GFP_KERNEL); + if (!cq_host->slot) { + err = -ENOMEM; + goto out_err; + } + + spin_lock_init(&cq_host->lock); + + init_completion(&cq_host->halt_comp); + init_waitqueue_head(&cq_host->wait_queue); + + pr_info("%s: CQHCI version %u.%02u\n", + mmc_hostname(mmc), cqhci_ver_major(cq_host), + cqhci_ver_minor(cq_host)); + + return 0; + +out_err: + pr_err("%s: CQHCI version %u.%02u failed to initialize, error %d\n", + mmc_hostname(mmc), cqhci_ver_major(cq_host), + cqhci_ver_minor(cq_host), err); + return err; +} +EXPORT_SYMBOL(cqhci_init); + +MODULE_AUTHOR("Venkat Gopalakrishnan "); +MODULE_DESCRIPTION("Command Queue Host Controller Interface driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/cqhci.c b/drivers/mmc/host/cqhci.c deleted file mode 100644 index 697fe40756bf..000000000000 --- a/drivers/mmc/host/cqhci.c +++ /dev/null @@ -1,1165 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "cqhci.h" - -#define DCMD_SLOT 31 -#define NUM_SLOTS 32 - -struct cqhci_slot { - struct mmc_request *mrq; - unsigned int flags; -#define CQHCI_EXTERNAL_TIMEOUT BIT(0) -#define CQHCI_COMPLETED BIT(1) -#define CQHCI_HOST_CRC BIT(2) -#define CQHCI_HOST_TIMEOUT BIT(3) -#define CQHCI_HOST_OTHER BIT(4) -}; - -static inline u8 *get_desc(struct cqhci_host *cq_host, u8 tag) -{ - return cq_host->desc_base + (tag * cq_host->slot_sz); -} - -static inline u8 *get_link_desc(struct cqhci_host *cq_host, u8 tag) -{ - u8 *desc = get_desc(cq_host, tag); - - return desc + cq_host->task_desc_len; -} - -static inline dma_addr_t get_trans_desc_dma(struct cqhci_host *cq_host, u8 tag) -{ - return cq_host->trans_desc_dma_base + - (cq_host->mmc->max_segs * tag * - cq_host->trans_desc_len); -} - -static inline u8 *get_trans_desc(struct cqhci_host *cq_host, u8 tag) -{ - return cq_host->trans_desc_base + - (cq_host->trans_desc_len * cq_host->mmc->max_segs * tag); -} - -static void setup_trans_desc(struct cqhci_host *cq_host, u8 tag) -{ - u8 *link_temp; - dma_addr_t trans_temp; - - link_temp = get_link_desc(cq_host, tag); - trans_temp = get_trans_desc_dma(cq_host, tag); - - memset(link_temp, 0, cq_host->link_desc_len); - if (cq_host->link_desc_len > 8) - *(link_temp + 8) = 0; - - if (tag == DCMD_SLOT && (cq_host->mmc->caps2 & MMC_CAP2_CQE_DCMD)) { - *link_temp = CQHCI_VALID(0) | CQHCI_ACT(0) | CQHCI_END(1); - return; - } - - *link_temp = CQHCI_VALID(1) | CQHCI_ACT(0x6) | CQHCI_END(0); - - if (cq_host->dma64) { - __le64 *data_addr = (__le64 __force *)(link_temp + 4); - - data_addr[0] = cpu_to_le64(trans_temp); - } else { - __le32 *data_addr = (__le32 __force *)(link_temp + 4); - - data_addr[0] = cpu_to_le32(trans_temp); - } -} - -static void cqhci_set_irqs(struct cqhci_host *cq_host, u32 set) -{ - cqhci_writel(cq_host, set, CQHCI_ISTE); - cqhci_writel(cq_host, set, CQHCI_ISGE); -} - -#define DRV_NAME "cqhci" - -#define CQHCI_DUMP(f, x...) \ - pr_err("%s: " DRV_NAME ": " f, mmc_hostname(mmc), ## x) - -static void cqhci_dumpregs(struct cqhci_host *cq_host) -{ - struct mmc_host *mmc = cq_host->mmc; - - CQHCI_DUMP("============ CQHCI REGISTER DUMP ===========\n"); - - CQHCI_DUMP("Caps: 0x%08x | Version: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_CAP), - cqhci_readl(cq_host, CQHCI_VER)); - CQHCI_DUMP("Config: 0x%08x | Control: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_CFG), - cqhci_readl(cq_host, CQHCI_CTL)); - CQHCI_DUMP("Int stat: 0x%08x | Int enab: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_IS), - cqhci_readl(cq_host, CQHCI_ISTE)); - CQHCI_DUMP("Int sig: 0x%08x | Int Coal: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_ISGE), - cqhci_readl(cq_host, CQHCI_IC)); - CQHCI_DUMP("TDL base: 0x%08x | TDL up32: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_TDLBA), - cqhci_readl(cq_host, CQHCI_TDLBAU)); - CQHCI_DUMP("Doorbell: 0x%08x | TCN: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_TDBR), - cqhci_readl(cq_host, CQHCI_TCN)); - CQHCI_DUMP("Dev queue: 0x%08x | Dev Pend: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_DQS), - cqhci_readl(cq_host, CQHCI_DPT)); - CQHCI_DUMP("Task clr: 0x%08x | SSC1: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_TCLR), - cqhci_readl(cq_host, CQHCI_SSC1)); - CQHCI_DUMP("SSC2: 0x%08x | DCMD rsp: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_SSC2), - cqhci_readl(cq_host, CQHCI_CRDCT)); - CQHCI_DUMP("RED mask: 0x%08x | TERRI: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_RMEM), - cqhci_readl(cq_host, CQHCI_TERRI)); - CQHCI_DUMP("Resp idx: 0x%08x | Resp arg: 0x%08x\n", - cqhci_readl(cq_host, CQHCI_CRI), - cqhci_readl(cq_host, CQHCI_CRA)); - - if (cq_host->ops->dumpregs) - cq_host->ops->dumpregs(mmc); - else - CQHCI_DUMP(": ===========================================\n"); -} - -/* - * The allocated descriptor table for task, link & transfer descritors - * looks like: - * |----------| - * |task desc | |->|----------| - * |----------| | |trans desc| - * |link desc-|->| |----------| - * |----------| . - * . . - * no. of slots max-segs - * . |----------| - * |----------| - * The idea here is to create the [task+trans] table and mark & point the - * link desc to the transfer desc table on a per slot basis. - */ -static int cqhci_host_alloc_tdl(struct cqhci_host *cq_host) -{ - int i = 0; - - /* task descriptor can be 64/128 bit irrespective of arch */ - if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) { - cqhci_writel(cq_host, cqhci_readl(cq_host, CQHCI_CFG) | - CQHCI_TASK_DESC_SZ, CQHCI_CFG); - cq_host->task_desc_len = 16; - } else { - cq_host->task_desc_len = 8; - } - - /* - * 96 bits length of transfer desc instead of 128 bits which means - * ADMA would expect next valid descriptor at the 96th bit - * or 128th bit - */ - if (cq_host->dma64) { - if (cq_host->quirks & CQHCI_QUIRK_SHORT_TXFR_DESC_SZ) - cq_host->trans_desc_len = 12; - else - cq_host->trans_desc_len = 16; - cq_host->link_desc_len = 16; - } else { - cq_host->trans_desc_len = 8; - cq_host->link_desc_len = 8; - } - - /* total size of a slot: 1 task & 1 transfer (link) */ - cq_host->slot_sz = cq_host->task_desc_len + cq_host->link_desc_len; - - cq_host->desc_size = cq_host->slot_sz * cq_host->num_slots; - - cq_host->data_size = cq_host->trans_desc_len * cq_host->mmc->max_segs * - cq_host->mmc->cqe_qdepth; - - pr_debug("%s: cqhci: desc_size: %zu data_sz: %zu slot-sz: %d\n", - mmc_hostname(cq_host->mmc), cq_host->desc_size, cq_host->data_size, - cq_host->slot_sz); - - /* - * allocate a dma-mapped chunk of memory for the descriptors - * allocate a dma-mapped chunk of memory for link descriptors - * setup each link-desc memory offset per slot-number to - * the descriptor table. - */ - cq_host->desc_base = dmam_alloc_coherent(mmc_dev(cq_host->mmc), - cq_host->desc_size, - &cq_host->desc_dma_base, - GFP_KERNEL); - if (!cq_host->desc_base) - return -ENOMEM; - - cq_host->trans_desc_base = dmam_alloc_coherent(mmc_dev(cq_host->mmc), - cq_host->data_size, - &cq_host->trans_desc_dma_base, - GFP_KERNEL); - if (!cq_host->trans_desc_base) { - dmam_free_coherent(mmc_dev(cq_host->mmc), cq_host->desc_size, - cq_host->desc_base, - cq_host->desc_dma_base); - cq_host->desc_base = NULL; - cq_host->desc_dma_base = 0; - return -ENOMEM; - } - - pr_debug("%s: cqhci: desc-base: 0x%p trans-base: 0x%p\n desc_dma 0x%llx trans_dma: 0x%llx\n", - mmc_hostname(cq_host->mmc), cq_host->desc_base, cq_host->trans_desc_base, - (unsigned long long)cq_host->desc_dma_base, - (unsigned long long)cq_host->trans_desc_dma_base); - - for (; i < (cq_host->num_slots); i++) - setup_trans_desc(cq_host, i); - - return 0; -} - -static void __cqhci_enable(struct cqhci_host *cq_host) -{ - struct mmc_host *mmc = cq_host->mmc; - u32 cqcfg; - - cqcfg = cqhci_readl(cq_host, CQHCI_CFG); - - /* Configuration must not be changed while enabled */ - if (cqcfg & CQHCI_ENABLE) { - cqcfg &= ~CQHCI_ENABLE; - cqhci_writel(cq_host, cqcfg, CQHCI_CFG); - } - - cqcfg &= ~(CQHCI_DCMD | CQHCI_TASK_DESC_SZ); - - if (mmc->caps2 & MMC_CAP2_CQE_DCMD) - cqcfg |= CQHCI_DCMD; - - if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) - cqcfg |= CQHCI_TASK_DESC_SZ; - - cqhci_writel(cq_host, cqcfg, CQHCI_CFG); - - cqhci_writel(cq_host, lower_32_bits(cq_host->desc_dma_base), - CQHCI_TDLBA); - cqhci_writel(cq_host, upper_32_bits(cq_host->desc_dma_base), - CQHCI_TDLBAU); - - cqhci_writel(cq_host, cq_host->rca, CQHCI_SSC2); - - cqhci_set_irqs(cq_host, 0); - - cqcfg |= CQHCI_ENABLE; - - cqhci_writel(cq_host, cqcfg, CQHCI_CFG); - - mmc->cqe_on = true; - - if (cq_host->ops->enable) - cq_host->ops->enable(mmc); - - /* Ensure all writes are done before interrupts are enabled */ - wmb(); - - cqhci_set_irqs(cq_host, CQHCI_IS_MASK); - - cq_host->activated = true; -} - -static void __cqhci_disable(struct cqhci_host *cq_host) -{ - u32 cqcfg; - - cqcfg = cqhci_readl(cq_host, CQHCI_CFG); - cqcfg &= ~CQHCI_ENABLE; - cqhci_writel(cq_host, cqcfg, CQHCI_CFG); - - cq_host->mmc->cqe_on = false; - - cq_host->activated = false; -} - -int cqhci_deactivate(struct mmc_host *mmc) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - - if (cq_host->enabled && cq_host->activated) - __cqhci_disable(cq_host); - - return 0; -} -EXPORT_SYMBOL(cqhci_deactivate); - -int cqhci_resume(struct mmc_host *mmc) -{ - /* Re-enable is done upon first request */ - return 0; -} -EXPORT_SYMBOL(cqhci_resume); - -static int cqhci_enable(struct mmc_host *mmc, struct mmc_card *card) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - int err; - - if (!card->ext_csd.cmdq_en) - return -EINVAL; - - if (cq_host->enabled) - return 0; - - cq_host->rca = card->rca; - - err = cqhci_host_alloc_tdl(cq_host); - if (err) { - pr_err("%s: Failed to enable CQE, error %d\n", - mmc_hostname(mmc), err); - return err; - } - - __cqhci_enable(cq_host); - - cq_host->enabled = true; - -#ifdef DEBUG - cqhci_dumpregs(cq_host); -#endif - return 0; -} - -/* CQHCI is idle and should halt immediately, so set a small timeout */ -#define CQHCI_OFF_TIMEOUT 100 - -static u32 cqhci_read_ctl(struct cqhci_host *cq_host) -{ - return cqhci_readl(cq_host, CQHCI_CTL); -} - -static void cqhci_off(struct mmc_host *mmc) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - u32 reg; - int err; - - if (!cq_host->enabled || !mmc->cqe_on || cq_host->recovery_halt) - return; - - if (cq_host->ops->disable) - cq_host->ops->disable(mmc, false); - - cqhci_writel(cq_host, CQHCI_HALT, CQHCI_CTL); - - err = readx_poll_timeout(cqhci_read_ctl, cq_host, reg, - reg & CQHCI_HALT, 0, CQHCI_OFF_TIMEOUT); - if (err < 0) - pr_err("%s: cqhci: CQE stuck on\n", mmc_hostname(mmc)); - else - pr_debug("%s: cqhci: CQE off\n", mmc_hostname(mmc)); - - if (cq_host->ops->post_disable) - cq_host->ops->post_disable(mmc); - - mmc->cqe_on = false; -} - -static void cqhci_disable(struct mmc_host *mmc) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - - if (!cq_host->enabled) - return; - - cqhci_off(mmc); - - __cqhci_disable(cq_host); - - dmam_free_coherent(mmc_dev(mmc), cq_host->data_size, - cq_host->trans_desc_base, - cq_host->trans_desc_dma_base); - - dmam_free_coherent(mmc_dev(mmc), cq_host->desc_size, - cq_host->desc_base, - cq_host->desc_dma_base); - - cq_host->trans_desc_base = NULL; - cq_host->desc_base = NULL; - - cq_host->enabled = false; -} - -static void cqhci_prep_task_desc(struct mmc_request *mrq, - u64 *data, bool intr) -{ - u32 req_flags = mrq->data->flags; - - *data = CQHCI_VALID(1) | - CQHCI_END(1) | - CQHCI_INT(intr) | - CQHCI_ACT(0x5) | - CQHCI_FORCED_PROG(!!(req_flags & MMC_DATA_FORCED_PRG)) | - CQHCI_DATA_TAG(!!(req_flags & MMC_DATA_DAT_TAG)) | - CQHCI_DATA_DIR(!!(req_flags & MMC_DATA_READ)) | - CQHCI_PRIORITY(!!(req_flags & MMC_DATA_PRIO)) | - CQHCI_QBAR(!!(req_flags & MMC_DATA_QBR)) | - CQHCI_REL_WRITE(!!(req_flags & MMC_DATA_REL_WR)) | - CQHCI_BLK_COUNT(mrq->data->blocks) | - CQHCI_BLK_ADDR((u64)mrq->data->blk_addr); - - pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n", - mmc_hostname(mrq->host), mrq->tag, (unsigned long long)*data); -} - -static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq) -{ - int sg_count; - struct mmc_data *data = mrq->data; - - if (!data) - return -EINVAL; - - sg_count = dma_map_sg(mmc_dev(host), data->sg, - data->sg_len, - (data->flags & MMC_DATA_WRITE) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (!sg_count) { - pr_err("%s: sg-len: %d\n", __func__, data->sg_len); - return -ENOMEM; - } - - return sg_count; -} - -static void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, - bool dma64) -{ - __le32 *attr = (__le32 __force *)desc; - - *attr = (CQHCI_VALID(1) | - CQHCI_END(end ? 1 : 0) | - CQHCI_INT(0) | - CQHCI_ACT(0x4) | - CQHCI_DAT_LENGTH(len)); - - if (dma64) { - __le64 *dataddr = (__le64 __force *)(desc + 4); - - dataddr[0] = cpu_to_le64(addr); - } else { - __le32 *dataddr = (__le32 __force *)(desc + 4); - - dataddr[0] = cpu_to_le32(addr); - } -} - -static int cqhci_prep_tran_desc(struct mmc_request *mrq, - struct cqhci_host *cq_host, int tag) -{ - struct mmc_data *data = mrq->data; - int i, sg_count, len; - bool end = false; - bool dma64 = cq_host->dma64; - dma_addr_t addr; - u8 *desc; - struct scatterlist *sg; - - sg_count = cqhci_dma_map(mrq->host, mrq); - if (sg_count < 0) { - pr_err("%s: %s: unable to map sg lists, %d\n", - mmc_hostname(mrq->host), __func__, sg_count); - return sg_count; - } - - desc = get_trans_desc(cq_host, tag); - - for_each_sg(data->sg, sg, sg_count, i) { - addr = sg_dma_address(sg); - len = sg_dma_len(sg); - - if ((i+1) == sg_count) - end = true; - cqhci_set_tran_desc(desc, addr, len, end, dma64); - desc += cq_host->trans_desc_len; - } - - return 0; -} - -static void cqhci_prep_dcmd_desc(struct mmc_host *mmc, - struct mmc_request *mrq) -{ - u64 *task_desc = NULL; - u64 data = 0; - u8 resp_type; - u8 *desc; - __le64 *dataddr; - struct cqhci_host *cq_host = mmc->cqe_private; - u8 timing; - - if (!(mrq->cmd->flags & MMC_RSP_PRESENT)) { - resp_type = 0x0; - timing = 0x1; - } else { - if (mrq->cmd->flags & MMC_RSP_R1B) { - resp_type = 0x3; - timing = 0x0; - } else { - resp_type = 0x2; - timing = 0x1; - } - } - - task_desc = (__le64 __force *)get_desc(cq_host, cq_host->dcmd_slot); - memset(task_desc, 0, cq_host->task_desc_len); - data |= (CQHCI_VALID(1) | - CQHCI_END(1) | - CQHCI_INT(1) | - CQHCI_QBAR(1) | - CQHCI_ACT(0x5) | - CQHCI_CMD_INDEX(mrq->cmd->opcode) | - CQHCI_CMD_TIMING(timing) | CQHCI_RESP_TYPE(resp_type)); - if (cq_host->ops->update_dcmd_desc) - cq_host->ops->update_dcmd_desc(mmc, mrq, &data); - *task_desc |= data; - desc = (u8 *)task_desc; - pr_debug("%s: cqhci: dcmd: cmd: %d timing: %d resp: %d\n", - mmc_hostname(mmc), mrq->cmd->opcode, timing, resp_type); - dataddr = (__le64 __force *)(desc + 4); - dataddr[0] = cpu_to_le64((u64)mrq->cmd->arg); - -} - -static void cqhci_post_req(struct mmc_host *host, struct mmc_request *mrq) -{ - struct mmc_data *data = mrq->data; - - if (data) { - dma_unmap_sg(mmc_dev(host), data->sg, data->sg_len, - (data->flags & MMC_DATA_READ) ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); - } -} - -static inline int cqhci_tag(struct mmc_request *mrq) -{ - return mrq->cmd ? DCMD_SLOT : mrq->tag; -} - -static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - int err = 0; - u64 data = 0; - u64 *task_desc = NULL; - int tag = cqhci_tag(mrq); - struct cqhci_host *cq_host = mmc->cqe_private; - unsigned long flags; - - if (!cq_host->enabled) { - pr_err("%s: cqhci: not enabled\n", mmc_hostname(mmc)); - return -EINVAL; - } - - /* First request after resume has to re-enable */ - if (!cq_host->activated) - __cqhci_enable(cq_host); - - if (!mmc->cqe_on) { - if (cq_host->ops->pre_enable) - cq_host->ops->pre_enable(mmc); - - cqhci_writel(cq_host, 0, CQHCI_CTL); - mmc->cqe_on = true; - pr_debug("%s: cqhci: CQE on\n", mmc_hostname(mmc)); - if (cqhci_readl(cq_host, CQHCI_CTL) && CQHCI_HALT) { - pr_err("%s: cqhci: CQE failed to exit halt state\n", - mmc_hostname(mmc)); - } - if (cq_host->ops->enable) - cq_host->ops->enable(mmc); - } - - if (mrq->data) { - task_desc = (__le64 __force *)get_desc(cq_host, tag); - cqhci_prep_task_desc(mrq, &data, 1); - *task_desc = cpu_to_le64(data); - err = cqhci_prep_tran_desc(mrq, cq_host, tag); - if (err) { - pr_err("%s: cqhci: failed to setup tx desc: %d\n", - mmc_hostname(mmc), err); - return err; - } - } else { - cqhci_prep_dcmd_desc(mmc, mrq); - } - - spin_lock_irqsave(&cq_host->lock, flags); - - if (cq_host->recovery_halt) { - err = -EBUSY; - goto out_unlock; - } - - cq_host->slot[tag].mrq = mrq; - cq_host->slot[tag].flags = 0; - - cq_host->qcnt += 1; - /* Make sure descriptors are ready before ringing the doorbell */ - wmb(); - cqhci_writel(cq_host, 1 << tag, CQHCI_TDBR); - if (!(cqhci_readl(cq_host, CQHCI_TDBR) & (1 << tag))) - pr_debug("%s: cqhci: doorbell not set for tag %d\n", - mmc_hostname(mmc), tag); -out_unlock: - spin_unlock_irqrestore(&cq_host->lock, flags); - - if (err) - cqhci_post_req(mmc, mrq); - - return err; -} - -static void cqhci_recovery_needed(struct mmc_host *mmc, struct mmc_request *mrq, - bool notify) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - - if (!cq_host->recovery_halt) { - cq_host->recovery_halt = true; - pr_debug("%s: cqhci: recovery needed\n", mmc_hostname(mmc)); - wake_up(&cq_host->wait_queue); - if (notify && mrq->recovery_notifier) - mrq->recovery_notifier(mrq); - } -} - -static unsigned int cqhci_error_flags(int error1, int error2) -{ - int error = error1 ? error1 : error2; - - switch (error) { - case -EILSEQ: - return CQHCI_HOST_CRC; - case -ETIMEDOUT: - return CQHCI_HOST_TIMEOUT; - default: - return CQHCI_HOST_OTHER; - } -} - -static void cqhci_error_irq(struct mmc_host *mmc, u32 status, int cmd_error, - int data_error) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - struct cqhci_slot *slot; - u32 terri; - int tag; - - spin_lock(&cq_host->lock); - - terri = cqhci_readl(cq_host, CQHCI_TERRI); - - pr_debug("%s: cqhci: error IRQ status: 0x%08x cmd error %d data error %d TERRI: 0x%08x\n", - mmc_hostname(mmc), status, cmd_error, data_error, terri); - - /* Forget about errors when recovery has already been triggered */ - if (cq_host->recovery_halt) - goto out_unlock; - - if (!cq_host->qcnt) { - WARN_ONCE(1, "%s: cqhci: error when idle. IRQ status: 0x%08x cmd error %d data error %d TERRI: 0x%08x\n", - mmc_hostname(mmc), status, cmd_error, data_error, - terri); - goto out_unlock; - } - - if (CQHCI_TERRI_C_VALID(terri)) { - tag = CQHCI_TERRI_C_TASK(terri); - slot = &cq_host->slot[tag]; - if (slot->mrq) { - slot->flags = cqhci_error_flags(cmd_error, data_error); - cqhci_recovery_needed(mmc, slot->mrq, true); - } - } - - if (CQHCI_TERRI_D_VALID(terri)) { - tag = CQHCI_TERRI_D_TASK(terri); - slot = &cq_host->slot[tag]; - if (slot->mrq) { - slot->flags = cqhci_error_flags(data_error, cmd_error); - cqhci_recovery_needed(mmc, slot->mrq, true); - } - } - - if (!cq_host->recovery_halt) { - /* - * The only way to guarantee forward progress is to mark at - * least one task in error, so if none is indicated, pick one. - */ - for (tag = 0; tag < NUM_SLOTS; tag++) { - slot = &cq_host->slot[tag]; - if (!slot->mrq) - continue; - slot->flags = cqhci_error_flags(data_error, cmd_error); - cqhci_recovery_needed(mmc, slot->mrq, true); - break; - } - } - -out_unlock: - spin_unlock(&cq_host->lock); -} - -static void cqhci_finish_mrq(struct mmc_host *mmc, unsigned int tag) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - struct cqhci_slot *slot = &cq_host->slot[tag]; - struct mmc_request *mrq = slot->mrq; - struct mmc_data *data; - - if (!mrq) { - WARN_ONCE(1, "%s: cqhci: spurious TCN for tag %d\n", - mmc_hostname(mmc), tag); - return; - } - - /* No completions allowed during recovery */ - if (cq_host->recovery_halt) { - slot->flags |= CQHCI_COMPLETED; - return; - } - - slot->mrq = NULL; - - cq_host->qcnt -= 1; - - data = mrq->data; - if (data) { - if (data->error) - data->bytes_xfered = 0; - else - data->bytes_xfered = data->blksz * data->blocks; - } - - mmc_cqe_request_done(mmc, mrq); -} - -irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, - int data_error) -{ - u32 status; - unsigned long tag = 0, comp_status; - struct cqhci_host *cq_host = mmc->cqe_private; - - status = cqhci_readl(cq_host, CQHCI_IS); - cqhci_writel(cq_host, status, CQHCI_IS); - - pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status); - - if ((status & CQHCI_IS_RED) || cmd_error || data_error) - cqhci_error_irq(mmc, status, cmd_error, data_error); - - if (status & CQHCI_IS_TCC) { - /* read TCN and complete the request */ - comp_status = cqhci_readl(cq_host, CQHCI_TCN); - cqhci_writel(cq_host, comp_status, CQHCI_TCN); - pr_debug("%s: cqhci: TCN: 0x%08lx\n", - mmc_hostname(mmc), comp_status); - - spin_lock(&cq_host->lock); - - for_each_set_bit(tag, &comp_status, cq_host->num_slots) { - /* complete the corresponding mrq */ - pr_debug("%s: cqhci: completing tag %lu\n", - mmc_hostname(mmc), tag); - cqhci_finish_mrq(mmc, tag); - } - - if (cq_host->waiting_for_idle && !cq_host->qcnt) { - cq_host->waiting_for_idle = false; - wake_up(&cq_host->wait_queue); - } - - spin_unlock(&cq_host->lock); - } - - if (status & CQHCI_IS_TCL) - wake_up(&cq_host->wait_queue); - - if (status & CQHCI_IS_HAC) - wake_up(&cq_host->wait_queue); - - return IRQ_HANDLED; -} -EXPORT_SYMBOL(cqhci_irq); - -static bool cqhci_is_idle(struct cqhci_host *cq_host, int *ret) -{ - unsigned long flags; - bool is_idle; - - spin_lock_irqsave(&cq_host->lock, flags); - is_idle = !cq_host->qcnt || cq_host->recovery_halt; - *ret = cq_host->recovery_halt ? -EBUSY : 0; - cq_host->waiting_for_idle = !is_idle; - spin_unlock_irqrestore(&cq_host->lock, flags); - - return is_idle; -} - -static int cqhci_wait_for_idle(struct mmc_host *mmc) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - int ret; - - wait_event(cq_host->wait_queue, cqhci_is_idle(cq_host, &ret)); - - return ret; -} - -static bool cqhci_timeout(struct mmc_host *mmc, struct mmc_request *mrq, - bool *recovery_needed) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - int tag = cqhci_tag(mrq); - struct cqhci_slot *slot = &cq_host->slot[tag]; - unsigned long flags; - bool timed_out; - - spin_lock_irqsave(&cq_host->lock, flags); - timed_out = slot->mrq == mrq; - if (timed_out) { - slot->flags |= CQHCI_EXTERNAL_TIMEOUT; - cqhci_recovery_needed(mmc, mrq, false); - *recovery_needed = cq_host->recovery_halt; - } - spin_unlock_irqrestore(&cq_host->lock, flags); - - if (timed_out) { - pr_err("%s: cqhci: timeout for tag %d\n", - mmc_hostname(mmc), tag); - cqhci_dumpregs(cq_host); - } - - return timed_out; -} - -static bool cqhci_tasks_cleared(struct cqhci_host *cq_host) -{ - return !(cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_CLEAR_ALL_TASKS); -} - -static bool cqhci_clear_all_tasks(struct mmc_host *mmc, unsigned int timeout) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - bool ret; - u32 ctl; - - cqhci_set_irqs(cq_host, CQHCI_IS_TCL); - - ctl = cqhci_readl(cq_host, CQHCI_CTL); - ctl |= CQHCI_CLEAR_ALL_TASKS; - cqhci_writel(cq_host, ctl, CQHCI_CTL); - - wait_event_timeout(cq_host->wait_queue, cqhci_tasks_cleared(cq_host), - msecs_to_jiffies(timeout) + 1); - - cqhci_set_irqs(cq_host, 0); - - ret = cqhci_tasks_cleared(cq_host); - - if (!ret) - pr_debug("%s: cqhci: Failed to clear tasks\n", - mmc_hostname(mmc)); - - return ret; -} - -static bool cqhci_halted(struct cqhci_host *cq_host) -{ - return cqhci_readl(cq_host, CQHCI_CTL) & CQHCI_HALT; -} - -static bool cqhci_halt(struct mmc_host *mmc, unsigned int timeout) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - bool ret; - u32 ctl; - - if (cqhci_halted(cq_host)) - return true; - - cqhci_set_irqs(cq_host, CQHCI_IS_HAC); - - ctl = cqhci_readl(cq_host, CQHCI_CTL); - ctl |= CQHCI_HALT; - cqhci_writel(cq_host, ctl, CQHCI_CTL); - - wait_event_timeout(cq_host->wait_queue, cqhci_halted(cq_host), - msecs_to_jiffies(timeout) + 1); - - cqhci_set_irqs(cq_host, 0); - - ret = cqhci_halted(cq_host); - - if (!ret) - pr_debug("%s: cqhci: Failed to halt\n", mmc_hostname(mmc)); - - return ret; -} - -/* - * After halting we expect to be able to use the command line. We interpret the - * failure to halt to mean the data lines might still be in use (and the upper - * layers will need to send a STOP command), so we set the timeout based on a - * generous command timeout. - */ -#define CQHCI_START_HALT_TIMEOUT 5 - -static void cqhci_recovery_start(struct mmc_host *mmc) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - - pr_debug("%s: cqhci: %s\n", mmc_hostname(mmc), __func__); - - WARN_ON(!cq_host->recovery_halt); - - cqhci_halt(mmc, CQHCI_START_HALT_TIMEOUT); - - if (cq_host->ops->disable) - cq_host->ops->disable(mmc, true); - - mmc->cqe_on = false; -} - -static int cqhci_error_from_flags(unsigned int flags) -{ - if (!flags) - return 0; - - /* CRC errors might indicate re-tuning so prefer to report that */ - if (flags & CQHCI_HOST_CRC) - return -EILSEQ; - - if (flags & (CQHCI_EXTERNAL_TIMEOUT | CQHCI_HOST_TIMEOUT)) - return -ETIMEDOUT; - - return -EIO; -} - -static void cqhci_recover_mrq(struct cqhci_host *cq_host, unsigned int tag) -{ - struct cqhci_slot *slot = &cq_host->slot[tag]; - struct mmc_request *mrq = slot->mrq; - struct mmc_data *data; - - if (!mrq) - return; - - slot->mrq = NULL; - - cq_host->qcnt -= 1; - - data = mrq->data; - if (data) { - data->bytes_xfered = 0; - data->error = cqhci_error_from_flags(slot->flags); - } else { - mrq->cmd->error = cqhci_error_from_flags(slot->flags); - } - - mmc_cqe_request_done(cq_host->mmc, mrq); -} - -static void cqhci_recover_mrqs(struct cqhci_host *cq_host) -{ - int i; - - for (i = 0; i < cq_host->num_slots; i++) - cqhci_recover_mrq(cq_host, i); -} - -/* - * By now the command and data lines should be unused so there is no reason for - * CQHCI to take a long time to halt, but if it doesn't halt there could be - * problems clearing tasks, so be generous. - */ -#define CQHCI_FINISH_HALT_TIMEOUT 20 - -/* CQHCI could be expected to clear it's internal state pretty quickly */ -#define CQHCI_CLEAR_TIMEOUT 20 - -static void cqhci_recovery_finish(struct mmc_host *mmc) -{ - struct cqhci_host *cq_host = mmc->cqe_private; - unsigned long flags; - u32 cqcfg; - bool ok; - - pr_debug("%s: cqhci: %s\n", mmc_hostname(mmc), __func__); - - WARN_ON(!cq_host->recovery_halt); - - ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT); - - if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT)) - ok = false; - - /* - * The specification contradicts itself, by saying that tasks cannot be - * cleared if CQHCI does not halt, but if CQHCI does not halt, it should - * be disabled/re-enabled, but not to disable before clearing tasks. - * Have a go anyway. - */ - if (!ok) { - pr_debug("%s: cqhci: disable / re-enable\n", mmc_hostname(mmc)); - cqcfg = cqhci_readl(cq_host, CQHCI_CFG); - cqcfg &= ~CQHCI_ENABLE; - cqhci_writel(cq_host, cqcfg, CQHCI_CFG); - cqcfg |= CQHCI_ENABLE; - cqhci_writel(cq_host, cqcfg, CQHCI_CFG); - /* Be sure that there are no tasks */ - ok = cqhci_halt(mmc, CQHCI_FINISH_HALT_TIMEOUT); - if (!cqhci_clear_all_tasks(mmc, CQHCI_CLEAR_TIMEOUT)) - ok = false; - WARN_ON(!ok); - } - - cqhci_recover_mrqs(cq_host); - - WARN_ON(cq_host->qcnt); - - spin_lock_irqsave(&cq_host->lock, flags); - cq_host->qcnt = 0; - cq_host->recovery_halt = false; - mmc->cqe_on = false; - spin_unlock_irqrestore(&cq_host->lock, flags); - - /* Ensure all writes are done before interrupts are re-enabled */ - wmb(); - - cqhci_writel(cq_host, CQHCI_IS_HAC | CQHCI_IS_TCL, CQHCI_IS); - - cqhci_set_irqs(cq_host, CQHCI_IS_MASK); - - pr_debug("%s: cqhci: recovery done\n", mmc_hostname(mmc)); -} - -static const struct mmc_cqe_ops cqhci_cqe_ops = { - .cqe_enable = cqhci_enable, - .cqe_disable = cqhci_disable, - .cqe_request = cqhci_request, - .cqe_post_req = cqhci_post_req, - .cqe_off = cqhci_off, - .cqe_wait_for_idle = cqhci_wait_for_idle, - .cqe_timeout = cqhci_timeout, - .cqe_recovery_start = cqhci_recovery_start, - .cqe_recovery_finish = cqhci_recovery_finish, -}; - -struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev) -{ - struct cqhci_host *cq_host; - struct resource *cqhci_memres = NULL; - - /* check and setup CMDQ interface */ - cqhci_memres = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "cqhci"); - if (!cqhci_memres) { - dev_dbg(&pdev->dev, "CMDQ not supported\n"); - return ERR_PTR(-EINVAL); - } - - cq_host = devm_kzalloc(&pdev->dev, sizeof(*cq_host), GFP_KERNEL); - if (!cq_host) - return ERR_PTR(-ENOMEM); - cq_host->mmio = devm_ioremap(&pdev->dev, - cqhci_memres->start, - resource_size(cqhci_memres)); - if (!cq_host->mmio) { - dev_err(&pdev->dev, "failed to remap cqhci regs\n"); - return ERR_PTR(-EBUSY); - } - dev_dbg(&pdev->dev, "CMDQ ioremap: done\n"); - - return cq_host; -} -EXPORT_SYMBOL(cqhci_pltfm_init); - -static unsigned int cqhci_ver_major(struct cqhci_host *cq_host) -{ - return CQHCI_VER_MAJOR(cqhci_readl(cq_host, CQHCI_VER)); -} - -static unsigned int cqhci_ver_minor(struct cqhci_host *cq_host) -{ - u32 ver = cqhci_readl(cq_host, CQHCI_VER); - - return CQHCI_VER_MINOR1(ver) * 10 + CQHCI_VER_MINOR2(ver); -} - -int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, - bool dma64) -{ - int err; - - cq_host->dma64 = dma64; - cq_host->mmc = mmc; - cq_host->mmc->cqe_private = cq_host; - - cq_host->num_slots = NUM_SLOTS; - cq_host->dcmd_slot = DCMD_SLOT; - - mmc->cqe_ops = &cqhci_cqe_ops; - - mmc->cqe_qdepth = NUM_SLOTS; - if (mmc->caps2 & MMC_CAP2_CQE_DCMD) - mmc->cqe_qdepth -= 1; - - cq_host->slot = devm_kcalloc(mmc_dev(mmc), cq_host->num_slots, - sizeof(*cq_host->slot), GFP_KERNEL); - if (!cq_host->slot) { - err = -ENOMEM; - goto out_err; - } - - spin_lock_init(&cq_host->lock); - - init_completion(&cq_host->halt_comp); - init_waitqueue_head(&cq_host->wait_queue); - - pr_info("%s: CQHCI version %u.%02u\n", - mmc_hostname(mmc), cqhci_ver_major(cq_host), - cqhci_ver_minor(cq_host)); - - return 0; - -out_err: - pr_err("%s: CQHCI version %u.%02u failed to initialize, error %d\n", - mmc_hostname(mmc), cqhci_ver_major(cq_host), - cqhci_ver_minor(cq_host), err); - return err; -} -EXPORT_SYMBOL(cqhci_init); - -MODULE_AUTHOR("Venkat Gopalakrishnan "); -MODULE_DESCRIPTION("Command Queue Host Controller Interface driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ee49d0321f02596a046173be16fddcdfb8ceb7c1 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 25 Jan 2021 16:14:50 -0800 Subject: mmc: cqhci: initialize upper 64 bits of 128-bit task descriptors Move the task descriptor initialization into cqhci_prep_task_desc(). In addition, make it explicitly initialize all 128 bits of the task descriptor if the host controller is using 128-bit task descriptors, rather than relying on the implicit zeroing from dmam_alloc_coherent(). This is needed to prepare for CQHCI inline encryption support, which requires 128-bit task descriptors and uses the upper 64 bits. Acked-by: Adrian Hunter Reviewed-by: Satya Tangirala Reviewed-and-tested-by: Peng Zhou Signed-off-by: Eric Biggers Link: https://lore.kernel.org/r/20210126001456.382989-4-ebiggers@kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/cqhci-core.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c index 697fe40756bf..ad7c9acff172 100644 --- a/drivers/mmc/host/cqhci-core.c +++ b/drivers/mmc/host/cqhci-core.c @@ -408,13 +408,15 @@ static void cqhci_disable(struct mmc_host *mmc) } static void cqhci_prep_task_desc(struct mmc_request *mrq, - u64 *data, bool intr) + struct cqhci_host *cq_host, int tag) { + __le64 *task_desc = (__le64 __force *)get_desc(cq_host, tag); u32 req_flags = mrq->data->flags; + u64 desc0; - *data = CQHCI_VALID(1) | + desc0 = CQHCI_VALID(1) | CQHCI_END(1) | - CQHCI_INT(intr) | + CQHCI_INT(1) | CQHCI_ACT(0x5) | CQHCI_FORCED_PROG(!!(req_flags & MMC_DATA_FORCED_PRG)) | CQHCI_DATA_TAG(!!(req_flags & MMC_DATA_DAT_TAG)) | @@ -425,8 +427,19 @@ static void cqhci_prep_task_desc(struct mmc_request *mrq, CQHCI_BLK_COUNT(mrq->data->blocks) | CQHCI_BLK_ADDR((u64)mrq->data->blk_addr); - pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n", - mmc_hostname(mrq->host), mrq->tag, (unsigned long long)*data); + task_desc[0] = cpu_to_le64(desc0); + + if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) { + u64 desc1 = 0; + + task_desc[1] = cpu_to_le64(desc1); + + pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx%016llx\n", + mmc_hostname(mrq->host), mrq->tag, desc1, desc0); + } else { + pr_debug("%s: cqhci: tag %d task descriptor 0x%016llx\n", + mmc_hostname(mrq->host), mrq->tag, desc0); + } } static int cqhci_dma_map(struct mmc_host *host, struct mmc_request *mrq) @@ -567,8 +580,6 @@ static inline int cqhci_tag(struct mmc_request *mrq) static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { int err = 0; - u64 data = 0; - u64 *task_desc = NULL; int tag = cqhci_tag(mrq); struct cqhci_host *cq_host = mmc->cqe_private; unsigned long flags; @@ -598,9 +609,8 @@ static int cqhci_request(struct mmc_host *mmc, struct mmc_request *mrq) } if (mrq->data) { - task_desc = (__le64 __force *)get_desc(cq_host, tag); - cqhci_prep_task_desc(mrq, &data, 1); - *task_desc = cpu_to_le64(data); + cqhci_prep_task_desc(mrq, cq_host, tag); + err = cqhci_prep_tran_desc(mrq, cq_host, tag); if (err) { pr_err("%s: cqhci: failed to setup tx desc: %d\n", -- cgit v1.2.3 From 1e80709bdbfc1e1f3cac0ba8ed9a58f5789bcf51 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 25 Jan 2021 10:38:05 -0800 Subject: mmc: cqhci: add support for inline encryption Add support for eMMC inline encryption using the blk-crypto framework (Documentation/block/inline-encryption.rst). eMMC inline encryption support is specified by the upcoming JEDEC eMMC v5.2 specification. It is only specified for the CQ interface, not the non-CQ interface. Although the eMMC v5.2 specification hasn't been officially released yet, the crypto support was already agreed on several years ago, and it was already implemented by at least two major hardware vendors. Lots of hardware in the field already supports and uses it, e.g. Snapdragon 630 to give one example. eMMC inline encryption support is very similar to the UFS inline encryption support which was standardized in the UFS v2.1 specification and was already upstreamed. The only major difference is that eMMC limits data unit numbers to 32 bits, unlike UFS's 64 bits. Like we did with UFS, make the crypto support opt-in by individual drivers; don't enable it automatically whenever the hardware declares crypto support. This is necessary because in every case we've seen, some extra vendor-specific logic is needed to use the crypto support. Co-developed-by: Satya Tangirala Signed-off-by: Satya Tangirala Acked-by: Adrian Hunter Reviewed-by: Satya Tangirala Reviewed-and-tested-by: Peng Zhou Signed-off-by: Eric Biggers Link: https://lore.kernel.org/r/20210125183810.198008-5-ebiggers@kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/Makefile | 1 + drivers/mmc/host/cqhci-core.c | 41 ++++++- drivers/mmc/host/cqhci-crypto.c | 238 ++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/cqhci-crypto.h | 47 ++++++++ drivers/mmc/host/cqhci.h | 80 +++++++++++++- 5 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 drivers/mmc/host/cqhci-crypto.c create mode 100644 drivers/mmc/host/cqhci-crypto.h diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 19687ad42c6b..9ea86a18f012 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -103,6 +103,7 @@ obj-$(CONFIG_MMC_SDHCI_OMAP) += sdhci-omap.o obj-$(CONFIG_MMC_SDHCI_SPRD) += sdhci-sprd.o obj-$(CONFIG_MMC_CQHCI) += cqhci.o cqhci-y += cqhci-core.o +cqhci-$(CONFIG_MMC_CRYPTO) += cqhci-crypto.o obj-$(CONFIG_MMC_HSQ) += mmc_hsq.o ifeq ($(CONFIG_CB710_DEBUG),y) diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c index ad7c9acff172..93b0432bb601 100644 --- a/drivers/mmc/host/cqhci-core.c +++ b/drivers/mmc/host/cqhci-core.c @@ -18,6 +18,7 @@ #include #include "cqhci.h" +#include "cqhci-crypto.h" #define DCMD_SLOT 31 #define NUM_SLOTS 32 @@ -258,6 +259,9 @@ static void __cqhci_enable(struct cqhci_host *cq_host) if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) cqcfg |= CQHCI_TASK_DESC_SZ; + if (mmc->caps2 & MMC_CAP2_CRYPTO) + cqcfg |= CQHCI_CRYPTO_GENERAL_ENABLE; + cqhci_writel(cq_host, cqcfg, CQHCI_CFG); cqhci_writel(cq_host, lower_32_bits(cq_host->desc_dma_base), @@ -430,7 +434,7 @@ static void cqhci_prep_task_desc(struct mmc_request *mrq, task_desc[0] = cpu_to_le64(desc0); if (cq_host->caps & CQHCI_TASK_DESC_SZ_128) { - u64 desc1 = 0; + u64 desc1 = cqhci_crypto_prep_task_desc(mrq); task_desc[1] = cpu_to_le64(desc1); @@ -681,6 +685,7 @@ static void cqhci_error_irq(struct mmc_host *mmc, u32 status, int cmd_error, struct cqhci_host *cq_host = mmc->cqe_private; struct cqhci_slot *slot; u32 terri; + u32 tdpe; int tag; spin_lock(&cq_host->lock); @@ -719,6 +724,30 @@ static void cqhci_error_irq(struct mmc_host *mmc, u32 status, int cmd_error, } } + /* + * Handle ICCE ("Invalid Crypto Configuration Error"). This should + * never happen, since the block layer ensures that all crypto-enabled + * I/O requests have a valid keyslot before they reach the driver. + * + * Note that GCE ("General Crypto Error") is different; it already got + * handled above by checking TERRI. + */ + if (status & CQHCI_IS_ICCE) { + tdpe = cqhci_readl(cq_host, CQHCI_TDPE); + WARN_ONCE(1, + "%s: cqhci: invalid crypto configuration error. IRQ status: 0x%08x TDPE: 0x%08x\n", + mmc_hostname(mmc), status, tdpe); + while (tdpe != 0) { + tag = __ffs(tdpe); + tdpe &= ~(1 << tag); + slot = &cq_host->slot[tag]; + if (!slot->mrq) + continue; + slot->flags = cqhci_error_flags(data_error, cmd_error); + cqhci_recovery_needed(mmc, slot->mrq, true); + } + } + if (!cq_host->recovery_halt) { /* * The only way to guarantee forward progress is to mark at @@ -784,7 +813,8 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status); - if ((status & CQHCI_IS_RED) || cmd_error || data_error) + if ((status & (CQHCI_IS_RED | CQHCI_IS_GCE | CQHCI_IS_ICCE)) || + cmd_error || data_error) cqhci_error_irq(mmc, status, cmd_error, data_error); if (status & CQHCI_IS_TCC) { @@ -1151,6 +1181,13 @@ int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, goto out_err; } + err = cqhci_crypto_init(cq_host); + if (err) { + pr_err("%s: CQHCI crypto initialization failed\n", + mmc_hostname(mmc)); + goto out_err; + } + spin_lock_init(&cq_host->lock); init_completion(&cq_host->halt_comp); diff --git a/drivers/mmc/host/cqhci-crypto.c b/drivers/mmc/host/cqhci-crypto.c new file mode 100644 index 000000000000..0e2a9dcac630 --- /dev/null +++ b/drivers/mmc/host/cqhci-crypto.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * CQHCI crypto engine (inline encryption) support + * + * Copyright 2020 Google LLC + */ + +#include +#include +#include + +#include "cqhci-crypto.h" + +/* Map from blk-crypto modes to CQHCI crypto algorithm IDs and key sizes */ +static const struct cqhci_crypto_alg_entry { + enum cqhci_crypto_alg alg; + enum cqhci_crypto_key_size key_size; +} cqhci_crypto_algs[BLK_ENCRYPTION_MODE_MAX] = { + [BLK_ENCRYPTION_MODE_AES_256_XTS] = { + .alg = CQHCI_CRYPTO_ALG_AES_XTS, + .key_size = CQHCI_CRYPTO_KEY_SIZE_256, + }, +}; + +static inline struct cqhci_host * +cqhci_host_from_ksm(struct blk_keyslot_manager *ksm) +{ + struct mmc_host *mmc = container_of(ksm, struct mmc_host, ksm); + + return mmc->cqe_private; +} + +static void cqhci_crypto_program_key(struct cqhci_host *cq_host, + const union cqhci_crypto_cfg_entry *cfg, + int slot) +{ + u32 slot_offset = cq_host->crypto_cfg_register + slot * sizeof(*cfg); + int i; + + /* Clear CFGE */ + cqhci_writel(cq_host, 0, slot_offset + 16 * sizeof(cfg->reg_val[0])); + + /* Write the key */ + for (i = 0; i < 16; i++) { + cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[i]), + slot_offset + i * sizeof(cfg->reg_val[0])); + } + /* Write dword 17 */ + cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[17]), + slot_offset + 17 * sizeof(cfg->reg_val[0])); + /* Write dword 16, which includes the new value of CFGE */ + cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[16]), + slot_offset + 16 * sizeof(cfg->reg_val[0])); +} + +static int cqhci_crypto_keyslot_program(struct blk_keyslot_manager *ksm, + const struct blk_crypto_key *key, + unsigned int slot) + +{ + struct cqhci_host *cq_host = cqhci_host_from_ksm(ksm); + const union cqhci_crypto_cap_entry *ccap_array = + cq_host->crypto_cap_array; + const struct cqhci_crypto_alg_entry *alg = + &cqhci_crypto_algs[key->crypto_cfg.crypto_mode]; + u8 data_unit_mask = key->crypto_cfg.data_unit_size / 512; + int i; + int cap_idx = -1; + union cqhci_crypto_cfg_entry cfg = {}; + + BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0); + for (i = 0; i < cq_host->crypto_capabilities.num_crypto_cap; i++) { + if (ccap_array[i].algorithm_id == alg->alg && + ccap_array[i].key_size == alg->key_size && + (ccap_array[i].sdus_mask & data_unit_mask)) { + cap_idx = i; + break; + } + } + if (WARN_ON(cap_idx < 0)) + return -EOPNOTSUPP; + + cfg.data_unit_size = data_unit_mask; + cfg.crypto_cap_idx = cap_idx; + cfg.config_enable = CQHCI_CRYPTO_CONFIGURATION_ENABLE; + + if (ccap_array[cap_idx].algorithm_id == CQHCI_CRYPTO_ALG_AES_XTS) { + /* In XTS mode, the blk_crypto_key's size is already doubled */ + memcpy(cfg.crypto_key, key->raw, key->size/2); + memcpy(cfg.crypto_key + CQHCI_CRYPTO_KEY_MAX_SIZE/2, + key->raw + key->size/2, key->size/2); + } else { + memcpy(cfg.crypto_key, key->raw, key->size); + } + + cqhci_crypto_program_key(cq_host, &cfg, slot); + + memzero_explicit(&cfg, sizeof(cfg)); + return 0; +} + +static void cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot) +{ + /* + * Clear the crypto cfg on the device. Clearing CFGE + * might not be sufficient, so just clear the entire cfg. + */ + union cqhci_crypto_cfg_entry cfg = {}; + + cqhci_crypto_program_key(cq_host, &cfg, slot); +} + +static int cqhci_crypto_keyslot_evict(struct blk_keyslot_manager *ksm, + const struct blk_crypto_key *key, + unsigned int slot) +{ + struct cqhci_host *cq_host = cqhci_host_from_ksm(ksm); + + cqhci_crypto_clear_keyslot(cq_host, slot); + return 0; +} + +/* + * The keyslot management operations for CQHCI crypto. + * + * Note that the block layer ensures that these are never called while the host + * controller is runtime-suspended. However, the CQE won't necessarily be + * "enabled" when these are called, i.e. CQHCI_ENABLE might not be set in the + * CQHCI_CFG register. But the hardware allows that. + */ +static const struct blk_ksm_ll_ops cqhci_ksm_ops = { + .keyslot_program = cqhci_crypto_keyslot_program, + .keyslot_evict = cqhci_crypto_keyslot_evict, +}; + +static enum blk_crypto_mode_num +cqhci_find_blk_crypto_mode(union cqhci_crypto_cap_entry cap) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cqhci_crypto_algs); i++) { + BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0); + if (cqhci_crypto_algs[i].alg == cap.algorithm_id && + cqhci_crypto_algs[i].key_size == cap.key_size) + return i; + } + return BLK_ENCRYPTION_MODE_INVALID; +} + +/** + * cqhci_crypto_init - initialize CQHCI crypto support + * @cq_host: a cqhci host + * + * If the driver previously set MMC_CAP2_CRYPTO and the CQE declares + * CQHCI_CAP_CS, initialize the crypto support. This involves reading the + * crypto capability registers, initializing the keyslot manager, clearing all + * keyslots, and enabling 128-bit task descriptors. + * + * Return: 0 if crypto was initialized or isn't supported; whether + * MMC_CAP2_CRYPTO remains set indicates which one of those cases it is. + * Also can return a negative errno value on unexpected error. + */ +int cqhci_crypto_init(struct cqhci_host *cq_host) +{ + struct mmc_host *mmc = cq_host->mmc; + struct device *dev = mmc_dev(mmc); + struct blk_keyslot_manager *ksm = &mmc->ksm; + unsigned int num_keyslots; + unsigned int cap_idx; + enum blk_crypto_mode_num blk_mode_num; + unsigned int slot; + int err = 0; + + if (!(mmc->caps2 & MMC_CAP2_CRYPTO) || + !(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS)) + goto out; + + cq_host->crypto_capabilities.reg_val = + cpu_to_le32(cqhci_readl(cq_host, CQHCI_CCAP)); + + cq_host->crypto_cfg_register = + (u32)cq_host->crypto_capabilities.config_array_ptr * 0x100; + + cq_host->crypto_cap_array = + devm_kcalloc(dev, cq_host->crypto_capabilities.num_crypto_cap, + sizeof(cq_host->crypto_cap_array[0]), GFP_KERNEL); + if (!cq_host->crypto_cap_array) { + err = -ENOMEM; + goto out; + } + + /* + * CCAP.CFGC is off by one, so the actual number of crypto + * configurations (a.k.a. keyslots) is CCAP.CFGC + 1. + */ + num_keyslots = cq_host->crypto_capabilities.config_count + 1; + + err = devm_blk_ksm_init(dev, ksm, num_keyslots); + if (err) + goto out; + + ksm->ksm_ll_ops = cqhci_ksm_ops; + ksm->dev = dev; + + /* Unfortunately, CQHCI crypto only supports 32 DUN bits. */ + ksm->max_dun_bytes_supported = 4; + + /* + * Cache all the crypto capabilities and advertise the supported crypto + * modes and data unit sizes to the block layer. + */ + for (cap_idx = 0; cap_idx < cq_host->crypto_capabilities.num_crypto_cap; + cap_idx++) { + cq_host->crypto_cap_array[cap_idx].reg_val = + cpu_to_le32(cqhci_readl(cq_host, + CQHCI_CRYPTOCAP + + cap_idx * sizeof(__le32))); + blk_mode_num = cqhci_find_blk_crypto_mode( + cq_host->crypto_cap_array[cap_idx]); + if (blk_mode_num == BLK_ENCRYPTION_MODE_INVALID) + continue; + ksm->crypto_modes_supported[blk_mode_num] |= + cq_host->crypto_cap_array[cap_idx].sdus_mask * 512; + } + + /* Clear all the keyslots so that we start in a known state. */ + for (slot = 0; slot < num_keyslots; slot++) + cqhci_crypto_clear_keyslot(cq_host, slot); + + /* CQHCI crypto requires the use of 128-bit task descriptors. */ + cq_host->caps |= CQHCI_TASK_DESC_SZ_128; + + return 0; + +out: + mmc->caps2 &= ~MMC_CAP2_CRYPTO; + return err; +} diff --git a/drivers/mmc/host/cqhci-crypto.h b/drivers/mmc/host/cqhci-crypto.h new file mode 100644 index 000000000000..60b58ee0e625 --- /dev/null +++ b/drivers/mmc/host/cqhci-crypto.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * CQHCI crypto engine (inline encryption) support + * + * Copyright 2020 Google LLC + */ + +#ifndef LINUX_MMC_CQHCI_CRYPTO_H +#define LINUX_MMC_CQHCI_CRYPTO_H + +#include + +#include "cqhci.h" + +#ifdef CONFIG_MMC_CRYPTO + +int cqhci_crypto_init(struct cqhci_host *host); + +/* + * Returns the crypto bits that should be set in bits 64-127 of the + * task descriptor. + */ +static inline u64 cqhci_crypto_prep_task_desc(struct mmc_request *mrq) +{ + if (!mrq->crypto_enabled) + return 0; + + return CQHCI_CRYPTO_ENABLE_BIT | + CQHCI_CRYPTO_KEYSLOT(mrq->crypto_key_slot) | + mrq->data_unit_num; +} + +#else /* CONFIG_MMC_CRYPTO */ + +static inline int cqhci_crypto_init(struct cqhci_host *host) +{ + return 0; +} + +static inline u64 cqhci_crypto_prep_task_desc(struct mmc_request *mrq) +{ + return 0; +} + +#endif /* !CONFIG_MMC_CRYPTO */ + +#endif /* LINUX_MMC_CQHCI_CRYPTO_H */ diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h index 89bf6adbce8c..8e9e8f5db5bc 100644 --- a/drivers/mmc/host/cqhci.h +++ b/drivers/mmc/host/cqhci.h @@ -22,10 +22,13 @@ /* capabilities */ #define CQHCI_CAP 0x04 +#define CQHCI_CAP_CS 0x10000000 /* Crypto Support */ + /* configuration */ #define CQHCI_CFG 0x08 #define CQHCI_DCMD 0x00001000 #define CQHCI_TASK_DESC_SZ 0x00000100 +#define CQHCI_CRYPTO_GENERAL_ENABLE 0x00000002 #define CQHCI_ENABLE 0x00000001 /* control */ @@ -39,8 +42,11 @@ #define CQHCI_IS_TCC BIT(1) #define CQHCI_IS_RED BIT(2) #define CQHCI_IS_TCL BIT(3) +#define CQHCI_IS_GCE BIT(4) /* General Crypto Error */ +#define CQHCI_IS_ICCE BIT(5) /* Invalid Crypto Config Error */ -#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED) +#define CQHCI_IS_MASK (CQHCI_IS_TCC | CQHCI_IS_RED | \ + CQHCI_IS_GCE | CQHCI_IS_ICCE) /* interrupt status enable */ #define CQHCI_ISTE 0x14 @@ -78,6 +84,9 @@ /* task clear */ #define CQHCI_TCLR 0x38 +/* task descriptor processing error */ +#define CQHCI_TDPE 0x3c + /* send status config 1 */ #define CQHCI_SSC1 0x40 #define CQHCI_SSC1_CBC_MASK GENMASK(19, 16) @@ -107,6 +116,10 @@ /* command response argument */ #define CQHCI_CRA 0x5C +/* crypto capabilities */ +#define CQHCI_CCAP 0x100 +#define CQHCI_CRYPTOCAP 0x104 + #define CQHCI_INT_ALL 0xF #define CQHCI_IC_DEFAULT_ICCTH 31 #define CQHCI_IC_DEFAULT_ICTOVAL 1 @@ -133,11 +146,70 @@ #define CQHCI_CMD_TIMING(x) (((x) & 1) << 22) #define CQHCI_RESP_TYPE(x) (((x) & 0x3) << 23) +/* crypto task descriptor fields (for bits 64-127 of task descriptor) */ +#define CQHCI_CRYPTO_ENABLE_BIT (1ULL << 47) +#define CQHCI_CRYPTO_KEYSLOT(x) ((u64)(x) << 32) + /* transfer descriptor fields */ #define CQHCI_DAT_LENGTH(x) (((x) & 0xFFFF) << 16) #define CQHCI_DAT_ADDR_LO(x) (((x) & 0xFFFFFFFF) << 32) #define CQHCI_DAT_ADDR_HI(x) (((x) & 0xFFFFFFFF) << 0) +/* CCAP - Crypto Capability 100h */ +union cqhci_crypto_capabilities { + __le32 reg_val; + struct { + u8 num_crypto_cap; + u8 config_count; + u8 reserved; + u8 config_array_ptr; + }; +}; + +enum cqhci_crypto_key_size { + CQHCI_CRYPTO_KEY_SIZE_INVALID = 0, + CQHCI_CRYPTO_KEY_SIZE_128 = 1, + CQHCI_CRYPTO_KEY_SIZE_192 = 2, + CQHCI_CRYPTO_KEY_SIZE_256 = 3, + CQHCI_CRYPTO_KEY_SIZE_512 = 4, +}; + +enum cqhci_crypto_alg { + CQHCI_CRYPTO_ALG_AES_XTS = 0, + CQHCI_CRYPTO_ALG_BITLOCKER_AES_CBC = 1, + CQHCI_CRYPTO_ALG_AES_ECB = 2, + CQHCI_CRYPTO_ALG_ESSIV_AES_CBC = 3, +}; + +/* x-CRYPTOCAP - Crypto Capability X */ +union cqhci_crypto_cap_entry { + __le32 reg_val; + struct { + u8 algorithm_id; + u8 sdus_mask; /* Supported data unit size mask */ + u8 key_size; + u8 reserved; + }; +}; + +#define CQHCI_CRYPTO_CONFIGURATION_ENABLE (1 << 7) +#define CQHCI_CRYPTO_KEY_MAX_SIZE 64 +/* x-CRYPTOCFG - Crypto Configuration X */ +union cqhci_crypto_cfg_entry { + __le32 reg_val[32]; + struct { + u8 crypto_key[CQHCI_CRYPTO_KEY_MAX_SIZE]; + u8 data_unit_size; + u8 crypto_cap_idx; + u8 reserved_1; + u8 config_enable; + u8 reserved_multi_host; + u8 reserved_2; + u8 vsb[2]; + u8 reserved_3[56]; + }; +}; + struct cqhci_host_ops; struct mmc_host; struct mmc_request; @@ -196,6 +268,12 @@ struct cqhci_host { struct completion halt_comp; wait_queue_head_t wait_queue; struct cqhci_slot *slot; + +#ifdef CONFIG_MMC_CRYPTO + union cqhci_crypto_capabilities crypto_capabilities; + union cqhci_crypto_cap_entry *crypto_cap_array; + u32 crypto_cfg_register; +#endif }; struct cqhci_host_ops { -- cgit v1.2.3 From 0a0c866f377ebea6ed5b65942cf1269651c21072 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 25 Jan 2021 16:14:52 -0800 Subject: mmc: cqhci: add cqhci_host_ops::program_key On Snapdragon SoCs, the Linux kernel isn't permitted to directly access the standard CQHCI crypto configuration registers. Instead, programming and evicting keys must be done through vendor-specific SMC calls. To support this hardware, add a ->program_key() method to 'struct cqhci_host_ops'. This allows overriding the standard CQHCI crypto key programming / eviction procedure. This is inspired by the corresponding UFS crypto support, which uses these same SMC calls. See commit 1bc726e26ef3 ("scsi: ufs: Add program_key() variant op"). Acked-by: Adrian Hunter Reviewed-by: Satya Tangirala Reviewed-and-tested-by: Peng Zhou Signed-off-by: Eric Biggers Link: https://lore.kernel.org/r/20210126001456.382989-6-ebiggers@kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/cqhci-crypto.c | 22 +++++++++++++--------- drivers/mmc/host/cqhci.h | 4 ++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/cqhci-crypto.c b/drivers/mmc/host/cqhci-crypto.c index 0e2a9dcac630..6419cfbb4ab7 100644 --- a/drivers/mmc/host/cqhci-crypto.c +++ b/drivers/mmc/host/cqhci-crypto.c @@ -30,13 +30,16 @@ cqhci_host_from_ksm(struct blk_keyslot_manager *ksm) return mmc->cqe_private; } -static void cqhci_crypto_program_key(struct cqhci_host *cq_host, - const union cqhci_crypto_cfg_entry *cfg, - int slot) +static int cqhci_crypto_program_key(struct cqhci_host *cq_host, + const union cqhci_crypto_cfg_entry *cfg, + int slot) { u32 slot_offset = cq_host->crypto_cfg_register + slot * sizeof(*cfg); int i; + if (cq_host->ops->program_key) + return cq_host->ops->program_key(cq_host, cfg, slot); + /* Clear CFGE */ cqhci_writel(cq_host, 0, slot_offset + 16 * sizeof(cfg->reg_val[0])); @@ -51,6 +54,7 @@ static void cqhci_crypto_program_key(struct cqhci_host *cq_host, /* Write dword 16, which includes the new value of CFGE */ cqhci_writel(cq_host, le32_to_cpu(cfg->reg_val[16]), slot_offset + 16 * sizeof(cfg->reg_val[0])); + return 0; } static int cqhci_crypto_keyslot_program(struct blk_keyslot_manager *ksm, @@ -67,6 +71,7 @@ static int cqhci_crypto_keyslot_program(struct blk_keyslot_manager *ksm, int i; int cap_idx = -1; union cqhci_crypto_cfg_entry cfg = {}; + int err; BUILD_BUG_ON(CQHCI_CRYPTO_KEY_SIZE_INVALID != 0); for (i = 0; i < cq_host->crypto_capabilities.num_crypto_cap; i++) { @@ -93,13 +98,13 @@ static int cqhci_crypto_keyslot_program(struct blk_keyslot_manager *ksm, memcpy(cfg.crypto_key, key->raw, key->size); } - cqhci_crypto_program_key(cq_host, &cfg, slot); + err = cqhci_crypto_program_key(cq_host, &cfg, slot); memzero_explicit(&cfg, sizeof(cfg)); - return 0; + return err; } -static void cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot) +static int cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot) { /* * Clear the crypto cfg on the device. Clearing CFGE @@ -107,7 +112,7 @@ static void cqhci_crypto_clear_keyslot(struct cqhci_host *cq_host, int slot) */ union cqhci_crypto_cfg_entry cfg = {}; - cqhci_crypto_program_key(cq_host, &cfg, slot); + return cqhci_crypto_program_key(cq_host, &cfg, slot); } static int cqhci_crypto_keyslot_evict(struct blk_keyslot_manager *ksm, @@ -116,8 +121,7 @@ static int cqhci_crypto_keyslot_evict(struct blk_keyslot_manager *ksm, { struct cqhci_host *cq_host = cqhci_host_from_ksm(ksm); - cqhci_crypto_clear_keyslot(cq_host, slot); - return 0; + return cqhci_crypto_clear_keyslot(cq_host, slot); } /* diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h index 8e9e8f5db5bc..ba9387ed90eb 100644 --- a/drivers/mmc/host/cqhci.h +++ b/drivers/mmc/host/cqhci.h @@ -286,6 +286,10 @@ struct cqhci_host_ops { u64 *data); void (*pre_enable)(struct mmc_host *mmc); void (*post_disable)(struct mmc_host *mmc); +#ifdef CONFIG_MMC_CRYPTO + int (*program_key)(struct cqhci_host *cq_host, + const union cqhci_crypto_cfg_entry *cfg, int slot); +#endif }; static inline void cqhci_writel(struct cqhci_host *host, u32 val, int reg) -- cgit v1.2.3 From 433611ea8df3666845ecd3480c22667f065ea7af Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 25 Jan 2021 10:38:07 -0800 Subject: firmware: qcom_scm: update comment for ICE-related functions The SCM calls QCOM_SCM_ES_INVALIDATE_ICE_KEY and QCOM_SCM_ES_CONFIG_SET_ICE_KEY are also needed for eMMC inline encryption support, not just for UFS. Update the comments accordingly. Reviewed-by: Satya Tangirala Acked-by: Bjorn Andersson Signed-off-by: Eric Biggers Link: https://lore.kernel.org/r/20210125183810.198008-7-ebiggers@kernel.org Signed-off-by: Ulf Hansson --- drivers/firmware/qcom_scm.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 7be48c1bec96..f57779fc7ee9 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -965,8 +965,11 @@ EXPORT_SYMBOL(qcom_scm_ice_available); * qcom_scm_ice_invalidate_key() - Invalidate an inline encryption key * @index: the keyslot to invalidate * - * The UFSHCI standard defines a standard way to do this, but it doesn't work on - * these SoCs; only this SCM call does. + * The UFSHCI and eMMC standards define a standard way to do this, but it + * doesn't work on these SoCs; only this SCM call does. + * + * It is assumed that the SoC has only one ICE instance being used, as this SCM + * call doesn't specify which ICE instance the keyslot belongs to. * * Return: 0 on success; -errno on failure. */ @@ -995,10 +998,13 @@ EXPORT_SYMBOL(qcom_scm_ice_invalidate_key); * units, e.g. 1 = 512 bytes, 8 = 4096 bytes, etc. * * Program a key into a keyslot of Qualcomm ICE (Inline Crypto Engine), where it - * can then be used to encrypt/decrypt UFS I/O requests inline. + * can then be used to encrypt/decrypt UFS or eMMC I/O requests inline. + * + * The UFSHCI and eMMC standards define a standard way to do this, but it + * doesn't work on these SoCs; only this SCM call does. * - * The UFSHCI standard defines a standard way to do this, but it doesn't work on - * these SoCs; only this SCM call does. + * It is assumed that the SoC has only one ICE instance being used, as this SCM + * call doesn't specify which ICE instance the keyslot belongs to. * * Return: 0 on success; -errno on failure. */ -- cgit v1.2.3 From 5cc046eb134f680f3ab6e2bb4ff43b94683336eb Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 25 Jan 2021 16:14:54 -0800 Subject: dt-bindings: mmc: sdhci-msm: add ICE registers and clock Document the bindings for the registers and clock for the MMC instance of the Inline Crypto Engine (ICE) on Snapdragon SoCs. These bindings are needed in order for sdhci-msm to support inline encryption. Reviewed-by: Satya Tangirala Acked-by: Rob Herring Signed-off-by: Eric Biggers Link: https://lore.kernel.org/r/20210126001456.382989-8-ebiggers@kernel.org Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-msm.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index 9fa8a24fbc97..4c7fa6a4ed15 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -31,10 +31,12 @@ Required properties: - SD Core register map (required for controllers earlier than msm-v5) - CQE register map (Optional, CQE support is present on SDHC instance meant for eMMC and version v4.2 and above) + - Inline Crypto Engine register map (optional) - reg-names: When CQE register map is supplied, below reg-names are required - "hc" for Host controller register map - "core" for SD core register map - "cqhci" for CQE register map + - "ice" for Inline Crypto Engine register map (optional) - interrupts: Should contain an interrupt-specifiers for the interrupts: - Host controller interrupt (required) - pinctrl-names: Should contain only one value - "default". @@ -47,6 +49,7 @@ Required properties: "xo" - TCXO clock (optional) "cal" - reference clock for RCLK delay calibration (optional) "sleep" - sleep clock for RCLK delay calibration (optional) + "ice" - clock for Inline Crypto Engine (optional) - qcom,ddr-config: Certain chipsets and platforms require particular settings for the DDR_CONFIG register. Use this field to specify the register -- cgit v1.2.3 From c93767cf64ebf41c65d8834af27df63f2f0f7ec5 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Mon, 25 Jan 2021 16:14:55 -0800 Subject: mmc: sdhci-msm: add Inline Crypto Engine support Add support for Qualcomm Inline Crypto Engine (ICE) to sdhci-msm. The standard-compliant parts, such as querying the crypto capabilities and enabling crypto for individual MMC requests, are already handled by cqhci-crypto.c, which itself is wired into the blk-crypto framework. However, ICE requires vendor-specific init, enable, and resume logic, and it requires that keys be programmed and evicted by vendor-specific SMC calls. Make the sdhci-msm driver handle these details. This is heavily inspired by the similar changes made for UFS, since the UFS and eMMC ICE instances are very similar. See commit df4ec2fa7a4d ("scsi: ufs-qcom: Add Inline Crypto Engine support"). I tested this on a Sony Xperia 10, which uses the Snapdragon 630 SoC, which has basic upstream support. Mainly, I used android-xfstests (https://github.com/tytso/xfstests-bld/blob/master/Documentation/android-xfstests.md) to run the ext4 and f2fs encryption tests in a Debian chroot: android-xfstests -c ext4,f2fs -g encrypt -m inlinecrypt These tests included tests which verify that the on-disk ciphertext is identical to that produced by a software implementation. I also verified that ICE was actually being used. Acked-by: Adrian Hunter Reviewed-by: Satya Tangirala Signed-off-by: Eric Biggers Link: https://lore.kernel.org/r/20210126001456.382989-9-ebiggers@kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 1 + drivers/mmc/host/sdhci-msm.c | 276 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 273 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index bbf6989e3638..a29411ca626f 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -546,6 +546,7 @@ config MMC_SDHCI_MSM depends on MMC_SDHCI_PLTFM select MMC_SDHCI_IO_ACCESSORS select MMC_CQHCI + select QCOM_SCM if MMC_CRYPTO && ARCH_QCOM help This selects the Secure Digital Host Controller Interface (SDHCI) support present in Qualcomm SOCs. The controller supports diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 97902616a695..5e1da4df096f 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -255,10 +256,12 @@ struct sdhci_msm_variant_info { struct sdhci_msm_host { struct platform_device *pdev; void __iomem *core_mem; /* MSM SDCC mapped address */ + void __iomem *ice_mem; /* MSM ICE mapped address (if available) */ int pwr_irq; /* power irq */ struct clk *bus_clk; /* SDHC bus voter clock */ struct clk *xo_clk; /* TCXO clk needed for FLL feature of cm_dll*/ - struct clk_bulk_data bulk_clks[4]; /* core, iface, cal, sleep clocks */ + /* core, iface, cal, sleep, and ice clocks */ + struct clk_bulk_data bulk_clks[5]; unsigned long clk_rate; struct mmc_host *mmc; struct opp_table *opp_table; @@ -1792,6 +1795,246 @@ out: __sdhci_msm_set_clock(host, clock); } +/*****************************************************************************\ + * * + * Inline Crypto Engine (ICE) support * + * * +\*****************************************************************************/ + +#ifdef CONFIG_MMC_CRYPTO + +#define AES_256_XTS_KEY_SIZE 64 + +/* QCOM ICE registers */ + +#define QCOM_ICE_REG_VERSION 0x0008 + +#define QCOM_ICE_REG_FUSE_SETTING 0x0010 +#define QCOM_ICE_FUSE_SETTING_MASK 0x1 +#define QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK 0x2 +#define QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK 0x4 + +#define QCOM_ICE_REG_BIST_STATUS 0x0070 +#define QCOM_ICE_BIST_STATUS_MASK 0xF0000000 + +#define QCOM_ICE_REG_ADVANCED_CONTROL 0x1000 + +#define sdhci_msm_ice_writel(host, val, reg) \ + writel((val), (host)->ice_mem + (reg)) +#define sdhci_msm_ice_readl(host, reg) \ + readl((host)->ice_mem + (reg)) + +static bool sdhci_msm_ice_supported(struct sdhci_msm_host *msm_host) +{ + struct device *dev = mmc_dev(msm_host->mmc); + u32 regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_VERSION); + int major = regval >> 24; + int minor = (regval >> 16) & 0xFF; + int step = regval & 0xFFFF; + + /* For now this driver only supports ICE version 3. */ + if (major != 3) { + dev_warn(dev, "Unsupported ICE version: v%d.%d.%d\n", + major, minor, step); + return false; + } + + dev_info(dev, "Found QC Inline Crypto Engine (ICE) v%d.%d.%d\n", + major, minor, step); + + /* If fuses are blown, ICE might not work in the standard way. */ + regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_FUSE_SETTING); + if (regval & (QCOM_ICE_FUSE_SETTING_MASK | + QCOM_ICE_FORCE_HW_KEY0_SETTING_MASK | + QCOM_ICE_FORCE_HW_KEY1_SETTING_MASK)) { + dev_warn(dev, "Fuses are blown; ICE is unusable!\n"); + return false; + } + return true; +} + +static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev) +{ + return devm_clk_get(dev, "ice"); +} + +static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, + struct cqhci_host *cq_host) +{ + struct mmc_host *mmc = msm_host->mmc; + struct device *dev = mmc_dev(mmc); + struct resource *res; + int err; + + if (!(cqhci_readl(cq_host, CQHCI_CAP) & CQHCI_CAP_CS)) + return 0; + + res = platform_get_resource_byname(msm_host->pdev, IORESOURCE_MEM, + "ice"); + if (!res) { + dev_warn(dev, "ICE registers not found\n"); + goto disable; + } + + if (!qcom_scm_ice_available()) { + dev_warn(dev, "ICE SCM interface not found\n"); + goto disable; + } + + msm_host->ice_mem = devm_ioremap_resource(dev, res); + if (IS_ERR(msm_host->ice_mem)) { + err = PTR_ERR(msm_host->ice_mem); + dev_err(dev, "Failed to map ICE registers; err=%d\n", err); + return err; + } + + if (!sdhci_msm_ice_supported(msm_host)) + goto disable; + + mmc->caps2 |= MMC_CAP2_CRYPTO; + return 0; + +disable: + dev_warn(dev, "Disabling inline encryption support\n"); + return 0; +} + +static void sdhci_msm_ice_low_power_mode_enable(struct sdhci_msm_host *msm_host) +{ + u32 regval; + + regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL); + /* + * Enable low power mode sequence + * [0]-0, [1]-0, [2]-0, [3]-E, [4]-0, [5]-0, [6]-0, [7]-0 + */ + regval |= 0x7000; + sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL); +} + +static void sdhci_msm_ice_optimization_enable(struct sdhci_msm_host *msm_host) +{ + u32 regval; + + /* ICE Optimizations Enable Sequence */ + regval = sdhci_msm_ice_readl(msm_host, QCOM_ICE_REG_ADVANCED_CONTROL); + regval |= 0xD807100; + /* ICE HPG requires delay before writing */ + udelay(5); + sdhci_msm_ice_writel(msm_host, regval, QCOM_ICE_REG_ADVANCED_CONTROL); + udelay(5); +} + +/* + * Wait until the ICE BIST (built-in self-test) has completed. + * + * This may be necessary before ICE can be used. + * + * Note that we don't really care whether the BIST passed or failed; we really + * just want to make sure that it isn't still running. This is because (a) the + * BIST is a FIPS compliance thing that never fails in practice, (b) ICE is + * documented to reject crypto requests if the BIST fails, so we needn't do it + * in software too, and (c) properly testing storage encryption requires testing + * the full storage stack anyway, and not relying on hardware-level self-tests. + */ +static int sdhci_msm_ice_wait_bist_status(struct sdhci_msm_host *msm_host) +{ + u32 regval; + int err; + + err = readl_poll_timeout(msm_host->ice_mem + QCOM_ICE_REG_BIST_STATUS, + regval, !(regval & QCOM_ICE_BIST_STATUS_MASK), + 50, 5000); + if (err) + dev_err(mmc_dev(msm_host->mmc), + "Timed out waiting for ICE self-test to complete\n"); + return err; +} + +static void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host) +{ + if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)) + return; + sdhci_msm_ice_low_power_mode_enable(msm_host); + sdhci_msm_ice_optimization_enable(msm_host); + sdhci_msm_ice_wait_bist_status(msm_host); +} + +static int __maybe_unused sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) +{ + if (!(msm_host->mmc->caps2 & MMC_CAP2_CRYPTO)) + return 0; + return sdhci_msm_ice_wait_bist_status(msm_host); +} + +/* + * Program a key into a QC ICE keyslot, or evict a keyslot. QC ICE requires + * vendor-specific SCM calls for this; it doesn't support the standard way. + */ +static int sdhci_msm_program_key(struct cqhci_host *cq_host, + const union cqhci_crypto_cfg_entry *cfg, + int slot) +{ + struct device *dev = mmc_dev(cq_host->mmc); + union cqhci_crypto_cap_entry cap; + union { + u8 bytes[AES_256_XTS_KEY_SIZE]; + u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)]; + } key; + int i; + int err; + + if (!(cfg->config_enable & CQHCI_CRYPTO_CONFIGURATION_ENABLE)) + return qcom_scm_ice_invalidate_key(slot); + + /* Only AES-256-XTS has been tested so far. */ + cap = cq_host->crypto_cap_array[cfg->crypto_cap_idx]; + if (cap.algorithm_id != CQHCI_CRYPTO_ALG_AES_XTS || + cap.key_size != CQHCI_CRYPTO_KEY_SIZE_256) { + dev_err_ratelimited(dev, + "Unhandled crypto capability; algorithm_id=%d, key_size=%d\n", + cap.algorithm_id, cap.key_size); + return -EINVAL; + } + + memcpy(key.bytes, cfg->crypto_key, AES_256_XTS_KEY_SIZE); + + /* + * The SCM call byte-swaps the 32-bit words of the key. So we have to + * do the same, in order for the final key be correct. + */ + for (i = 0; i < ARRAY_SIZE(key.words); i++) + __cpu_to_be32s(&key.words[i]); + + err = qcom_scm_ice_set_key(slot, key.bytes, AES_256_XTS_KEY_SIZE, + QCOM_SCM_ICE_CIPHER_AES_256_XTS, + cfg->data_unit_size); + memzero_explicit(&key, sizeof(key)); + return err; +} +#else /* CONFIG_MMC_CRYPTO */ +static inline struct clk *sdhci_msm_ice_get_clk(struct device *dev) +{ + return NULL; +} + +static inline int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, + struct cqhci_host *cq_host) +{ + return 0; +} + +static inline void sdhci_msm_ice_enable(struct sdhci_msm_host *msm_host) +{ +} + +static inline int __maybe_unused +sdhci_msm_ice_resume(struct sdhci_msm_host *msm_host) +{ + return 0; +} +#endif /* !CONFIG_MMC_CRYPTO */ + /*****************************************************************************\ * * * MSM Command Queue Engine (CQE) * @@ -1810,6 +2053,16 @@ static u32 sdhci_msm_cqe_irq(struct sdhci_host *host, u32 intmask) return 0; } +static void sdhci_msm_cqe_enable(struct mmc_host *mmc) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + + sdhci_cqe_enable(mmc); + sdhci_msm_ice_enable(msm_host); +} + static void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery) { struct sdhci_host *host = mmc_priv(mmc); @@ -1842,8 +2095,11 @@ static void sdhci_msm_cqe_disable(struct mmc_host *mmc, bool recovery) } static const struct cqhci_host_ops sdhci_msm_cqhci_ops = { - .enable = sdhci_cqe_enable, + .enable = sdhci_msm_cqe_enable, .disable = sdhci_msm_cqe_disable, +#ifdef CONFIG_MMC_CRYPTO + .program_key = sdhci_msm_program_key, +#endif }; static int sdhci_msm_cqe_add_host(struct sdhci_host *host, @@ -1879,6 +2135,10 @@ static int sdhci_msm_cqe_add_host(struct sdhci_host *host, dma64 = host->flags & SDHCI_USE_64_BIT_DMA; + ret = sdhci_msm_ice_init(msm_host, cq_host); + if (ret) + goto cleanup; + ret = cqhci_init(cq_host, host->mmc, dma64); if (ret) { dev_err(&pdev->dev, "%s: CQE init: failed (%d)\n", @@ -2319,6 +2579,11 @@ static int sdhci_msm_probe(struct platform_device *pdev) clk = NULL; msm_host->bulk_clks[3].clk = clk; + clk = sdhci_msm_ice_get_clk(&pdev->dev); + if (IS_ERR(clk)) + clk = NULL; + msm_host->bulk_clks[4].clk = clk; + ret = clk_bulk_prepare_enable(ARRAY_SIZE(msm_host->bulk_clks), msm_host->bulk_clks); if (ret) @@ -2532,12 +2797,15 @@ static __maybe_unused int sdhci_msm_runtime_resume(struct device *dev) * Whenever core-clock is gated dynamically, it's needed to * restore the SDR DLL settings when the clock is ungated. */ - if (msm_host->restore_dll_config && msm_host->clk_rate) + if (msm_host->restore_dll_config && msm_host->clk_rate) { ret = sdhci_msm_restore_sdr_dll_config(host); + if (ret) + return ret; + } dev_pm_opp_set_rate(dev, msm_host->clk_rate); - return ret; + return sdhci_msm_ice_resume(msm_host); } static const struct dev_pm_ops sdhci_msm_pm_ops = { -- cgit v1.2.3 From bbaa298f57cbcd214a4bffc4d877579686ee709e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 24 Jan 2021 18:02:55 +0100 Subject: mmc: mmci: Add bindings to operate CMD, CK, CKIN pins as GPIO Add DT bindings to describe GPIO line associated with CMD, CK, CKIN pins. Signed-off-by: Marek Vasut Cc: Alexandre Torgue Cc: Ludovic Barre Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20210124170258.32862-1-marex@denx.de Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/arm,pl18x.yaml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml index eddc1f6bdbe5..47595cb483be 100644 --- a/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml +++ b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml @@ -127,6 +127,26 @@ properties: driver to sample the receive data (for example with a voltage switch transceiver). + st,cmd-gpios: + maxItems: 1 + description: + The GPIO matching the CMD pin. + + st,ck-gpios: + maxItems: 1 + description: + The GPIO matching the CK pin. + + st,ckin-gpios: + maxItems: 1 + description: + The GPIO matching the CKIN pin. + +dependencies: + st,cmd-gpios: [ "st,use-ckin" ] + st,ck-gpios: [ "st,use-ckin" ] + st,ckin-gpios: [ "st,use-ckin" ] + unevaluatedProperties: false required: -- cgit v1.2.3 From 6351cac92a8c598115d74a700f1720861d57ef20 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Sun, 24 Jan 2021 18:02:56 +0100 Subject: mmc: mmci: Add support for probing bus voltage level translator Add support for testing whether bus voltage level translator is present and operational. This is useful on systems where the bus voltage level translator is optional, as the translator can be auto-detected by the driver and the feedback clock functionality can be disabled if it is not present. This requires additional pinmux state, "init", where the CMD, CK, CKIN lines are not configured, so they can be claimed as GPIOs early on in probe(). The translator test sets CMD high to avoid interfering with a card, and then verifies whether signal set on CK is detected on CKIN. If the signal is detected, translator is present, otherwise the CKIN feedback clock are disabled. Cc: Alexandre Torgue Cc: Ludovic Barre Tested-by: Yann Gautier Reviewed-by: Linus Walleij Signed-off-by: Marek Vasut Link: https://lore.kernel.org/r/20210124170258.32862-2-marex@denx.de Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 70 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index b5a41a7ce165..1bc674577ff9 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -1888,6 +1889,65 @@ static struct mmc_host_ops mmci_ops = { .start_signal_voltage_switch = mmci_sig_volt_switch, }; +static void mmci_probe_level_translator(struct mmc_host *mmc) +{ + struct device *dev = mmc_dev(mmc); + struct mmci_host *host = mmc_priv(mmc); + struct gpio_desc *cmd_gpio; + struct gpio_desc *ck_gpio; + struct gpio_desc *ckin_gpio; + int clk_hi, clk_lo; + + /* + * Assume the level translator is present if st,use-ckin is set. + * This is to cater for DTs which do not implement this test. + */ + host->clk_reg_add |= MCI_STM32_CLK_SELCKIN; + + cmd_gpio = gpiod_get(dev, "st,cmd", GPIOD_OUT_HIGH); + if (IS_ERR(cmd_gpio)) + goto exit_cmd; + + ck_gpio = gpiod_get(dev, "st,ck", GPIOD_OUT_HIGH); + if (IS_ERR(ck_gpio)) + goto exit_ck; + + ckin_gpio = gpiod_get(dev, "st,ckin", GPIOD_IN); + if (IS_ERR(ckin_gpio)) + goto exit_ckin; + + /* All GPIOs are valid, test whether level translator works */ + + /* Sample CKIN */ + clk_hi = !!gpiod_get_value(ckin_gpio); + + /* Set CK low */ + gpiod_set_value(ck_gpio, 0); + + /* Sample CKIN */ + clk_lo = !!gpiod_get_value(ckin_gpio); + + /* Tristate all */ + gpiod_direction_input(cmd_gpio); + gpiod_direction_input(ck_gpio); + + /* Level translator is present if CK signal is propagated to CKIN */ + if (!clk_hi || clk_lo) { + host->clk_reg_add &= ~MCI_STM32_CLK_SELCKIN; + dev_warn(dev, + "Level translator inoperable, CK signal not detected on CKIN, disabling.\n"); + } + + gpiod_put(ckin_gpio); + +exit_ckin: + gpiod_put(ck_gpio); +exit_ck: + gpiod_put(cmd_gpio); +exit_cmd: + pinctrl_select_default_state(dev); +} + static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc) { struct mmci_host *host = mmc_priv(mmc); @@ -1913,7 +1973,7 @@ static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc) if (of_get_property(np, "st,neg-edge", NULL)) host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE; if (of_get_property(np, "st,use-ckin", NULL)) - host->clk_reg_add |= MCI_STM32_CLK_SELCKIN; + mmci_probe_level_translator(mmc); if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL)) mmc->caps |= MMC_CAP_MMC_HIGHSPEED; @@ -1949,15 +2009,15 @@ static int mmci_probe(struct amba_device *dev, if (!mmc) return -ENOMEM; - ret = mmci_of_parse(np, mmc); - if (ret) - goto host_free; - host = mmc_priv(mmc); host->mmc = mmc; host->mmc_ops = &mmci_ops; mmc->ops = &mmci_ops; + ret = mmci_of_parse(np, mmc); + if (ret) + goto host_free; + /* * Some variant (STM32) doesn't have opendrain bit, nevertheless * pins can be set accordingly using pinctrl -- cgit v1.2.3 From 3feb459be5c9de1b6fdc2866d7603252a975e52f Mon Sep 17 00:00:00 2001 From: ChanWoo Lee Date: Mon, 25 Jan 2021 15:43:55 +0900 Subject: mmc: core: Exclude unnecessary header file From the 4.19 kernel, thread related code has been removed in queue.c. So we can exclude unnecessary header file. Signed-off-by: ChanWoo Lee Acked-by: Coly Li Link: https://lore.kernel.org/r/20210125064355.28545-1-cw9316.lee@samsung.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/queue.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index 33e7e65b6dde..8a774b7197d9 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 9dbd1ddf74e84289bcaa85371eed64c55ca16eb2 Mon Sep 17 00:00:00 2001 From: Yue Hu Date: Mon, 1 Feb 2021 13:55:07 +0800 Subject: mmc: core: Use host instead of card argument to mmc_spi_send_csd() Since mmc_send_cxd_data() does not care card if opcode is MMC_SEND_CSD. Signed-off-by: Yue Hu Link: https://lore.kernel.org/r/20210201055507.542-1-zbestahu@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_ops.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index baa6314f69b4..265d95ec82ce 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -296,7 +296,7 @@ mmc_send_cxd_data(struct mmc_card *card, struct mmc_host *host, return 0; } -static int mmc_spi_send_csd(struct mmc_card *card, u32 *csd) +static int mmc_spi_send_csd(struct mmc_host *host, u32 *csd) { int ret, i; __be32 *csd_tmp; @@ -305,7 +305,7 @@ static int mmc_spi_send_csd(struct mmc_card *card, u32 *csd) if (!csd_tmp) return -ENOMEM; - ret = mmc_send_cxd_data(card, card->host, MMC_SEND_CSD, csd_tmp, 16); + ret = mmc_send_cxd_data(NULL, host, MMC_SEND_CSD, csd_tmp, 16); if (ret) goto err; @@ -320,7 +320,7 @@ err: int mmc_send_csd(struct mmc_card *card, u32 *csd) { if (mmc_host_is_spi(card->host)) - return mmc_spi_send_csd(card, csd); + return mmc_spi_send_csd(card->host, csd); return mmc_send_cxd_native(card->host, card->rca << 16, csd, MMC_SEND_CSD); -- cgit v1.2.3 From 951f6ccfcbb7e4a18bf5fef1fb373d21e5831957 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 2 Feb 2021 11:19:24 +0100 Subject: mmc: core: Drop redundant member in struct mmc host The Kconfig option to use the blk-mq support was removed in commit 1bec43a3b181 ("mmc: core: Remove option not to use blk-mq"), but forgot to remove the use_blk_mq member in the struct mmc_host, let's fix it. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20210202101924.69970-1-ulf.hansson@linaro.org --- include/linux/mmc/host.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 927ba7566617..26a3c7bc29ae 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -429,7 +429,6 @@ struct mmc_host { unsigned int doing_retune:1; /* re-tuning in progress */ unsigned int retune_now:1; /* do re-tuning at next req */ unsigned int retune_paused:1; /* re-tuning is temporarily disabled */ - unsigned int use_blk_mq:1; /* use blk-mq */ unsigned int retune_crc_disable:1; /* don't trigger retune upon crc */ unsigned int can_dma_map_merge:1; /* merging can be used */ -- cgit v1.2.3 From 40c735db06e16bf29c74c3626318719783be3784 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 2 Feb 2021 11:16:26 +0100 Subject: mmc: core: Drop redundant bouncesz from struct mmc_card The commit de3ee99b097d ("mmc: Delete bounce buffer handling") removed the bounce buffer handling from the mmc core, but forgot to remove the bouncesz member from the struct mmc_card, let's fix it. Signed-off-by: Ulf Hansson Link: https://lore.kernel.org/r/20210202101626.64503-1-ulf.hansson@linaro.org --- include/linux/mmc/card.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 42df06c6b19c..f9ad35dd6012 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -311,7 +311,6 @@ struct mmc_card { struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */ unsigned int nr_parts; - unsigned int bouncesz; /* Bounce buffer size */ struct workqueue_struct *complete_wq; /* Private workqueue */ }; -- cgit v1.2.3 From 1791557ffbafd280c6e566f7f9a70353a470841a Mon Sep 17 00:00:00 2001 From: ChanWoo Lee Date: Wed, 3 Feb 2021 16:20:14 +0900 Subject: mmc: queue: Remove unused define MMC_CQE_QUEUE_FULL is not set and is only cleared. Therefore, define is unnecessary. Signed-off-by: ChanWoo Lee Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20210203072014.30272-1-cw9316.lee@samsung.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/queue.c | 2 -- drivers/mmc/core/queue.h | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index 8a774b7197d9..27d2b8ed9484 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -33,8 +33,6 @@ void mmc_cqe_check_busy(struct mmc_queue *mq) { if ((mq->cqe_busy & MMC_CQE_DCMD_BUSY) && !mmc_cqe_dcmd_busy(mq)) mq->cqe_busy &= ~MMC_CQE_DCMD_BUSY; - - mq->cqe_busy &= ~MMC_CQE_QUEUE_FULL; } static inline bool mmc_cqe_can_dcmd(struct mmc_host *host) diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h index fd11491ced9f..57c59b6cb1b9 100644 --- a/drivers/mmc/core/queue.h +++ b/drivers/mmc/core/queue.h @@ -81,7 +81,6 @@ struct mmc_queue { int in_flight[MMC_ISSUE_MAX]; unsigned int cqe_busy; #define MMC_CQE_DCMD_BUSY BIT(0) -#define MMC_CQE_QUEUE_FULL BIT(1) bool busy; bool use_cqe; bool recovery_needed; -- cgit v1.2.3 From f9261eb7d38dc0e69c623e58d4cc2c486f5f27dc Mon Sep 17 00:00:00 2001 From: Xiaofei Tan Date: Thu, 4 Feb 2021 15:07:18 +0800 Subject: mmc: cavium: Replace spin_lock_irqsave with spin_lock in hard IRQ It is redundant to do irqsave and irqrestore in hardIRQ context, where it has been in a irq-disabled context. Signed-off-by: Xiaofei Tan Link: https://lore.kernel.org/r/1612422438-32525-1-git-send-email-tanxiaofei@huawei.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/cavium.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c index c5da3aaee334..4bb8f2800a2b 100644 --- a/drivers/mmc/host/cavium.c +++ b/drivers/mmc/host/cavium.c @@ -436,12 +436,11 @@ irqreturn_t cvm_mmc_interrupt(int irq, void *dev_id) { struct cvm_mmc_host *host = dev_id; struct mmc_request *req; - unsigned long flags = 0; u64 emm_int, rsp_sts; bool host_done; if (host->need_irq_handler_lock) - spin_lock_irqsave(&host->irq_handler_lock, flags); + spin_lock(&host->irq_handler_lock); else __acquire(&host->irq_handler_lock); @@ -504,7 +503,7 @@ no_req_done: host->release_bus(host); out: if (host->need_irq_handler_lock) - spin_unlock_irqrestore(&host->irq_handler_lock, flags); + spin_unlock(&host->irq_handler_lock); else __release(&host->irq_handler_lock); return IRQ_RETVAL(emm_int != 0); -- cgit v1.2.3 From 82a5d372ed01806c002812d13978abb582866e48 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 4 Feb 2021 16:18:39 +0100 Subject: mmc: atmel-mci: Use new tasklet API This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210204151847.91353-2-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 632412066eda..807177c953f3 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1719,9 +1719,9 @@ static void atmci_detect_change(struct timer_list *t) } } -static void atmci_tasklet_func(unsigned long priv) +static void atmci_tasklet_func(struct tasklet_struct *t) { - struct atmel_mci *host = (struct atmel_mci *)priv; + struct atmel_mci *host = from_tasklet(host, t, tasklet); struct mmc_request *mrq = host->mrq; struct mmc_data *data = host->data; enum atmel_mci_state state = host->state; @@ -2496,7 +2496,7 @@ static int atmci_probe(struct platform_device *pdev) host->mapbase = regs->start; - tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)host); + tasklet_setup(&host->tasklet, atmci_tasklet_func); ret = request_irq(irq, atmci_interrupt, 0, dev_name(&pdev->dev), host); if (ret) { -- cgit v1.2.3 From a1149c387c6d5a8021843ae4a387447ff07f195f Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 4 Feb 2021 16:18:40 +0100 Subject: mmc: au1xmmc: Use new tasklet API This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210204151847.91353-3-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/au1xmmc.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index bd00515fbaba..0acc237843f7 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -253,9 +253,9 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host) mmc_request_done(host->mmc, mrq); } -static void au1xmmc_tasklet_finish(unsigned long param) +static void au1xmmc_tasklet_finish(struct tasklet_struct *t) { - struct au1xmmc_host *host = (struct au1xmmc_host *) param; + struct au1xmmc_host *host = from_tasklet(host, t, finish_task); au1xmmc_finish_request(host); } @@ -363,9 +363,9 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) au1xmmc_finish_request(host); } -static void au1xmmc_tasklet_data(unsigned long param) +static void au1xmmc_tasklet_data(struct tasklet_struct *t) { - struct au1xmmc_host *host = (struct au1xmmc_host *)param; + struct au1xmmc_host *host = from_tasklet(host, t, data_task); u32 status = __raw_readl(HOST_STATUS(host)); au1xmmc_data_complete(host, status); @@ -1037,11 +1037,9 @@ static int au1xmmc_probe(struct platform_device *pdev) if (host->platdata) mmc->caps &= ~(host->platdata->mask_host_caps); - tasklet_init(&host->data_task, au1xmmc_tasklet_data, - (unsigned long)host); + tasklet_setup(&host->data_task, au1xmmc_tasklet_data); - tasklet_init(&host->finish_task, au1xmmc_tasklet_finish, - (unsigned long)host); + tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish); if (has_dbdma()) { ret = au1xmmc_dbdma_init(host); -- cgit v1.2.3 From 6078df1565c9bfe580c66009b39538c4ae47a9c9 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 4 Feb 2021 16:18:41 +0100 Subject: mmc: dw_mmc: Use new tasklet API This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210204151847.91353-4-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index a5244435556b..2f4de30f650b 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1952,9 +1952,9 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host) return true; } -static void dw_mci_tasklet_func(unsigned long priv) +static void dw_mci_tasklet_func(struct tasklet_struct *t) { - struct dw_mci *host = (struct dw_mci *)priv; + struct dw_mci *host = from_tasklet(host, t, tasklet); struct mmc_data *data; struct mmc_command *cmd; struct mmc_request *mrq; @@ -3308,7 +3308,7 @@ int dw_mci_probe(struct dw_mci *host) else host->fifo_reg = host->regs + DATA_240A_OFFSET; - tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); + tasklet_setup(&host->tasklet, dw_mci_tasklet_func); ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host); if (ret) -- cgit v1.2.3 From 0f5a7cb639d042392d26353d817bfb60dc2b9467 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 4 Feb 2021 16:18:42 +0100 Subject: mmc: omap: Use new tasklet API This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210204151847.91353-5-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 6aa0537f1f84..5e5af34090f1 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -878,9 +878,9 @@ static void mmc_omap_cover_timer(struct timer_list *t) tasklet_schedule(&slot->cover_tasklet); } -static void mmc_omap_cover_handler(unsigned long param) +static void mmc_omap_cover_handler(struct tasklet_struct *t) { - struct mmc_omap_slot *slot = (struct mmc_omap_slot *)param; + struct mmc_omap_slot *slot = from_tasklet(slot, t, cover_tasklet); int cover_open = mmc_omap_cover_is_open(slot); mmc_detect_change(slot->mmc, 0); @@ -1269,8 +1269,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id) if (slot->pdata->get_cover_state != NULL) { timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0); - tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler, - (unsigned long)slot); + tasklet_setup(&slot->cover_tasklet, mmc_omap_cover_handler); } r = mmc_add_host(mmc); -- cgit v1.2.3 From ddf116f96f8ff11fe02ac1925ba846976327a451 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 4 Feb 2021 16:18:43 +0100 Subject: mmc: s3cmci: Use new tasklet API This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210204151847.91353-6-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/s3cmci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index a33a7823c265..0ca6f6d30b75 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -540,9 +540,9 @@ static void do_pio_write(struct s3cmci_host *host) enable_imask(host, S3C2410_SDIIMSK_TXFIFOHALF); } -static void pio_tasklet(unsigned long data) +static void pio_tasklet(struct tasklet_struct *t) { - struct s3cmci_host *host = (struct s3cmci_host *) data; + struct s3cmci_host *host = from_tasklet(host, t, pio_tasklet); s3cmci_disable_irq(host, true); @@ -1532,7 +1532,7 @@ static int s3cmci_probe(struct platform_device *pdev) host->pdata = pdev->dev.platform_data; spin_lock_init(&host->complete_lock); - tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host); + tasklet_setup(&host->pio_tasklet, pio_tasklet); if (host->is2440) { host->sdiimsk = S3C2440_SDIIMSK; -- cgit v1.2.3 From c36dd04282463aea9414da0b84def5ab081c7f38 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 4 Feb 2021 16:18:44 +0100 Subject: mmc: tifm_sd: Use new tasklet API This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210204151847.91353-7-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/tifm_sd.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index fd8b72d3e02c..9fdf7ea06e3f 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -731,9 +731,9 @@ err_out: mmc_request_done(mmc, mrq); } -static void tifm_sd_end_cmd(unsigned long data) +static void tifm_sd_end_cmd(struct tasklet_struct *t) { - struct tifm_sd *host = (struct tifm_sd*)data; + struct tifm_sd *host = from_tasklet(host, t, finish_tasklet); struct tifm_dev *sock = host->dev; struct mmc_host *mmc = tifm_get_drvdata(sock); struct mmc_request *mrq; @@ -968,8 +968,7 @@ static int tifm_sd_probe(struct tifm_dev *sock) */ mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS; - tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd, - (unsigned long)host); + tasklet_setup(&host->finish_tasklet, tifm_sd_end_cmd); timer_setup(&host->timer, tifm_sd_abort, 0); mmc->ops = &tifm_sd_ops; -- cgit v1.2.3 From 39d2969d7b359d4e07966b83ed8b4ec2b6cfb228 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 4 Feb 2021 16:18:45 +0100 Subject: mmc: uniphier-sd: Use new tasklet API This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210204151847.91353-8-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/uniphier-sd.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c index a6cd16771d4e..2413b6750cec 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -81,9 +81,9 @@ static void uniphier_sd_dma_endisable(struct tmio_mmc_host *host, int enable) } /* external DMA engine */ -static void uniphier_sd_external_dma_issue(unsigned long arg) +static void uniphier_sd_external_dma_issue(struct tasklet_struct *t) { - struct tmio_mmc_host *host = (void *)arg; + struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue); struct uniphier_sd_priv *priv = uniphier_sd_priv(host); uniphier_sd_dma_endisable(host, 1); @@ -190,8 +190,7 @@ static void uniphier_sd_external_dma_request(struct tmio_mmc_host *host, host->chan_rx = chan; host->chan_tx = chan; - tasklet_init(&host->dma_issue, uniphier_sd_external_dma_issue, - (unsigned long)host); + tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue); } static void uniphier_sd_external_dma_release(struct tmio_mmc_host *host) @@ -228,9 +227,9 @@ static const struct tmio_mmc_dma_ops uniphier_sd_external_dma_ops = { .dataend = uniphier_sd_external_dma_dataend, }; -static void uniphier_sd_internal_dma_issue(unsigned long arg) +static void uniphier_sd_internal_dma_issue(struct tasklet_struct *t) { - struct tmio_mmc_host *host = (void *)arg; + struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue); unsigned long flags; spin_lock_irqsave(&host->lock, flags); @@ -309,8 +308,7 @@ static void uniphier_sd_internal_dma_request(struct tmio_mmc_host *host, host->chan_tx = (void *)0xdeadbeaf; - tasklet_init(&host->dma_issue, uniphier_sd_internal_dma_issue, - (unsigned long)host); + tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue); } static void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host) -- cgit v1.2.3 From e821afd8e0356e156aa28fa729d5f288c54696df Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 4 Feb 2021 16:18:46 +0100 Subject: mmc: via-sdmmc: Use new tasklet API This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210204151847.91353-9-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/via-sdmmc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index 9b755ea0fa03..4f4c0813f9fd 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -959,14 +959,12 @@ static void via_sdc_timeout(struct timer_list *t) spin_unlock_irqrestore(&sdhost->lock, flags); } -static void via_sdc_tasklet_finish(unsigned long param) +static void via_sdc_tasklet_finish(struct tasklet_struct *t) { - struct via_crdr_mmc_host *host; + struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet); unsigned long flags; struct mmc_request *mrq; - host = (struct via_crdr_mmc_host *)param; - spin_lock_irqsave(&host->lock, flags); del_timer(&host->timer); @@ -1050,8 +1048,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host) INIT_WORK(&host->carddet_work, via_sdc_card_detect); - tasklet_init(&host->finish_tasklet, via_sdc_tasklet_finish, - (unsigned long)host); + tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish); addrbase = host->sdhc_mmiobase; writel(0x0, addrbase + VIA_CRDR_SDINTMASK); -- cgit v1.2.3 From 0c1a3e8b2e98e8e73453ff86c2ce727441005fb3 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Thu, 4 Feb 2021 16:18:47 +0100 Subject: mmc: wbsd: Use new tasklet API This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Link: https://lore.kernel.org/r/20210204151847.91353-10-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/wbsd.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index cd63ea865b77..67ecd342fe5f 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -987,9 +987,9 @@ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) return host->mrq->cmd->data; } -static void wbsd_tasklet_card(unsigned long param) +static void wbsd_tasklet_card(struct tasklet_struct *t) { - struct wbsd_host *host = (struct wbsd_host *)param; + struct wbsd_host *host = from_tasklet(host, t, card_tasklet); u8 csr; int delay = -1; @@ -1036,9 +1036,9 @@ static void wbsd_tasklet_card(unsigned long param) mmc_detect_change(host->mmc, msecs_to_jiffies(delay)); } -static void wbsd_tasklet_fifo(unsigned long param) +static void wbsd_tasklet_fifo(struct tasklet_struct *t) { - struct wbsd_host *host = (struct wbsd_host *)param; + struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet); struct mmc_data *data; spin_lock(&host->lock); @@ -1067,9 +1067,9 @@ end: spin_unlock(&host->lock); } -static void wbsd_tasklet_crc(unsigned long param) +static void wbsd_tasklet_crc(struct tasklet_struct *t) { - struct wbsd_host *host = (struct wbsd_host *)param; + struct wbsd_host *host = from_tasklet(host, t, crc_tasklet); struct mmc_data *data; spin_lock(&host->lock); @@ -1091,9 +1091,9 @@ end: spin_unlock(&host->lock); } -static void wbsd_tasklet_timeout(unsigned long param) +static void wbsd_tasklet_timeout(struct tasklet_struct *t) { - struct wbsd_host *host = (struct wbsd_host *)param; + struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet); struct mmc_data *data; spin_lock(&host->lock); @@ -1115,9 +1115,9 @@ end: spin_unlock(&host->lock); } -static void wbsd_tasklet_finish(unsigned long param) +static void wbsd_tasklet_finish(struct tasklet_struct *t) { - struct wbsd_host *host = (struct wbsd_host *)param; + struct wbsd_host *host = from_tasklet(host, t, finish_tasklet); struct mmc_data *data; spin_lock(&host->lock); @@ -1449,16 +1449,11 @@ static int wbsd_request_irq(struct wbsd_host *host, int irq) /* * Set up tasklets. Must be done before requesting interrupt. */ - tasklet_init(&host->card_tasklet, wbsd_tasklet_card, - (unsigned long)host); - tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, - (unsigned long)host); - tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, - (unsigned long)host); - tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, - (unsigned long)host); - tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, - (unsigned long)host); + tasklet_setup(&host->card_tasklet, wbsd_tasklet_card); + tasklet_setup(&host->fifo_tasklet, wbsd_tasklet_fifo); + tasklet_setup(&host->crc_tasklet, wbsd_tasklet_crc); + tasklet_setup(&host->timeout_tasklet, wbsd_tasklet_timeout); + tasklet_setup(&host->finish_tasklet, wbsd_tasklet_finish); /* * Allocate interrupt. -- cgit v1.2.3 From 3f957dec6dc219a8a1bf6d7994ee95ed7004f701 Mon Sep 17 00:00:00 2001 From: Yann Gautier Date: Tue, 9 Feb 2021 15:52:14 +0100 Subject: mmc: mmc_test: use erase_arg for mmc_erase command Since [1], the erase argument for mmc_erase() function is saved in erase_arg field of card structure. It is preferable to use it instead of hard-coded MMC_SECURE_ERASE_ARG, which from eMMC 4.51 spec is not recommended: "6.6.16 Secure Erase NOTE Secure Erase is included for backwards compatibility. New system level implementations (based on v4.51 devices and beyond) should use Erase combined with Sanitize instead of secure erase." On STM32MP157C-EV1 board, embedding a THGBMDG5D1LBAIL eMMC, using MMC_ERASE command with MMC_SECURE_ERASE_ARG may stuck the STM32 SDMMC IP, if test 37 or test 38 are launched just after a write test, e.g. test 36. Using the default MMC_ERASE argument from framework with erase_arg, which default in our case to MMC_DISCARD_ARG does no more trig the issue. [1] commit 01904ff77676 ("mmc: core: Calculate the discard arg only once") Signed-off-by: Yann Gautier Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20210209145214.10518-1-yann.gautier@foss.st.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index 39a478874ca3..63524551a13a 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -2110,7 +2110,7 @@ static int mmc_test_rw_multiple(struct mmc_test_card *test, if (mmc_can_erase(test->card) && tdata->prepare & MMC_TEST_PREP_ERASE) { ret = mmc_erase(test->card, dev_addr, - size / 512, MMC_SECURE_ERASE_ARG); + size / 512, test->card->erase_arg); if (ret) ret = mmc_erase(test->card, dev_addr, size / 512, MMC_ERASE_ARG); -- cgit v1.2.3 From 1ad9f88014ae1d5abccb6fe930bc4c5c311bdc05 Mon Sep 17 00:00:00 2001 From: Shirley Her Date: Fri, 5 Feb 2021 17:40:51 -0800 Subject: mmc: sdhci-pci-o2micro: Bug fix for SDR104 HW tuning failure Force chip enter L0 power state during SDR104 HW tuning to avoid tuning failure Signed-off-by: Shirley Her Link: https://lore.kernel.org/r/20210206014051.3418-1-shirley.her@bayhubtech.com Fixes: 7b7d897e8898 ("mmc: sdhci-pci-o2micro: Add HW tuning for SDR104 mode") Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-o2micro.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-o2micro.c b/drivers/mmc/host/sdhci-pci-o2micro.c index fa76748d8929..94e3f72f6405 100644 --- a/drivers/mmc/host/sdhci-pci-o2micro.c +++ b/drivers/mmc/host/sdhci-pci-o2micro.c @@ -33,6 +33,8 @@ #define O2_SD_ADMA2 0xE7 #define O2_SD_INF_MOD 0xF1 #define O2_SD_MISC_CTRL4 0xFC +#define O2_SD_MISC_CTRL 0x1C0 +#define O2_SD_PWR_FORCE_L0 0x0002 #define O2_SD_TUNING_CTRL 0x300 #define O2_SD_PLL_SETTING 0x304 #define O2_SD_MISC_SETTING 0x308 @@ -300,6 +302,8 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); int current_bus_width = 0; + u32 scratch32 = 0; + u16 scratch = 0; /* * This handler only implements the eMMC tuning that is specific to @@ -312,6 +316,17 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) if (WARN_ON((opcode != MMC_SEND_TUNING_BLOCK_HS200) && (opcode != MMC_SEND_TUNING_BLOCK))) return -EINVAL; + + /* Force power mode enter L0 */ + scratch = sdhci_readw(host, O2_SD_MISC_CTRL); + scratch |= O2_SD_PWR_FORCE_L0; + sdhci_writew(host, scratch, O2_SD_MISC_CTRL); + + /* wait DLL lock, timeout value 5ms */ + if (readx_poll_timeout(sdhci_o2_pll_dll_wdt_control, host, + scratch32, (scratch32 & O2_DLL_LOCK_STATUS), 1, 5000)) + pr_warn("%s: DLL can't lock in 5ms after force L0 during tuning.\n", + mmc_hostname(host->mmc)); /* * Judge the tuning reason, whether caused by dll shift * If cause by dll shift, should call sdhci_o2_dll_recovery @@ -344,6 +359,11 @@ static int sdhci_o2_execute_tuning(struct mmc_host *mmc, u32 opcode) sdhci_set_bus_width(host, current_bus_width); } + /* Cancel force power mode enter L0 */ + scratch = sdhci_readw(host, O2_SD_MISC_CTRL); + scratch &= ~(O2_SD_PWR_FORCE_L0); + sdhci_writew(host, scratch, O2_SD_MISC_CTRL); + sdhci_reset(host, SDHCI_RESET_CMD); sdhci_reset(host, SDHCI_RESET_DATA); -- cgit v1.2.3 From f8f007c13ea77f1009bece60771add3204def8f7 Mon Sep 17 00:00:00 2001 From: Emil Renner Berthing Date: Mon, 8 Feb 2021 14:45:51 +0100 Subject: mmc: cb710: Use new tasklet API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This converts the driver to use the new tasklet API introduced in commit 12cc923f1ccc ("tasklet: Introduce new initialization API") Signed-off-by: Emil Renner Berthing Acked-by: Michał Mirosław Link: https://lore.kernel.org/r/20210208134551.39696-1-kernel@esmil.dk Signed-off-by: Ulf Hansson --- drivers/mmc/host/cb710-mmc.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index e84ed84ea4cc..6d623b2681c3 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -646,14 +646,14 @@ static int cb710_mmc_irq_handler(struct cb710_slot *slot) return 1; } -static void cb710_mmc_finish_request_tasklet(unsigned long data) +static void cb710_mmc_finish_request_tasklet(struct tasklet_struct *t) { - struct mmc_host *mmc = (void *)data; - struct cb710_mmc_reader *reader = mmc_priv(mmc); + struct cb710_mmc_reader *reader = from_tasklet(reader, t, + finish_req_tasklet); struct mmc_request *mrq = reader->mrq; reader->mrq = NULL; - mmc_request_done(mmc, mrq); + mmc_request_done(mmc_from_priv(reader), mrq); } static const struct mmc_host_ops cb710_mmc_host = { @@ -718,8 +718,8 @@ static int cb710_mmc_init(struct platform_device *pdev) reader = mmc_priv(mmc); - tasklet_init(&reader->finish_req_tasklet, - cb710_mmc_finish_request_tasklet, (unsigned long)mmc); + tasklet_setup(&reader->finish_req_tasklet, + cb710_mmc_finish_request_tasklet); spin_lock_init(&reader->irq_lock); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); -- cgit v1.2.3 From 11b2ce62bd07fb116cba68a8833bc1d8033370f4 Mon Sep 17 00:00:00 2001 From: Roman Kiryanov Date: Tue, 9 Feb 2021 11:08:50 -0800 Subject: mmc: host: Retire MMC_GOLDFISH Android Studio Emulator no longer uses this driver, so let's drop it. Signed-off-by: Roman Kiryanov Link: https://lore.kernel.org/r/20210209190850.16647-1-rkir@google.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 7 - drivers/mmc/host/Makefile | 1 - drivers/mmc/host/android-goldfish.c | 545 ------------------------------------ 3 files changed, 553 deletions(-) delete mode 100644 drivers/mmc/host/android-goldfish.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index a29411ca626f..b236dfe2e879 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -611,13 +611,6 @@ config MMC_DAVINCI If you have an DAVINCI board with a Multimedia Card slot, say Y or M here. If unsure, say N. -config MMC_GOLDFISH - tristate "goldfish qemu Multimedia Card Interface support" - depends on GOLDFISH || COMPILE_TEST - help - This selects the Goldfish Multimedia card Interface emulation - found on the Goldfish Android virtual device emulation. - config MMC_SPI tristate "MMC/SD/SDIO over SPI" depends on SPI_MASTER diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 9ea86a18f012..6df5c4774260 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -33,7 +33,6 @@ obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o -obj-$(CONFIG_MMC_GOLDFISH) += android-goldfish.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o ifeq ($(CONFIG_OF),y) obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o diff --git a/drivers/mmc/host/android-goldfish.c b/drivers/mmc/host/android-goldfish.c deleted file mode 100644 index e878fdf8f20a..000000000000 --- a/drivers/mmc/host/android-goldfish.c +++ /dev/null @@ -1,545 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright 2007, Google Inc. - * Copyright 2012, Intel Inc. - * - * based on omap.c driver, which was - * Copyright (C) 2004 Nokia Corporation - * Written by Tuukka Tikkanen and Juha Yrjölä - * Misc hacks here and there by Tony Lindgren - * Other hacks (DMA, SD, etc) by David Brownell - */ - -#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 - -#define DRIVER_NAME "goldfish_mmc" - -#define BUFFER_SIZE 16384 - -#define GOLDFISH_MMC_READ(host, addr) (readl(host->reg_base + addr)) -#define GOLDFISH_MMC_WRITE(host, addr, x) (writel(x, host->reg_base + addr)) - -enum { - /* status register */ - MMC_INT_STATUS = 0x00, - /* set this to enable IRQ */ - MMC_INT_ENABLE = 0x04, - /* set this to specify buffer address */ - MMC_SET_BUFFER = 0x08, - - /* MMC command number */ - MMC_CMD = 0x0C, - - /* MMC argument */ - MMC_ARG = 0x10, - - /* MMC response (or R2 bits 0 - 31) */ - MMC_RESP_0 = 0x14, - - /* MMC R2 response bits 32 - 63 */ - MMC_RESP_1 = 0x18, - - /* MMC R2 response bits 64 - 95 */ - MMC_RESP_2 = 0x1C, - - /* MMC R2 response bits 96 - 127 */ - MMC_RESP_3 = 0x20, - - MMC_BLOCK_LENGTH = 0x24, - MMC_BLOCK_COUNT = 0x28, - - /* MMC state flags */ - MMC_STATE = 0x2C, - - /* MMC_INT_STATUS bits */ - - MMC_STAT_END_OF_CMD = 1U << 0, - MMC_STAT_END_OF_DATA = 1U << 1, - MMC_STAT_STATE_CHANGE = 1U << 2, - MMC_STAT_CMD_TIMEOUT = 1U << 3, - - /* MMC_STATE bits */ - MMC_STATE_INSERTED = 1U << 0, - MMC_STATE_READ_ONLY = 1U << 1, -}; - -/* - * Command types - */ -#define OMAP_MMC_CMDTYPE_BC 0 -#define OMAP_MMC_CMDTYPE_BCR 1 -#define OMAP_MMC_CMDTYPE_AC 2 -#define OMAP_MMC_CMDTYPE_ADTC 3 - - -struct goldfish_mmc_host { - struct mmc_request *mrq; - struct mmc_command *cmd; - struct mmc_data *data; - struct device *dev; - unsigned char id; /* 16xx chips have 2 MMC blocks */ - void *virt_base; - unsigned int phys_base; - int irq; - unsigned char bus_mode; - unsigned char hw_bus_mode; - - unsigned int sg_len; - unsigned dma_done:1; - unsigned dma_in_use:1; - - void __iomem *reg_base; -}; - -static inline int -goldfish_mmc_cover_is_open(struct goldfish_mmc_host *host) -{ - return 0; -} - -static ssize_t -goldfish_mmc_show_cover_switch(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct goldfish_mmc_host *host = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", goldfish_mmc_cover_is_open(host) ? "open" : - "closed"); -} - -static DEVICE_ATTR(cover_switch, S_IRUGO, goldfish_mmc_show_cover_switch, NULL); - -static void -goldfish_mmc_start_command(struct goldfish_mmc_host *host, struct mmc_command *cmd) -{ - u32 cmdreg; - u32 resptype; - u32 cmdtype; - - host->cmd = cmd; - - resptype = 0; - cmdtype = 0; - - /* Our hardware needs to know exact type */ - switch (mmc_resp_type(cmd)) { - case MMC_RSP_NONE: - break; - case MMC_RSP_R1: - case MMC_RSP_R1B: - /* resp 1, 1b, 6, 7 */ - resptype = 1; - break; - case MMC_RSP_R2: - resptype = 2; - break; - case MMC_RSP_R3: - resptype = 3; - break; - default: - dev_err(mmc_dev(mmc_from_priv(host)), - "Invalid response type: %04x\n", mmc_resp_type(cmd)); - break; - } - - if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) - cmdtype = OMAP_MMC_CMDTYPE_ADTC; - else if (mmc_cmd_type(cmd) == MMC_CMD_BC) - cmdtype = OMAP_MMC_CMDTYPE_BC; - else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) - cmdtype = OMAP_MMC_CMDTYPE_BCR; - else - cmdtype = OMAP_MMC_CMDTYPE_AC; - - cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); - - if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) - cmdreg |= 1 << 6; - - if (cmd->flags & MMC_RSP_BUSY) - cmdreg |= 1 << 11; - - if (host->data && !(host->data->flags & MMC_DATA_WRITE)) - cmdreg |= 1 << 15; - - GOLDFISH_MMC_WRITE(host, MMC_ARG, cmd->arg); - GOLDFISH_MMC_WRITE(host, MMC_CMD, cmdreg); -} - -static void goldfish_mmc_xfer_done(struct goldfish_mmc_host *host, - struct mmc_data *data) -{ - if (host->dma_in_use) { - enum dma_data_direction dma_data_dir; - - dma_data_dir = mmc_get_dma_dir(data); - - if (dma_data_dir == DMA_FROM_DEVICE) { - /* - * We don't really have DMA, so we need - * to copy from our platform driver buffer - */ - sg_copy_from_buffer(data->sg, 1, host->virt_base, - data->sg->length); - } - host->data->bytes_xfered += data->sg->length; - dma_unmap_sg(mmc_dev(mmc_from_priv(host)), data->sg, - host->sg_len, dma_data_dir); - } - - host->data = NULL; - host->sg_len = 0; - - /* - * NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing - * dozens of requests until the card finishes writing data. - * It'd be cheaper to just wait till an EOFB interrupt arrives... - */ - - if (!data->stop) { - host->mrq = NULL; - mmc_request_done(mmc_from_priv(host), data->mrq); - return; - } - - goldfish_mmc_start_command(host, data->stop); -} - -static void goldfish_mmc_end_of_data(struct goldfish_mmc_host *host, - struct mmc_data *data) -{ - if (!host->dma_in_use) { - goldfish_mmc_xfer_done(host, data); - return; - } - if (host->dma_done) - goldfish_mmc_xfer_done(host, data); -} - -static void goldfish_mmc_cmd_done(struct goldfish_mmc_host *host, - struct mmc_command *cmd) -{ - host->cmd = NULL; - if (cmd->flags & MMC_RSP_PRESENT) { - if (cmd->flags & MMC_RSP_136) { - /* response type 2 */ - cmd->resp[3] = - GOLDFISH_MMC_READ(host, MMC_RESP_0); - cmd->resp[2] = - GOLDFISH_MMC_READ(host, MMC_RESP_1); - cmd->resp[1] = - GOLDFISH_MMC_READ(host, MMC_RESP_2); - cmd->resp[0] = - GOLDFISH_MMC_READ(host, MMC_RESP_3); - } else { - /* response types 1, 1b, 3, 4, 5, 6 */ - cmd->resp[0] = - GOLDFISH_MMC_READ(host, MMC_RESP_0); - } - } - - if (host->data == NULL || cmd->error) { - host->mrq = NULL; - mmc_request_done(mmc_from_priv(host), cmd->mrq); - } -} - -static irqreturn_t goldfish_mmc_irq(int irq, void *dev_id) -{ - struct goldfish_mmc_host *host = (struct goldfish_mmc_host *)dev_id; - u16 status; - int end_command = 0; - int end_transfer = 0; - int state_changed = 0; - int cmd_timeout = 0; - - while ((status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS)) != 0) { - GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status); - - if (status & MMC_STAT_END_OF_CMD) - end_command = 1; - - if (status & MMC_STAT_END_OF_DATA) - end_transfer = 1; - - if (status & MMC_STAT_STATE_CHANGE) - state_changed = 1; - - if (status & MMC_STAT_CMD_TIMEOUT) { - end_command = 0; - cmd_timeout = 1; - } - } - - if (cmd_timeout) { - struct mmc_request *mrq = host->mrq; - mrq->cmd->error = -ETIMEDOUT; - host->mrq = NULL; - mmc_request_done(mmc_from_priv(host), mrq); - } - - if (end_command) - goldfish_mmc_cmd_done(host, host->cmd); - - if (end_transfer) { - host->dma_done = 1; - goldfish_mmc_end_of_data(host, host->data); - } else if (host->data != NULL) { - /* - * WORKAROUND -- after porting this driver from 2.6 to 3.4, - * during device initialization, cases where host->data is - * non-null but end_transfer is false would occur. Doing - * nothing in such cases results in no further interrupts, - * and initialization failure. - * TODO -- find the real cause. - */ - host->dma_done = 1; - goldfish_mmc_end_of_data(host, host->data); - } - - if (state_changed) { - u32 state = GOLDFISH_MMC_READ(host, MMC_STATE); - pr_info("%s: Card detect now %d\n", __func__, - (state & MMC_STATE_INSERTED)); - mmc_detect_change(mmc_from_priv(host), 0); - } - - if (!end_command && !end_transfer && !state_changed && !cmd_timeout) { - status = GOLDFISH_MMC_READ(host, MMC_INT_STATUS); - dev_info(mmc_dev(mmc_from_priv(host)), "spurious irq 0x%04x\n", - status); - if (status != 0) { - GOLDFISH_MMC_WRITE(host, MMC_INT_STATUS, status); - GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, 0); - } - } - - return IRQ_HANDLED; -} - -static void goldfish_mmc_prepare_data(struct goldfish_mmc_host *host, - struct mmc_request *req) -{ - struct mmc_data *data = req->data; - int block_size; - unsigned sg_len; - enum dma_data_direction dma_data_dir; - - host->data = data; - if (data == NULL) { - GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, 0); - GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, 0); - host->dma_in_use = 0; - return; - } - - block_size = data->blksz; - - GOLDFISH_MMC_WRITE(host, MMC_BLOCK_COUNT, data->blocks - 1); - GOLDFISH_MMC_WRITE(host, MMC_BLOCK_LENGTH, block_size - 1); - - /* - * Cope with calling layer confusion; it issues "single - * block" writes using multi-block scatterlists. - */ - sg_len = (data->blocks == 1) ? 1 : data->sg_len; - - dma_data_dir = mmc_get_dma_dir(data); - - host->sg_len = dma_map_sg(mmc_dev(mmc_from_priv(host)), data->sg, - sg_len, dma_data_dir); - host->dma_done = 0; - host->dma_in_use = 1; - - if (dma_data_dir == DMA_TO_DEVICE) { - /* - * We don't really have DMA, so we need to copy to our - * platform driver buffer - */ - sg_copy_to_buffer(data->sg, 1, host->virt_base, - data->sg->length); - } -} - -static void goldfish_mmc_request(struct mmc_host *mmc, struct mmc_request *req) -{ - struct goldfish_mmc_host *host = mmc_priv(mmc); - - WARN_ON(host->mrq != NULL); - - host->mrq = req; - goldfish_mmc_prepare_data(host, req); - goldfish_mmc_start_command(host, req->cmd); -} - -static void goldfish_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct goldfish_mmc_host *host = mmc_priv(mmc); - - host->bus_mode = ios->bus_mode; - host->hw_bus_mode = host->bus_mode; -} - -static int goldfish_mmc_get_ro(struct mmc_host *mmc) -{ - uint32_t state; - struct goldfish_mmc_host *host = mmc_priv(mmc); - - state = GOLDFISH_MMC_READ(host, MMC_STATE); - return ((state & MMC_STATE_READ_ONLY) != 0); -} - -static const struct mmc_host_ops goldfish_mmc_ops = { - .request = goldfish_mmc_request, - .set_ios = goldfish_mmc_set_ios, - .get_ro = goldfish_mmc_get_ro, -}; - -static int goldfish_mmc_probe(struct platform_device *pdev) -{ - struct mmc_host *mmc; - struct goldfish_mmc_host *host = NULL; - struct resource *res; - int ret = 0; - int irq; - dma_addr_t buf_addr; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (res == NULL || irq < 0) - return -ENXIO; - - mmc = mmc_alloc_host(sizeof(struct goldfish_mmc_host), &pdev->dev); - if (mmc == NULL) { - ret = -ENOMEM; - goto err_alloc_host_failed; - } - - host = mmc_priv(mmc); - - pr_err("mmc: Mapping %lX to %lX\n", (long)res->start, (long)res->end); - host->reg_base = ioremap(res->start, resource_size(res)); - if (host->reg_base == NULL) { - ret = -ENOMEM; - goto ioremap_failed; - } - host->virt_base = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE, - &buf_addr, GFP_KERNEL); - - if (host->virt_base == 0) { - ret = -ENOMEM; - goto dma_alloc_failed; - } - host->phys_base = buf_addr; - - host->id = pdev->id; - host->irq = irq; - - mmc->ops = &goldfish_mmc_ops; - mmc->f_min = 400000; - mmc->f_max = 24000000; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA; - mmc->caps2 = MMC_CAP2_NO_SDIO; - - /* Use scatterlist DMA to reduce per-transfer costs. - * NOTE max_seg_size assumption that small blocks aren't - * normally used (except e.g. for reading SD registers). - */ - mmc->max_segs = 32; - mmc->max_blk_size = 2048; /* MMC_BLOCK_LENGTH is 11 bits (+1) */ - mmc->max_blk_count = 2048; /* MMC_BLOCK_COUNT is 11 bits (+1) */ - mmc->max_req_size = BUFFER_SIZE; - mmc->max_seg_size = mmc->max_req_size; - - ret = request_irq(host->irq, goldfish_mmc_irq, 0, DRIVER_NAME, host); - if (ret) { - dev_err(&pdev->dev, "Failed IRQ Adding goldfish MMC\n"); - goto err_request_irq_failed; - } - - host->dev = &pdev->dev; - platform_set_drvdata(pdev, host); - - ret = device_create_file(&pdev->dev, &dev_attr_cover_switch); - if (ret) - dev_warn(mmc_dev(mmc), "Unable to create sysfs attributes\n"); - - GOLDFISH_MMC_WRITE(host, MMC_SET_BUFFER, host->phys_base); - GOLDFISH_MMC_WRITE(host, MMC_INT_ENABLE, - MMC_STAT_END_OF_CMD | MMC_STAT_END_OF_DATA | - MMC_STAT_STATE_CHANGE | MMC_STAT_CMD_TIMEOUT); - - mmc_add_host(mmc); - return 0; - -err_request_irq_failed: - dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base, - host->phys_base); -dma_alloc_failed: - iounmap(host->reg_base); -ioremap_failed: - mmc_free_host(mmc); -err_alloc_host_failed: - return ret; -} - -static int goldfish_mmc_remove(struct platform_device *pdev) -{ - struct goldfish_mmc_host *host = platform_get_drvdata(pdev); - struct mmc_host *mmc = mmc_from_priv(host); - - BUG_ON(host == NULL); - - mmc_remove_host(mmc); - free_irq(host->irq, host); - dma_free_coherent(&pdev->dev, BUFFER_SIZE, host->virt_base, host->phys_base); - iounmap(host->reg_base); - mmc_free_host(mmc); - return 0; -} - -static struct platform_driver goldfish_mmc_driver = { - .probe = goldfish_mmc_probe, - .remove = goldfish_mmc_remove, - .driver = { - .name = DRIVER_NAME, - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - }, -}; - -module_platform_driver(goldfish_mmc_driver); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From a56f44138a2c57047f1ea94ea121af31c595132b Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 10 Feb 2021 12:19:33 -0600 Subject: mmc: sdhci-esdhc-imx: fix kernel panic when remove module In sdhci_esdhc_imx_remove() the SDHCI_INT_STATUS in read. Under some circumstances, this may be done while the device is runtime suspended, triggering the below splat. Fix the problem by adding a pm_runtime_get_sync(), before reading the register, which will turn on clocks etc making the device accessible again. [ 1811.323148] mmc1: card aaaa removed [ 1811.347483] Internal error: synchronous external abort: 96000210 [#1] PREEMPT SMP [ 1811.354988] Modules linked in: sdhci_esdhc_imx(-) sdhci_pltfm sdhci cqhci mmc_block mmc_core [last unloaded: mmc_core] [ 1811.365726] CPU: 0 PID: 3464 Comm: rmmod Not tainted 5.10.1-sd-99871-g53835a2e8186 #5 [ 1811.373559] Hardware name: Freescale i.MX8DXL EVK (DT) [ 1811.378705] pstate: 60000005 (nZCv daif -PAN -UAO -TCO BTYPE=--) [ 1811.384723] pc : sdhci_esdhc_imx_remove+0x28/0x15c [sdhci_esdhc_imx] [ 1811.391090] lr : platform_drv_remove+0x2c/0x50 [ 1811.395536] sp : ffff800012c7bcb0 [ 1811.398855] x29: ffff800012c7bcb0 x28: ffff00002c72b900 [ 1811.404181] x27: 0000000000000000 x26: 0000000000000000 [ 1811.409497] x25: 0000000000000000 x24: 0000000000000000 [ 1811.414814] x23: ffff0000042b3890 x22: ffff800009127120 [ 1811.420131] x21: ffff00002c4c9580 x20: ffff0000042d0810 [ 1811.425456] x19: ffff0000042d0800 x18: 0000000000000020 [ 1811.430773] x17: 0000000000000000 x16: 0000000000000000 [ 1811.436089] x15: 0000000000000004 x14: ffff000004019c10 [ 1811.441406] x13: 0000000000000000 x12: 0000000000000020 [ 1811.446723] x11: 0101010101010101 x10: 7f7f7f7f7f7f7f7f [ 1811.452040] x9 : fefefeff6364626d x8 : 7f7f7f7f7f7f7f7f [ 1811.457356] x7 : 78725e6473607372 x6 : 0000000080808080 [ 1811.462673] x5 : 0000000000000000 x4 : 0000000000000000 [ 1811.467990] x3 : ffff800011ac1cb0 x2 : 0000000000000000 [ 1811.473307] x1 : ffff8000091214d4 x0 : ffff8000133a0030 [ 1811.478624] Call trace: [ 1811.481081] sdhci_esdhc_imx_remove+0x28/0x15c [sdhci_esdhc_imx] [ 1811.487098] platform_drv_remove+0x2c/0x50 [ 1811.491198] __device_release_driver+0x188/0x230 [ 1811.495818] driver_detach+0xc0/0x14c [ 1811.499487] bus_remove_driver+0x5c/0xb0 [ 1811.503413] driver_unregister+0x30/0x60 [ 1811.507341] platform_driver_unregister+0x14/0x20 [ 1811.512048] sdhci_esdhc_imx_driver_exit+0x1c/0x3a8 [sdhci_esdhc_imx] [ 1811.518495] __arm64_sys_delete_module+0x19c/0x230 [ 1811.523291] el0_svc_common.constprop.0+0x78/0x1a0 [ 1811.528086] do_el0_svc+0x24/0x90 [ 1811.531405] el0_svc+0x14/0x20 [ 1811.534461] el0_sync_handler+0x1a4/0x1b0 [ 1811.538474] el0_sync+0x174/0x180 [ 1811.541801] Code: a9025bf5 f9403e95 f9400ea0 9100c000 (b9400000) [ 1811.547902] ---[ end trace 3fb1a3bd48ff7be5 ]--- Signed-off-by: Frank Li Cc: stable@vger.kernel.org # v4.0+ Link: https://lore.kernel.org/r/20210210181933.29263-1-Frank.Li@nxp.com [Ulf: Clarified the commit message a bit] Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 16ed19f47939..a20459744d21 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1666,9 +1666,10 @@ static int sdhci_esdhc_imx_remove(struct platform_device *pdev) struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); - int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); + int dead; pm_runtime_get_sync(&pdev->dev); + dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); -- cgit v1.2.3