From 734401c84172520b54f3fe75839dc82b646c7d88 Mon Sep 17 00:00:00 2001 From: Andrea della Porta Date: Thu, 30 May 2024 12:11:58 +0200 Subject: [PATCH 01/26] dt-bindings: arm: bcm: Add BCM2712 SoC support The BCM2712 SoC is found on Raspberry Pi 5. Add compatible string to acknowledge its new chipset. Signed-off-by: Andrea della Porta Reviewed-by: Stefan Wahren Acked-by: Conor Dooley Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/cfc4db17981ef946a71d40d522118a560aa0f15b.1717061147.git.andrea.porta@suse.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml b/Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml index 162a39dab218..e4ff71f006b8 100644 --- a/Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml +++ b/Documentation/devicetree/bindings/arm/bcm/bcm2835.yaml @@ -23,6 +23,12 @@ properties: - raspberrypi,4-model-b - const: brcm,bcm2711 + - description: BCM2712 based Boards + items: + - enum: + - raspberrypi,5-model-b + - const: brcm,bcm2712 + - description: BCM2835 based Boards items: - enum: From ad177268cb15950f86e44717cf500bbfe2e8be43 Mon Sep 17 00:00:00 2001 From: Andrea della Porta Date: Thu, 30 May 2024 12:11:59 +0200 Subject: [PATCH 02/26] dt-bindings: mmc: Add support for BCM2712 SD host controller The BCM2712 has an SDHCI capable host interface similar to the one found in other STB chipsets. Add the relevant compatible string. Signed-off-by: Andrea della Porta Reviewed-by: Stefan Wahren Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/f6e0d1fbc6c5e620897b5c65c95147ceaeed1485.1717061147.git.andrea.porta@suse.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml index cbd3d6c6c77f..eee6be7a7867 100644 --- a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml +++ b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml @@ -20,6 +20,7 @@ properties: - const: brcm,sdhci-brcmstb - items: - enum: + - brcm,bcm2712-sdhci - brcm,bcm74165b0-sdhci - brcm,bcm7445-sdhci - brcm,bcm7425-sdhci From 78d08697e3932052d8855821d5c0864666bfa832 Mon Sep 17 00:00:00 2001 From: Andrea della Porta Date: Thu, 30 May 2024 12:12:00 +0200 Subject: [PATCH 03/26] mmc: sdhci-brcmstb: Add BCM2712 support Broadcom BCM2712 SoC has an SDHCI card controller using the SDIO CFG register block present on other STB chips. Add support for BCM2712 SD capabilities of this chipset. The silicon is SD Express capable but this driver port does not currently include that feature yet. Based on downstream driver by raspberry foundation maintained kernel. Signed-off-by: Andrea della Porta Reviewed-by: Stefan Wahren Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/ad2ec39c62c2783dd5de4bf2ec581866e822e2b1.1717061147.git.andrea.porta@suse.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-brcmstb.c | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 9053526fa212..db1c7f5cd5fd 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -30,6 +30,21 @@ #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200 +#define SDIO_CFG_CQ_CAPABILITY 0x4c +#define SDIO_CFG_CQ_CAPABILITY_FMUL GENMASK(13, 12) + +#define SDIO_CFG_CTRL 0x0 +#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31) +#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30) + +#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac +#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31) +#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0) + +#define MMC_CAP_HSE_MASK (MMC_CAP2_HSX00_1_2V | MMC_CAP2_HSX00_1_8V) +/* Select all SD UHS type I SDR speed above 50MB/s */ +#define MMC_CAP_UHS_I_SDR_MASK (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104) + struct sdhci_brcmstb_priv { void __iomem *cfg_regs; unsigned int flags; @@ -38,6 +53,7 @@ struct sdhci_brcmstb_priv { }; struct brcmstb_match_priv { + void (*cfginit)(struct sdhci_host *host); void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios); struct sdhci_ops *ops; const unsigned int flags; @@ -168,6 +184,33 @@ static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host, sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } +static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host); + u32 reg; + + /* + * If we support a speed that requires tuning, + * then select the delay line PHY as the clock source. + */ + if ((host->mmc->caps & MMC_CAP_UHS_I_SDR_MASK) || (host->mmc->caps2 & MMC_CAP_HSE_MASK)) { + reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE); + reg &= ~SDIO_CFG_MAX_50MHZ_MODE_ENABLE; + reg |= SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE; + writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE); + } + + if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) || + (host->mmc->caps & MMC_CAP_NEEDS_POLL)) { + /* Force presence */ + reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_CTRL); + reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV; + reg |= SDIO_CFG_CTRL_SDCD_N_TEST_EN; + writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_CTRL); + } +} + static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc) { sdhci_dumpregs(mmc_priv(mmc)); @@ -200,6 +243,14 @@ static struct sdhci_ops sdhci_brcmstb_ops = { .set_uhs_signaling = sdhci_set_uhs_signaling, }; +static struct sdhci_ops sdhci_brcmstb_ops_2712 = { + .set_clock = sdhci_set_clock, + .set_power = sdhci_set_power_and_bus_voltage, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .set_uhs_signaling = sdhci_set_uhs_signaling, +}; + static struct sdhci_ops sdhci_brcmstb_ops_7216 = { .set_clock = sdhci_brcmstb_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -214,6 +265,11 @@ static struct sdhci_ops sdhci_brcmstb_ops_74165b0 = { .set_uhs_signaling = sdhci_brcmstb_set_uhs_signaling, }; +static const struct brcmstb_match_priv match_priv_2712 = { + .cfginit = sdhci_brcmstb_cfginit_2712, + .ops = &sdhci_brcmstb_ops_2712, +}; + static struct brcmstb_match_priv match_priv_7425 = { .flags = BRCMSTB_MATCH_FLAGS_NO_64BIT | BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT, @@ -238,6 +294,7 @@ static struct brcmstb_match_priv match_priv_74165b0 = { }; static const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = { + { .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 }, { .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 }, { .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 }, { .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 }, @@ -370,6 +427,9 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) (host->mmc->caps2 & MMC_CAP2_HS400_ES)) host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es; + if (match_priv->cfginit) + match_priv->cfginit(host); + /* * Supply the existing CAPS, but clear the UHS modes. This * will allow these modes to be specified by device tree From b81e4cc84bfcf9d06b90701cc3112b7ecbe2ae71 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Thu, 30 May 2024 23:55:47 +0200 Subject: [PATCH 04/26] mmc: sdhci-of-dwcmshc: set CQE irq-handler for rockchip variants The dwcmshc used on Rockchip rk3568 and rk3588 can use cqe, so set the needed irq handler. Tested on a rk3588-tiger SoM with dd, hdparm and fio. fio performance does increase slightly from Run status group 0 (all jobs): READ: bw=209MiB/s (219MB/s), 209MiB/s-209MiB/s (219MB/s-219MB/s), io=4096MiB (4295MB), run=19607-19607msec without CQE to Run status group 0 (all jobs): READ: bw=215MiB/s (225MB/s), 215MiB/s-215MiB/s (225MB/s-225MB/s), io=4096MiB (4295MB), run=19062-19062msec with CQE enabled. Signed-off-by: Heiko Stuebner Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240530215547.2192457-1-heiko@sntech.de Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 39edf04fedcf..e79aa4b3b6c3 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -908,6 +908,7 @@ static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = { .get_max_clock = rk35xx_get_max_clock, .reset = rk35xx_sdhci_reset, .adma_write_desc = dwcmshc_adma_write_desc, + .irq = dwcmshc_cqe_irq_handler, }; static const struct sdhci_ops sdhci_dwcmshc_th1520_ops = { From e83c868521597b27c7c06b9c92fde0ae1869e2a2 Mon Sep 17 00:00:00 2001 From: Naina Mehta Date: Thu, 23 May 2024 17:33:35 +0530 Subject: [PATCH 05/26] dt-bindings: mmc: sdhci-msm: Document the SDX75 compatible Document the compatible for SDHCI on SDX75 SoC. Signed-off-by: Naina Mehta Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240523120337.9530-2-quic_nainmeht@quicinc.com Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-msm.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml index c24c537f62b1..11979b026d21 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml @@ -51,6 +51,7 @@ properties: - qcom,sdm845-sdhci - qcom,sdx55-sdhci - qcom,sdx65-sdhci + - qcom,sdx75-sdhci - qcom,sm6115-sdhci - qcom,sm6125-sdhci - qcom,sm6350-sdhci From 99464ff96a2e14c8293c9a8ccb7d6d7ce6ecf167 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 5 Jun 2024 11:34:46 +0200 Subject: [PATCH 06/26] dt-bindings: mmc: meson-gx: add optional power-domains On newer SoCs, the MMC controller can require a power-domain to operate, add it as optional. Signed-off-by: Neil Armstrong Acked-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240605-topic-amlogic-upstream-bindings-fixes-power-domains-mmc-v1-1-4acbb8cc2626@linaro.org Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml index bc403ae9e5d9..57646575a13f 100644 --- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml @@ -51,6 +51,9 @@ properties: set when controller's internal DMA engine cannot access the DRAM memory, like on the G12A dedicated SDIO controller. + power-domains: + maxItems: 1 + required: - compatible - reg From 8eb57fd069e7b12bd585d6c831beae8403a181ae Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Tue, 11 Jun 2024 08:50:42 -0700 Subject: [PATCH 07/26] mmc: add missing MODULE_DESCRIPTION() macros make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/mmc/host/of_mmc_spi.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/mmc/host/tmio_mmc_core.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/mmc/host/renesas_sdhi_core.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/mmc/core/mmc_core.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/mmc/core/pwrseq_simple.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/mmc/core/pwrseq_sd8787.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/mmc/core/pwrseq_emmc.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/mmc/core/sdio_uart.o Add the missing invocations of the MODULE_DESCRIPTION() macro. Reviewed-by: Wolfram Sang # for TMIO and SDHI Signed-off-by: Jeff Johnson Reviewed-by: Dragan Simic Link: https://lore.kernel.org/r/20240611-md-drivers-mmc-v2-1-2ef2cbcdc061@quicinc.com Signed-off-by: Ulf Hansson --- drivers/mmc/core/core.c | 1 + drivers/mmc/core/pwrseq_emmc.c | 1 + drivers/mmc/core/pwrseq_sd8787.c | 1 + drivers/mmc/core/pwrseq_simple.c | 1 + drivers/mmc/core/sdio_uart.c | 1 + drivers/mmc/host/of_mmc_spi.c | 1 + drivers/mmc/host/renesas_sdhi_core.c | 1 + drivers/mmc/host/tmio_mmc_core.c | 1 + 8 files changed, 8 insertions(+) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index a8c17b4cd737..d6c819dd68ed 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2362,4 +2362,5 @@ static void __exit mmc_exit(void) subsys_initcall(mmc_init); module_exit(mmc_exit); +MODULE_DESCRIPTION("MMC core driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/core/pwrseq_emmc.c b/drivers/mmc/core/pwrseq_emmc.c index 3b6d69cefb4e..96fa4c508900 100644 --- a/drivers/mmc/core/pwrseq_emmc.c +++ b/drivers/mmc/core/pwrseq_emmc.c @@ -115,4 +115,5 @@ static struct platform_driver mmc_pwrseq_emmc_driver = { }; module_platform_driver(mmc_pwrseq_emmc_driver); +MODULE_DESCRIPTION("Hardware reset support for eMMC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/core/pwrseq_sd8787.c b/drivers/mmc/core/pwrseq_sd8787.c index 0c5808fc3206..f24bbd68e251 100644 --- a/drivers/mmc/core/pwrseq_sd8787.c +++ b/drivers/mmc/core/pwrseq_sd8787.c @@ -130,4 +130,5 @@ static struct platform_driver mmc_pwrseq_sd8787_driver = { }; module_platform_driver(mmc_pwrseq_sd8787_driver); +MODULE_DESCRIPTION("Power sequence support for Marvell SD8787 BT + Wifi chip"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/core/pwrseq_simple.c b/drivers/mmc/core/pwrseq_simple.c index df9588503ad0..154a8921ae75 100644 --- a/drivers/mmc/core/pwrseq_simple.c +++ b/drivers/mmc/core/pwrseq_simple.c @@ -159,4 +159,5 @@ static struct platform_driver mmc_pwrseq_simple_driver = { }; module_platform_driver(mmc_pwrseq_simple_driver); +MODULE_DESCRIPTION("Simple power sequence management for MMC"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/core/sdio_uart.c b/drivers/mmc/core/sdio_uart.c index 575ebbce378e..6b7471dba3bf 100644 --- a/drivers/mmc/core/sdio_uart.c +++ b/drivers/mmc/core/sdio_uart.c @@ -1162,4 +1162,5 @@ module_init(sdio_uart_init); module_exit(sdio_uart_exit); MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("SDIO UART/GPS driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/of_mmc_spi.c b/drivers/mmc/host/of_mmc_spi.c index bf54776fb26c..05939f30a5ae 100644 --- a/drivers/mmc/host/of_mmc_spi.c +++ b/drivers/mmc/host/of_mmc_spi.c @@ -19,6 +19,7 @@ #include #include +MODULE_DESCRIPTION("OpenFirmware bindings for the MMC-over-SPI driver"); MODULE_LICENSE("GPL"); struct of_mmc_spi { diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 12f4faaaf4ee..58536626e6c5 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -1162,4 +1162,5 @@ void renesas_sdhi_remove(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(renesas_sdhi_remove); +MODULE_DESCRIPTION("Renesas SDHI core driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 93e912afd3ae..c1a4ade5f949 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -1319,4 +1319,5 @@ int tmio_mmc_host_runtime_resume(struct device *dev) EXPORT_SYMBOL_GPL(tmio_mmc_host_runtime_resume); #endif +MODULE_DESCRIPTION("TMIO MMC core driver"); MODULE_LICENSE("GPL v2"); From ce4f2199a099c1394ad2343c0e3393690d0cd08f Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 13 Jun 2024 10:32:05 -0400 Subject: [PATCH 08/26] dt-bindings: mmc: mmc-spi-slot: Change voltage-ranges to uint32-matrix According to common mmc core, voltages-ranges should be matrix. Signed-off-by: Frank Li Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20240613-ls_waring_esdhc-v5-1-36644e2fe21c@nxp.com Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/mmc-spi-slot.yaml | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml index 36acc40c7d18..6e2cdac6a85d 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml +++ b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml @@ -27,17 +27,19 @@ properties: maxItems: 1 voltage-ranges: - $ref: /schemas/types.yaml#/definitions/uint32-array + $ref: /schemas/types.yaml#/definitions/uint32-matrix description: | Two cells are required, first cell specifies minimum slot voltage (mV), second cell specifies maximum slot voltage (mV). items: - - description: | - value for minimum slot voltage in mV - default: 3200 - - description: | - value for maximum slot voltage in mV - default: 3400 + items: + - description: | + value for minimum slot voltage in mV + default: 3200 + - description: | + value for maximum slot voltage in mV + default: 3400 + maxItems: 1 gpios: description: | From 1f14028666386d806d00dd6e95da95327b146ded Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 13 Jun 2024 10:32:06 -0400 Subject: [PATCH 09/26] dt-bindings: mmc: Convert fsl-esdhc.txt to yaml Convert layerscape fsl-esdhc binding doc from txt to yaml format. Addtional change during convert: - Deprecate "sdhci,wp-inverted", "sdhci,1-bit-only". - Add "reg" and "interrupts" property. - Change example "sdhci@2e000" to "mmc@2e000". - Compatible string require fsl,-esdhc followed by fsl,esdhc to match most existed dts file. - Set clock-frequency to 100mhz in example. - clock-frequency is not required now. - Allow dma-coherence - Add clocks Reviewed-by: Krzysztof Kozlowski Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20240613-ls_waring_esdhc-v5-2-36644e2fe21c@nxp.com Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/fsl,esdhc.yaml | 105 ++++++++++++++++++ .../devicetree/bindings/mmc/fsl-esdhc.txt | 52 --------- 2 files changed, 105 insertions(+), 52 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml delete mode 100644 Documentation/devicetree/bindings/mmc/fsl-esdhc.txt diff --git a/Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml b/Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml new file mode 100644 index 000000000000..b86ffb53b18b --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/fsl,esdhc.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/fsl,esdhc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale Enhanced Secure Digital Host Controller (eSDHC) + +description: + The Enhanced Secure Digital Host Controller provides an interface + for MMC, SD, and SDIO types of memory cards. + +maintainers: + - Frank Li + +properties: + compatible: + items: + - enum: + - fsl,mpc8536-esdhc + - fsl,mpc8378-esdhc + - fsl,p2020-esdhc + - fsl,p4080-esdhc + - fsl,t1040-esdhc + - fsl,t4240-esdhc + - fsl,ls1012a-esdhc + - fsl,ls1028a-esdhc + - fsl,ls1088a-esdhc + - fsl,ls1043a-esdhc + - fsl,ls1046a-esdhc + - fsl,ls2080a-esdhc + - const: fsl,esdhc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-frequency: + $ref: /schemas/types.yaml#/definitions/uint32 + description: specifies eSDHC base clock frequency. + + sdhci,wp-inverted: + $ref: /schemas/types.yaml#/definitions/flag + deprecated: true + description: + specifies that eSDHC controller reports + inverted write-protect state; New devices should use the generic + "wp-inverted" property. + + sdhci,1-bit-only: + $ref: /schemas/types.yaml#/definitions/flag + deprecated: true + description: + specifies that a controller can only handle + 1-bit data transfers. New devices should use the generic + "bus-width = <1>" property. + + sdhci,auto-cmd12: + $ref: /schemas/types.yaml#/definitions/flag + description: + specifies that a controller can only handle auto CMD12. + + voltage-ranges: + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: specifies minimum slot voltage (mV). + - description: specifies maximum slot voltage (mV). + minItems: 1 + maxItems: 8 + + dma-coherent: true + + little-endian: + $ref: /schemas/types.yaml#/definitions/flag + description: + If the host controller is little-endian mode, specify + this property. The default endian mode is big-endian. + +required: + - compatible + - reg + - interrupts + +allOf: + - $ref: sdhci-common.yaml# + +unevaluatedProperties: false + +examples: + - | + mmc@2e000 { + compatible = "fsl,mpc8378-esdhc", "fsl,esdhc"; + reg = <0x2e000 0x1000>; + interrupts = <42 0x8>; + interrupt-parent = <&ipic>; + /* Filled in by U-Boot */ + clock-frequency = <100000000>; + voltage-ranges = <3300 3300>; + }; diff --git a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt b/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt deleted file mode 100644 index edb8cadb9541..000000000000 --- a/Documentation/devicetree/bindings/mmc/fsl-esdhc.txt +++ /dev/null @@ -1,52 +0,0 @@ -* Freescale Enhanced Secure Digital Host Controller (eSDHC) - -The Enhanced Secure Digital Host Controller provides an interface -for MMC, SD, and SDIO types of memory cards. - -This file documents differences between the core properties described -by mmc.txt and the properties used by the sdhci-esdhc driver. - -Required properties: - - compatible : should be "fsl,esdhc", or "fsl,-esdhc". - Possible compatibles for PowerPC: - "fsl,mpc8536-esdhc" - "fsl,mpc8378-esdhc" - "fsl,p2020-esdhc" - "fsl,p4080-esdhc" - "fsl,t1040-esdhc" - "fsl,t4240-esdhc" - Possible compatibles for ARM: - "fsl,ls1012a-esdhc" - "fsl,ls1028a-esdhc" - "fsl,ls1088a-esdhc" - "fsl,ls1043a-esdhc" - "fsl,ls1046a-esdhc" - "fsl,ls2080a-esdhc" - - clock-frequency : specifies eSDHC base clock frequency. - -Optional properties: - - sdhci,wp-inverted : specifies that eSDHC controller reports - inverted write-protect state; New devices should use the generic - "wp-inverted" property. - - sdhci,1-bit-only : specifies that a controller can only handle - 1-bit data transfers. New devices should use the generic - "bus-width = <1>" property. - - sdhci,auto-cmd12: specifies that a controller can only handle auto - CMD12. - - voltage-ranges : two cells are required, first cell specifies minimum - slot voltage (mV), second cell specifies maximum slot voltage (mV). - Several ranges could be specified. - - little-endian : If the host controller is little-endian mode, specify - this property. The default endian mode is big-endian. - -Example: - -sdhci@2e000 { - compatible = "fsl,mpc8378-esdhc", "fsl,esdhc"; - reg = <0x2e000 0x1000>; - interrupts = <42 0x8>; - interrupt-parent = <&ipic>; - /* Filled in by U-Boot */ - clock-frequency = <0>; - voltage-ranges = <3300 3300>; -}; From 254274cde1f22b3035c72e3f9477e89229e17d53 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 14 Jun 2024 11:00:51 +0300 Subject: [PATCH 10/26] mmc: sdhci: Eliminate SDHCI_QUIRK_UNSTABLE_RO_DETECT SDHCI_QUIRK_UNSTABLE_RO_DETECT is used by only one driver variant. It was added in 2011 by commit 82b0e23a295c ("mmc: sdhci: Fix read-only detection with JMicron 388 chip"). Simplify sdhci by moving the logic to the only place it is used. Signed-off-by: Adrian Hunter Link: https://lore.kernel.org/r/20240614080051.4005-4-adrian.hunter@intel.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 27 ++++++++++++++++++++++----- drivers/mmc/host/sdhci.c | 31 ++++++------------------------- drivers/mmc/host/sdhci.h | 3 +-- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 23e6ba70144c..ed45ed0bdafd 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1319,6 +1319,23 @@ static const struct sdhci_pci_fixes sdhci_intel_mrfld_mmc = { .probe_slot = intel_mrfld_mmc_probe_slot, }; +#define JMB388_SAMPLE_COUNT 5 + +static int jmicron_jmb388_get_ro(struct mmc_host *mmc) +{ + int i, ro_count; + + ro_count = 0; + for (i = 0; i < JMB388_SAMPLE_COUNT; i++) { + if (sdhci_get_ro(mmc) > 0) { + if (++ro_count > JMB388_SAMPLE_COUNT / 2) + return 1; + } + msleep(30); + } + return 0; +} + static int jmicron_pmos(struct sdhci_pci_chip *chip, int on) { u8 scratch; @@ -1403,11 +1420,6 @@ static int jmicron_probe(struct sdhci_pci_chip *chip) return ret; } - /* quirk for unsable RO-detection on JM388 chips */ - if (chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD || - chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) - chip->quirks |= SDHCI_QUIRK_UNSTABLE_RO_DETECT; - return 0; } @@ -1462,6 +1474,11 @@ static int jmicron_probe_slot(struct sdhci_pci_slot *slot) slot->host->mmc->caps |= MMC_CAP_BUS_WIDTH_TEST; + /* Handle unstable RO-detection on JM388 chips */ + if (slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_SD || + slot->chip->pdev->device == PCI_DEVICE_ID_JMICRON_JMB388_ESD) + slot->host->mmc_host_ops.get_ro = jmicron_jmb388_get_ro; + return 0; } diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 112584aa0772..a20df9383b20 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2513,8 +2513,9 @@ int sdhci_get_cd_nogpio(struct mmc_host *mmc) } EXPORT_SYMBOL_GPL(sdhci_get_cd_nogpio); -static int sdhci_check_ro(struct sdhci_host *host) +int sdhci_get_ro(struct mmc_host *mmc) { + struct sdhci_host *host = mmc_priv(mmc); bool allow_invert = false; int is_readonly; @@ -2522,10 +2523,10 @@ static int sdhci_check_ro(struct sdhci_host *host) is_readonly = 0; } else if (host->ops->get_ro) { is_readonly = host->ops->get_ro(host); - } else if (mmc_can_gpio_ro(host->mmc)) { - is_readonly = mmc_gpio_get_ro(host->mmc); + } else if (mmc_can_gpio_ro(mmc)) { + is_readonly = mmc_gpio_get_ro(mmc); /* Do not invert twice */ - allow_invert = !(host->mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); + allow_invert = !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); } else { is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT); @@ -2539,27 +2540,7 @@ static int sdhci_check_ro(struct sdhci_host *host) return is_readonly; } - -#define SAMPLE_COUNT 5 - -static int sdhci_get_ro(struct mmc_host *mmc) -{ - struct sdhci_host *host = mmc_priv(mmc); - int i, ro_count; - - if (!(host->quirks & SDHCI_QUIRK_UNSTABLE_RO_DETECT)) - return sdhci_check_ro(host); - - ro_count = 0; - for (i = 0; i < SAMPLE_COUNT; i++) { - if (sdhci_check_ro(host)) { - if (++ro_count > SAMPLE_COUNT / 2) - return 1; - } - msleep(30); - } - return 0; -} +EXPORT_SYMBOL_GPL(sdhci_get_ro); static void sdhci_hw_reset(struct mmc_host *mmc) { diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 957c7a917ffb..f531b617f28d 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -437,8 +437,6 @@ struct sdhci_host { #define SDHCI_QUIRK_NO_HISPD_BIT (1<<29) /* Controller treats ADMA descriptors with length 0000h incorrectly */ #define SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC (1<<30) -/* The read-only detection via SDHCI_PRESENT_STATE register is unstable */ -#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1<<31) unsigned int quirks2; /* More deviations from spec. */ @@ -788,6 +786,7 @@ void sdhci_set_power_and_bus_voltage(struct sdhci_host *host, void sdhci_set_power_noreg(struct sdhci_host *host, unsigned char mode, unsigned short vdd); int sdhci_get_cd_nogpio(struct mmc_host *mmc); +int sdhci_get_ro(struct mmc_host *mmc); void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq); int sdhci_request_atomic(struct mmc_host *mmc, struct mmc_request *mrq); void sdhci_set_bus_width(struct sdhci_host *host, int width); From 6a1326de591739784c74f1adaf2965c5e3255453 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Thu, 20 Jun 2024 08:42:38 +0100 Subject: [PATCH 11/26] mmc: sdhci-brcmstb: Add ARCH_BCM2835 option The Raspberry Pi devices have to date all used ARCH_BCM2835 as their SoC arch dependency so configurations that use this and not BRCMSTB won't end up with this module in their config. Signed-off-by: Peter Robinson Reviewed-by: Andrea della Porta Link: https://lore.kernel.org/r/20240620074248.152353-1-pbrobinson@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index bb0d4fb0892a..eb3ecfe05591 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -1016,7 +1016,7 @@ config MMC_SDHCI_MICROCHIP_PIC32 config MMC_SDHCI_BRCMSTB tristate "Broadcom SDIO/SD/MMC support" - depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST + depends on ARCH_BRCMSTB || ARCH_BCM2835 || BMIPS_GENERIC || COMPILE_TEST depends on MMC_SDHCI_PLTFM select MMC_CQHCI default ARCH_BRCMSTB || BMIPS_GENERIC From 89f415b99050fbf7bcf3fca0cde30c09b62265eb Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 20 Jun 2024 17:39:45 +0200 Subject: [PATCH 12/26] mfd: tmio: Remove obsolete .set_clk_div() callback Commit bef64d2908e825c5 ("mmc: remove tmio_mmc driver") removed the last user of the .set_clk_div() callback in the tmio_mmc_data structure. Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Reviewed-by: Lad Prabhakar Acked-by: Lee Jones Link: https://lore.kernel.org/r/e0fa98f138a7b2836128178f8b3a757978517307.1718897545.git.geert+renesas@glider.be Signed-off-by: Ulf Hansson --- include/linux/mfd/tmio.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index eace8ea6cda0..aca74ac1ff69 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -101,7 +101,6 @@ struct tmio_mmc_data { unsigned int max_blk_count; unsigned short max_segs; void (*set_pwr)(struct platform_device *host, int state); - void (*set_clk_div)(struct platform_device *host, int state); }; /* From f86937afb446d056e9e385da05536eb26483874c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 20 Jun 2024 17:39:46 +0200 Subject: [PATCH 13/26] mmc: tmio: Remove obsolete .set_pwr() callback() Commit ca78476e4888f1f1 ("mfd: Remove toshiba tmio drivers") removed the last users of the .set_pwr() callback in the tmio_mmc_data structure. Remove the callback, and all related infrastructure. Signed-off-by: Geert Uytterhoeven Acked-by: Lee Jones Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Reviewed-by: Lad Prabhakar Link: https://lore.kernel.org/r/fbbc13ddd19df2c40933ffa3b82fb14841bf1d4c.1718897545.git.geert+renesas@glider.be --- drivers/mmc/host/tmio_mmc.h | 3 --- drivers/mmc/host/tmio_mmc_core.c | 8 -------- include/linux/mfd/tmio.h | 1 - 3 files changed, 12 deletions(-) diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index de56e6534aea..2af5730c21f4 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -139,9 +139,6 @@ struct tmio_mmc_host { struct mmc_host *mmc; struct mmc_host_ops ops; - /* Callbacks for clock / power control */ - void (*set_pwr)(struct platform_device *host, int state); - /* pio related stuff */ struct scatterlist *sg_ptr; struct scatterlist *sg_orig; diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index c1a4ade5f949..2780f0a29871 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -880,9 +880,6 @@ static void tmio_mmc_power_on(struct tmio_mmc_host *host, unsigned short vdd) /* .set_ios() is returning void, so, no chance to report an error */ - if (host->set_pwr) - host->set_pwr(host->pdev, 1); - if (!IS_ERR(mmc->supply.vmmc)) { ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); /* @@ -916,9 +913,6 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host) if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - - if (host->set_pwr) - host->set_pwr(host->pdev, 0); } static unsigned int tmio_mmc_get_timeout_cycles(struct tmio_mmc_host *host) @@ -1160,8 +1154,6 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) if (pdata->flags & TMIO_MMC_USE_BUSY_TIMEOUT && !_host->get_timeout_cycles) _host->get_timeout_cycles = tmio_mmc_get_timeout_cycles; - _host->set_pwr = pdata->set_pwr; - ret = tmio_mmc_init_ocr(_host); if (ret < 0) return ret; diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h index aca74ac1ff69..8c09d14a3a28 100644 --- a/include/linux/mfd/tmio.h +++ b/include/linux/mfd/tmio.h @@ -100,7 +100,6 @@ struct tmio_mmc_data { dma_addr_t dma_rx_offset; unsigned int max_blk_count; unsigned short max_segs; - void (*set_pwr)(struct platform_device *host, int state); }; /* From f7e7e00f7b86bceb0816d25a0372096ac6de3fc5 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 21 Jun 2024 12:18:34 +0200 Subject: [PATCH 14/26] MAINTAINERS: drop entry for VIA SD/MMC controller EMail addresses of both maintainers bounce. Since there have been only cleanups for the last 10 years, let it fall back to the default MMC entry. Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20240621101833.24703-2-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- MAINTAINERS | 6 ------ 1 file changed, 6 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index cf9c9221c388..5ef1e83e0fd7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23645,12 +23645,6 @@ M: Kevin Brace S: Maintained F: drivers/net/ethernet/via/via-rhine.c -VIA SD/MMC CARD CONTROLLER DRIVER -M: Bruce Chang -M: Harald Welte -S: Maintained -F: drivers/mmc/host/via-sdmmc.c - VIA UNICHROME(PRO)/CHROME9 FRAMEBUFFER DRIVER M: Florian Tobias Schandinat L: linux-fbdev@vger.kernel.org From d0f459259c13163336242fecca8468be8e543849 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Thu, 20 Jun 2024 11:11:59 +0800 Subject: [PATCH 15/26] memstick: rtsx_pci_ms: Remove Realtek PCI memstick driver Commit c0e5f4e73a71 ("misc: rtsx: Add support for RTS5261") silently removed RTSX_MS_CARD from its MFD cell, so rtsx_pci_ms isn't used by any driver anymore. Since there doesn't seem to any complaint, hence drop the driver. Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20240620031159.142637-1-kai.heng.feng@canonical.com Signed-off-by: Ulf Hansson --- drivers/memstick/host/Kconfig | 10 - drivers/memstick/host/Makefile | 1 - drivers/memstick/host/rtsx_pci_ms.c | 638 ---------------------------- 3 files changed, 649 deletions(-) delete mode 100644 drivers/memstick/host/rtsx_pci_ms.c diff --git a/drivers/memstick/host/Kconfig b/drivers/memstick/host/Kconfig index 4113343da056..fcd2c2cc3cb4 100644 --- a/drivers/memstick/host/Kconfig +++ b/drivers/memstick/host/Kconfig @@ -44,16 +44,6 @@ config MEMSTICK_R592 To compile this driver as a module, choose M here: the module will be called r592. -config MEMSTICK_REALTEK_PCI - tristate "Realtek PCI-E Memstick Card Interface Driver" - depends on MISC_RTSX_PCI - help - Say Y here to include driver code to support Memstick card interface - of Realtek PCI-E card reader - - To compile this driver as a module, choose M here: the module will - be called rtsx_pci_ms. - config MEMSTICK_REALTEK_USB tristate "Realtek USB Memstick Card Interface Driver" depends on MISC_RTSX_USB diff --git a/drivers/memstick/host/Makefile b/drivers/memstick/host/Makefile index 1abaa03ee68c..0c90df33165d 100644 --- a/drivers/memstick/host/Makefile +++ b/drivers/memstick/host/Makefile @@ -6,5 +6,4 @@ obj-$(CONFIG_MEMSTICK_TIFM_MS) += tifm_ms.o obj-$(CONFIG_MEMSTICK_JMICRON_38X) += jmb38x_ms.o obj-$(CONFIG_MEMSTICK_R592) += r592.o -obj-$(CONFIG_MEMSTICK_REALTEK_PCI) += rtsx_pci_ms.o obj-$(CONFIG_MEMSTICK_REALTEK_USB) += rtsx_usb_ms.o diff --git a/drivers/memstick/host/rtsx_pci_ms.c b/drivers/memstick/host/rtsx_pci_ms.c deleted file mode 100644 index 980a54513e6c..000000000000 --- a/drivers/memstick/host/rtsx_pci_ms.c +++ /dev/null @@ -1,638 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* Realtek PCI-Express Memstick Card Interface driver - * - * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. - * - * Author: - * Wei WANG - */ - -#include -#include -#include -#include -#include -#include -#include - -struct realtek_pci_ms { - struct platform_device *pdev; - struct rtsx_pcr *pcr; - struct memstick_host *msh; - struct memstick_request *req; - - struct mutex host_mutex; - struct work_struct handle_req; - - u8 ssc_depth; - unsigned int clock; - unsigned char ifmode; - bool eject; -}; - -static inline struct device *ms_dev(struct realtek_pci_ms *host) -{ - return &(host->pdev->dev); -} - -static inline void ms_clear_error(struct realtek_pci_ms *host) -{ - rtsx_pci_write_register(host->pcr, CARD_STOP, - MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR); -} - -#ifdef DEBUG - -static void ms_print_debug_regs(struct realtek_pci_ms *host) -{ - struct rtsx_pcr *pcr = host->pcr; - u16 i; - u8 *ptr; - - /* Print MS host internal registers */ - rtsx_pci_init_cmd(pcr); - for (i = 0xFD40; i <= 0xFD44; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); - for (i = 0xFD52; i <= 0xFD69; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); - rtsx_pci_send_cmd(pcr, 100); - - ptr = rtsx_pci_get_cmd_data(pcr); - for (i = 0xFD40; i <= 0xFD44; i++) - dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); - for (i = 0xFD52; i <= 0xFD69; i++) - dev_dbg(ms_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); -} - -#else - -#define ms_print_debug_regs(host) - -#endif - -static int ms_power_on(struct realtek_pci_ms *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - - rtsx_pci_init_cmd(pcr); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, MS_MOD_SEL); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE, - CARD_SHARE_MASK, CARD_SHARE_48_MS); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, - MS_CLK_EN, MS_CLK_EN); - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_MS_CARD); - if (err < 0) - return err; - - err = rtsx_pci_card_power_on(pcr, RTSX_MS_CARD); - if (err < 0) - return err; - - /* Wait ms power stable */ - msleep(150); - - err = rtsx_pci_write_register(pcr, CARD_OE, - MS_OUTPUT_EN, MS_OUTPUT_EN); - if (err < 0) - return err; - - return 0; -} - -static int ms_power_off(struct realtek_pci_ms *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, MS_CLK_EN, 0); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, MS_OUTPUT_EN, 0); - - err = rtsx_pci_send_cmd(pcr, 100); - if (err < 0) - return err; - - err = rtsx_pci_card_power_off(pcr, RTSX_MS_CARD); - if (err < 0) - return err; - - return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_MS_CARD); -} - -static int ms_transfer_data(struct realtek_pci_ms *host, unsigned char data_dir, - u8 tpc, u8 cfg, struct scatterlist *sg) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - unsigned int length = sg->length; - u16 sec_cnt = (u16)(length / 512); - u8 val, trans_mode, dma_dir; - struct memstick_dev *card = host->msh->card; - bool pro_card = card->id.type == MEMSTICK_TYPE_PRO; - - dev_dbg(ms_dev(host), "%s: tpc = 0x%02x, data_dir = %s, length = %d\n", - __func__, tpc, (data_dir == READ) ? "READ" : "WRITE", - length); - - if (data_dir == READ) { - dma_dir = DMA_DIR_FROM_CARD; - trans_mode = pro_card ? MS_TM_AUTO_READ : MS_TM_NORMAL_READ; - } else { - dma_dir = DMA_DIR_TO_CARD; - trans_mode = pro_card ? MS_TM_AUTO_WRITE : MS_TM_NORMAL_WRITE; - } - - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - if (pro_card) { - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_H, - 0xFF, (u8)(sec_cnt >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_SECTOR_CNT_L, - 0xFF, (u8)sec_cnt); - } - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, - DMA_DONE_INT, DMA_DONE_INT); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, 0xFF, (u8)(length >> 24)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, 0xFF, (u8)(length >> 16)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, 0xFF, (u8)(length >> 8)); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)length); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, - 0x03 | DMA_PACK_SIZE_MASK, dma_dir | DMA_EN | DMA_512); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, RING_BUFFER); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, - 0xFF, MS_TRANSFER_START | trans_mode); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - - rtsx_pci_send_cmd_no_wait(pcr); - - err = rtsx_pci_transfer_data(pcr, sg, 1, data_dir == READ, 10000); - if (err < 0) { - ms_clear_error(host); - return err; - } - - rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val); - if (pro_card) { - if (val & (MS_INT_CMDNK | MS_INT_ERR | - MS_CRC16_ERR | MS_RDY_TIMEOUT)) - return -EIO; - } else { - if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) - return -EIO; - } - - return 0; -} - -static int ms_write_bytes(struct realtek_pci_ms *host, u8 tpc, - u8 cfg, u8 cnt, u8 *data, u8 *int_reg) -{ - struct rtsx_pcr *pcr = host->pcr; - int err, i; - - dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc); - - if (!data) - return -EINVAL; - - rtsx_pci_init_cmd(pcr); - - for (i = 0; i < cnt; i++) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - PPBUF_BASE2 + i, 0xFF, data[i]); - if (cnt % 2) - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, - PPBUF_BASE2 + i, 0xFF, 0xFF); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, - 0xFF, MS_TRANSFER_START | MS_TM_WRITE_BYTES); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - if (int_reg) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0); - - err = rtsx_pci_send_cmd(pcr, 5000); - if (err < 0) { - u8 val; - - rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val); - dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val); - - if (int_reg) - *int_reg = val & 0x0F; - - ms_print_debug_regs(host); - - ms_clear_error(host); - - if (!(tpc & 0x08)) { - if (val & MS_CRC16_ERR) - return -EIO; - } else { - if (!(val & 0x80)) { - if (val & (MS_INT_ERR | MS_INT_CMDNK)) - return -EIO; - } - } - - return -ETIMEDOUT; - } - - if (int_reg) { - u8 *ptr = rtsx_pci_get_cmd_data(pcr) + 1; - *int_reg = *ptr & 0x0F; - } - - return 0; -} - -static int ms_read_bytes(struct realtek_pci_ms *host, u8 tpc, - u8 cfg, u8 cnt, u8 *data, u8 *int_reg) -{ - struct rtsx_pcr *pcr = host->pcr; - int err, i; - u8 *ptr; - - dev_dbg(ms_dev(host), "%s: tpc = 0x%02x\n", __func__, tpc); - - if (!data) - return -EINVAL; - - rtsx_pci_init_cmd(pcr); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TPC, 0xFF, tpc); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANS_CFG, 0xFF, cfg); - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, - 0x01, PINGPONG_BUFFER); - - rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, MS_TRANSFER, - 0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES); - rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, MS_TRANSFER, - MS_TRANSFER_END, MS_TRANSFER_END); - for (i = 0; i < cnt - 1; i++) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0); - if (cnt % 2) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, PPBUF_BASE2 + cnt, 0, 0); - else - rtsx_pci_add_cmd(pcr, READ_REG_CMD, - PPBUF_BASE2 + cnt - 1, 0, 0); - if (int_reg) - rtsx_pci_add_cmd(pcr, READ_REG_CMD, MS_TRANS_CFG, 0, 0); - - err = rtsx_pci_send_cmd(pcr, 5000); - if (err < 0) { - u8 val; - - rtsx_pci_read_register(pcr, MS_TRANS_CFG, &val); - dev_dbg(ms_dev(host), "MS_TRANS_CFG: 0x%02x\n", val); - - if (int_reg) - *int_reg = val & 0x0F; - - ms_print_debug_regs(host); - - ms_clear_error(host); - - if (!(tpc & 0x08)) { - if (val & MS_CRC16_ERR) - return -EIO; - } else { - if (!(val & 0x80)) { - if (val & (MS_INT_ERR | MS_INT_CMDNK)) - return -EIO; - } - } - - return -ETIMEDOUT; - } - - ptr = rtsx_pci_get_cmd_data(pcr) + 1; - for (i = 0; i < cnt; i++) - data[i] = *ptr++; - - if (int_reg) - *int_reg = *ptr & 0x0F; - - return 0; -} - -static int rtsx_pci_ms_issue_cmd(struct realtek_pci_ms *host) -{ - struct memstick_request *req = host->req; - int err = 0; - u8 cfg = 0, int_reg; - - dev_dbg(ms_dev(host), "%s\n", __func__); - - if (req->need_card_int) { - if (host->ifmode != MEMSTICK_SERIAL) - cfg = WAIT_INT; - } - - if (req->long_data) { - err = ms_transfer_data(host, req->data_dir, - req->tpc, cfg, &(req->sg)); - } else { - if (req->data_dir == READ) { - err = ms_read_bytes(host, req->tpc, cfg, - req->data_len, req->data, &int_reg); - } else { - err = ms_write_bytes(host, req->tpc, cfg, - req->data_len, req->data, &int_reg); - } - } - if (err < 0) - return err; - - if (req->need_card_int && (host->ifmode == MEMSTICK_SERIAL)) { - err = ms_read_bytes(host, MS_TPC_GET_INT, - NO_WAIT_INT, 1, &int_reg, NULL); - if (err < 0) - return err; - } - - if (req->need_card_int) { - dev_dbg(ms_dev(host), "int_reg: 0x%02x\n", int_reg); - - if (int_reg & MS_INT_CMDNK) - req->int_reg |= MEMSTICK_INT_CMDNAK; - if (int_reg & MS_INT_BREQ) - req->int_reg |= MEMSTICK_INT_BREQ; - if (int_reg & MS_INT_ERR) - req->int_reg |= MEMSTICK_INT_ERR; - if (int_reg & MS_INT_CED) - req->int_reg |= MEMSTICK_INT_CED; - } - - return 0; -} - -static void rtsx_pci_ms_handle_req(struct work_struct *work) -{ - struct realtek_pci_ms *host = container_of(work, - struct realtek_pci_ms, handle_req); - struct rtsx_pcr *pcr = host->pcr; - struct memstick_host *msh = host->msh; - int rc; - - mutex_lock(&pcr->pcr_mutex); - - rtsx_pci_start_run(pcr); - - rtsx_pci_switch_clock(host->pcr, host->clock, host->ssc_depth, - false, true, false); - rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, MS_MOD_SEL); - rtsx_pci_write_register(pcr, CARD_SHARE_MODE, - CARD_SHARE_MASK, CARD_SHARE_48_MS); - - if (!host->req) { - do { - rc = memstick_next_req(msh, &host->req); - dev_dbg(ms_dev(host), "next req %d\n", rc); - - if (!rc) - host->req->error = rtsx_pci_ms_issue_cmd(host); - } while (!rc); - } - - mutex_unlock(&pcr->pcr_mutex); -} - -static void rtsx_pci_ms_request(struct memstick_host *msh) -{ - struct realtek_pci_ms *host = memstick_priv(msh); - - dev_dbg(ms_dev(host), "--> %s\n", __func__); - - if (rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD)) - return; - - schedule_work(&host->handle_req); -} - -static int rtsx_pci_ms_set_param(struct memstick_host *msh, - enum memstick_param param, int value) -{ - struct realtek_pci_ms *host = memstick_priv(msh); - struct rtsx_pcr *pcr = host->pcr; - unsigned int clock = 0; - u8 ssc_depth = 0; - int err; - - dev_dbg(ms_dev(host), "%s: param = %d, value = %d\n", - __func__, param, value); - - err = rtsx_pci_card_exclusive_check(host->pcr, RTSX_MS_CARD); - if (err) - return err; - - switch (param) { - case MEMSTICK_POWER: - if (value == MEMSTICK_POWER_ON) - err = ms_power_on(host); - else if (value == MEMSTICK_POWER_OFF) - err = ms_power_off(host); - else - return -EINVAL; - break; - - case MEMSTICK_INTERFACE: - if (value == MEMSTICK_SERIAL) { - clock = 19000000; - ssc_depth = RTSX_SSC_DEPTH_500K; - - err = rtsx_pci_write_register(pcr, MS_CFG, 0x58, - MS_BUS_WIDTH_1 | PUSH_TIME_DEFAULT); - if (err < 0) - return err; - } else if (value == MEMSTICK_PAR4) { - clock = 39000000; - ssc_depth = RTSX_SSC_DEPTH_1M; - - err = rtsx_pci_write_register(pcr, MS_CFG, - 0x58, MS_BUS_WIDTH_4 | PUSH_TIME_ODD); - if (err < 0) - return err; - } else { - return -EINVAL; - } - - err = rtsx_pci_switch_clock(pcr, clock, - ssc_depth, false, true, false); - if (err < 0) - return err; - - host->ssc_depth = ssc_depth; - host->clock = clock; - host->ifmode = value; - break; - } - - return 0; -} - -#ifdef CONFIG_PM - -static int rtsx_pci_ms_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct realtek_pci_ms *host = platform_get_drvdata(pdev); - struct memstick_host *msh = host->msh; - - dev_dbg(ms_dev(host), "--> %s\n", __func__); - - memstick_suspend_host(msh); - return 0; -} - -static int rtsx_pci_ms_resume(struct platform_device *pdev) -{ - struct realtek_pci_ms *host = platform_get_drvdata(pdev); - struct memstick_host *msh = host->msh; - - dev_dbg(ms_dev(host), "--> %s\n", __func__); - - memstick_resume_host(msh); - return 0; -} - -#else /* CONFIG_PM */ - -#define rtsx_pci_ms_suspend NULL -#define rtsx_pci_ms_resume NULL - -#endif /* CONFIG_PM */ - -static void rtsx_pci_ms_card_event(struct platform_device *pdev) -{ - struct realtek_pci_ms *host = platform_get_drvdata(pdev); - - memstick_detect_change(host->msh); -} - -static int rtsx_pci_ms_drv_probe(struct platform_device *pdev) -{ - struct memstick_host *msh; - struct realtek_pci_ms *host; - struct rtsx_pcr *pcr; - struct pcr_handle *handle = pdev->dev.platform_data; - int rc; - - if (!handle) - return -ENXIO; - - pcr = handle->pcr; - if (!pcr) - return -ENXIO; - - dev_dbg(&(pdev->dev), - ": Realtek PCI-E Memstick controller found\n"); - - msh = memstick_alloc_host(sizeof(*host), &pdev->dev); - if (!msh) - return -ENOMEM; - - host = memstick_priv(msh); - host->pcr = pcr; - host->msh = msh; - host->pdev = pdev; - platform_set_drvdata(pdev, host); - pcr->slots[RTSX_MS_CARD].p_dev = pdev; - pcr->slots[RTSX_MS_CARD].card_event = rtsx_pci_ms_card_event; - - mutex_init(&host->host_mutex); - - INIT_WORK(&host->handle_req, rtsx_pci_ms_handle_req); - msh->request = rtsx_pci_ms_request; - msh->set_param = rtsx_pci_ms_set_param; - msh->caps = MEMSTICK_CAP_PAR4; - - rc = memstick_add_host(msh); - if (rc) { - memstick_free_host(msh); - return rc; - } - - return 0; -} - -static void rtsx_pci_ms_drv_remove(struct platform_device *pdev) -{ - struct realtek_pci_ms *host = platform_get_drvdata(pdev); - struct rtsx_pcr *pcr; - struct memstick_host *msh; - int rc; - - pcr = host->pcr; - pcr->slots[RTSX_MS_CARD].p_dev = NULL; - pcr->slots[RTSX_MS_CARD].card_event = NULL; - msh = host->msh; - host->eject = true; - cancel_work_sync(&host->handle_req); - - mutex_lock(&host->host_mutex); - if (host->req) { - dev_dbg(&(pdev->dev), - "%s: Controller removed during transfer\n", - dev_name(&msh->dev)); - - rtsx_pci_complete_unfinished_transfer(pcr); - - host->req->error = -ENOMEDIUM; - do { - rc = memstick_next_req(msh, &host->req); - if (!rc) - host->req->error = -ENOMEDIUM; - } while (!rc); - } - mutex_unlock(&host->host_mutex); - - memstick_remove_host(msh); - memstick_free_host(msh); - - dev_dbg(&(pdev->dev), - ": Realtek PCI-E Memstick controller has been removed\n"); -} - -static struct platform_device_id rtsx_pci_ms_ids[] = { - { - .name = DRV_NAME_RTSX_PCI_MS, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, rtsx_pci_ms_ids); - -static struct platform_driver rtsx_pci_ms_driver = { - .probe = rtsx_pci_ms_drv_probe, - .remove_new = rtsx_pci_ms_drv_remove, - .id_table = rtsx_pci_ms_ids, - .suspend = rtsx_pci_ms_suspend, - .resume = rtsx_pci_ms_resume, - .driver = { - .name = DRV_NAME_RTSX_PCI_MS, - }, -}; -module_platform_driver(rtsx_pci_ms_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Wei WANG "); -MODULE_DESCRIPTION("Realtek PCI-E Memstick Card Host Driver"); From a1382d193ca449de550b393dd6f763ff7dc8cf75 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 25 Jun 2024 13:33:21 +0100 Subject: [PATCH 16/26] mmc: mmc_spi: allow for spi controllers incapable of getting as low as 400k Some controllers may not be able to reach a bus clock as low as 400 KHz due to a lack of sufficient divisors. In these cases, the SD card slot becomes non-functional as Linux continuously attempts to set the bus clock to 400 KHz. If the controller is incapable of getting that low, set its minimum frequency instead. While this may eliminate some SD cards, it allows those capable of operating at the controller's minimum frequency to be used. Signed-off-by: Conor Dooley Link: https://lore.kernel.org/r/20240625-gigantic-frown-1ef4afa3e6fa@wendy Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmc_spi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 09d7a6a0dc1a..c9caa1ece7ef 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1208,7 +1208,10 @@ static int mmc_spi_probe(struct spi_device *spi) * that's the only reason not to use a few MHz for f_min (until * the upper layer reads the target frequency from the CSD). */ - mmc->f_min = 400000; + if (spi->controller->min_speed_hz > 400000) + dev_warn(&spi->dev,"Controller unable to reduce bus clock to 400 KHz\n"); + + mmc->f_min = max(spi->controller->min_speed_hz, 400000); mmc->f_max = spi->max_speed_hz; host = mmc_priv(mmc); From 85683fb39d9b671620b5a9343fab3356e486a9a6 Mon Sep 17 00:00:00 2001 From: Allen Pais Date: Wed, 26 Jun 2024 10:48:21 +0200 Subject: [PATCH 17/26] mmc: sdhi: Convert from tasklet to BH workqueue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only generic interface to execute asynchronously in the BH context is tasklet; however, it's marked deprecated and has some design flaws. To replace tasklets, BH workqueue support was recently added. A BH workqueue behaves similarly to regular workqueues except that the queued work items are executed in the BH context. This patch converts the SDHI driver from tasklet to BH workqueue. Based on the work done by Tejun Heo Signed-off-by: Allen Pais [wsa: fixed build faliures, corrected whitespace issues] Signed-off-by: Wolfram Sang Reviewed-by: Niklas Söderlund Tested-by: Lad Prabhakar Link: https://lore.kernel.org/r/20240626085015.32171-2-wsa+renesas@sang-engineering.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi.h | 4 ++- drivers/mmc/host/renesas_sdhi_core.c | 2 ++ drivers/mmc/host/renesas_sdhi_internal_dmac.c | 26 +++++++++---------- drivers/mmc/host/renesas_sdhi_sys_dmac.c | 9 +++---- drivers/mmc/host/tmio_mmc.h | 3 ++- drivers/mmc/host/tmio_mmc_core.c | 4 +-- 6 files changed, 26 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index 586f94d4dbfd..f12a87442338 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -11,6 +11,7 @@ #include #include +#include #include "tmio_mmc.h" struct renesas_sdhi_scc { @@ -67,7 +68,7 @@ struct renesas_sdhi_dma { dma_filter_fn filter; void (*enable)(struct tmio_mmc_host *host, bool enable); struct completion dma_dataend; - struct tasklet_struct dma_complete; + struct work_struct dma_complete; }; struct renesas_sdhi { @@ -93,6 +94,7 @@ struct renesas_sdhi { unsigned int tap_set; struct reset_control *rstc; + struct tmio_mmc_host *host; }; #define host_to_priv(host) \ diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 58536626e6c5..04874791541f 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -970,6 +970,8 @@ int renesas_sdhi_probe(struct platform_device *pdev, if (IS_ERR(host)) return PTR_ERR(host); + priv->host = host; + if (of_data) { mmc_data->flags |= of_data->tmio_flags; mmc_data->ocr_mask = of_data->tmio_ocr_mask; diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 422fa63a2e99..d4b66daeda66 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -337,7 +337,7 @@ static bool renesas_sdhi_internal_dmac_dma_irq(struct tmio_mmc_host *host) writel(status ^ dma_irqs, host->ctl + DM_CM_INFO1); set_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags); if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags)) - tasklet_schedule(&dma_priv->dma_complete); + queue_work(system_bh_wq, &dma_priv->dma_complete); } return status & dma_irqs; @@ -352,7 +352,7 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host) set_bit(SDHI_DMA_END_FLAG_ACCESS, &dma_priv->end_flags); if (test_bit(SDHI_DMA_END_FLAG_DMA, &dma_priv->end_flags) || host->data->error) - tasklet_schedule(&dma_priv->dma_complete); + queue_work(system_bh_wq, &dma_priv->dma_complete); } /* @@ -440,9 +440,9 @@ renesas_sdhi_internal_dmac_start_dma(struct tmio_mmc_host *host, renesas_sdhi_internal_dmac_enable_dma(host, false); } -static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg) +static void renesas_sdhi_internal_dmac_issue_work_fn(struct work_struct *work) { - struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg; + struct tmio_mmc_host *host = from_work(host, work, dma_issue); struct renesas_sdhi *priv = host_to_priv(host); tmio_mmc_enable_mmc_irqs(host, TMIO_STAT_DATAEND); @@ -454,7 +454,7 @@ static void renesas_sdhi_internal_dmac_issue_tasklet_fn(unsigned long arg) /* on CMD errors, simulate DMA end immediately */ set_bit(SDHI_DMA_END_FLAG_DMA, &priv->dma_priv.end_flags); if (test_bit(SDHI_DMA_END_FLAG_ACCESS, &priv->dma_priv.end_flags)) - tasklet_schedule(&priv->dma_priv.dma_complete); + queue_work(system_bh_wq, &priv->dma_priv.dma_complete); } } @@ -484,9 +484,11 @@ static bool renesas_sdhi_internal_dmac_complete(struct tmio_mmc_host *host) return true; } -static void renesas_sdhi_internal_dmac_complete_tasklet_fn(unsigned long arg) +static void renesas_sdhi_internal_dmac_complete_work_fn(struct work_struct *work) { - struct tmio_mmc_host *host = (struct tmio_mmc_host *)arg; + struct renesas_sdhi_dma *dma_priv = from_work(dma_priv, work, dma_complete); + struct renesas_sdhi *priv = container_of(dma_priv, typeof(*priv), dma_priv); + struct tmio_mmc_host *host = priv->host; spin_lock_irq(&host->lock); if (!renesas_sdhi_internal_dmac_complete(host)) @@ -544,12 +546,10 @@ renesas_sdhi_internal_dmac_request_dma(struct tmio_mmc_host *host, /* Each value is set to non-zero to assume "enabling" each DMA */ host->chan_rx = host->chan_tx = (void *)0xdeadbeaf; - tasklet_init(&priv->dma_priv.dma_complete, - renesas_sdhi_internal_dmac_complete_tasklet_fn, - (unsigned long)host); - tasklet_init(&host->dma_issue, - renesas_sdhi_internal_dmac_issue_tasklet_fn, - (unsigned long)host); + INIT_WORK(&priv->dma_priv.dma_complete, + renesas_sdhi_internal_dmac_complete_work_fn); + INIT_WORK(&host->dma_issue, + renesas_sdhi_internal_dmac_issue_work_fn); /* Add pre_req and post_req */ host->ops.pre_req = renesas_sdhi_internal_dmac_pre_req; diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c index 9cf7f9feab72..5a6f41318645 100644 --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -312,9 +312,9 @@ static void renesas_sdhi_sys_dmac_start_dma(struct tmio_mmc_host *host, } } -static void renesas_sdhi_sys_dmac_issue_tasklet_fn(unsigned long priv) +static void renesas_sdhi_sys_dmac_issue_work_fn(struct work_struct *work) { - struct tmio_mmc_host *host = (struct tmio_mmc_host *)priv; + struct tmio_mmc_host *host = from_work(host, work, dma_issue); struct dma_chan *chan = NULL; spin_lock_irq(&host->lock); @@ -401,9 +401,8 @@ static void renesas_sdhi_sys_dmac_request_dma(struct tmio_mmc_host *host, goto ebouncebuf; init_completion(&priv->dma_priv.dma_dataend); - tasklet_init(&host->dma_issue, - renesas_sdhi_sys_dmac_issue_tasklet_fn, - (unsigned long)host); + INIT_WORK(&host->dma_issue, + renesas_sdhi_sys_dmac_issue_work_fn); } renesas_sdhi_sys_dmac_enable_dma(host, true); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index 2af5730c21f4..a75755f31d31 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -21,6 +21,7 @@ #include #include #include +#include #define CTL_SD_CMD 0x00 #define CTL_ARG_REG 0x04 @@ -153,7 +154,7 @@ struct tmio_mmc_host { bool dma_on; struct dma_chan *chan_rx; struct dma_chan *chan_tx; - struct tasklet_struct dma_issue; + struct work_struct dma_issue; struct scatterlist bounce_sg; u8 *bounce_buf; diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index 2780f0a29871..b61a6310311d 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -608,7 +608,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat) } else { tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_READOP); - tasklet_schedule(&host->dma_issue); + queue_work(system_bh_wq, &host->dma_issue); } } else { if (!host->dma_on) { @@ -616,7 +616,7 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat) } else { tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_WRITEOP); - tasklet_schedule(&host->dma_issue); + queue_work(system_bh_wq, &host->dma_issue); } } } else { From 921c87ba3893b5d3608e7f248366266b40b86c75 Mon Sep 17 00:00:00 2001 From: Allen Pais Date: Mon, 1 Jul 2024 03:07:33 -0700 Subject: [PATCH 18/26] mmc: Convert from tasklet to BH workqueue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only generic interface to execute asynchronously in the BH context is tasklet; however, it's marked deprecated and has some design flaws. To replace tasklets, BH workqueue support was recently added. A BH workqueue behaves similarly to regular workqueues except that the queued work items are executed in the BH context. This patch converts drivers/mmc/* from tasklet to BH workqueue. Based on the work done by Tejun Heo Tested-by: Christian Loehle Tested-by: Aubin Constans Acked-by: Aubin Constans Acked-by: Michał Mirosław Reviewed-by: Christian Loehle Signed-off-by: Allen Pais Link: https://lore.kernel.org/r/20240701100736.4001658-1-allen.lkml@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 35 ++++++++------- drivers/mmc/host/au1xmmc.c | 37 ++++++++-------- drivers/mmc/host/cb710-mmc.c | 14 +++--- drivers/mmc/host/cb710-mmc.h | 3 +- drivers/mmc/host/dw_mmc.c | 24 +++++----- drivers/mmc/host/dw_mmc.h | 9 ++-- drivers/mmc/host/omap.c | 17 +++---- drivers/mmc/host/sdhci-bcm-kona.c | 2 +- drivers/mmc/host/tifm_sd.c | 15 ++++--- drivers/mmc/host/uniphier-sd.c | 12 ++--- drivers/mmc/host/via-sdmmc.c | 25 ++++++----- drivers/mmc/host/wbsd.c | 74 +++++++++++++++---------------- drivers/mmc/host/wbsd.h | 10 ++--- 13 files changed, 142 insertions(+), 135 deletions(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 8199d9620075..6490df54a6f5 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -272,12 +273,12 @@ struct atmel_mci_dma { * EVENT_DATA_ERROR is pending. * @stop_cmdr: Value to be loaded into CMDR when the stop command is * to be sent. - * @tasklet: Tasklet running the request state machine. + * @bh_work: Work running the request state machine. * @pending_events: Bitmask of events flagged by the interrupt handler - * to be processed by the tasklet. + * to be processed by the work. * @completed_events: Bitmask of events which the state machine has * processed. - * @state: Tasklet state. + * @state: Work state. * @queue: List of slots waiting for access to the controller. * @need_clock_update: Update the clock rate before the next request. * @need_reset: Reset controller before next request. @@ -352,7 +353,7 @@ struct atmel_mci { u32 data_status; u32 stop_cmdr; - struct tasklet_struct tasklet; + struct work_struct bh_work; unsigned long pending_events; unsigned long completed_events; enum atmel_mci_state state; @@ -735,7 +736,7 @@ static void atmci_timeout_timer(struct timer_list *t) host->need_reset = 1; host->state = STATE_END_REQUEST; smp_wmb(); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); } static inline unsigned int atmci_ns_to_clocks(struct atmel_mci *host, @@ -958,7 +959,7 @@ static void atmci_pdc_complete(struct atmel_mci *host) dev_dbg(dev, "(%s) set pending xfer complete\n", __func__); atmci_set_pending(host, EVENT_XFER_COMPLETE); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); } static void atmci_dma_cleanup(struct atmel_mci *host) @@ -972,7 +973,7 @@ static void atmci_dma_cleanup(struct atmel_mci *host) } /* - * This function is called by the DMA driver from tasklet context. + * This function is called by the DMA driver from bh context. */ static void atmci_dma_complete(void *arg) { @@ -995,7 +996,7 @@ static void atmci_dma_complete(void *arg) if (data) { dev_dbg(dev, "(%s) set pending xfer complete\n", __func__); atmci_set_pending(host, EVENT_XFER_COMPLETE); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); /* * Regardless of what the documentation says, we have @@ -1008,7 +1009,7 @@ static void atmci_dma_complete(void *arg) * haven't seen all the potential error bits yet. * * The interrupt handler will schedule a different - * tasklet to finish things up when the data transfer + * bh work to finish things up when the data transfer * is completely done. * * We may not complete the mmc request here anyway @@ -1745,9 +1746,9 @@ static void atmci_detect_change(struct timer_list *t) } } -static void atmci_tasklet_func(struct tasklet_struct *t) +static void atmci_work_func(struct work_struct *t) { - struct atmel_mci *host = from_tasklet(host, t, tasklet); + struct atmel_mci *host = from_work(host, t, bh_work); struct mmc_request *mrq = host->mrq; struct mmc_data *data = host->data; struct device *dev = host->dev; @@ -1759,7 +1760,7 @@ static void atmci_tasklet_func(struct tasklet_struct *t) state = host->state; - dev_vdbg(dev, "tasklet: state %u pending/completed/mask %lx/%lx/%x\n", + dev_vdbg(dev, "bh_work: state %u pending/completed/mask %lx/%lx/%x\n", state, host->pending_events, host->completed_events, atmci_readl(host, ATMCI_IMR)); @@ -2118,7 +2119,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) dev_dbg(dev, "set pending data error\n"); smp_wmb(); atmci_set_pending(host, EVENT_DATA_ERROR); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); } if (pending & ATMCI_TXBUFE) { @@ -2187,7 +2188,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) smp_wmb(); dev_dbg(dev, "set pending notbusy\n"); atmci_set_pending(host, EVENT_NOTBUSY); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); } if (pending & ATMCI_NOTBUSY) { @@ -2196,7 +2197,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) smp_wmb(); dev_dbg(dev, "set pending notbusy\n"); atmci_set_pending(host, EVENT_NOTBUSY); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); } if (pending & ATMCI_RXRDY) @@ -2211,7 +2212,7 @@ static irqreturn_t atmci_interrupt(int irq, void *dev_id) smp_wmb(); dev_dbg(dev, "set pending cmd rdy\n"); atmci_set_pending(host, EVENT_CMD_RDY); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); } if (pending & (ATMCI_SDIOIRQA | ATMCI_SDIOIRQB)) @@ -2487,7 +2488,7 @@ static int atmci_probe(struct platform_device *pdev) host->mapbase = regs->start; - tasklet_setup(&host->tasklet, atmci_tasklet_func); + INIT_WORK(&host->bh_work, atmci_work_func); ret = request_irq(irq, atmci_interrupt, 0, dev_name(dev), host); if (ret) { diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index b5a5c6a2fe8b..6e80bcb668ec 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include @@ -113,8 +114,8 @@ struct au1xmmc_host { int irq; - struct tasklet_struct finish_task; - struct tasklet_struct data_task; + struct work_struct finish_bh_work; + struct work_struct data_bh_work; struct au1xmmc_platform_data *platdata; struct platform_device *pdev; struct resource *ioarea; @@ -253,9 +254,9 @@ static void au1xmmc_finish_request(struct au1xmmc_host *host) mmc_request_done(host->mmc, mrq); } -static void au1xmmc_tasklet_finish(struct tasklet_struct *t) +static void au1xmmc_finish_bh_work(struct work_struct *t) { - struct au1xmmc_host *host = from_tasklet(host, t, finish_task); + struct au1xmmc_host *host = from_work(host, t, finish_bh_work); au1xmmc_finish_request(host); } @@ -363,9 +364,9 @@ static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) au1xmmc_finish_request(host); } -static void au1xmmc_tasklet_data(struct tasklet_struct *t) +static void au1xmmc_data_bh_work(struct work_struct *t) { - struct au1xmmc_host *host = from_tasklet(host, t, data_task); + struct au1xmmc_host *host = from_work(host, t, data_bh_work); u32 status = __raw_readl(HOST_STATUS(host)); au1xmmc_data_complete(host, status); @@ -425,7 +426,7 @@ static void au1xmmc_send_pio(struct au1xmmc_host *host) if (host->flags & HOST_F_STOP) SEND_STOP(host); - tasklet_schedule(&host->data_task); + queue_work(system_bh_wq, &host->data_bh_work); } } @@ -505,7 +506,7 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host) if (host->flags & HOST_F_STOP) SEND_STOP(host); - tasklet_schedule(&host->data_task); + queue_work(system_bh_wq, &host->data_bh_work); } } @@ -561,7 +562,7 @@ static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status) if (!trans || cmd->error) { IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); - tasklet_schedule(&host->finish_task); + queue_work(system_bh_wq, &host->finish_bh_work); return; } @@ -797,7 +798,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id) IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); /* IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); */ - tasklet_schedule(&host->finish_task); + queue_work(system_bh_wq, &host->finish_bh_work); } #if 0 else if (status & SD_STATUS_DD) { @@ -806,7 +807,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id) au1xmmc_receive_pio(host); else { au1xmmc_data_complete(host, status); - /* tasklet_schedule(&host->data_task); */ + /* queue_work(system_bh_wq, &host->data_bh_work); */ } } #endif @@ -854,7 +855,7 @@ static void au1xmmc_dbdma_callback(int irq, void *dev_id) if (host->flags & HOST_F_STOP) SEND_STOP(host); - tasklet_schedule(&host->data_task); + queue_work(system_bh_wq, &host->data_bh_work); } static int au1xmmc_dbdma_init(struct au1xmmc_host *host) @@ -1039,9 +1040,9 @@ static int au1xmmc_probe(struct platform_device *pdev) if (host->platdata) mmc->caps &= ~(host->platdata->mask_host_caps); - tasklet_setup(&host->data_task, au1xmmc_tasklet_data); + INIT_WORK(&host->data_bh_work, au1xmmc_data_bh_work); - tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish); + INIT_WORK(&host->finish_bh_work, au1xmmc_finish_bh_work); if (has_dbdma()) { ret = au1xmmc_dbdma_init(host); @@ -1091,8 +1092,8 @@ static int au1xmmc_probe(struct platform_device *pdev) if (host->flags & HOST_F_DBDMA) au1xmmc_dbdma_shutdown(host); - tasklet_kill(&host->data_task); - tasklet_kill(&host->finish_task); + cancel_work_sync(&host->data_bh_work); + cancel_work_sync(&host->finish_bh_work); if (host->platdata && host->platdata->cd_setup && !(mmc->caps & MMC_CAP_NEEDS_POLL)) @@ -1135,8 +1136,8 @@ static void au1xmmc_remove(struct platform_device *pdev) __raw_writel(0, HOST_CONFIG2(host)); wmb(); /* drain writebuffer */ - tasklet_kill(&host->data_task); - tasklet_kill(&host->finish_task); + cancel_work_sync(&host->data_bh_work); + cancel_work_sync(&host->finish_bh_work); if (host->flags & HOST_F_DBDMA) au1xmmc_dbdma_shutdown(host); diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 0aec33b88bef..902f7f20abaa 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -493,7 +493,7 @@ static void cb710_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) if (!cb710_mmc_command(mmc, mrq->cmd) && mrq->stop) cb710_mmc_command(mmc, mrq->stop); - tasklet_schedule(&reader->finish_req_tasklet); + queue_work(system_bh_wq, &reader->finish_req_bh_work); } static int cb710_mmc_powerup(struct cb710_slot *slot) @@ -646,10 +646,10 @@ static int cb710_mmc_irq_handler(struct cb710_slot *slot) return 1; } -static void cb710_mmc_finish_request_tasklet(struct tasklet_struct *t) +static void cb710_mmc_finish_request_bh_work(struct work_struct *t) { - struct cb710_mmc_reader *reader = from_tasklet(reader, t, - finish_req_tasklet); + struct cb710_mmc_reader *reader = from_work(reader, t, + finish_req_bh_work); struct mmc_request *mrq = reader->mrq; reader->mrq = NULL; @@ -718,8 +718,8 @@ static int cb710_mmc_init(struct platform_device *pdev) reader = mmc_priv(mmc); - tasklet_setup(&reader->finish_req_tasklet, - cb710_mmc_finish_request_tasklet); + INIT_WORK(&reader->finish_req_bh_work, + cb710_mmc_finish_request_bh_work); spin_lock_init(&reader->irq_lock); cb710_dump_regs(chip, CB710_DUMP_REGS_MMC); @@ -763,7 +763,7 @@ static void cb710_mmc_exit(struct platform_device *pdev) cb710_write_port_32(slot, CB710_MMC_CONFIG_PORT, 0); cb710_write_port_16(slot, CB710_MMC_CONFIGB_PORT, 0); - tasklet_kill(&reader->finish_req_tasklet); + cancel_work_sync(&reader->finish_req_bh_work); mmc_free_host(mmc); } diff --git a/drivers/mmc/host/cb710-mmc.h b/drivers/mmc/host/cb710-mmc.h index 5e053077dbed..59abaccaad10 100644 --- a/drivers/mmc/host/cb710-mmc.h +++ b/drivers/mmc/host/cb710-mmc.h @@ -8,10 +8,11 @@ #define LINUX_CB710_MMC_H #include +#include /* per-MMC-reader structure */ struct cb710_mmc_reader { - struct tasklet_struct finish_req_tasklet; + struct work_struct finish_req_bh_work; struct mmc_request *mrq; spinlock_t irq_lock; unsigned char last_power_mode; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8e2d676b9239..7195de2cde44 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -493,7 +493,7 @@ static void dw_mci_dmac_complete_dma(void *arg) */ if (data) { set_bit(EVENT_XFER_COMPLETE, &host->pending_events); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); } } @@ -1834,7 +1834,7 @@ static enum hrtimer_restart dw_mci_fault_timer(struct hrtimer *t) if (!host->data_status) { host->data_status = SDMMC_INT_DCRC; set_bit(EVENT_DATA_ERROR, &host->pending_events); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); } spin_unlock_irqrestore(&host->irq_lock, flags); @@ -2056,9 +2056,9 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host) return true; } -static void dw_mci_tasklet_func(struct tasklet_struct *t) +static void dw_mci_work_func(struct work_struct *t) { - struct dw_mci *host = from_tasklet(host, t, tasklet); + struct dw_mci *host = from_work(host, t, bh_work); struct mmc_data *data; struct mmc_command *cmd; struct mmc_request *mrq; @@ -2113,7 +2113,7 @@ static void dw_mci_tasklet_func(struct tasklet_struct *t) * will waste a bit of time (we already know * the command was bad), it can't cause any * errors since it's possible it would have - * taken place anyway if this tasklet got + * taken place anyway if this bh work got * delayed. Allowing the transfer to take place * avoids races and keeps things simple. */ @@ -2706,7 +2706,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) smp_wmb(); /* drain writebuffer */ set_bit(EVENT_CMD_COMPLETE, &host->pending_events); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); dw_mci_start_fault_timer(host); } @@ -2774,7 +2774,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) set_bit(EVENT_DATA_COMPLETE, &host->pending_events); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); spin_unlock(&host->irq_lock); } @@ -2793,7 +2793,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) dw_mci_read_data_pio(host, true); } set_bit(EVENT_DATA_COMPLETE, &host->pending_events); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); spin_unlock(&host->irq_lock); } @@ -3098,7 +3098,7 @@ static void dw_mci_cmd11_timer(struct timer_list *t) host->cmd_status = SDMMC_INT_RTO; set_bit(EVENT_CMD_COMPLETE, &host->pending_events); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); } static void dw_mci_cto_timer(struct timer_list *t) @@ -3144,7 +3144,7 @@ static void dw_mci_cto_timer(struct timer_list *t) */ host->cmd_status = SDMMC_INT_RTO; set_bit(EVENT_CMD_COMPLETE, &host->pending_events); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); break; default: dev_warn(host->dev, "Unexpected command timeout, state %d\n", @@ -3195,7 +3195,7 @@ static void dw_mci_dto_timer(struct timer_list *t) host->data_status = SDMMC_INT_DRTO; set_bit(EVENT_DATA_ERROR, &host->pending_events); set_bit(EVENT_DATA_COMPLETE, &host->pending_events); - tasklet_schedule(&host->tasklet); + queue_work(system_bh_wq, &host->bh_work); break; default: dev_warn(host->dev, "Unexpected data timeout, state %d\n", @@ -3435,7 +3435,7 @@ int dw_mci_probe(struct dw_mci *host) else host->fifo_reg = host->regs + DATA_240A_OFFSET; - tasklet_setup(&host->tasklet, dw_mci_tasklet_func); + INIT_WORK(&host->bh_work, dw_mci_work_func); ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host); if (ret) diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 4ed81f94f7ca..b504f7ae513a 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -17,6 +17,7 @@ #include #include #include +#include enum dw_mci_state { STATE_IDLE = 0, @@ -89,12 +90,12 @@ struct dw_mci_dma_slave { * @stop_cmdr: Value to be loaded into CMDR when the stop command is * to be sent. * @dir_status: Direction of current transfer. - * @tasklet: Tasklet running the request state machine. + * @bh_work: Work running the request state machine. * @pending_events: Bitmask of events flagged by the interrupt handler - * to be processed by the tasklet. + * to be processed by bh work. * @completed_events: Bitmask of events which the state machine has * processed. - * @state: Tasklet state. + * @state: BH work state. * @queue: List of slots waiting for access to the controller. * @bus_hz: The rate of @mck in Hz. This forms the basis for MMC bus * rate and timeout calculations. @@ -194,7 +195,7 @@ struct dw_mci { u32 data_status; u32 stop_cmdr; u32 dir_status; - struct tasklet_struct tasklet; + struct work_struct bh_work; unsigned long pending_events; unsigned long completed_events; enum dw_mci_state state; diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index a8ee0df47148..335350a4e99a 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -28,6 +28,7 @@ #include #include #include +#include #define OMAP_MMC_REG_CMD 0x00 @@ -105,7 +106,7 @@ struct mmc_omap_slot { u16 power_mode; unsigned int fclk_freq; - struct tasklet_struct cover_tasklet; + struct work_struct cover_bh_work; struct timer_list cover_timer; unsigned cover_open; @@ -873,18 +874,18 @@ void omap_mmc_notify_cover_event(struct device *dev, int num, int is_closed) sysfs_notify(&slot->mmc->class_dev.kobj, NULL, "cover_switch"); } - tasklet_hi_schedule(&slot->cover_tasklet); + queue_work(system_bh_highpri_wq, &slot->cover_bh_work); } static void mmc_omap_cover_timer(struct timer_list *t) { struct mmc_omap_slot *slot = from_timer(slot, t, cover_timer); - tasklet_schedule(&slot->cover_tasklet); + queue_work(system_bh_wq, &slot->cover_bh_work); } -static void mmc_omap_cover_handler(struct tasklet_struct *t) +static void mmc_omap_cover_bh_handler(struct work_struct *t) { - struct mmc_omap_slot *slot = from_tasklet(slot, t, cover_tasklet); + struct mmc_omap_slot *slot = from_work(slot, t, cover_bh_work); int cover_open = mmc_omap_cover_is_open(slot); mmc_detect_change(slot->mmc, 0); @@ -1314,7 +1315,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id) if (slot->pdata->get_cover_state != NULL) { timer_setup(&slot->cover_timer, mmc_omap_cover_timer, 0); - tasklet_setup(&slot->cover_tasklet, mmc_omap_cover_handler); + INIT_WORK(&slot->cover_bh_work, mmc_omap_cover_bh_handler); } r = mmc_add_host(mmc); @@ -1333,7 +1334,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id) &dev_attr_cover_switch); if (r < 0) goto err_remove_slot_name; - tasklet_schedule(&slot->cover_tasklet); + queue_work(system_bh_wq, &slot->cover_bh_work); } return 0; @@ -1356,7 +1357,7 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot) if (slot->pdata->get_cover_state != NULL) device_remove_file(&mmc->class_dev, &dev_attr_cover_switch); - tasklet_kill(&slot->cover_tasklet); + cancel_work_sync(&slot->cover_bh_work); del_timer_sync(&slot->cover_timer); flush_workqueue(slot->host->mmc_omap_wq); diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index cb9152c6a65d..e067c7f5c537 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -107,7 +107,7 @@ static void sdhci_bcm_kona_sd_init(struct sdhci_host *host) * Software emulation of the SD card insertion/removal. Set insert=1 for insert * and insert=0 for removal. The card detection is done by GPIO. For Broadcom * IP to function properly the bit 0 of CORESTAT register needs to be set/reset - * to generate the CD IRQ handled in sdhci.c which schedules card_tasklet. +* to generate the CD IRQ handled in sdhci.c */ static int sdhci_bcm_kona_sd_card_emulate(struct sdhci_host *host, int insert) { diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index b5a2f2f25ad9..aea14bf3e2e8 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #define DRIVER_NAME "tifm_sd" @@ -97,7 +98,7 @@ struct tifm_sd { unsigned int clk_div; unsigned long timeout_jiffies; - struct tasklet_struct finish_tasklet; + struct work_struct finish_bh_work; struct timer_list timer; struct mmc_request *req; @@ -463,7 +464,7 @@ static void tifm_sd_check_status(struct tifm_sd *host) } } finish_request: - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); } /* Called from interrupt handler */ @@ -723,9 +724,9 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) mmc_request_done(mmc, mrq); } -static void tifm_sd_end_cmd(struct tasklet_struct *t) +static void tifm_sd_end_cmd(struct work_struct *t) { - struct tifm_sd *host = from_tasklet(host, t, finish_tasklet); + struct tifm_sd *host = from_work(host, t, finish_bh_work); struct tifm_dev *sock = host->dev; struct mmc_host *mmc = tifm_get_drvdata(sock); struct mmc_request *mrq; @@ -960,7 +961,7 @@ static int tifm_sd_probe(struct tifm_dev *sock) */ mmc->max_busy_timeout = TIFM_MMCSD_REQ_TIMEOUT_MS; - tasklet_setup(&host->finish_tasklet, tifm_sd_end_cmd); + INIT_WORK(&host->finish_bh_work, tifm_sd_end_cmd); timer_setup(&host->timer, tifm_sd_abort, 0); mmc->ops = &tifm_sd_ops; @@ -999,7 +1000,7 @@ static void tifm_sd_remove(struct tifm_dev *sock) writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); spin_unlock_irqrestore(&sock->lock, flags); - tasklet_kill(&host->finish_tasklet); + cancel_work_sync(&host->finish_bh_work); spin_lock_irqsave(&sock->lock, flags); if (host->req) { @@ -1009,7 +1010,7 @@ static void tifm_sd_remove(struct tifm_dev *sock) host->req->cmd->error = -ENOMEDIUM; if (host->req->stop) host->req->stop->error = -ENOMEDIUM; - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); } spin_unlock_irqrestore(&sock->lock, flags); mmc_remove_host(mmc); diff --git a/drivers/mmc/host/uniphier-sd.c b/drivers/mmc/host/uniphier-sd.c index 1404989e6151..417fd13efdfd 100644 --- a/drivers/mmc/host/uniphier-sd.c +++ b/drivers/mmc/host/uniphier-sd.c @@ -90,9 +90,9 @@ static void uniphier_sd_dma_endisable(struct tmio_mmc_host *host, int enable) } /* external DMA engine */ -static void uniphier_sd_external_dma_issue(struct tasklet_struct *t) +static void uniphier_sd_external_dma_issue(struct work_struct *t) { - struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue); + struct tmio_mmc_host *host = from_work(host, t, dma_issue); struct uniphier_sd_priv *priv = uniphier_sd_priv(host); uniphier_sd_dma_endisable(host, 1); @@ -199,7 +199,7 @@ static void uniphier_sd_external_dma_request(struct tmio_mmc_host *host, host->chan_rx = chan; host->chan_tx = chan; - tasklet_setup(&host->dma_issue, uniphier_sd_external_dma_issue); + INIT_WORK(&host->dma_issue, uniphier_sd_external_dma_issue); } static void uniphier_sd_external_dma_release(struct tmio_mmc_host *host) @@ -236,9 +236,9 @@ static const struct tmio_mmc_dma_ops uniphier_sd_external_dma_ops = { .dataend = uniphier_sd_external_dma_dataend, }; -static void uniphier_sd_internal_dma_issue(struct tasklet_struct *t) +static void uniphier_sd_internal_dma_issue(struct work_struct *t) { - struct tmio_mmc_host *host = from_tasklet(host, t, dma_issue); + struct tmio_mmc_host *host = from_work(host, t, dma_issue); unsigned long flags; spin_lock_irqsave(&host->lock, flags); @@ -317,7 +317,7 @@ static void uniphier_sd_internal_dma_request(struct tmio_mmc_host *host, host->chan_tx = (void *)0xdeadbeaf; - tasklet_setup(&host->dma_issue, uniphier_sd_internal_dma_issue); + INIT_WORK(&host->dma_issue, uniphier_sd_internal_dma_issue); } static void uniphier_sd_internal_dma_release(struct tmio_mmc_host *host) diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index ba6044b16e07..f77457105ec3 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -12,6 +12,7 @@ #include #include +#include #define DRV_NAME "via_sdmmc" @@ -307,7 +308,7 @@ struct via_crdr_mmc_host { struct sdhcreg pm_sdhc_reg; struct work_struct carddet_work; - struct tasklet_struct finish_tasklet; + struct work_struct finish_bh_work; struct timer_list timer; spinlock_t lock; @@ -643,7 +644,7 @@ static void via_sdc_finish_data(struct via_crdr_mmc_host *host) if (data->stop) via_sdc_send_command(host, data->stop); else - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); } static void via_sdc_finish_command(struct via_crdr_mmc_host *host) @@ -653,7 +654,7 @@ static void via_sdc_finish_command(struct via_crdr_mmc_host *host) host->cmd->error = 0; if (!host->cmd->data) - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); host->cmd = NULL; } @@ -682,7 +683,7 @@ static void via_sdc_request(struct mmc_host *mmc, struct mmc_request *mrq) status = readw(host->sdhc_mmiobase + VIA_CRDR_SDSTATUS); if (!(status & VIA_CRDR_SDSTS_SLOTG) || host->reject) { host->mrq->cmd->error = -ENOMEDIUM; - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); } else { via_sdc_send_command(host, mrq->cmd); } @@ -848,7 +849,7 @@ static void via_sdc_cmd_isr(struct via_crdr_mmc_host *host, u16 intmask) host->cmd->error = -EILSEQ; if (host->cmd->error) - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); else if (intmask & VIA_CRDR_SDSTS_CRD) via_sdc_finish_command(host); } @@ -955,16 +956,16 @@ static void via_sdc_timeout(struct timer_list *t) sdhost->cmd->error = -ETIMEDOUT; else sdhost->mrq->cmd->error = -ETIMEDOUT; - tasklet_schedule(&sdhost->finish_tasklet); + queue_work(system_bh_wq, &sdhost->finish_bh_work); } } spin_unlock_irqrestore(&sdhost->lock, flags); } -static void via_sdc_tasklet_finish(struct tasklet_struct *t) +static void via_sdc_finish_bh_work(struct work_struct *t) { - struct via_crdr_mmc_host *host = from_tasklet(host, t, finish_tasklet); + struct via_crdr_mmc_host *host = from_work(host, t, finish_bh_work); unsigned long flags; struct mmc_request *mrq; @@ -1005,7 +1006,7 @@ static void via_sdc_card_detect(struct work_struct *work) pr_err("%s: Card removed during transfer!\n", mmc_hostname(host->mmc)); host->mrq->cmd->error = -ENOMEDIUM; - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); } spin_unlock_irqrestore(&host->lock, flags); @@ -1051,7 +1052,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host) INIT_WORK(&host->carddet_work, via_sdc_card_detect); - tasklet_setup(&host->finish_tasklet, via_sdc_tasklet_finish); + INIT_WORK(&host->finish_bh_work, via_sdc_finish_bh_work); addrbase = host->sdhc_mmiobase; writel(0x0, addrbase + VIA_CRDR_SDINTMASK); @@ -1193,7 +1194,7 @@ static void via_sd_remove(struct pci_dev *pcidev) sdhost->mrq->cmd->error = -ENOMEDIUM; if (sdhost->mrq->stop) sdhost->mrq->stop->error = -ENOMEDIUM; - tasklet_schedule(&sdhost->finish_tasklet); + queue_work(system_bh_wq, &sdhost->finish_bh_work); } spin_unlock_irqrestore(&sdhost->lock, flags); @@ -1203,7 +1204,7 @@ static void via_sd_remove(struct pci_dev *pcidev) del_timer_sync(&sdhost->timer); - tasklet_kill(&sdhost->finish_tasklet); + cancel_work_sync(&sdhost->finish_bh_work); /* switch off power */ gatt = readb(sdhost->pcictrl_mmiobase + VIA_CRDR_PCICLKGATT); diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index f0562f712d98..6e20405d0430 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -459,7 +459,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) * FIFO threshold interrupts properly. */ if ((data->blocks * data->blksz - data->bytes_xfered) < 16) - tasklet_schedule(&host->fifo_tasklet); + queue_work(system_bh_wq, &host->fifo_bh_work); } static void wbsd_fill_fifo(struct wbsd_host *host) @@ -524,7 +524,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) * 'FIFO empty' under certain conditions. So we * need to be a bit more pro-active. */ - tasklet_schedule(&host->fifo_tasklet); + queue_work(system_bh_wq, &host->fifo_bh_work); } static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) @@ -746,7 +746,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_command *cmd; /* - * Disable tasklets to avoid a deadlock. + * Disable bh works to avoid a deadlock. */ spin_lock_bh(&host->lock); @@ -821,7 +821,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) * Dirty fix for hardware bug. */ if (host->dma == -1) - tasklet_schedule(&host->fifo_tasklet); + queue_work(system_bh_wq, &host->fifo_bh_work); spin_unlock_bh(&host->lock); @@ -961,13 +961,13 @@ static void wbsd_reset_ignore(struct timer_list *t) * Card status might have changed during the * blackout. */ - tasklet_schedule(&host->card_tasklet); + queue_work(system_bh_wq, &host->card_bh_work); spin_unlock_bh(&host->lock); } /* - * Tasklets + * BH Works */ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) @@ -987,9 +987,9 @@ static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) return host->mrq->cmd->data; } -static void wbsd_tasklet_card(struct tasklet_struct *t) +static void wbsd_card_bh_work(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, card_tasklet); + struct wbsd_host *host = from_work(host, t, card_bh_work); u8 csr; int delay = -1; @@ -1020,7 +1020,7 @@ static void wbsd_tasklet_card(struct tasklet_struct *t) wbsd_reset(host); host->mrq->cmd->error = -ENOMEDIUM; - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); } delay = 0; @@ -1036,9 +1036,9 @@ static void wbsd_tasklet_card(struct tasklet_struct *t) mmc_detect_change(host->mmc, msecs_to_jiffies(delay)); } -static void wbsd_tasklet_fifo(struct tasklet_struct *t) +static void wbsd_fifo_bh_work(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, fifo_tasklet); + struct wbsd_host *host = from_work(host, t, fifo_bh_work); struct mmc_data *data; spin_lock(&host->lock); @@ -1060,16 +1060,16 @@ static void wbsd_tasklet_fifo(struct tasklet_struct *t) */ if (host->num_sg == 0) { wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); } end: spin_unlock(&host->lock); } -static void wbsd_tasklet_crc(struct tasklet_struct *t) +static void wbsd_crc_bh_work(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, crc_tasklet); + struct wbsd_host *host = from_work(host, t, crc_bh_work); struct mmc_data *data; spin_lock(&host->lock); @@ -1085,15 +1085,15 @@ static void wbsd_tasklet_crc(struct tasklet_struct *t) data->error = -EILSEQ; - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); end: spin_unlock(&host->lock); } -static void wbsd_tasklet_timeout(struct tasklet_struct *t) +static void wbsd_timeout_bh_work(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, timeout_tasklet); + struct wbsd_host *host = from_work(host, t, timeout_bh_work); struct mmc_data *data; spin_lock(&host->lock); @@ -1109,15 +1109,15 @@ static void wbsd_tasklet_timeout(struct tasklet_struct *t) data->error = -ETIMEDOUT; - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); end: spin_unlock(&host->lock); } -static void wbsd_tasklet_finish(struct tasklet_struct *t) +static void wbsd_finish_bh_work(struct work_struct *t) { - struct wbsd_host *host = from_tasklet(host, t, finish_tasklet); + struct wbsd_host *host = from_work(host, t, finish_bh_work); struct mmc_data *data; spin_lock(&host->lock); @@ -1156,18 +1156,18 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id) host->isr |= isr; /* - * Schedule tasklets as needed. + * Schedule bh work as needed. */ if (isr & WBSD_INT_CARD) - tasklet_schedule(&host->card_tasklet); + queue_work(system_bh_wq, &host->card_bh_work); if (isr & WBSD_INT_FIFO_THRE) - tasklet_schedule(&host->fifo_tasklet); + queue_work(system_bh_wq, &host->fifo_bh_work); if (isr & WBSD_INT_CRC) - tasklet_hi_schedule(&host->crc_tasklet); + queue_work(system_bh_highpri_wq, &host->crc_bh_work); if (isr & WBSD_INT_TIMEOUT) - tasklet_hi_schedule(&host->timeout_tasklet); + queue_work(system_bh_highpri_wq, &host->timeout_bh_work); if (isr & WBSD_INT_TC) - tasklet_schedule(&host->finish_tasklet); + queue_work(system_bh_wq, &host->finish_bh_work); return IRQ_HANDLED; } @@ -1443,13 +1443,13 @@ static int wbsd_request_irq(struct wbsd_host *host, int irq) int ret; /* - * Set up tasklets. Must be done before requesting interrupt. + * Set up bh works. Must be done before requesting interrupt. */ - tasklet_setup(&host->card_tasklet, wbsd_tasklet_card); - tasklet_setup(&host->fifo_tasklet, wbsd_tasklet_fifo); - tasklet_setup(&host->crc_tasklet, wbsd_tasklet_crc); - tasklet_setup(&host->timeout_tasklet, wbsd_tasklet_timeout); - tasklet_setup(&host->finish_tasklet, wbsd_tasklet_finish); + INIT_WORK(&host->card_bh_work, wbsd_card_bh_work); + INIT_WORK(&host->fifo_bh_work, wbsd_fifo_bh_work); + INIT_WORK(&host->crc_bh_work, wbsd_crc_bh_work); + INIT_WORK(&host->timeout_bh_work, wbsd_timeout_bh_work); + INIT_WORK(&host->finish_bh_work, wbsd_finish_bh_work); /* * Allocate interrupt. @@ -1472,11 +1472,11 @@ static void wbsd_release_irq(struct wbsd_host *host) host->irq = 0; - tasklet_kill(&host->card_tasklet); - tasklet_kill(&host->fifo_tasklet); - tasklet_kill(&host->crc_tasklet); - tasklet_kill(&host->timeout_tasklet); - tasklet_kill(&host->finish_tasklet); + cancel_work_sync(&host->card_bh_work); + cancel_work_sync(&host->fifo_bh_work); + cancel_work_sync(&host->crc_bh_work); + cancel_work_sync(&host->timeout_bh_work); + cancel_work_sync(&host->finish_bh_work); } /* diff --git a/drivers/mmc/host/wbsd.h b/drivers/mmc/host/wbsd.h index be30b4d8ce4c..970886831305 100644 --- a/drivers/mmc/host/wbsd.h +++ b/drivers/mmc/host/wbsd.h @@ -171,11 +171,11 @@ struct wbsd_host int irq; /* Interrupt */ int dma; /* DMA channel */ - struct tasklet_struct card_tasklet; /* Tasklet structures */ - struct tasklet_struct fifo_tasklet; - struct tasklet_struct crc_tasklet; - struct tasklet_struct timeout_tasklet; - struct tasklet_struct finish_tasklet; + struct work_struct card_bh_work; /* Work structures */ + struct work_struct fifo_bh_work; + struct work_struct crc_bh_work; + struct work_struct timeout_bh_work; + struct work_struct finish_bh_work; struct timer_list ignore_timer; /* Ignore detection timer */ }; From 1535085f99c554460966995ecec934b74185cd5c Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Fri, 5 Jul 2024 12:51:23 +0200 Subject: [PATCH 19/26] mmc: sdhci_am654: Constify struct regmap_config `sdhci_am654_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20240705-sdhci_am654-const-regmap_config-v1-1-c166a8d48a66@gmail.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci_am654.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c index 17ad32cfc0c3..64e10f7c9faa 100644 --- a/drivers/mmc/host/sdhci_am654.c +++ b/drivers/mmc/host/sdhci_am654.c @@ -90,7 +90,7 @@ /* Command Queue Host Controller Interface Base address */ #define SDHCI_AM654_CQE_BASE_ADDR 0x200 -static struct regmap_config sdhci_am654_regmap_config = { +static const struct regmap_config sdhci_am654_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, From f21adcb86653e2b2784bbc0f180c332b50c404df Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Wed, 12 Jun 2024 18:52:37 -0400 Subject: [PATCH 20/26] mmc: dw_mmc: Add support for platform specific eMMC HW reset This commit adds a new callback to allow drivers to support platform specific eMMC HW reset. Reviewed-by: David Thompson Signed-off-by: Liming Sun Link: https://lore.kernel.org/r/3df02ffa8bdaa74f5261c8914d2545b97fb3478a.1718213918.git.limings@nvidia.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 6 ++++++ drivers/mmc/host/dw_mmc.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 7195de2cde44..2333ef4893ee 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1617,6 +1617,7 @@ static void dw_mci_hw_reset(struct mmc_host *mmc) { struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; + const struct dw_mci_drv_data *drv_data = host->drv_data; int reset; if (host->use_dma == TRANS_MODE_IDMAC) @@ -1626,6 +1627,11 @@ static void dw_mci_hw_reset(struct mmc_host *mmc) SDMMC_CTRL_FIFO_RESET)) return; + if (drv_data && drv_data->hw_reset) { + drv_data->hw_reset(host); + return; + } + /* * According to eMMC spec, card reset procedure: * tRstW >= 1us: RST_n pulse width diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index b504f7ae513a..6447b916990d 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -566,6 +566,7 @@ struct dw_mci_slot { * @execute_tuning: implementation specific tuning procedure. * @set_data_timeout: implementation specific timeout. * @get_drto_clks: implementation specific cycle count for data read timeout. + * @hw_reset: implementation specific HW reset. * * Provide controller implementation specific extensions. The usage of this * data structure is fully optional and usage of each member in this structure @@ -586,5 +587,6 @@ struct dw_mci_drv_data { void (*set_data_timeout)(struct dw_mci *host, unsigned int timeout_ns); u32 (*get_drto_clks)(struct dw_mci *host); + void (*hw_reset)(struct dw_mci *host); }; #endif /* _DW_MMC_H_ */ From c17aecf85800ac91ec67fd7134107526a4bab3b0 Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Wed, 12 Jun 2024 18:52:38 -0400 Subject: [PATCH 21/26] mmc: dw_mmc-bluefield: Add support for eMMC HW reset The eMMC RST_N register is implemented as secure register on the BlueField SoC and controlled by TF-A. This commit sends an SMC call to TF-A for the eMMC HW reset. Reviewed-by: David Thompson Signed-off-by: Liming Sun Link: https://lore.kernel.org/r/2c459196c6867e325f9386ec0559efea464cfdd6.1718213918.git.limings@nvidia.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-bluefield.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c index 4747e5698f48..24e0b604b405 100644 --- a/drivers/mmc/host/dw_mmc-bluefield.c +++ b/drivers/mmc/host/dw_mmc-bluefield.c @@ -3,6 +3,7 @@ * Copyright (C) 2018 Mellanox Technologies. */ +#include #include #include #include @@ -20,6 +21,9 @@ #define BLUEFIELD_UHS_REG_EXT_SAMPLE 2 #define BLUEFIELD_UHS_REG_EXT_DRIVE 4 +/* SMC call for RST_N */ +#define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007 + static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios) { u32 reg; @@ -34,8 +38,20 @@ static void dw_mci_bluefield_set_ios(struct dw_mci *host, struct mmc_ios *ios) mci_writel(host, UHS_REG_EXT, reg); } +static void dw_mci_bluefield_hw_reset(struct dw_mci *host) +{ + struct arm_smccc_res res = { 0 }; + + arm_smccc_smc(BLUEFIELD_SMC_SET_EMMC_RST_N, 0, 0, 0, 0, 0, 0, 0, + &res); + + if (res.a0) + pr_err("RST_N failed.\n"); +} + static const struct dw_mci_drv_data bluefield_drv_data = { - .set_ios = dw_mci_bluefield_set_ios + .set_ios = dw_mci_bluefield_set_ios, + .hw_reset = dw_mci_bluefield_hw_reset }; static const struct of_device_id dw_mci_bluefield_match[] = { From ca04fff3886d449a6549929d487b4179214144fd Mon Sep 17 00:00:00 2001 From: Bastien Curutchet Date: Thu, 11 Jul 2024 10:18:38 +0200 Subject: [PATCH 22/26] mmc: davinci_mmc: report all possible bus widths A dev_info() at probe's end() report the supported bus width. It never reports 8-bits width while the driver can handle it. Update the info message at then end of the probe to report the use of 8-bits data when needed. Signed-off-by: Bastien Curutchet Link: https://lore.kernel.org/r/20240711081838.47256-3-bastien.curutchet@bootlin.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/davinci_mmc.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index c302eb380e42..9cbde800685d 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1187,7 +1187,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) struct mmc_davinci_host *host = NULL; struct mmc_host *mmc = NULL; struct resource *r, *mem = NULL; - int ret, irq; + int ret, irq, bus_width; size_t mem_size; const struct platform_device_id *id_entry; @@ -1317,9 +1317,14 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) rename_region(mem, mmc_hostname(mmc)); + if (mmc->caps & MMC_CAP_8_BIT_DATA) + bus_width = 8; + else if (mmc->caps & MMC_CAP_4_BIT_DATA) + bus_width = 4; + else + bus_width = 1; dev_info(mmc_dev(host->mmc), "Using %s, %d-bit mode\n", - host->use_dma ? "DMA" : "PIO", - (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1); + host->use_dma ? "DMA" : "PIO", bus_width); return 0; From cb1f1c7d38b56b6289e4e6dde554321be2dd4530 Mon Sep 17 00:00:00 2001 From: Stanislav Jakubek Date: Tue, 9 Jul 2024 08:30:18 +0200 Subject: [PATCH 23/26] dt-bindings: mmc: sdhci-sprd: convert to YAML Covert the Spreadtrum SDHCI controller bindings to DT schema. Rename the file to match compatible. Drop assigned-* properties as these should not be needed. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Stanislav Jakubek Link: https://lore.kernel.org/r/ZozY+tOkzK9yfjbo@standask-GA-A55M-S2HP Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/sdhci-sprd.txt | 67 ----------- .../bindings/mmc/sprd,sdhci-r11.yaml | 112 ++++++++++++++++++ 2 files changed, 112 insertions(+), 67 deletions(-) delete mode 100644 Documentation/devicetree/bindings/mmc/sdhci-sprd.txt create mode 100644 Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml diff --git a/Documentation/devicetree/bindings/mmc/sdhci-sprd.txt b/Documentation/devicetree/bindings/mmc/sdhci-sprd.txt deleted file mode 100644 index eb7eb1b529f0..000000000000 --- a/Documentation/devicetree/bindings/mmc/sdhci-sprd.txt +++ /dev/null @@ -1,67 +0,0 @@ -* Spreadtrum SDHCI controller (sdhci-sprd) - -The Secure Digital (SD) Host controller on Spreadtrum SoCs provides an interface -for MMC, SD and SDIO types of cards. - -This file documents differences between the core properties in mmc.txt -and the properties used by the sdhci-sprd driver. - -Required properties: -- compatible: Should contain "sprd,sdhci-r11". -- reg: physical base address of the controller and length. -- interrupts: Interrupts used by the SDHCI controller. -- clocks: Should contain phandle for the clock feeding the SDHCI controller -- clock-names: Should contain the following: - "sdio" - SDIO source clock (required) - "enable" - gate clock which used for enabling/disabling the device (required) - "2x_enable" - gate clock controlling the device for some special platforms (optional) - -Optional properties: -- assigned-clocks: the same with "sdio" clock -- assigned-clock-parents: the default parent of "sdio" clock -- pinctrl-names: should be "default", "state_uhs" -- pinctrl-0: should contain default/high speed pin control -- pinctrl-1: should contain uhs mode pin control - -PHY DLL delays are used to delay the data valid window, and align the window -to sampling clock. PHY DLL delays can be configured by following properties, -and each property contains 4 cells which are used to configure the clock data -write line delay value, clock read command line delay value, clock read data -positive edge delay value and clock read data negative edge delay value. -Each cell's delay value unit is cycle of the PHY clock. - -- sprd,phy-delay-legacy: Delay value for legacy timing. -- sprd,phy-delay-sd-highspeed: Delay value for SD high-speed timing. -- sprd,phy-delay-sd-uhs-sdr50: Delay value for SD UHS SDR50 timing. -- sprd,phy-delay-sd-uhs-sdr104: Delay value for SD UHS SDR50 timing. -- sprd,phy-delay-mmc-highspeed: Delay value for MMC high-speed timing. -- sprd,phy-delay-mmc-ddr52: Delay value for MMC DDR52 timing. -- sprd,phy-delay-mmc-hs200: Delay value for MMC HS200 timing. -- sprd,phy-delay-mmc-hs400: Delay value for MMC HS400 timing. -- sprd,phy-delay-mmc-hs400es: Delay value for MMC HS400 enhanced strobe timing. - -Examples: - -sdio0: sdio@20600000 { - compatible = "sprd,sdhci-r11"; - reg = <0 0x20600000 0 0x1000>; - interrupts = ; - - clock-names = "sdio", "enable"; - clocks = <&ap_clk CLK_EMMC_2X>, - <&apahb_gate CLK_EMMC_EB>; - assigned-clocks = <&ap_clk CLK_EMMC_2X>; - assigned-clock-parents = <&rpll CLK_RPLL_390M>; - - pinctrl-names = "default", "state_uhs"; - pinctrl-0 = <&sd0_pins_default>; - pinctrl-1 = <&sd0_pins_uhs>; - - sprd,phy-delay-sd-uhs-sdr104 = <0x3f 0x7f 0x2e 0x2e>; - bus-width = <8>; - non-removable; - no-sdio; - no-sd; - cap-mmc-hw-reset; - status = "okay"; -}; diff --git a/Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml b/Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml new file mode 100644 index 000000000000..b08081bc018b --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sprd,sdhci-r11.yaml @@ -0,0 +1,112 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/sprd,sdhci-r11.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Spreadtrum SDHCI controller + +maintainers: + - Orson Zhai + - Baolin Wang + - Chunyan Zhang + +properties: + compatible: + const: sprd,sdhci-r11 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 2 + items: + - description: SDIO source clock + - description: gate clock for enabling/disabling the device + - description: gate clock controlling the device for some special platforms (optional) + + clock-names: + minItems: 2 + items: + - const: sdio + - const: enable + - const: 2x_enable + + pinctrl-0: + description: default/high speed pin control + maxItems: 1 + + pinctrl-1: + description: UHS mode pin control + maxItems: 1 + + pinctrl-names: + minItems: 1 + items: + - const: default + - const: state_uhs + +patternProperties: + "^sprd,phy-delay-(legacy|mmc-(ddr52|highspeed|hs[24]00|hs400es)|sd-(highspeed|uhs-sdr(50|104)))$": + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: clock data write line delay value + - description: clock read command line delay value + - description: clock read data positive edge delay value + - description: clock read data negative edge delay value + description: + PHY DLL delays are used to delay the data valid window, and align + the window to the sampling clock. Each cell's delay value unit is + cycle of the PHY clock. + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +allOf: + - $ref: sdhci-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + + mmc@50430000 { + compatible = "sprd,sdhci-r11"; + reg = <0x50430000 0x1000>; + interrupts = ; + + clocks = <&aon_prediv CLK_EMMC_2X>, + <&apahb_gate CLK_EMMC_EB>, + <&aon_gate CLK_EMMC_2X_EN>; + clock-names = "sdio", "enable", "2x_enable"; + + pinctrl-0 = <&sd0_pins_default>; + pinctrl-1 = <&sd0_pins_uhs>; + pinctrl-names = "default", "state_uhs"; + + bus-width = <8>; + cap-mmc-hw-reset; + mmc-hs400-enhanced-strobe; + mmc-hs400-1_8v; + mmc-hs200-1_8v; + mmc-ddr-1_8v; + non-removable; + no-sdio; + no-sd; + + sprd,phy-delay-mmc-ddr52 = <0x3f 0x75 0x14 0x14>; + sprd,phy-delay-mmc-hs200 = <0x0 0x8c 0x8c 0x8c>; + sprd,phy-delay-mmc-hs400 = <0x44 0x7f 0x2e 0x2e>; + sprd,phy-delay-mmc-hs400es = <0x3f 0x3f 0x2e 0x2e>; + }; +... From a52b67bdf44cc673e3232b8456b2d85c9988fcad Mon Sep 17 00:00:00 2001 From: Ciprian Costea Date: Mon, 8 Jul 2024 15:10:16 +0300 Subject: [PATCH 24/26] mmc: sdhci-esdhc-imx: disable card detect wake for S32G based platforms In case of S32G based platforms, GPIO CD used for card detect wake mechanism is not available. For this scenario the newly introduced flag 'ESDHC_FLAG_SKIP_CD_WAKE' is used. Signed-off-by: Ciprian Costea Reviewed-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240708121018.246476-2-ciprianmarian.costea@oss.nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 40a6e2f8145a..21d984a77be8 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -201,6 +201,9 @@ /* ERR004536 is not applicable for the IP */ #define ESDHC_FLAG_SKIP_ERR004536 BIT(17) +/* The IP does not have GPIO CD wake capabilities */ +#define ESDHC_FLAG_SKIP_CD_WAKE BIT(18) + enum wp_types { ESDHC_WP_NONE, /* no WP, neither controller nor gpio */ ESDHC_WP_CONTROLLER, /* mmc controller internal WP */ @@ -298,7 +301,7 @@ static struct esdhc_soc_data usdhc_s32g2_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 | ESDHC_FLAG_HS400 | ESDHC_FLAG_HS400_ES - | ESDHC_FLAG_SKIP_ERR004536, + | ESDHC_FLAG_SKIP_ERR004536 | ESDHC_FLAG_SKIP_CD_WAKE, }; static struct esdhc_soc_data usdhc_imx7ulp_data = { @@ -1726,7 +1729,8 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) host->mmc->caps |= MMC_CAP_1_8V_DDR | MMC_CAP_3_3V_DDR; /* GPIO CD can be set as a wakeup source */ - host->mmc->caps |= MMC_CAP_CD_WAKE; + if (!(imx_data->socdata->flags & ESDHC_FLAG_SKIP_CD_WAKE)) + host->mmc->caps |= MMC_CAP_CD_WAKE; if (!(imx_data->socdata->flags & ESDHC_FLAG_HS200)) host->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200; From 63e555d9bf76dc710842c13edb7d0986149f16a3 Mon Sep 17 00:00:00 2001 From: Ciprian Costea Date: Mon, 8 Jul 2024 15:10:17 +0300 Subject: [PATCH 25/26] mmc: sdhci-esdhc-imx: obtain the 'per' clock rate after its enablement The I.MX SDHCI driver assumes that the frequency of the 'per' clock can be obtained even on disabled clocks, which is not always the case. According to 'clk_get_rate' documentation, it is only valid once the clock source has been enabled. Signed-off-by: Ciprian Costea Reviewed-by: Haibo Chen Acked-by: Adrian Hunter Link: https://lore.kernel.org/r/20240708121018.246476-3-ciprianmarian.costea@oss.nxp.com Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 21d984a77be8..8f0bc6dca2b0 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1709,7 +1709,6 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) } pltfm_host->clk = imx_data->clk_per; - pltfm_host->clock = clk_get_rate(pltfm_host->clk); err = clk_prepare_enable(imx_data->clk_per); if (err) goto free_sdhci; @@ -1720,6 +1719,13 @@ static int sdhci_esdhc_imx_probe(struct platform_device *pdev) if (err) goto disable_ipg_clk; + pltfm_host->clock = clk_get_rate(pltfm_host->clk); + if (!pltfm_host->clock) { + dev_err(mmc_dev(host->mmc), "could not get clk rate\n"); + err = -EINVAL; + goto disable_ahb_clk; + } + imx_data->pinctrl = devm_pinctrl_get(&pdev->dev); if (IS_ERR(imx_data->pinctrl)) dev_warn(mmc_dev(host->mmc), "could not get pinctrl\n"); From b85e021853976aaebd3788e7e721020570754199 Mon Sep 17 00:00:00 2001 From: Ciprian Costea Date: Mon, 8 Jul 2024 15:10:18 +0300 Subject: [PATCH 26/26] MAINTAINERS: add 's32@nxp.com' as relevant mailing list for 'sdhci-esdhc-imx' driver Since NXP S32G2 and S32G3 SoCs share the SDHCI controller with I.MX platforms it would be valuable to add 's32@nxp.com' as a relevant mailing list in this area. Signed-off-by: Ciprian Costea Acked-by: Adrian Hunter Acked-by: Haibo Chen Link: https://lore.kernel.org/r/20240708121018.246476-4-ciprianmarian.costea@oss.nxp.com Signed-off-by: Ulf Hansson --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5ef1e83e0fd7..08d167d53a90 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20098,6 +20098,7 @@ SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) NXP i.MX DRIVER M: Haibo Chen L: imx@lists.linux.dev L: linux-mmc@vger.kernel.org +L: s32@nxp.com S: Maintained F: drivers/mmc/host/sdhci-esdhc-imx.c