From 08e03039e0000a7ec7ba13dc18bddc6afd32cb27 Mon Sep 17 00:00:00 2001 From: Fabrizio Castro Date: Tue, 13 Dec 2022 23:01:28 +0000 Subject: mmc: renesas_sdhi: Add RZ/V2M compatible string The SDHI/eMMC IPs found with the RZ/V2M (a.k.a. r9a09g011), are very similar to the ones found in R-Car Gen3, but they are not exactly the same, and as a result need an SoC specific compatible string for fine tuning driver support. Signed-off-by: Fabrizio Castro Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/20221213230129.549968-4-fabrizio.castro.jz@renesas.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_internal_dmac.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 29f562115c66..f38003f6b1ca 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -210,6 +210,11 @@ static const struct renesas_sdhi_quirks sdhi_quirks_r8a77990 = { .manual_tap_correction = true, }; +static const struct renesas_sdhi_quirks sdhi_quirks_r9a09g011 = { + .fixed_addr_mode = true, + .hs400_disabled = true, +}; + /* * Note for r8a7796 / r8a774a1: we can't distinguish ES1.1 and 1.2 as of now. * So, we want to treat them equally and only have a match for ES1.2 to enforce @@ -251,6 +256,11 @@ static const struct renesas_sdhi_of_data_with_quirks of_r8a77990_compatible = { .quirks = &sdhi_quirks_r8a77990, }; +static const struct renesas_sdhi_of_data_with_quirks of_r9a09g011_compatible = { + .of_data = &of_data_rcar_gen3, + .quirks = &sdhi_quirks_r9a09g011, +}; + static const struct renesas_sdhi_of_data_with_quirks of_rcar_gen3_compatible = { .of_data = &of_data_rcar_gen3, }; @@ -274,6 +284,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { { .compatible = "renesas,sdhi-r8a77970", .data = &of_r8a77970_compatible, }, { .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, }, { .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, }, + { .compatible = "renesas,sdhi-r9a09g011", .data = &of_r9a09g011_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, { .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, }, {}, -- cgit v1.2.3 From c8c49a5a0b2be2da993cee2a2d7fbb5994e47d7a Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Fri, 23 Dec 2022 10:50:21 +0800 Subject: mmc: sdhci-esdhc-imx: simplify the auto tuning logic Clear auto tuning bit when reset tuning, and enable auto tuning only after tuning done successfully for both standard tuning and manual tuning. Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20221223025022.1893102-2-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 9e73c34b6401..db5b5a6ba09f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -430,9 +430,10 @@ static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host) } /* Enable the auto tuning circuit to check the CMD line and BUS line */ -static inline void usdhc_auto_tuning_mode_sel(struct sdhci_host *host) +static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host) { u32 buswidth, auto_tune_buswidth; + u32 reg; buswidth = USDHC_GET_BUSWIDTH(readl(host->ioaddr + SDHCI_HOST_CONTROL)); @@ -451,6 +452,10 @@ static inline void usdhc_auto_tuning_mode_sel(struct sdhci_host *host) esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK, auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN, ESDHC_VEND_SPEC2); + + reg = readl(host->ioaddr + ESDHC_MIX_CTRL); + reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN; + writel(reg, host->ioaddr + ESDHC_MIX_CTRL); } static u32 esdhc_readl_le(struct sdhci_host *host, int reg) @@ -682,14 +687,11 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg) } else { v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; m &= ~ESDHC_MIX_CTRL_FBCLK_SEL; - m &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN; } if (val & SDHCI_CTRL_EXEC_TUNING) { v |= ESDHC_MIX_CTRL_EXE_TUNE; m |= ESDHC_MIX_CTRL_FBCLK_SEL; - m |= ESDHC_MIX_CTRL_AUTO_TUNE_EN; - usdhc_auto_tuning_mode_sel(host); } else { v &= ~ESDHC_MIX_CTRL_EXE_TUNE; } @@ -1023,13 +1025,15 @@ static void esdhc_reset_tuning(struct sdhci_host *host) /* Reset the tuning circuit */ if (esdhc_is_usdhc(imx_data)) { + ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL); + ctrl &= ~ESDHC_MIX_CTRL_AUTO_TUNE_EN; if (imx_data->socdata->flags & ESDHC_FLAG_MAN_TUNING) { - ctrl = readl(host->ioaddr + ESDHC_MIX_CTRL); ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; ctrl &= ~ESDHC_MIX_CTRL_FBCLK_SEL; writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL); writel(0, host->ioaddr + ESDHC_TUNE_CTRL_STATUS); } else if (imx_data->socdata->flags & ESDHC_FLAG_STD_TUNING) { + writel(ctrl, host->ioaddr + ESDHC_MIX_CTRL); ctrl = readl(host->ioaddr + SDHCI_AUTO_CMD_STATUS); ctrl &= ~ESDHC_MIX_CTRL_SMPCLK_SEL; ctrl &= ~ESDHC_MIX_CTRL_EXE_TUNE; @@ -1055,6 +1059,7 @@ static void esdhc_reset_tuning(struct sdhci_host *host) static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); + int err; /* * i.MX uSDHC internally already uses a fixed optimized timing for @@ -1069,7 +1074,12 @@ static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) * correct delay cell. */ esdhc_reset_tuning(host); - return sdhci_execute_tuning(mmc, opcode); + err = sdhci_execute_tuning(mmc, opcode); + /* If tuning done, enable auto tuning */ + if (!err && !host->tuning_err) + usdhc_auto_tuning_mode_sel_and_en(host); + + return err; } static void esdhc_prepare_tuning(struct sdhci_host *host, u32 val) @@ -1103,11 +1113,8 @@ static void esdhc_post_tuning(struct sdhci_host *host) { u32 reg; - usdhc_auto_tuning_mode_sel(host); - reg = readl(host->ioaddr + ESDHC_MIX_CTRL); reg &= ~ESDHC_MIX_CTRL_EXE_TUNE; - reg |= ESDHC_MIX_CTRL_AUTO_TUNE_EN; writel(reg, host->ioaddr + ESDHC_MIX_CTRL); } -- cgit v1.2.3 From 52e4c32baed2a4c79e20a82dc22e307499759852 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Fri, 23 Dec 2022 10:50:22 +0800 Subject: mmc: sdhci-esdhc-imx: only enable DAT[0] and CMD line auto tuning for SDIO device USDHC IP has one limitation: the tuning circuit can't handle the async sdio device interrupt correctly. When sdio device use 4 data lines, async sdio interrupt will use the shared DAT[1], if enable auto tuning circuit to check these 4 data lines, include the DAT[1], this circuit will detect this interrupt, take this as data on DAT[1], and adjust the delay cell wrongly, finally will cause the DATA/CMD CRC error. So for SDIO device, only enable DAT[0] and CMD line for auto tuning. To distinguish the card type during card init, involve init_card(). Signed-off-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20221223025022.1893102-3-haibo.chen@nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index db5b5a6ba09f..58f042fdd4f4 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -338,6 +338,16 @@ struct pltfm_imx_data { struct clk *clk_ahb; struct clk *clk_per; unsigned int actual_clock; + + /* + * USDHC has one limition, require the SDIO device a different + * register setting. Driver has to recognize card type during + * the card init, but at this stage, mmc_host->card is not + * available. So involve this field to save the card type + * during card init through usdhc_init_card(). + */ + unsigned int init_card_type; + enum { NO_CMD_PENDING, /* no multiblock command pending */ MULTIBLK_IN_PROCESS, /* exact multiblock cmd in process */ @@ -432,6 +442,8 @@ static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci_host *host) /* Enable the auto tuning circuit to check the CMD line and BUS line */ static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); u32 buswidth, auto_tune_buswidth; u32 reg; @@ -449,6 +461,20 @@ static inline void usdhc_auto_tuning_mode_sel_and_en(struct sdhci_host *host) break; } + /* + * For USDHC, auto tuning circuit can not handle the async sdio + * device interrupt correctly. When sdio device use 4 data lines, + * async sdio interrupt will use the shared DAT[1], if enable auto + * tuning circuit check these 4 data lines, include the DAT[1], + * this circuit will detect this interrupt, take this as a data on + * DAT[1], and adjust the delay cell wrongly. + * This is the hardware design limitation, to avoid this, for sdio + * device, config the auto tuning circuit only check DAT[0] and CMD + * line. + */ + if (imx_data->init_card_type == MMC_TYPE_SDIO) + auto_tune_buswidth = ESDHC_VEND_SPEC2_AUTO_TUNE_1BIT_EN; + esdhc_clrset_le(host, ESDHC_VEND_SPEC2_AUTO_TUNE_MODE_MASK, auto_tune_buswidth | ESDHC_VEND_SPEC2_AUTO_TUNE_CMD_EN, ESDHC_VEND_SPEC2); @@ -1056,6 +1082,15 @@ static void esdhc_reset_tuning(struct sdhci_host *host) } } +static void usdhc_init_card(struct mmc_host *mmc, struct mmc_card *card) +{ + struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host); + + imx_data->init_card_type = card->type; +} + static int usdhc_execute_tuning(struct mmc_host *mmc, u32 opcode) { struct sdhci_host *host = mmc_priv(mmc); @@ -1681,6 +1716,12 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) * to replace the standard one in sdhci_ops. */ host->mmc_host_ops.execute_tuning = usdhc_execute_tuning; + + /* + * Link usdhc specific mmc_host_ops init card function, + * to distinguish the card type. + */ + host->mmc_host_ops.init_card = usdhc_init_card; } err = sdhci_esdhc_imx_probe_dt(pdev, host, imx_data); -- cgit v1.2.3 From b0197e47a5022c750ce03fed6978fa88ef39b631 Mon Sep 17 00:00:00 2001 From: Tom Fitzhenry Date: Thu, 29 Dec 2022 01:23:54 +1100 Subject: mmc: pwrseq_simple: include deferred probe reasons This adds a reason to /sys/kernel/debug/devices_deferred, which makes it easier to debug deferred probes such as [0]. 0. https://lore.kernel.org/all/20221228140708.26431-1-tom@tom-fitzhenry.me.uk/ Signed-off-by: Tom Fitzhenry Link: https://lore.kernel.org/r/20221228142354.28454-1-tom@tom-fitzhenry.me.uk Signed-off-by: Ulf Hansson --- drivers/mmc/core/pwrseq_simple.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index 988467fbb621..3bac1e71411b 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -119,14 +119,14 @@ static int mmc_pwrseq_simple_probe(struct platform_device *pdev) pwrseq->ext_clk = devm_clk_get(dev, "ext_clock"); if (IS_ERR(pwrseq->ext_clk) && PTR_ERR(pwrseq->ext_clk) != -ENOENT) - return PTR_ERR(pwrseq->ext_clk); + return dev_err_probe(dev, PTR_ERR(pwrseq->ext_clk), "external clock not ready\n"); pwrseq->reset_gpios = devm_gpiod_get_array(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(pwrseq->reset_gpios) && PTR_ERR(pwrseq->reset_gpios) != -ENOENT && PTR_ERR(pwrseq->reset_gpios) != -ENOSYS) { - return PTR_ERR(pwrseq->reset_gpios); + return dev_err_probe(dev, PTR_ERR(pwrseq->reset_gpios), "reset GPIOs not ready\n"); } device_property_read_u32(dev, "post-power-on-delay-ms", -- cgit v1.2.3 From 930ba0cb7db2f67f80f950df914e35d00df86264 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 3 Jan 2023 17:38:51 +0100 Subject: mmc: sdio: Spelling s/compement/complement/ Fix a misspelling of "complement". Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/a650c8b930a30f5902f4fcfe23877314d098abde.1672763862.git.geert+renesas@glider.be Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio_io.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index 79dbf90216b5..b774bf51981d 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -766,7 +766,7 @@ EXPORT_SYMBOL_GPL(sdio_retune_crc_disable); * sdio_retune_crc_enable - re-enable retuning on CRC errors * @func: SDIO function attached to host * - * This is the compement to sdio_retune_crc_disable(). + * This is the complement to sdio_retune_crc_disable(). */ void sdio_retune_crc_enable(struct sdio_func *func) { -- cgit v1.2.3 From 8e0ec111c67e90f9e51f189c8bb09f75194962ee Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 13 Jan 2023 13:00:06 +0200 Subject: mmc: sdhci-pci: Replace SDHCI_QUIRK_MISSING_CAPS for Ricoh controller SDHCI_QUIRK_MISSING_CAPS is not needed because __sdhci_read_caps() can be called instead. In preparation to get rid of SDHCI_QUIRK_MISSING_CAPS, replace SDHCI_QUIRK_MISSING_CAPS with __sdhci_read_caps() for Ricoh SDHCI controller. __sdhci_read_caps() is also called from sdhci_setup_host() via sdhci_read_caps(), however only the first call to __sdhci_read_caps() does anything because after that host->read_caps has been set to true. Note, __sdhci_read_caps() does more than just set host->caps, such as do a reset, so calling __sdhci_read_caps() earlier could have unforeseen side-effects. However the code flow has been reviewed with that in mind. Signed-off-by: Adrian Hunter Link: https://lore.kernel.org/r/20230113110011.129835-2-adrian.hunter@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index c359f867df0a..01975d145200 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -251,13 +251,16 @@ static int ricoh_probe(struct sdhci_pci_chip *chip) static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) { - slot->host->caps = + u32 caps = FIELD_PREP(SDHCI_TIMEOUT_CLK_MASK, 0x21) | FIELD_PREP(SDHCI_CLOCK_BASE_MASK, 0x21) | SDHCI_TIMEOUT_CLK_UNIT | SDHCI_CAN_VDD_330 | SDHCI_CAN_DO_HISPD | SDHCI_CAN_DO_SDMA; + u32 caps1 = 0; + + __sdhci_read_caps(slot->host, NULL, &caps, &caps1); return 0; } @@ -286,8 +289,7 @@ static const struct sdhci_pci_fixes sdhci_ricoh_mmc = { #endif .quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_CLOCK_BEFORE_RESET | - SDHCI_QUIRK_NO_CARD_NO_RESET | - SDHCI_QUIRK_MISSING_CAPS + SDHCI_QUIRK_NO_CARD_NO_RESET, }; static void ene_714_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -- cgit v1.2.3 From 759329ed7aa58da283fde300227b7a979744f0fd Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 13 Jan 2023 13:00:07 +0200 Subject: mmc: sdhci-brcmstb: Replace SDHCI_QUIRK_MISSING_CAPS SDHCI_QUIRK_MISSING_CAPS is not needed because sdhci_read_caps() can be called instead. In preparation to get rid of SDHCI_QUIRK_MISSING_CAPS, replace SDHCI_QUIRK_MISSING_CAPS with sdhci_read_caps(). __sdhci_read_caps() is also called from sdhci_setup_host() via sdhci_read_caps(), however only the first call to __sdhci_read_caps() does anything because after that host->read_caps has been set to true. Note, __sdhci_read_caps() does more than just set host->caps, such as do a reset, so calling __sdhci_read_caps() earlier could have unforeseen side-effects. However the code flow has been reviewed with that in mind. Signed-off-by: Adrian Hunter Link: https://lore.kernel.org/r/20230113110011.129835-3-adrian.hunter@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-brcmstb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index f2cf3d70db79..0a19b7af1d41 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -324,13 +324,11 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) * will allow these modes to be specified by device tree * properties through mmc_of_parse(). */ - host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); + sdhci_read_caps(host); if (match_priv->flags & BRCMSTB_MATCH_FLAGS_NO_64BIT) host->caps &= ~SDHCI_CAN_64BIT; - host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_DDR50); - host->quirks |= SDHCI_QUIRK_MISSING_CAPS; if (match_priv->flags & BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT) host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; -- cgit v1.2.3 From 924ea310a5a2b3091bca5753f351f908019d2c64 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 13 Jan 2023 13:00:08 +0200 Subject: mmc: sdhci-sprd: Replace SDHCI_QUIRK_MISSING_CAPS SDHCI_QUIRK_MISSING_CAPS is not needed because sdhci_read_caps() can be called instead. In preparation to get rid of SDHCI_QUIRK_MISSING_CAPS, replace SDHCI_QUIRK_MISSING_CAPS with sdhci_read_caps(). __sdhci_read_caps() is also called from sdhci_setup_host() via sdhci_read_caps(), however only the first call to __sdhci_read_caps() does anything because after that host->read_caps has been set to true. Note, __sdhci_read_caps() does more than just set host->caps, such as do a reset, so calling __sdhci_read_caps() earlier could have unforeseen side-effects. However the code flow has been reviewed with that in mind. Signed-off-by: Adrian Hunter Acked-by: Chunyan Zhang Link: https://lore.kernel.org/r/20230113110011.129835-4-adrian.hunter@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-sprd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c index 525f979e2a97..7f4ee2e12735 100644 --- a/drivers/mmc/host/sdhci-sprd.c +++ b/drivers/mmc/host/sdhci-sprd.c @@ -553,8 +553,7 @@ static void sdhci_sprd_phy_param_parse(struct sdhci_sprd_host *sprd_host, static const struct sdhci_pltfm_data sdhci_sprd_pdata = { .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | - SDHCI_QUIRK_MISSING_CAPS, + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK, .quirks2 = SDHCI_QUIRK2_BROKEN_HS200 | SDHCI_QUIRK2_USE_32BIT_BLK_CNT | SDHCI_QUIRK2_PRESET_VALUE_BROKEN, @@ -671,8 +670,7 @@ static int sdhci_sprd_probe(struct platform_device *pdev) * will allow these modes to be specified only by device * tree properties through mmc_of_parse(). */ - host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); - host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + sdhci_read_caps(host); host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_DDR50); -- cgit v1.2.3 From 4f1896ddf6ec0a393a84a5559cd99f1be55d3d71 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 13 Jan 2023 13:00:09 +0200 Subject: mmc: sdhci-pxav3: Replace SDHCI_QUIRK_MISSING_CAPS SDHCI_QUIRK_MISSING_CAPS is not needed because sdhci_read_caps() can be called instead. In preparation to get rid of SDHCI_QUIRK_MISSING_CAPS, replace SDHCI_QUIRK_MISSING_CAPS with sdhci_read_caps(). __sdhci_read_caps() is also called from sdhci_setup_host() via sdhci_read_caps(), however only the first call to __sdhci_read_caps() does anything because after that host->read_caps has been set to true. Note, __sdhci_read_caps() does more than just set host->caps, such as do a reset, so calling __sdhci_read_caps() earlier could have unforeseen side-effects. However the code flow has been reviewed with that in mind. Signed-off-by: Adrian Hunter Reviewed-by: Jisheng Zhang Link: https://lore.kernel.org/r/20230113110011.129835-5-adrian.hunter@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index a6d89a3f1946..e39dcc998772 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -124,10 +124,8 @@ static int armada_38x_quirks(struct platform_device *pdev, struct resource *res; host->quirks &= ~SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; - host->quirks |= SDHCI_QUIRK_MISSING_CAPS; - host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); - host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); + sdhci_read_caps(host); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "conf-sdio3"); -- cgit v1.2.3 From f3200164b47922f7a4cb8a8504046d0b3b8d143b Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 13 Jan 2023 13:00:10 +0200 Subject: mmc: sdhci-iproc: Replace SDHCI_QUIRK_MISSING_CAPS SDHCI_QUIRK_MISSING_CAPS is not needed because __sdhci_read_caps() can be called instead. In preparation to get rid of SDHCI_QUIRK_MISSING_CAPS, replace SDHCI_QUIRK_MISSING_CAPS with __sdhci_read_caps(). __sdhci_read_caps() is also called from sdhci_setup_host() via sdhci_read_caps(), however only the first call to __sdhci_read_caps() does anything because after that host->read_caps has been set to true. Note, __sdhci_read_caps() does more than just set host->caps, such as do a reset, so calling __sdhci_read_caps() earlier could have unforeseen side-effects. However the code flow has been reviewed with that in mind. Signed-off-by: Adrian Hunter Acked-by: Scott Branden Link: https://lore.kernel.org/r/20230113110011.129835-6-adrian.hunter@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-iproc.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index 6db35b1b8557..86eb0045515e 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -18,6 +18,7 @@ struct sdhci_iproc_data { u32 caps; u32 caps1; u32 mmc_caps; + bool missing_caps; }; struct sdhci_iproc_host { @@ -251,7 +252,6 @@ static const struct sdhci_iproc_data iproc_data = { static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = { .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | - SDHCI_QUIRK_MISSING_CAPS | SDHCI_QUIRK_NO_HISPD_BIT, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, .ops = &sdhci_iproc_32only_ops, @@ -266,6 +266,7 @@ static const struct sdhci_iproc_data bcm2835_data = { .caps1 = SDHCI_DRIVER_TYPE_A | SDHCI_DRIVER_TYPE_C, .mmc_caps = 0x00000000, + .missing_caps = true, }; static const struct sdhci_ops sdhci_iproc_bcm2711_ops = { @@ -295,8 +296,7 @@ static const struct sdhci_iproc_data bcm2711_data = { }; static const struct sdhci_pltfm_data sdhci_bcm7211a0_pltfm_data = { - .quirks = SDHCI_QUIRK_MISSING_CAPS | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | + .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_BROKEN_DMA | SDHCI_QUIRK_BROKEN_ADMA, .ops = &sdhci_iproc_ops, @@ -315,6 +315,7 @@ static const struct sdhci_iproc_data bcm7211a0_data = { SDHCI_CAN_DO_HISPD, .caps1 = SDHCI_DRIVER_TYPE_C | SDHCI_DRIVER_TYPE_D, + .missing_caps = true, }; static const struct of_device_id sdhci_iproc_of_match[] = { @@ -397,9 +398,10 @@ static int sdhci_iproc_probe(struct platform_device *pdev) } } - if (iproc_host->data->pdata->quirks & SDHCI_QUIRK_MISSING_CAPS) { - host->caps = iproc_host->data->caps; - host->caps1 = iproc_host->data->caps1; + if (iproc_host->data->missing_caps) { + __sdhci_read_caps(host, NULL, + &iproc_host->data->caps, + &iproc_host->data->caps1); } ret = sdhci_add_host(host); -- cgit v1.2.3 From 681b9596ed9eeabdc8be08f9b07c3b311ff138d1 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 13 Jan 2023 13:00:11 +0200 Subject: mmc: sdhci: Remove SDHCI_QUIRK_MISSING_CAPS Now that it is no longer used, remove SDHCI_QUIRK_MISSING_CAPS. Note, from now on, __sdhci_read_caps() should be used to provide missing capability flags. Signed-off-by: Adrian Hunter Link: https://lore.kernel.org/r/20230113110011.129835-7-adrian.hunter@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 3 --- drivers/mmc/host/sdhci.h | 2 -- 2 files changed, 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index f3af1bd0f7b9..3241916141d7 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -4121,9 +4121,6 @@ void __sdhci_read_caps(struct sdhci_host *host, const u16 *ver, v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION); host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; - if (host->quirks & SDHCI_QUIRK_MISSING_CAPS) - return; - if (caps) { host->caps = *caps; } else { diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 605eaee805f7..f4f2085c274c 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -423,8 +423,6 @@ struct sdhci_host { #define SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN (1<<25) /* Controller cannot support End Attribute in NOP ADMA descriptor */ #define SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC (1<<26) -/* Controller is missing device caps. Use caps provided by host */ -#define SDHCI_QUIRK_MISSING_CAPS (1<<27) /* Controller uses Auto CMD12 command to stop the transfer */ #define SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 (1<<28) /* Controller doesn't have HISPD bit field in HI-SPEED SD card */ -- cgit v1.2.3 From 95921151e04335e9e2ddf4f294405e64be4ea947 Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Wed, 11 Jan 2023 13:14:58 -0500 Subject: mmc: sdhci-of-dwcmshc: enable host V4 support for BlueField-3 SoC This commit enables SDHCI Host V4 support on Bluefield-3 SoC to be consistent with the default setting in firmware(UEFI). Reviewed-by: David Woods Signed-off-by: Liming Sun Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/990885f566c32ac8e6888ad6b434fb70d1a5d7af.1673460632.git.limings@nvidia.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index a7343d4bc50e..49338670c89f 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -528,6 +528,11 @@ static int dwcmshc_probe(struct platform_device *pdev) goto err_clk; } +#ifdef CONFIG_ACPI + if (pltfm_data == &sdhci_dwcmshc_bf3_pdata) + sdhci_enable_v4_mode(host); +#endif + host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; err = sdhci_setup_host(host); -- cgit v1.2.3 From 05f0430f923851048f800bbf03ba10e5beca376c Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 16 Jan 2023 12:14:30 +0100 Subject: mmc: pwrseq_sd8787: Allow being built-in irrespective of dependencies pwrseq_sd8787 is forced to be built as a module if its dependencies are. That's unnecessary, it's perfectly fine for it to be built-in even though the wireless drivers that need it are modules. Relax the depends definition in Kconfig accordingly. Signed-off-by: Lukas Wunner Cc: Matt Ranostay Cc: Lubomir Rintel Cc: Claudiu Beznea Link: https://lore.kernel.org/r/8bb3d7c3a36985e030ba40e853c57578de8fb303.1673866725.git.lukas@wunner.de Signed-off-by: Ulf Hansson --- drivers/mmc/core/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index 6f25c34e4fec..e9e9d31b39b4 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -15,7 +15,7 @@ config PWRSEQ_EMMC config PWRSEQ_SD8787 tristate "HW reset support for SD8787 BT + Wifi module" - depends on OF && (MWIFIEX || BT_MRVL_SDIO || LIBERTAS_SDIO || WILC1000_SDIO) + depends on OF && (MWIFIEX != n || BT_MRVL_SDIO != n || LIBERTAS_SDIO != n || WILC1000_SDIO != n) help This selects hardware reset support for the SD8787 BT + Wifi module. By default this option is set to n. -- cgit v1.2.3 From dfe9746aed2d1267f00b880ae232170d7d9e4202 Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Mon, 16 Jan 2023 11:43:54 -0800 Subject: mmc: sdhci-pxav2: add initial support for PXA168 V1 controller Add a new compatible string for the version 1 controller used in the PXA168, along with necessary quirks. Use a separate ops struct in preparation for a silicon bug workaround only necessary on V1. Signed-off-by: Doug Brown Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230116194401.20372-2-doug@schmorgal.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav2.c | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index f18906b5575f..5707d597ecae 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -101,6 +101,24 @@ static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); } +struct sdhci_pxa_variant { + const struct sdhci_ops *ops; + unsigned int extra_quirks; +}; + +static const struct sdhci_ops pxav1_sdhci_ops = { + .set_clock = sdhci_set_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .set_bus_width = pxav2_mmc_set_bus_width, + .reset = pxav2_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + +static const struct sdhci_pxa_variant __maybe_unused pxav1_variant = { + .ops = &pxav1_sdhci_ops, + .extra_quirks = SDHCI_QUIRK_NO_BUSY_IRQ | SDHCI_QUIRK_32BIT_DMA_SIZE, +}; + static const struct sdhci_ops pxav2_sdhci_ops = { .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, @@ -109,11 +127,14 @@ static const struct sdhci_ops pxav2_sdhci_ops = { .set_uhs_signaling = sdhci_set_uhs_signaling, }; +static const struct sdhci_pxa_variant pxav2_variant = { + .ops = &pxav2_sdhci_ops, +}; + #ifdef CONFIG_OF static const struct of_device_id sdhci_pxav2_of_match[] = { - { - .compatible = "mrvl,pxav2-mmc", - }, + { .compatible = "mrvl,pxav1-mmc", .data = &pxav1_variant, }, + { .compatible = "mrvl,pxav2-mmc", .data = &pxav2_variant, }, {}, }; MODULE_DEVICE_TABLE(of, sdhci_pxav2_of_match); @@ -157,7 +178,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; - const struct of_device_id *match; + const struct sdhci_pxa_variant *variant; int ret; struct clk *clk; @@ -185,10 +206,12 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; - match = of_match_device(of_match_ptr(sdhci_pxav2_of_match), &pdev->dev); - if (match) { + variant = of_device_get_match_data(dev); + if (variant) pdata = pxav2_get_mmc_pdata(dev); - } + else + variant = &pxav2_variant; + if (pdata) { if (pdata->flags & PXA_FLAG_CARD_PERMANENT) { /* on-chip device */ @@ -208,7 +231,8 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) host->mmc->pm_caps |= pdata->pm_caps; } - host->ops = &pxav2_sdhci_ops; + host->quirks |= variant->extra_quirks; + host->ops = variant->ops; ret = sdhci_add_host(host); if (ret) -- cgit v1.2.3 From e764395080184544aa963b0dead24c01d1ca3b0e Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Mon, 16 Jan 2023 11:43:55 -0800 Subject: mmc: sdhci-pxav2: enable CONFIG_MMC_SDHCI_IO_ACCESSORS Enable CONFIG_MMC_SDHCI_IO_ACCESSORS for the pxav2 driver. The read_w callback is needed for a silicon bug workaround in the PXA168. Signed-off-by: Doug Brown Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230116194401.20372-3-doug@schmorgal.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 5e19a961c34d..b9e9185c86a6 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -360,6 +360,7 @@ config MMC_SDHCI_PXAV2 depends on MMC_SDHCI_PLTFM depends on ARCH_MMP || COMPILE_TEST default CPU_PXA910 + select MMC_SDHCI_IO_ACCESSORS help This selects the Marvell(R) PXAV2 SD Host Controller. If you have a PXA9XX platform with SD Host Controller -- cgit v1.2.3 From 7f7a201ad1fcd916526d10b21685690a35aa8d7a Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Mon, 16 Jan 2023 11:43:56 -0800 Subject: mmc: sdhci-pxav2: add register workaround for PXA168 silicon bug The PXA168 has a documented silicon bug that results in a data abort exception when accessing the SDHCI_HOST_VERSION register on SDH2 and SDH4 through a 16-bit read. Implement the workaround described in the errata, which performs a 32-bit read from a lower address instead. This is safe to use on all four SDH peripherals. Signed-off-by: Doug Brown Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230116194401.20372-4-doug@schmorgal.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav2.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 5707d597ecae..5e01dab94426 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -80,6 +80,15 @@ static void pxav2_reset(struct sdhci_host *host, u8 mask) } } +static u16 pxav1_readw(struct sdhci_host *host, int reg) +{ + /* Workaround for data abort exception on SDH2 and SDH4 on PXA168 */ + if (reg == SDHCI_HOST_VERSION) + return readl(host->ioaddr + SDHCI_HOST_VERSION - 2) >> 16; + + return readw(host->ioaddr + reg); +} + static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; @@ -107,6 +116,7 @@ struct sdhci_pxa_variant { }; static const struct sdhci_ops pxav1_sdhci_ops = { + .read_w = pxav1_readw, .set_clock = sdhci_set_clock, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .set_bus_width = pxav2_mmc_set_bus_width, -- cgit v1.2.3 From c7c60bf62820abfbfc7d154ade06a6043be0b479 Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Mon, 16 Jan 2023 11:43:57 -0800 Subject: mmc: sdhci-pxav2: change clock name to match DT bindings The devicetree bindings for this driver specify that the two allowed clock names are io and core. Change this driver to look for io, but allow any name if it fails for backwards compatibility. Follow the same pattern used in sdhci-pxav3, but add support for EPROBE_DEFER. Get rid of an unnecessary pdev->dev while we're at it. Signed-off-by: Doug Brown Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230116194401.20372-5-doug@schmorgal.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav2.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 5e01dab94426..f5c86e1ba734 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -199,16 +199,18 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); - clk = devm_clk_get(dev, "PXA-SDHCLK"); + clk = devm_clk_get(dev, "io"); + if (IS_ERR(clk) && PTR_ERR(clk) != -EPROBE_DEFER) + clk = devm_clk_get(dev, NULL); if (IS_ERR(clk)) { - dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(clk); + dev_err_probe(dev, ret, "failed to get io clock\n"); goto free; } pltfm_host->clk = clk; ret = clk_prepare_enable(clk); if (ret) { - dev_err(&pdev->dev, "failed to enable io clock\n"); + dev_err(dev, "failed to enable io clock\n"); goto free; } -- cgit v1.2.3 From e41c48b4bcb3ceee43a101ba7d514483271fb2fb Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Mon, 16 Jan 2023 11:43:58 -0800 Subject: mmc: sdhci-pxav2: add optional core clock Add ability to have an optional core clock just like the pxav3 driver. The PXA168 needs this because its SDHC controllers have separate core and io clocks that both need to be enabled. This also correctly matches the documented devicetree bindings for this driver. Reported-by: kernel test robot Reported-by: Dan Carpenter Signed-off-by: Doug Brown Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230116194401.20372-6-doug@schmorgal.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav2.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index f5c86e1ba734..3141901e1558 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -191,7 +191,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) const struct sdhci_pxa_variant *variant; int ret; - struct clk *clk; + struct clk *clk, *clk_core; host = sdhci_pltfm_init(pdev, NULL, 0); if (IS_ERR(host)) @@ -214,6 +214,13 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) goto free; } + clk_core = devm_clk_get_optional_enabled(dev, "core"); + if (IS_ERR(clk_core)) { + ret = PTR_ERR(clk_core); + dev_err_probe(dev, ret, "failed to enable core clock\n"); + goto disable_clk; + } + host->quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; -- cgit v1.2.3 From 24552ccb4f1e0a9bf961056057aa8bd4c0d8dfe8 Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Mon, 16 Jan 2023 11:43:59 -0800 Subject: mmc: sdhci-pxav2: add SDIO card IRQ workaround for PXA168 V1 controller The PXA168 has a documented silicon bug that causes SDIO card IRQs to be missed. Implement the first half of the suggested workaround, which involves resetting the data port logic and issuing a dummy CMD0 to restart the clock. Signed-off-by: Doug Brown Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230116194401.20372-7-doug@schmorgal.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav2.c | 56 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 3141901e1558..3dafffaa0c6e 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "sdhci.h" #include "sdhci-pltfm.h" @@ -41,6 +43,10 @@ #define MMC_CARD 0x1000 #define MMC_WIDTH 0x0100 +struct sdhci_pxav2_host { + struct mmc_request *sdio_mrq; +}; + static void pxav2_reset(struct sdhci_host *host, u8 mask) { struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); @@ -89,6 +95,52 @@ static u16 pxav1_readw(struct sdhci_host *host, int reg) return readw(host->ioaddr + reg); } +static u32 pxav1_irq(struct sdhci_host *host, u32 intmask) +{ + struct sdhci_pxav2_host *pxav2_host = sdhci_pltfm_priv(sdhci_priv(host)); + struct mmc_request *sdio_mrq; + + if (pxav2_host->sdio_mrq && (intmask & SDHCI_INT_CMD_MASK)) { + /* The dummy CMD0 for the SDIO workaround just completed */ + sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, SDHCI_INT_STATUS); + intmask &= ~SDHCI_INT_CMD_MASK; + sdio_mrq = pxav2_host->sdio_mrq; + pxav2_host->sdio_mrq = NULL; + mmc_request_done(host->mmc, sdio_mrq); + } + + return intmask; +} + +static void pxav1_request_done(struct sdhci_host *host, struct mmc_request *mrq) +{ + u16 tmp; + struct sdhci_pxav2_host *pxav2_host; + + /* If this is an SDIO command, perform errata workaround for silicon bug */ + if (mrq->cmd && !mrq->cmd->error && + (mrq->cmd->opcode == SD_IO_RW_DIRECT || + mrq->cmd->opcode == SD_IO_RW_EXTENDED)) { + /* Reset data port */ + tmp = readw(host->ioaddr + SDHCI_TIMEOUT_CONTROL); + tmp |= 0x400; + writew(tmp, host->ioaddr + SDHCI_TIMEOUT_CONTROL); + + /* Clock is now stopped, so restart it by sending a dummy CMD0 */ + pxav2_host = sdhci_pltfm_priv(sdhci_priv(host)); + pxav2_host->sdio_mrq = mrq; + sdhci_writel(host, 0, SDHCI_ARGUMENT); + sdhci_writew(host, 0, SDHCI_TRANSFER_MODE); + sdhci_writew(host, SDHCI_MAKE_CMD(MMC_GO_IDLE_STATE, SDHCI_CMD_RESP_NONE), + SDHCI_COMMAND); + + /* Don't finish this request until the dummy CMD0 finishes */ + return; + } + + mmc_request_done(host->mmc, mrq); +} + static void pxav2_mmc_set_bus_width(struct sdhci_host *host, int width) { u8 ctrl; @@ -118,10 +170,12 @@ struct sdhci_pxa_variant { static const struct sdhci_ops pxav1_sdhci_ops = { .read_w = pxav1_readw, .set_clock = sdhci_set_clock, + .irq = pxav1_irq, .get_max_clock = sdhci_pltfm_clk_get_max_clock, .set_bus_width = pxav2_mmc_set_bus_width, .reset = pxav2_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, + .request_done = pxav1_request_done, }; static const struct sdhci_pxa_variant __maybe_unused pxav1_variant = { @@ -193,7 +247,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) int ret; struct clk *clk, *clk_core; - host = sdhci_pltfm_init(pdev, NULL, 0); + host = sdhci_pltfm_init(pdev, NULL, sizeof(struct sdhci_pxav2_host)); if (IS_ERR(host)) return PTR_ERR(host); -- cgit v1.2.3 From f35ca223882aa6faa25f39b9412066e33d8b6963 Mon Sep 17 00:00:00 2001 From: Doug Brown Date: Mon, 16 Jan 2023 11:44:00 -0800 Subject: mmc: sdhci-pxav2: add optional pinctrl for SDIO IRQ workaround The PXA168 errata recommends that the CMD signal should be detached from the SD bus while performing the dummy CMD0 to restart the clock. Implement this using pinctrl states. Signed-off-by: Doug Brown Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20230116194401.20372-8-doug@schmorgal.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav2.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 3dafffaa0c6e..fc306eb1f845 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "sdhci.h" #include "sdhci-pltfm.h" @@ -45,6 +46,9 @@ struct sdhci_pxav2_host { struct mmc_request *sdio_mrq; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_cmd_gpio; }; static void pxav2_reset(struct sdhci_host *host, u8 mask) @@ -104,6 +108,11 @@ static u32 pxav1_irq(struct sdhci_host *host, u32 intmask) /* The dummy CMD0 for the SDIO workaround just completed */ sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, SDHCI_INT_STATUS); intmask &= ~SDHCI_INT_CMD_MASK; + + /* Restore MMC function to CMD pin */ + if (pxav2_host->pinctrl && pxav2_host->pins_default) + pinctrl_select_state(pxav2_host->pinctrl, pxav2_host->pins_default); + sdio_mrq = pxav2_host->sdio_mrq; pxav2_host->sdio_mrq = NULL; mmc_request_done(host->mmc, sdio_mrq); @@ -129,6 +138,11 @@ static void pxav1_request_done(struct sdhci_host *host, struct mmc_request *mrq) /* Clock is now stopped, so restart it by sending a dummy CMD0 */ pxav2_host = sdhci_pltfm_priv(sdhci_priv(host)); pxav2_host->sdio_mrq = mrq; + + /* Set CMD as high output rather than MMC function while we do CMD0 */ + if (pxav2_host->pinctrl && pxav2_host->pins_cmd_gpio) + pinctrl_select_state(pxav2_host->pinctrl, pxav2_host->pins_cmd_gpio); + sdhci_writel(host, 0, SDHCI_ARGUMENT); sdhci_writew(host, 0, SDHCI_TRANSFER_MODE); sdhci_writew(host, SDHCI_MAKE_CMD(MMC_GO_IDLE_STATE, SDHCI_CMD_RESP_NONE), @@ -240,6 +254,7 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; + struct sdhci_pxav2_host *pxav2_host; struct device *dev = &pdev->dev; struct sdhci_host *host = NULL; const struct sdhci_pxa_variant *variant; @@ -247,11 +262,12 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) int ret; struct clk *clk, *clk_core; - host = sdhci_pltfm_init(pdev, NULL, sizeof(struct sdhci_pxav2_host)); + host = sdhci_pltfm_init(pdev, NULL, sizeof(*pxav2_host)); if (IS_ERR(host)) return PTR_ERR(host); pltfm_host = sdhci_priv(host); + pxav2_host = sdhci_pltfm_priv(pltfm_host); clk = devm_clk_get(dev, "io"); if (IS_ERR(clk) && PTR_ERR(clk) != -EPROBE_DEFER) @@ -307,6 +323,21 @@ static int sdhci_pxav2_probe(struct platform_device *pdev) host->quirks |= variant->extra_quirks; host->ops = variant->ops; + /* Set up optional pinctrl for PXA168 SDIO IRQ fix */ + pxav2_host->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(pxav2_host->pinctrl)) { + pxav2_host->pins_cmd_gpio = pinctrl_lookup_state(pxav2_host->pinctrl, + "state_cmd_gpio"); + if (IS_ERR(pxav2_host->pins_cmd_gpio)) + pxav2_host->pins_cmd_gpio = NULL; + pxav2_host->pins_default = pinctrl_lookup_state(pxav2_host->pinctrl, + "default"); + if (IS_ERR(pxav2_host->pins_default)) + pxav2_host->pins_default = NULL; + } else { + pxav2_host->pinctrl = NULL; + } + ret = sdhci_add_host(host); if (ret) goto disable_clk; -- cgit v1.2.3 From eca5bd666b0aa7dc0bca63292e4778968241134e Mon Sep 17 00:00:00 2001 From: Tobias Schramm Date: Fri, 30 Dec 2022 20:43:15 +0100 Subject: mmc: atmel-mci: fix race between stop command and start of next command This commit fixes a race between completion of stop command and start of a new command. Previously the command ready interrupt was enabled before stop command was written to the command register. This caused the command ready interrupt to fire immediately since the CMDRDY flag is asserted constantly while there is no command in progress. Consequently the command state machine will immediately advance to the next state when the tasklet function is executed again, no matter actual completion state of the stop command. Thus a new command can then be dispatched immediately, interrupting and corrupting the stop command on the CMD line. Fix that by dropping the command ready interrupt enable before calling atmci_send_stop_cmd. atmci_send_stop_cmd does already enable the command ready interrupt, no further writes to ATMCI_IER are necessary. Signed-off-by: Tobias Schramm Acked-by: Ludovic Desroches Link: https://lore.kernel.org/r/20221230194315.809903-2-t.schramm@manjaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index bb9bbf1c927b..dd18440a90c5 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1817,7 +1817,6 @@ static void atmci_tasklet_func(struct tasklet_struct *t) atmci_writel(host, ATMCI_IER, ATMCI_NOTBUSY); state = STATE_WAITING_NOTBUSY; } else if (host->mrq->stop) { - atmci_writel(host, ATMCI_IER, ATMCI_CMDRDY); atmci_send_stop_cmd(host, data); state = STATE_SENDING_STOP; } else { @@ -1850,8 +1849,6 @@ static void atmci_tasklet_func(struct tasklet_struct *t) * command to send. */ if (host->mrq->stop) { - atmci_writel(host, ATMCI_IER, - ATMCI_CMDRDY); atmci_send_stop_cmd(host, data); state = STATE_SENDING_STOP; } else { -- cgit v1.2.3 From 1c325ed9f95ad283926753558d364df1cd73410a Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Wed, 25 Jan 2023 10:01:58 +0900 Subject: mmc: uniphier-sd: Add control of UHS mode using SD interface logic Transition of UHS mode needs to control the register in SD interface logic. Add access to the register in the logic using the regmap from "socionext,syscon-uhs-mode" property. Define the start_signal_voltage_switch function only if UHS mode is available. Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/20230125010201.28246-3-hayashi.kunihiko@socionext.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/uniphier-sd.c | 61 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c index 3a8defdcca77..c9766d6a690f 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,7 @@ #include #include #include +#include #include #include "tmio_mmc.h" @@ -48,6 +50,11 @@ #define UNIPHIER_SD_DMA_ADDR_L 0x440 #define UNIPHIER_SD_DMA_ADDR_H 0x444 +/* SD control */ +#define UNIPHIER_SDCTRL_CHOFFSET 0x200 +#define UNIPHIER_SDCTRL_MODE 0x30 +#define UNIPHIER_SDCTRL_MODE_UHS1MOD BIT(15) + /* * IP is extended to support various features: built-in DMA engine, * 1/1024 divisor, etc. @@ -66,6 +73,8 @@ struct uniphier_sd_priv { struct reset_control *rst_hw; struct dma_chan *chan; enum dma_data_direction dma_dir; + struct regmap *sdctrl_regmap; + u32 sdctrl_ch; unsigned long clk_rate; unsigned long caps; }; @@ -420,6 +429,23 @@ static void uniphier_sd_hw_reset(struct mmc_host *mmc) usleep_range(300, 1000); } +static void uniphier_sd_uhs_enable(struct tmio_mmc_host *host, bool uhs_en) +{ + struct uniphier_sd_priv *priv = uniphier_sd_priv(host); + unsigned int offset; + u32 val; + + if (!(host->mmc->caps & MMC_CAP_UHS)) + return; + + val = (uhs_en) ? UNIPHIER_SDCTRL_MODE_UHS1MOD : 0; + + offset = UNIPHIER_SDCTRL_CHOFFSET * priv->sdctrl_ch + + UNIPHIER_SDCTRL_MODE; + regmap_write_bits(priv->sdctrl_regmap, offset, + UNIPHIER_SDCTRL_MODE_UHS1MOD, val); +} + static void uniphier_sd_set_clock(struct tmio_mmc_host *host, unsigned int clock) { @@ -500,14 +526,17 @@ static int uniphier_sd_start_signal_voltage_switch(struct mmc_host *mmc, struct uniphier_sd_priv *priv = uniphier_sd_priv(host); struct pinctrl_state *pinstate = NULL; u32 val, tmp; + bool uhs_en; switch (ios->signal_voltage) { case MMC_SIGNAL_VOLTAGE_330: val = UNIPHIER_SD_VOLT_330; + uhs_en = false; break; case MMC_SIGNAL_VOLTAGE_180: val = UNIPHIER_SD_VOLT_180; pinstate = priv->pinstate_uhs; + uhs_en = true; break; default: return -ENOTSUPP; @@ -523,12 +552,19 @@ static int uniphier_sd_start_signal_voltage_switch(struct mmc_host *mmc, else pinctrl_select_default_state(mmc_dev(mmc)); + uniphier_sd_uhs_enable(host, uhs_en); + return 0; } -static int uniphier_sd_uhs_init(struct tmio_mmc_host *host, - struct uniphier_sd_priv *priv) +static int uniphier_sd_uhs_init(struct tmio_mmc_host *host) { + struct uniphier_sd_priv *priv = uniphier_sd_priv(host); + struct device *dev = &host->pdev->dev; + struct device_node *np = dev->of_node; + struct of_phandle_args args; + int ret; + priv->pinctrl = devm_pinctrl_get(mmc_dev(host->mmc)); if (IS_ERR(priv->pinctrl)) return PTR_ERR(priv->pinctrl); @@ -537,8 +573,20 @@ static int uniphier_sd_uhs_init(struct tmio_mmc_host *host, if (IS_ERR(priv->pinstate_uhs)) return PTR_ERR(priv->pinstate_uhs); - host->ops.start_signal_voltage_switch = - uniphier_sd_start_signal_voltage_switch; + ret = of_parse_phandle_with_fixed_args(np, + "socionext,syscon-uhs-mode", + 1, 0, &args); + if (ret) { + dev_err(dev, "Can't get syscon-uhs-mode property\n"); + return ret; + } + priv->sdctrl_regmap = syscon_node_to_regmap(args.np); + of_node_put(args.np); + if (IS_ERR(priv->sdctrl_regmap)) { + dev_err(dev, "Can't map syscon-uhs-mode\n"); + return PTR_ERR(priv->sdctrl_regmap); + } + priv->sdctrl_ch = args.args[0]; return 0; } @@ -601,12 +649,15 @@ static int uniphier_sd_probe(struct platform_device *pdev) } if (host->mmc->caps & MMC_CAP_UHS) { - ret = uniphier_sd_uhs_init(host, priv); + ret = uniphier_sd_uhs_init(host); if (ret) { dev_warn(dev, "failed to setup UHS (error %d). Disabling UHS.", ret); host->mmc->caps &= ~MMC_CAP_UHS; + } else { + host->ops.start_signal_voltage_switch = + uniphier_sd_start_signal_voltage_switch; } } -- cgit v1.2.3 From 2cda1de0cb578900de6ac17a32d1d859f4798b99 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Wed, 25 Jan 2023 10:01:59 +0900 Subject: mmc: uniphier-sd: Add control to switch UHS speed SD interface logic has the register to switch UHS speed. The default is up to SDR25 and to support SDR50 or faster, add uniphier_sd_speed_switch() function to switch the speed mode. Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/20230125010201.28246-4-hayashi.kunihiko@socionext.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/uniphier-sd.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c index c9766d6a690f..61acd69fac0e 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -54,6 +54,7 @@ #define UNIPHIER_SDCTRL_CHOFFSET 0x200 #define UNIPHIER_SDCTRL_MODE 0x30 #define UNIPHIER_SDCTRL_MODE_UHS1MOD BIT(15) +#define UNIPHIER_SDCTRL_MODE_SDRSEL BIT(14) /* * IP is extended to support various features: built-in DMA engine, @@ -429,6 +430,25 @@ static void uniphier_sd_hw_reset(struct mmc_host *mmc) usleep_range(300, 1000); } +static void uniphier_sd_speed_switch(struct tmio_mmc_host *host) +{ + struct uniphier_sd_priv *priv = uniphier_sd_priv(host); + unsigned int offset; + u32 val = 0; + + if (!(host->mmc->caps & MMC_CAP_UHS)) + return; + + if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR50 || + host->mmc->ios.timing == MMC_TIMING_UHS_SDR104) + val = UNIPHIER_SDCTRL_MODE_SDRSEL; + + offset = UNIPHIER_SDCTRL_CHOFFSET * priv->sdctrl_ch + + UNIPHIER_SDCTRL_MODE; + regmap_write_bits(priv->sdctrl_regmap, offset, + UNIPHIER_SDCTRL_MODE_SDRSEL, val); +} + static void uniphier_sd_uhs_enable(struct tmio_mmc_host *host, bool uhs_en) { struct uniphier_sd_priv *priv = uniphier_sd_priv(host); @@ -459,6 +479,8 @@ static void uniphier_sd_set_clock(struct tmio_mmc_host *host, tmp &= ~CLK_CTL_SCLKEN; writel(tmp, host->ctl + (CTL_SD_CARD_CLK_CTL << 1)); + uniphier_sd_speed_switch(host); + if (clock == 0) return; -- cgit v1.2.3 From 1444fed25b59957452da72ab498afe63589f3419 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 31 Jan 2023 09:47:42 +0100 Subject: mmc: core: Imply IOSCHED_BFQ If we enable the MMC/SD block layer, use Kconfig to imply the BFQ I/O scheduler. As all MMC/SD devices are single-queue, this is the scheduler that users want so let's be helpful and make sure it gets default-selected into a manual kernel configuration. It will still need to be enabled at runtime (usually with udev scripts). Cc: linux-block@vger.kernel.org Cc: Paolo Valente Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20230131084742.1038135-1-linus.walleij@linaro.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig index e9e9d31b39b4..bf4e29ef023c 100644 --- a/drivers/mmc/core/Kconfig +++ b/drivers/mmc/core/Kconfig @@ -37,6 +37,7 @@ config PWRSEQ_SIMPLE config MMC_BLOCK tristate "MMC block device driver" depends on BLOCK + imply IOSCHED_BFQ default y help Say Y here to enable the MMC block device driver support. -- cgit v1.2.3 From 80df83c2c57e75cb482ccf0c639ce84703ab41a2 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 4 Feb 2023 00:53:35 +0100 Subject: mmc: core: add devm_mmc_alloc_host Add a device-managed version of mmc_alloc_host(). The argument order is reversed compared to mmc_alloc_host() because device-managed functions typically have the device argument first. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/6d8f9fdc-7c9e-8e4f-e6ef-5470b971c74e@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 26 ++++++++++++++++++++++++++ include/linux/mmc/host.h | 1 + 2 files changed, 27 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index d17eda753b7e..6a7475ad7685 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -588,6 +588,32 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) EXPORT_SYMBOL(mmc_alloc_host); +static void devm_mmc_host_release(struct device *dev, void *res) +{ + mmc_free_host(*(struct mmc_host **)res); +} + +struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra) +{ + struct mmc_host **dr, *host; + + dr = devres_alloc(devm_mmc_host_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return ERR_PTR(-ENOMEM); + + host = mmc_alloc_host(extra, dev); + if (IS_ERR(host)) { + devres_free(dr); + return host; + } + + *dr = host; + devres_add(dev, dr); + + return host; +} +EXPORT_SYMBOL(devm_mmc_alloc_host); + static int mmc_validate_host_caps(struct mmc_host *host) { struct device *dev = host->parent; diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 8fdd3cf971a3..812e6b583b25 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -527,6 +527,7 @@ struct mmc_host { struct device_node; struct mmc_host *mmc_alloc_host(int extra, struct device *); +struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra); int mmc_add_host(struct mmc_host *); void mmc_remove_host(struct mmc_host *); void mmc_free_host(struct mmc_host *); -- cgit v1.2.3 From 418f7c2de1334b70fbee790911a1b46503230137 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sat, 4 Feb 2023 00:54:48 +0100 Subject: mmc: meson-gx: use devm_mmc_alloc_host Use new function devm_mmc_alloc_host() to simplify the code. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/728f159b-885f-c78a-1a3d-f55c245250e1@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-gx-mmc.c | 52 ++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 35 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 6e5ea0213b47..96115bfdf794 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1179,7 +1179,7 @@ static int meson_mmc_probe(struct platform_device *pdev) struct mmc_host *mmc; int ret; - mmc = mmc_alloc_host(sizeof(struct meson_host), &pdev->dev); + mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(struct meson_host)); if (!mmc) return -ENOMEM; host = mmc_priv(mmc); @@ -1195,46 +1195,33 @@ static int meson_mmc_probe(struct platform_device *pdev) host->vqmmc_enabled = false; ret = mmc_regulator_get_supply(mmc); if (ret) - goto free_host; + return ret; ret = mmc_of_parse(mmc); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_warn(&pdev->dev, "error parsing DT: %d\n", ret); - goto free_host; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "error parsing DT\n"); host->data = (struct meson_mmc_data *) of_device_get_match_data(&pdev->dev); - if (!host->data) { - ret = -EINVAL; - goto free_host; - } + if (!host->data) + return -EINVAL; ret = device_reset_optional(&pdev->dev); - if (ret) { - dev_err_probe(&pdev->dev, ret, "device reset failed\n"); - goto free_host; - } + if (ret) + return dev_err_probe(&pdev->dev, ret, "device reset failed\n"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); host->regs = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(host->regs)) { - ret = PTR_ERR(host->regs); - goto free_host; - } + if (IS_ERR(host->regs)) + return PTR_ERR(host->regs); host->irq = platform_get_irq(pdev, 0); - if (host->irq <= 0) { - ret = -EINVAL; - goto free_host; - } + if (host->irq <= 0) + return -EINVAL; host->pinctrl = devm_pinctrl_get(&pdev->dev); - if (IS_ERR(host->pinctrl)) { - ret = PTR_ERR(host->pinctrl); - goto free_host; - } + if (IS_ERR(host->pinctrl)) + return PTR_ERR(host->pinctrl); host->pins_clk_gate = pinctrl_lookup_state(host->pinctrl, "clk-gate"); @@ -1245,14 +1232,12 @@ static int meson_mmc_probe(struct platform_device *pdev) } host->core_clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(host->core_clk)) { - ret = PTR_ERR(host->core_clk); - goto free_host; - } + if (IS_ERR(host->core_clk)) + return PTR_ERR(host->core_clk); ret = clk_prepare_enable(host->core_clk); if (ret) - goto free_host; + return ret; ret = meson_mmc_clk_init(host); if (ret) @@ -1347,8 +1332,6 @@ err_init_clk: clk_disable_unprepare(host->mmc_clk); err_core_clk: clk_disable_unprepare(host->core_clk); -free_host: - mmc_free_host(mmc); return ret; } @@ -1365,7 +1348,6 @@ static int meson_mmc_remove(struct platform_device *pdev) clk_disable_unprepare(host->mmc_clk); clk_disable_unprepare(host->core_clk); - mmc_free_host(host->mmc); return 0; } -- cgit v1.2.3 From 152b5245e361f9493d930a1aaa51c932e3dff660 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Tue, 31 Jan 2023 21:02:29 +0000 Subject: mmc: jz4740: Add support for vqmmc power supply Support enabling / disabling the vqmmc power supply if it was provided by the firmware. Provide the .start_signal_voltage_switch callback to change the voltage of the external vqmmc power supply. Signed-off-by: Paul Cercueil Link: https://lore.kernel.org/r/20230131210229.68129-2-paul@crapouillou.net Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index eda1e2ddcaca..698450afa7bb 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -158,6 +159,8 @@ struct jz4740_mmc_host { struct mmc_request *req; struct mmc_command *cmd; + bool vqmmc_enabled; + unsigned long waiting; uint32_t cmdat; @@ -935,6 +938,8 @@ static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct jz4740_mmc_host *host = mmc_priv(mmc); + int ret; + if (ios->clock) jz4740_mmc_set_clock_rate(host, ios->clock); @@ -947,12 +952,25 @@ static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) clk_prepare_enable(host->clk); break; case MMC_POWER_ON: + if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) { + ret = regulator_enable(mmc->supply.vqmmc); + if (ret) + dev_err(&host->pdev->dev, "Failed to set vqmmc power!\n"); + else + host->vqmmc_enabled = true; + } break; - default: + case MMC_POWER_OFF: if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); + if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) { + regulator_disable(mmc->supply.vqmmc); + host->vqmmc_enabled = false; + } clk_disable_unprepare(host->clk); break; + default: + break; } switch (ios->bus_width) { @@ -978,6 +996,23 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); } +static int jz4740_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios) +{ + int ret; + + /* vqmmc regulator is available */ + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = mmc_regulator_set_vqmmc(mmc, ios); + return ret < 0 ? ret : 0; + } + + /* no vqmmc regulator, assume fixed regulator at 3/3.3V */ + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) + return 0; + + return -EINVAL; +} + static const struct mmc_host_ops jz4740_mmc_ops = { .request = jz4740_mmc_request, .pre_req = jz4740_mmc_pre_request, @@ -986,6 +1021,7 @@ static const struct mmc_host_ops jz4740_mmc_ops = { .get_ro = mmc_gpio_get_ro, .get_cd = mmc_gpio_get_cd, .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, + .start_signal_voltage_switch = jz4740_voltage_switch, }; static const struct of_device_id jz4740_mmc_of_match[] = { -- cgit v1.2.3 From b75a52b0dda353aeefb4830a320589a363f49579 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Thu, 2 Feb 2023 08:35:16 +0800 Subject: mmc: sdhci-of-dwcmshc: Update DLL and pre-change delay for rockchip platform For Rockchip platform, DLL bypass bit and start bit need to be set if DLL is not locked. And adjust pre-change delay to 0x3 for better signal test result. Signed-off-by: Shawn Lin Link: https://lore.kernel.org/r/1675298118-64243-2-git-send-email-shawn.lin@rock-chips.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 49338670c89f..46b1ce7fabdc 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -48,6 +48,7 @@ #define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL 29 #define DWCMSHC_EMMC_DLL_START_POINT 16 #define DWCMSHC_EMMC_DLL_INC 8 +#define DWCMSHC_EMMC_DLL_BYPASS BIT(24) #define DWCMSHC_EMMC_DLL_DLYENA BIT(27) #define DLL_TXCLK_TAPNUM_DEFAULT 0x10 #define DLL_TXCLK_TAPNUM_90_DEGREES 0xA @@ -60,6 +61,7 @@ #define DLL_RXCLK_NO_INVERTER 1 #define DLL_RXCLK_INVERTER 0 #define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8 +#define DLL_RXCLK_ORI_GATE BIT(31) #define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24) #define DLL_CMDOUT_SRC_CLK_NEG BIT(28) #define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29) @@ -234,9 +236,12 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock sdhci_writel(host, extra, reg); if (clock <= 52000000) { - /* Disable DLL and reset both of sample and drive clock */ - sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); - sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK); + /* + * Disable DLL and reset both of sample and drive clock. + * The bypass bit and start bit need to be set if DLL is not locked. + */ + sdhci_writel(host, DWCMSHC_EMMC_DLL_BYPASS | DWCMSHC_EMMC_DLL_START, DWCMSHC_EMMC_DLL_CTRL); + sdhci_writel(host, DLL_RXCLK_ORI_GATE, DWCMSHC_EMMC_DLL_RXCLK); sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT); /* @@ -279,7 +284,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock } extra = 0x1 << 16 | /* tune clock stop en */ - 0x2 << 17 | /* pre-change delay */ + 0x3 << 17 | /* pre-change delay */ 0x3 << 19; /* post-change delay */ sdhci_writel(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); -- cgit v1.2.3 From cfd4ea4815d16f7426723a5cb80ee5db811ea1f2 Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Thu, 2 Feb 2023 10:29:15 -0500 Subject: mmc: sdhci-of-dwcmshc: add the missing device table IDs for acpi This commit adds the missing MODULE_DEVICE_TABLE for acpi, or else it won't be loaded automatically when compiled as a kernel module. Reviewed-by: David Thompson Signed-off-by: Liming Sun Link: https://lore.kernel.org/r/f57ad0f8fdf663465bca74467c344dfa305a3199.1675305696.git.limings@nvidia.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 46b1ce7fabdc..d1490469184b 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -451,6 +451,7 @@ static const struct acpi_device_id sdhci_dwcmshc_acpi_ids[] = { }, {} }; +MODULE_DEVICE_TABLE(acpi, sdhci_dwcmshc_acpi_ids); #endif static int dwcmshc_probe(struct platform_device *pdev) -- cgit v1.2.3 From 08623d741e94dc9ca033ecb8d04312960a105377 Mon Sep 17 00:00:00 2001 From: Ye Xingchen Date: Fri, 10 Feb 2023 16:28:32 +0800 Subject: mmc: sdhci-brcmstb: Use devm_platform_get_and_ioremap_resource() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Ye Xingchen Link: https://lore.kernel.org/r/202302101628321403257@zte.com.cn Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-brcmstb.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 0a19b7af1d41..4c22337199cf 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -255,7 +255,6 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) struct sdhci_brcmstb_priv *priv; u32 actual_clock_mhz; struct sdhci_host *host; - struct resource *iomem; struct clk *clk; struct clk *base_clk = NULL; int res; @@ -291,8 +290,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) } /* Map in the non-standard CFG registers */ - iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1); - priv->cfg_regs = devm_ioremap_resource(&pdev->dev, iomem); + priv->cfg_regs = devm_platform_get_and_ioremap_resource(pdev, 1, NULL); if (IS_ERR(priv->cfg_regs)) { res = PTR_ERR(priv->cfg_regs); goto err; -- cgit v1.2.3 From 16b492ce0a55735ed6f4c19ee9fb767badb3f19a Mon Sep 17 00:00:00 2001 From: Sergei Antonov Date: Fri, 10 Feb 2023 17:38:43 +0300 Subject: mmc: moxart: set maximum request/block/segment sizes Per datasheet: maximum block length is 2048 bytes, data length field is in bits 0-23 of the Data Length Register. Also for DMA mode we have to take into account rx/tx buffers' sizes. In my tests this change doubles SD card I/O performance on big files. Before the change Linux used default request size of 4 KB. Signed-off-by: Sergei Antonov Link: https://lore.kernel.org/r/20230210143843.369943-1-saproj@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/moxart-mmc.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index 52ed30f2d9f4..2d002c81dcf3 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -611,6 +611,9 @@ static int moxart_probe(struct platform_device *pdev) mmc->f_max = DIV_ROUND_CLOSEST(host->sysclk, 2); mmc->f_min = DIV_ROUND_CLOSEST(host->sysclk, CLK_DIV_MASK * 2); mmc->ocr_avail = 0xffff00; /* Support 2.0v - 3.6v power. */ + mmc->max_blk_size = 2048; /* Max. block length in REG_DATA_CONTROL */ + mmc->max_req_size = DATA_LEN_MASK; /* bits 0-23 in REG_DATA_LENGTH */ + mmc->max_blk_count = mmc->max_req_size / 512; if (IS_ERR(host->dma_chan_tx) || IS_ERR(host->dma_chan_rx)) { if (PTR_ERR(host->dma_chan_tx) == -EPROBE_DEFER || @@ -628,6 +631,8 @@ static int moxart_probe(struct platform_device *pdev) } dev_dbg(dev, "PIO mode transfer enabled\n"); host->have_dma = false; + + mmc->max_seg_size = mmc->max_req_size; } else { dev_dbg(dev, "DMA channels found (%p,%p)\n", host->dma_chan_tx, host->dma_chan_rx); @@ -646,6 +651,10 @@ static int moxart_probe(struct platform_device *pdev) cfg.src_addr = host->reg_phys + REG_DATA_WINDOW; cfg.dst_addr = 0; dmaengine_slave_config(host->dma_chan_rx, &cfg); + + mmc->max_seg_size = min3(mmc->max_req_size, + dma_get_max_seg_size(host->dma_chan_rx->device->dev), + dma_get_max_seg_size(host->dma_chan_tx->device->dev)); } if (readl(host->base + REG_BUS_WIDTH) & BUS_WIDTH_4_SUPPORT) -- cgit v1.2.3 From 91a3cba783f11c09b11476e64f432b39c933d7e8 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Sun, 12 Feb 2023 22:43:32 +0100 Subject: mmc: meson-gx: remove meson_mmc_get_cd MMC core only checks whether return value of .get_cd() equals zero. Therefore -ENOSYS and 1 are effectively the same and the function can be removed. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/16502040-3beb-a3cc-b28d-28184fba0f10@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-gx-mmc.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 6a63592ec121..f4b50793eece 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1083,20 +1083,6 @@ static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id) return IRQ_HANDLED; } -/* - * NOTE: we only need this until the GPIO/pinctrl driver can handle - * interrupts. For now, the MMC core will use this for polling. - */ -static int meson_mmc_get_cd(struct mmc_host *mmc) -{ - int status = mmc_gpio_get_cd(mmc); - - if (status == -ENOSYS) - return 1; /* assume present */ - - return status; -} - static void meson_mmc_cfg_init(struct meson_host *host) { u32 cfg = 0; @@ -1165,7 +1151,7 @@ static void meson_mmc_ack_sdio_irq(struct mmc_host *mmc) static const struct mmc_host_ops meson_mmc_ops = { .request = meson_mmc_request, .set_ios = meson_mmc_set_ios, - .get_cd = meson_mmc_get_cd, + .get_cd = mmc_gpio_get_cd, .pre_req = meson_mmc_pre_req, .post_req = meson_mmc_post_req, .execute_tuning = meson_mmc_resampling_tuning, -- cgit v1.2.3 From 51f5b3056790bc0518e49587996f1e6f3058cca9 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Mon, 13 Feb 2023 14:37:07 +0100 Subject: mmc: core: Align to common busy polling behaviour for mmc ioctls Let's align to the common busy polling behaviour for mmc ioctls, by updating the below two corresponding parts, that comes into play when using an R1B response for a command. *) A command with an R1B response should be prepared by calling mmc_prepare_busy_cmd(), which make us respects the host's busy timeout constraints. **) When an R1B response is being used and the host also supports HW busy detection, we should skip to poll for busy completion. Suggested-by: Christian Loehle Signed-off-by: Ulf Hansson Reviewed-by: Christian Loehle Link: https://lore.kernel.org/r/20230213133707.27857-1-ulf.hansson@linaro.org --- drivers/mmc/core/block.c | 25 +++++++++++++++++-------- drivers/mmc/core/mmc_ops.c | 1 + 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 20da7ed43e6d..672ab90c4b2d 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -470,6 +470,8 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, struct mmc_data data = {}; struct mmc_request mrq = {}; struct scatterlist sg; + bool r1b_resp, use_r1b_resp = false; + unsigned int busy_timeout_ms; int err; unsigned int target_part; @@ -545,6 +547,13 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, (cmd.opcode == MMC_SWITCH)) return mmc_sanitize(card, idata->ic.cmd_timeout_ms); + /* If it's an R1B response we need some more preparations. */ + busy_timeout_ms = idata->ic.cmd_timeout_ms ? : MMC_BLK_TIMEOUT_MS; + r1b_resp = (cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B; + if (r1b_resp) + use_r1b_resp = mmc_prepare_busy_cmd(card->host, &cmd, + busy_timeout_ms); + mmc_wait_for_req(card->host, &mrq); memcpy(&idata->ic.response, cmd.resp, sizeof(cmd.resp)); @@ -596,14 +605,14 @@ static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md, if (idata->ic.postsleep_min_us) usleep_range(idata->ic.postsleep_min_us, idata->ic.postsleep_max_us); - if (idata->rpmb || (cmd.flags & MMC_RSP_R1B) == MMC_RSP_R1B) { - /* - * Ensure RPMB/R1B command has completed by polling CMD13 "Send Status". Here we - * allow to override the default timeout value if a custom timeout is specified. - */ - err = mmc_poll_for_busy(card, idata->ic.cmd_timeout_ms ? : MMC_BLK_TIMEOUT_MS, - false, MMC_BUSY_IO); - } + /* No need to poll when using HW busy detection. */ + if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) + return 0; + + /* Ensure RPMB/R1B command has completed by polling with CMD13. */ + if (idata->rpmb || r1b_resp) + err = mmc_poll_for_busy(card, busy_timeout_ms, false, + MMC_BUSY_IO); return err; } diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index 81c55bfd6e0c..3b3adbddf664 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -575,6 +575,7 @@ bool mmc_prepare_busy_cmd(struct mmc_host *host, struct mmc_command *cmd, cmd->busy_timeout = timeout_ms; return true; } +EXPORT_SYMBOL_GPL(mmc_prepare_busy_cmd); /** * __mmc_switch - modify EXT_CSD register -- cgit v1.2.3 From 9e622229bbf4f2204f7a4bc47f9dffb2307e4949 Mon Sep 17 00:00:00 2001 From: William Qiu Date: Wed, 15 Feb 2023 19:32:47 +0800 Subject: mmc: starfive: Add sdio/emmc driver support Add sdio/emmc driver support for StarFive JH7110 soc. Tested-by: Conor Dooley Signed-off-by: William Qiu Link: https://lore.kernel.org/r/20230215113249.47727-3-william.qiu@starfivetech.com Signed-off-by: Ulf Hansson --- MAINTAINERS | 6 ++ drivers/mmc/host/Kconfig | 10 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-starfive.c | 186 +++++++++++++++++++++++++++++++++++++ 4 files changed, 203 insertions(+) create mode 100644 drivers/mmc/host/dw_mmc-starfive.c (limited to 'drivers/mmc') diff --git a/MAINTAINERS b/MAINTAINERS index f781f936ae35..94bde3a1db1e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19911,6 +19911,12 @@ F: Documentation/devicetree/bindings/clock/starfive,jh7100-*.yaml F: drivers/clk/starfive/clk-starfive-jh7100* F: include/dt-bindings/clock/starfive-jh7100*.h +STARFIVE JH7110 MMC/SD/SDIO DRIVER +M: William Qiu +S: Supported +F: Documentation/devicetree/bindings/mmc/starfive* +F: drivers/mmc/host/dw_mmc-starfive.c + STARFIVE JH7100 PINCTRL DRIVER M: Emil Renner Berthing L: linux-gpio@vger.kernel.org diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index b9e9185c86a6..f4d2d1a22d19 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -881,6 +881,16 @@ 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_STARFIVE + tristate "StarFive specific extensions for Synopsys DW Memory Card Interface" + depends on SOC_STARFIVE + depends on MMC_DW + select MMC_DW_PLTFM + help + This selects support for StarFive JH7110 SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on StarFive JH7110 SoC. + 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 ba0c6d0cd85d..47ae1b2c6056 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -56,6 +56,7 @@ 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_STARFIVE) += dw_mmc-starfive.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-starfive.c b/drivers/mmc/host/dw_mmc-starfive.c new file mode 100644 index 000000000000..40f5969b07a6 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-starfive.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * StarFive Designware Mobile Storage Host Controller Driver + * + * Copyright (c) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dw_mmc.h" +#include "dw_mmc-pltfm.h" + +#define ALL_INT_CLR 0x1ffff +#define MAX_DELAY_CHAIN 32 + +struct starfive_priv { + struct device *dev; + struct regmap *reg_syscon; + u32 syscon_offset; + u32 syscon_shift; + u32 syscon_mask; +}; + +static void dw_mci_starfive_set_ios(struct dw_mci *host, struct mmc_ios *ios) +{ + int ret; + unsigned int clock; + + if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50) { + clock = (ios->clock > 50000000 && ios->clock <= 52000000) ? 100000000 : ios->clock; + ret = clk_set_rate(host->ciu_clk, clock); + if (ret) + dev_dbg(host->dev, "Use an external frequency divider %uHz\n", ios->clock); + host->bus_hz = clk_get_rate(host->ciu_clk); + } else { + dev_dbg(host->dev, "Using the internal divider\n"); + } +} + +static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, + u32 opcode) +{ + static const int grade = MAX_DELAY_CHAIN; + struct dw_mci *host = slot->host; + struct starfive_priv *priv = host->priv; + int rise_point = -1, fall_point = -1; + int err, prev_err; + int i; + bool found = 0; + u32 regval; + + /* + * Use grade as the max delay chain, and use the rise_point and + * fall_point to ensure the best sampling point of a data input + * signals. + */ + for (i = 0; i < grade; i++) { + regval = i << priv->syscon_shift; + err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset, + priv->syscon_mask, regval); + if (err) + return err; + mci_writel(host, RINTSTS, ALL_INT_CLR); + + err = mmc_send_tuning(slot->mmc, opcode, NULL); + if (!err) + found = 1; + + if (i > 0) { + if (err && !prev_err) + fall_point = i - 1; + if (!err && prev_err) + rise_point = i; + } + + if (rise_point != -1 && fall_point != -1) + goto tuning_out; + + prev_err = err; + err = 0; + } + +tuning_out: + if (found) { + if (rise_point == -1) + rise_point = 0; + if (fall_point == -1) + fall_point = grade - 1; + if (fall_point < rise_point) { + if ((rise_point + fall_point) > + (grade - 1)) + i = fall_point / 2; + else + i = (rise_point + grade - 1) / 2; + } else { + i = (rise_point + fall_point) / 2; + } + + regval = i << priv->syscon_shift; + err = regmap_update_bits(priv->reg_syscon, priv->syscon_offset, + priv->syscon_mask, regval); + if (err) + return err; + mci_writel(host, RINTSTS, ALL_INT_CLR); + + dev_info(host->dev, "Found valid delay chain! use it [delay=%d]\n", i); + } else { + dev_err(host->dev, "No valid delay chain! use default\n"); + err = -EINVAL; + } + + mci_writel(host, RINTSTS, ALL_INT_CLR); + return err; +} + +static int dw_mci_starfive_parse_dt(struct dw_mci *host) +{ + struct of_phandle_args args; + struct starfive_priv *priv; + int ret; + + priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ret = of_parse_phandle_with_fixed_args(host->dev->of_node, + "starfive,sysreg", 3, 0, &args); + if (ret) { + dev_err(host->dev, "Failed to parse starfive,sysreg\n"); + return -EINVAL; + } + + priv->reg_syscon = syscon_node_to_regmap(args.np); + of_node_put(args.np); + if (IS_ERR(priv->reg_syscon)) + return PTR_ERR(priv->reg_syscon); + + priv->syscon_offset = args.args[0]; + priv->syscon_shift = args.args[1]; + priv->syscon_mask = args.args[2]; + + host->priv = priv; + + return 0; +} + +static const struct dw_mci_drv_data starfive_data = { + .common_caps = MMC_CAP_CMD23, + .set_ios = dw_mci_starfive_set_ios, + .parse_dt = dw_mci_starfive_parse_dt, + .execute_tuning = dw_mci_starfive_execute_tuning, +}; + +static const struct of_device_id dw_mci_starfive_match[] = { + { .compatible = "starfive,jh7110-mmc", + .data = &starfive_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, dw_mci_starfive_match); + +static int dw_mci_starfive_probe(struct platform_device *pdev) +{ + return dw_mci_pltfm_register(pdev, &starfive_data); +} + +static struct platform_driver dw_mci_starfive_driver = { + .probe = dw_mci_starfive_probe, + .remove = dw_mci_pltfm_remove, + .driver = { + .name = "dwmmc_starfive", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = dw_mci_starfive_match, + }, +}; +module_platform_driver(dw_mci_starfive_driver); + +MODULE_DESCRIPTION("StarFive JH7110 Specific DW-MSHC Driver Extension"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dwmmc_starfive"); -- cgit v1.2.3 From 88f94c782b0ee2297685764f942e7c176d6b89aa Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 14 Feb 2023 22:41:19 +0100 Subject: mmc: core: support setting card detect interrupt from drivers On certain platforms like Amlogic Meson gpiod_to_irq() isn't supported due to the design of gpio / interrupt controller. Therefore provide an option for drivers to pass the card detect interrupt number (retrieved e.g. from device tree) to mmc core. Suggested-by refers to the mechanism to pass and store the interrupt. Suggested-by: Ulf Hansson Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/5777f38b-465f-ce48-a87f-5eb8b3c57b0a@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/slot-gpio.c | 17 ++++++++++++++++- include/linux/mmc/slot-gpio.h | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index dd2a4b6ab6ad..2a2d949a9344 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -23,6 +23,7 @@ struct mmc_gpio { char *ro_label; char *cd_label; u32 cd_debounce_delay_ms; + int cd_irq; }; static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) @@ -53,12 +54,24 @@ int mmc_gpio_alloc(struct mmc_host *host) ctx->ro_label = devm_kasprintf(host->parent, GFP_KERNEL, "%s ro", devname); if (!ctx->ro_label) return -ENOMEM; + ctx->cd_irq = -EINVAL; host->slot.handler_priv = ctx; host->slot.cd_irq = -EINVAL; return 0; } +void mmc_gpio_set_cd_irq(struct mmc_host *host, int irq) +{ + struct mmc_gpio *ctx = host->slot.handler_priv; + + if (!ctx || irq < 0) + return; + + ctx->cd_irq = irq; +} +EXPORT_SYMBOL(mmc_gpio_set_cd_irq); + int mmc_gpio_get_ro(struct mmc_host *host) { struct mmc_gpio *ctx = host->slot.handler_priv; @@ -98,7 +111,9 @@ void mmc_gpiod_request_cd_irq(struct mmc_host *host) * Do not use IRQ if the platform prefers to poll, e.g., because that * IRQ number is already used by another unit and cannot be shared. */ - if (!(host->caps & MMC_CAP_NEEDS_POLL)) + if (ctx->cd_irq >= 0) + irq = ctx->cd_irq; + else if (!(host->caps & MMC_CAP_NEEDS_POLL)) irq = gpiod_to_irq(ctx->cd_gpio); if (irq >= 0) { diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h index 4ae2f2908f99..5d3d15e97868 100644 --- a/include/linux/mmc/slot-gpio.h +++ b/include/linux/mmc/slot-gpio.h @@ -15,6 +15,7 @@ struct mmc_host; int mmc_gpio_get_ro(struct mmc_host *host); int mmc_gpio_get_cd(struct mmc_host *host); +void mmc_gpio_set_cd_irq(struct mmc_host *host, int irq); int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id, unsigned int idx, bool override_active_level, unsigned int debounce); -- cgit v1.2.3 From a543f7024e35ea548dd483f06d4c871243663622 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Tue, 14 Feb 2023 22:44:03 +0100 Subject: mmc: meson-gx: support platform interrupt as card detect interrupt Use a new mmc core feature and support specifying the card detect gpio interrupt in device tree. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/2bb70611-5dea-1144-51bd-93c46b455392@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-gx-mmc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index f4b50793eece..079c52dbeca6 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1166,7 +1166,7 @@ static int meson_mmc_probe(struct platform_device *pdev) struct resource *res; struct meson_host *host; struct mmc_host *mmc; - int ret; + int cd_irq, ret; mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(struct meson_host)); if (!mmc) @@ -1213,6 +1213,9 @@ static int meson_mmc_probe(struct platform_device *pdev) if (host->irq <= 0) return -EINVAL; + cd_irq = platform_get_irq_optional(pdev, 1); + mmc_gpio_set_cd_irq(mmc, cd_irq); + host->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(host->pinctrl)) return PTR_ERR(host->pinctrl); -- cgit v1.2.3 From 71d04535e853305a76853b28a01512a62006351d Mon Sep 17 00:00:00 2001 From: Yang Yingliang Date: Fri, 17 Feb 2023 10:43:33 +0800 Subject: mmc: core: fix return value check in devm_mmc_alloc_host() mmc_alloc_host() returns NULL pointer not PTR_ERR(), if it fails, so replace the IS_ERR() check with NULL pointer check. In commit 418f7c2de133 ("mmc: meson-gx: use devm_mmc_alloc_host"), it checks NULL pointer not PTR_ERR, if devm_mmc_alloc_host() fails, so make it to return NULL pointer to keep same with mmc_alloc_host(), the drivers don't need to change the error handle when switch to use devm_mmc_alloc_host(). Fixes: 80df83c2c57e ("mmc: core: add devm_mmc_alloc_host") Signed-off-by: Yang Yingliang Reviewed-by: Heiner Kallweit Link: https://lore.kernel.org/r/20230217024333.4018279-1-yangyingliang@huawei.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 6a7475ad7685..096093f7be00 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -599,12 +599,12 @@ struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra) dr = devres_alloc(devm_mmc_host_release, sizeof(*dr), GFP_KERNEL); if (!dr) - return ERR_PTR(-ENOMEM); + return NULL; host = mmc_alloc_host(extra, dev); - if (IS_ERR(host)) { + if (!host) { devres_free(dr); - return host; + return NULL; } *dr = host; -- cgit v1.2.3 From 4c4fe4f0bb02a46f226db88cb1c7e7586e16fc37 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 15 Feb 2023 20:58:29 +0100 Subject: mmc: meson-gx: use devm_clk_get_enabled() for core clock Use devm_clk_get_enabled() to simplify the code. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/b316c6ba-a373-f1d2-27d2-9add5e25a9d2@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-gx-mmc.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 079c52dbeca6..6ed95c55545f 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -155,7 +155,6 @@ struct meson_host { struct mmc_command *cmd; void __iomem *regs; - struct clk *core_clk; struct clk *mux_clk; struct clk *mmc_clk; unsigned long req_rate; @@ -1166,6 +1165,7 @@ static int meson_mmc_probe(struct platform_device *pdev) struct resource *res; struct meson_host *host; struct mmc_host *mmc; + struct clk *core_clk; int cd_irq, ret; mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(struct meson_host)); @@ -1228,17 +1228,13 @@ static int meson_mmc_probe(struct platform_device *pdev) host->pins_clk_gate = NULL; } - host->core_clk = devm_clk_get(&pdev->dev, "core"); - if (IS_ERR(host->core_clk)) - return PTR_ERR(host->core_clk); - - ret = clk_prepare_enable(host->core_clk); - if (ret) - return ret; + core_clk = devm_clk_get_enabled(&pdev->dev, "core"); + if (IS_ERR(core_clk)) + return PTR_ERR(core_clk); ret = meson_mmc_clk_init(host); if (ret) - goto err_core_clk; + return ret; /* set config to sane default */ meson_mmc_cfg_init(host); @@ -1322,8 +1318,6 @@ err_free_irq: free_irq(host->irq, host); err_init_clk: clk_disable_unprepare(host->mmc_clk); -err_core_clk: - clk_disable_unprepare(host->core_clk); return ret; } @@ -1338,7 +1332,6 @@ static int meson_mmc_remove(struct platform_device *pdev) free_irq(host->irq, host); clk_disable_unprepare(host->mmc_clk); - clk_disable_unprepare(host->core_clk); return 0; } -- cgit v1.2.3 From c5a66dd8926baa60235bc8844925c36a1b83f488 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Wed, 15 Feb 2023 21:01:43 +0100 Subject: mmc: meson-gx: constify member data of struct meson_host Constify member data of struct meson_host. This also allows to remove the cast as of_device_get_match_data() returns a const void *. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/70e5520f-e327-111d-9ea4-824460e41561@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-gx-mmc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 6ed95c55545f..641ea4292cf6 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -150,7 +150,7 @@ struct sd_emmc_desc { struct meson_host { struct device *dev; - struct meson_mmc_data *data; + const struct meson_mmc_data *data; struct mmc_host *mmc; struct mmc_command *cmd; @@ -1195,8 +1195,7 @@ static int meson_mmc_probe(struct platform_device *pdev) if (mmc->caps & MMC_CAP_SDIO_IRQ) mmc->caps2 |= MMC_CAP2_SDIO_IRQ_NOTHREAD; - host->data = (struct meson_mmc_data *) - of_device_get_match_data(&pdev->dev); + host->data = of_device_get_match_data(&pdev->dev); if (!host->data) return -EINVAL; -- cgit v1.2.3 From 571f235163ac83407e212b78719175236962aede Mon Sep 17 00:00:00 2001 From: Yang Li Date: Fri, 17 Feb 2023 16:30:05 +0800 Subject: mmc: meson-gx: Use devm_platform_get_and_ioremap_resource() Convert platform_get_resource(), devm_ioremap_resource() to a single call to devm_platform_get_and_ioremap_resource(), as this is exactly what this function does. Signed-off-by: Yang Li Link: https://lore.kernel.org/r/20230217083005.128668-1-yang.lee@linux.alibaba.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/meson-gx-mmc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/mmc') diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 641ea4292cf6..2b963a81c2ad 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -1203,8 +1203,7 @@ static int meson_mmc_probe(struct platform_device *pdev) if (ret) return dev_err_probe(&pdev->dev, ret, "device reset failed\n"); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - host->regs = devm_ioremap_resource(&pdev->dev, res); + host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(host->regs)) return PTR_ERR(host->regs); -- cgit v1.2.3