From 40dfdd8d1449382cae3db2cefb1f5521ce19259b Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 26 Nov 2025 08:14:41 +0800 Subject: mmc: dw_mmc: Remove unused struct dma_pdata None of the variant drivers pass in dma_pdata, which means it's unused and could be removed. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 648b4a5641bf..b4ceca0167c4 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -255,8 +255,6 @@ struct dw_mci_dma_ops { void (*exit)(struct dw_mci *host); }; -struct dma_pdata; - /* Board platform data */ struct dw_mci_board { unsigned int bus_hz; /* Clock speed at the cclk_in pad */ @@ -276,7 +274,6 @@ struct dw_mci_board { struct reset_control *rstc; struct dw_mci_dma_ops *dma_ops; - struct dma_pdata *data; }; /* Support for longer data read timeout */ -- cgit v1.2.3 From 541fe77cc4d77799ac1b869e2183e1bdd716a1f2 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 26 Nov 2025 08:14:42 +0800 Subject: mmc: dw_mmc: add dw_mci_prepare_desc() for both of 32bit and 64bit DMA dw_mci_prepare_desc64() and dw_mci_prepare_desc32() duplicate a lot of code, add a new dw_mci_prepare_desc() to save the bits. No functional change intended. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/all/aUJatHzP5fr1zvRF@stanley.mountain Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 160 +++++++++++++++------------------------------- 1 file changed, 53 insertions(+), 107 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 07eb9f23b9d6..9c491cd5c368 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -575,23 +575,26 @@ static int dw_mci_idmac_init(struct dw_mci *host) return 0; } -static inline int dw_mci_prepare_desc64(struct dw_mci *host, - struct mmc_data *data, - unsigned int sg_len) +static inline int dw_mci_prepare_desc(struct dw_mci *host, struct mmc_data *data, + unsigned int sg_len, bool is_64bit) { unsigned int desc_len; - struct idmac_desc_64addr *desc_first, *desc_last, *desc; - u32 val; - int i; + struct idmac_desc *desc_first, *desc_last, *desc; + struct idmac_desc_64addr *desc64_first, *desc64_last, *desc64; + u32 val, des0; + int i, err; - desc_first = desc_last = desc = host->sg_cpu; + if (is_64bit) + desc64_first = desc64_last = desc64 = host->sg_cpu; + else + desc_first = desc_last = desc = host->sg_cpu; for (i = 0; i < sg_len; i++) { unsigned int length = sg_dma_len(&data->sg[i]); u64 mem_addr = sg_dma_address(&data->sg[i]); - for ( ; length ; desc++) { + while (length > 0) { desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? length : DW_MCI_DESC_DATA_LENGTH; @@ -603,113 +606,60 @@ static inline int dw_mci_prepare_desc64(struct dw_mci *host, * isn't still owned by IDMAC as IDMAC's write * ops and CPU's read ops are asynchronous. */ - if (readl_poll_timeout_atomic(&desc->des0, val, - !(val & IDMAC_DES0_OWN), - 10, 100 * USEC_PER_MSEC)) + if (is_64bit) + err = readl_poll_timeout_atomic(&desc64->des0, val, + IDMAC_OWN_CLR64(val), 10, 100 * USEC_PER_MSEC); + else + err = readl_poll_timeout_atomic(&desc->des0, val, + IDMAC_OWN_CLR64(val), 10, 100 * USEC_PER_MSEC); + if (err) goto err_own_bit; - /* - * Set the OWN bit and disable interrupts - * for this descriptor - */ - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | - IDMAC_DES0_CH; - - /* Buffer length */ - IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); - - /* Physical address to DMA to/from */ - desc->des4 = mem_addr & 0xffffffff; - desc->des5 = mem_addr >> 32; - - /* Update physical address for the next desc */ - mem_addr += desc_len; - - /* Save pointer to the last descriptor */ - desc_last = desc; - } - } - - /* Set first descriptor */ - desc_first->des0 |= IDMAC_DES0_FD; - - /* Set last descriptor */ - desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); - desc_last->des0 |= IDMAC_DES0_LD; - - return 0; -err_own_bit: - /* restore the descriptor chain as it's polluted */ - dev_dbg(host->dev, "descriptor is still owned by IDMAC.\n"); - memset(host->sg_cpu, 0, DESC_RING_BUF_SZ); - dw_mci_idmac_init(host); - return -EINVAL; -} - - -static inline int dw_mci_prepare_desc32(struct dw_mci *host, - struct mmc_data *data, - unsigned int sg_len) -{ - unsigned int desc_len; - struct idmac_desc *desc_first, *desc_last, *desc; - u32 val; - int i; - - desc_first = desc_last = desc = host->sg_cpu; - - for (i = 0; i < sg_len; i++) { - unsigned int length = sg_dma_len(&data->sg[i]); - - u32 mem_addr = sg_dma_address(&data->sg[i]); - - for ( ; length ; desc++) { - desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? - length : DW_MCI_DESC_DATA_LENGTH; - - length -= desc_len; - - /* - * Wait for the former clear OWN bit operation - * of IDMAC to make sure that this descriptor - * isn't still owned by IDMAC as IDMAC's write - * ops and CPU's read ops are asynchronous. - */ - if (readl_poll_timeout_atomic(&desc->des0, val, - IDMAC_OWN_CLR64(val), - 10, - 100 * USEC_PER_MSEC)) - goto err_own_bit; + des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + if (is_64bit) + desc64->des0 = des0; + else + desc->des0 = cpu_to_le32(des0); /* - * Set the OWN bit and disable interrupts - * for this descriptor + * 1. Set OWN bit and disable interrupts for this descriptor + * 2. Set Buffer length + * Set physical address to DMA to/from */ - desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | - IDMAC_DES0_DIC | - IDMAC_DES0_CH); - - /* Buffer length */ - IDMAC_SET_BUFFER1_SIZE(desc, desc_len); - - /* Physical address to DMA to/from */ - desc->des2 = cpu_to_le32(mem_addr); + if (is_64bit) { + desc64->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc64, desc_len); + desc64->des4 = mem_addr & 0xffffffff; + desc64->des5 = mem_addr >> 32; + } else { + IDMAC_SET_BUFFER1_SIZE(desc, desc_len); + desc->des2 = cpu_to_le32(mem_addr); + } /* Update physical address for the next desc */ mem_addr += desc_len; /* Save pointer to the last descriptor */ - desc_last = desc; + if (is_64bit) { + desc64_last = desc64; + desc64++; + } else { + desc_last = desc; + desc++; + } } } - /* Set first descriptor */ - desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); - - /* Set last descriptor */ - desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | - IDMAC_DES0_DIC)); - desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); + /* Set the first descriptor and the last descriptor */ + if (is_64bit) { + desc64_first->des0 |= IDMAC_DES0_FD; + desc64_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); + desc64_last->des0 |= IDMAC_DES0_LD; + } else { + desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); + desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC)); + desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); + } return 0; err_own_bit: @@ -725,11 +675,7 @@ static int dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) u32 temp; int ret; - if (host->dma_64bit_address == 1) - ret = dw_mci_prepare_desc64(host, host->data, sg_len); - else - ret = dw_mci_prepare_desc32(host, host->data, sg_len); - + ret = dw_mci_prepare_desc(host, host->data, sg_len, host->dma_64bit_address); if (ret) goto out; -- cgit v1.2.3 From 3d9ce86b52b7b328f5662436904789942a5eca1f Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Wed, 26 Nov 2025 12:12:51 +0530 Subject: mmc: sdhci-msm: Enable ICE for CQE-capable controllers with non-CQE cards Enable Inline Crypto Engine (ICE) support for CQE-capable sdhci-msm controllers when used with eMMC cards that do not support CQE. This addresses the scenario where: - The host controller supports CQE (and has CQHCI crypto infrastructure) - The eMMC card does not support CQE - Standard (non-CMDQ) requests need crypto support This allows hardware-accelerated encryption and decryption for standard requests on CQE-capable hardware by utilizing the existing CQHCI crypto register space even when CQE functionality is not available due to card limitations. The implementation: - Adds ICE register definitions for non-CQE crypto configuration - Implements per-request crypto setup via sdhci_msm_ice_cfg() - Hooks into the request path via mmc_host_ops.request for non-CQE requests - Uses CQHCI register space (NONCQ_CRYPTO_PARM/DUN) for crypto configuration With this, CQE-capable controllers can benefit from inline encryption when paired with non-CQE cards, improving performance for encrypted I/O while maintaining compatibility with existing CQE crypto support. Signed-off-by: Md Sadre Alam Acked-by: Adrian Hunter Acked-by: Eric Biggers Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 3b85233131b3..da356627d9de 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -157,6 +157,17 @@ #define CQHCI_VENDOR_CFG1 0xA00 #define CQHCI_VENDOR_DIS_RST_ON_CQ_EN (0x3 << 13) +/* non command queue crypto enable register*/ +#define NONCQ_CRYPTO_PARM 0x70 +#define NONCQ_CRYPTO_DUN 0x74 + +#define DISABLE_CRYPTO BIT(15) +#define CRYPTO_GENERAL_ENABLE BIT(1) +#define HC_VENDOR_SPECIFIC_FUNC4 0x260 + +#define ICE_HCI_PARAM_CCI GENMASK(7, 0) +#define ICE_HCI_PARAM_CE GENMASK(8, 8) + struct sdhci_msm_offset { u32 core_hc_mode; u32 core_mci_data_cnt; @@ -300,6 +311,7 @@ struct sdhci_msm_host { u32 dll_config; u32 ddr_config; bool vqmmc_enabled; + bool non_cqe_ice_init_done; }; static const struct sdhci_msm_offset *sdhci_priv_msm_offset(struct sdhci_host *host) @@ -2012,6 +2024,68 @@ static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile, return qcom_ice_evict_key(msm_host->ice, slot); } +static void sdhci_msm_non_cqe_ice_init(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = msm_host->mmc; + struct cqhci_host *cq_host = mmc->cqe_private; + u32 config; + + config = sdhci_readl(host, HC_VENDOR_SPECIFIC_FUNC4); + config &= ~DISABLE_CRYPTO; + sdhci_writel(host, config, HC_VENDOR_SPECIFIC_FUNC4); + config = cqhci_readl(cq_host, CQHCI_CFG); + config |= CRYPTO_GENERAL_ENABLE; + cqhci_writel(cq_host, config, CQHCI_CFG); +} + +static void sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host); + struct mmc_host *mmc = msm_host->mmc; + struct cqhci_host *cq_host = mmc->cqe_private; + unsigned int crypto_params = 0; + int key_index; + + if (mrq->crypto_ctx) { + if (!msm_host->non_cqe_ice_init_done) { + sdhci_msm_non_cqe_ice_init(host); + msm_host->non_cqe_ice_init_done = true; + } + + key_index = mrq->crypto_key_slot; + crypto_params = FIELD_PREP(ICE_HCI_PARAM_CE, 1) | + FIELD_PREP(ICE_HCI_PARAM_CCI, key_index); + + cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM); + cqhci_writel(cq_host, lower_32_bits(mrq->crypto_ctx->bc_dun[0]), + NONCQ_CRYPTO_DUN); + } else { + cqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM); + } + + /* Ensure crypto configuration is written before proceeding */ + wmb(); +} + +/* + * Handle non-CQE MMC requests with ICE crypto support. + * Configures ICE registers before passing the request to + * the standard SDHCI handler. + */ +static void sdhci_msm_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct sdhci_host *host = mmc_priv(mmc); + + /* Only need to handle non-CQE crypto requests in this path */ + if (mmc->caps2 & MMC_CAP2_CRYPTO) + sdhci_msm_ice_cfg(host, mrq); + + sdhci_request(mmc, mrq); +} + static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = { .keyslot_program = sdhci_msm_ice_keyslot_program, .keyslot_evict = sdhci_msm_ice_keyslot_evict, @@ -2762,6 +2836,9 @@ static int sdhci_msm_probe(struct platform_device *pdev) msm_host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_NEED_RSP_BUSY; +#ifdef CONFIG_MMC_CRYPTO + host->mmc_host_ops.request = sdhci_msm_request; +#endif /* Set the timeout value to max possible */ host->max_timeout_count = 0xF; -- cgit v1.2.3 From 3e487a634bc019166e452ea276f7522710eda9f4 Mon Sep 17 00:00:00 2001 From: Avri Altman Date: Fri, 28 Nov 2025 07:20:10 +0200 Subject: mmc: core: Adjust MDT beyond 2025 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JEDEC JESD84-B51B which was released in September 2025, increases the manufacturing year limit for eMMC devices. The eMMC manufacturing year is stored in a 4-bit field in the CID register. Originally, it covered 1997–2012. Later, with EXT_CSD_REV=8, it was extended up to 2025. Now, with EXT_CSD_REV=9, the range is rolled over by another 16 years, up to 2038. The mapping is as follows: cid[8..11] | rev ≤ 4 | 8 ≥ rev > 4 | rev > 8 --------------------------------------------- 0 | 1997 | 2013 | 2029 1 | 1998 | 2014 | 2030 2 | 1999 | 2015 | 2031 3 | 2000 | 2016 | 2032 4 | 2001 | 2017 | 2033 5 | 2002 | 2018 | 2034 6 | 2003 | 2019 | 2035 7 | 2004 | 2020 | 2036 8 | 2005 | 2021 | 2037 9 | 2006 | 2022 | 2038 10 | 2007 | 2023 | 11 | 2008 | 2024 | 12 | 2009 | 2025 | 13 | 2010 | | 2026 14 | 2011 | | 2027 15 | 2012 | | 2028 Signed-off-by: Avri Altman Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 7c86efb1044a..f744dd501842 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -671,7 +671,14 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) card->ext_csd.enhanced_rpmb_supported = (card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN_RPMB_REL_WR); + + if (card->ext_csd.rev >= 9) { + /* Adjust production date as per JEDEC JESD84-B51B September 2025 */ + if (card->cid.year < 2023) + card->cid.year += 16; + } } + out: return err; } -- cgit v1.2.3 From 263ff314cc5602599d481b0912a381555fcbad28 Mon Sep 17 00:00:00 2001 From: Avri Altman Date: Fri, 28 Nov 2025 07:20:11 +0200 Subject: mmc: core: Add quirk for incorrect manufacturing date Some eMMC vendors need to report manufacturing dates beyond 2025 but are reluctant to update the EXT_CSD revision from 8 to 9. Changing the Updating the EXT_CSD revision may involve additional testing or qualification steps with customers. To ease this transition and avoid a full re-qualification process, a workaround is needed. This patch introduces a temporary quirk that re-purposes the year codes corresponding to 2010, 2011, and 2012 to represent the years 2026, 2027, and 2028, respectively. This solution is only valid for this three-year period. After 2028, vendors must update their firmware to set EXT_CSD_REV=9 to continue reporting the correct manufacturing date in compliance with the JEDEC standard. The `MMC_QUIRK_BROKEN_MDT` is introduced and enabled for all Sandisk devices to handle this behavior. Signed-off-by: Avri Altman Signed-off-by: Ulf Hansson --- drivers/mmc/core/card.h | 6 ++++++ drivers/mmc/core/mmc.c | 5 +++++ drivers/mmc/core/quirks.h | 3 +++ include/linux/mmc/card.h | 1 + 4 files changed, 15 insertions(+) diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index 1200951bab08..a9619dd45270 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -89,6 +89,7 @@ struct mmc_fixup { #define CID_MANFID_MICRON 0x13 #define CID_MANFID_SAMSUNG 0x15 #define CID_MANFID_APACER 0x27 +#define CID_MANFID_SANDISK_MMC 0x45 #define CID_MANFID_SWISSBIT 0x5D #define CID_MANFID_KINGSTON 0x70 #define CID_MANFID_HYNIX 0x90 @@ -305,4 +306,9 @@ static inline int mmc_card_no_uhs_ddr50_tuning(const struct mmc_card *c) return c->quirks & MMC_QUIRK_NO_UHS_DDR50_TUNING; } +static inline int mmc_card_broken_mdt(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_BROKEN_MDT; +} + #endif diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index f744dd501842..8846550a8892 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -676,6 +676,11 @@ static int mmc_decode_ext_csd(struct mmc_card *card, u8 *ext_csd) /* Adjust production date as per JEDEC JESD84-B51B September 2025 */ if (card->cid.year < 2023) card->cid.year += 16; + } else { + /* Handle vendors with broken MDT reporting */ + if (mmc_card_broken_mdt(card) && card->cid.year >= 2010 && + card->cid.year <= 2012) + card->cid.year += 16; } } diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index c417ed34c057..f5e8a0f6d11b 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -170,6 +170,9 @@ static const struct mmc_fixup __maybe_unused mmc_ext_csd_fixups[] = { MMC_FIXUP_EXT_CSD_REV(CID_NAME_ANY, CID_MANFID_NUMONYX, 0x014e, add_quirk, MMC_QUIRK_BROKEN_HPI, 6), + MMC_FIXUP(CID_NAME_ANY, CID_MANFID_SANDISK_MMC, CID_OEMID_ANY, add_quirk_mmc, + MMC_QUIRK_BROKEN_MDT), + END_FIXUP }; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index e9e964c20e53..4722dd7e46ce 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -329,6 +329,7 @@ struct mmc_card { #define MMC_QUIRK_BROKEN_CACHE_FLUSH (1<<16) /* Don't flush cache until the write has occurred */ #define MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY (1<<17) /* Disable broken SD poweroff notify support */ #define MMC_QUIRK_NO_UHS_DDR50_TUNING (1<<18) /* Disable DDR50 tuning */ +#define MMC_QUIRK_BROKEN_MDT (1<<19) /* Wrong manufacturing year */ bool written_flag; /* Indicates eMMC has been written since power on */ bool reenable_cmdq; /* Re-enable Command Queue */ -- cgit v1.2.3 From 7514f64780a47306ebcc26f06b30eeec376b2bf4 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 3 Dec 2025 12:45:34 +0100 Subject: dt-bindings: mmc: mtk-sd: Add support for MT8189 SoC Add a new compatible for MMC IP in MT8189 SoC. Even though this is partially compatible with the one found in MT8196 SoC, the MT8189 SoC register layout has some slight differences and additional features. Signed-off-by: Louis-Alexis Eyraud Acked-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mtk-sd.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml index 6dd26ad31491..eb3755bdfdf7 100644 --- a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml +++ b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml @@ -25,6 +25,7 @@ properties: - mediatek,mt8135-mmc - mediatek,mt8173-mmc - mediatek,mt8183-mmc + - mediatek,mt8189-mmc - mediatek,mt8196-mmc - mediatek,mt8516-mmc - items: @@ -192,6 +193,7 @@ allOf: - mediatek,mt8183-mmc - mediatek,mt8186-mmc - mediatek,mt8188-mmc + - mediatek,mt8189-mmc - mediatek,mt8195-mmc - mediatek,mt8196-mmc - mediatek,mt8516-mmc @@ -240,6 +242,7 @@ allOf: - mediatek,mt7986-mmc - mediatek,mt7988-mmc - mediatek,mt8183-mmc + - mediatek,mt8189-mmc - mediatek,mt8196-mmc then: properties: -- cgit v1.2.3 From a834e60c8a9954974360a9486f1fdad7ce4776f5 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 3 Dec 2025 12:45:35 +0100 Subject: mmc: mtk-sd: add support for SPM resource release control The MT8189 SoC has in the status register an additional bit field to release all System Power management (SPM) resource requests. In preparation of MT8189 SoC support, add its use in suspend callback and a support flag in the platform data. Signed-off-by: Louis-Alexis Eyraud Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index daed659f63f6..4ce596d61640 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -203,9 +203,10 @@ #define SDC_CFG_DTOC GENMASK(31, 24) /* RW */ /* SDC_STS mask */ -#define SDC_STS_SDCBUSY BIT(0) /* RW */ -#define SDC_STS_CMDBUSY BIT(1) /* RW */ -#define SDC_STS_SWR_COMPL BIT(31) /* RW */ +#define SDC_STS_SDCBUSY BIT(0) /* RW */ +#define SDC_STS_CMDBUSY BIT(1) /* RW */ +#define SDC_STS_SPM_RESOURCE_RELEASE BIT(3) /* RW */ +#define SDC_STS_SWR_COMPL BIT(31) /* RW */ /* SDC_ADV_CFG0 mask */ #define SDC_DAT1_IRQ_TRIGGER BIT(19) /* RW */ @@ -448,6 +449,7 @@ struct mtk_mmc_compatible { bool use_internal_cd; bool support_new_tx; bool support_new_rx; + bool support_spm_res_release; }; struct msdc_tune_para { @@ -3296,6 +3298,10 @@ static int msdc_runtime_suspend(struct device *dev) __msdc_enable_sdio_irq(host, 0); } + + if (host->dev_comp->support_spm_res_release) + sdr_set_bits(host->base + SDC_STS, SDC_STS_SPM_RESOURCE_RELEASE); + msdc_gate_clock(host); return 0; } -- cgit v1.2.3 From 846a3a2fdff57131725029e0e95d46e08a323da6 Mon Sep 17 00:00:00 2001 From: Louis-Alexis Eyraud Date: Wed, 3 Dec 2025 12:45:36 +0100 Subject: mmc: mtk-sd: add support for MT8189 SoC Even though MMC IP in MT8189 SoC is partially compatible with the one found in MT8196 SoC, its register layout has some slight differences and additional features such as the system power management release resource control support. Thus, add new compatible and platform data to support this SoC. Signed-off-by: Louis-Alexis Eyraud Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 4ce596d61640..302ac8529c4f 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -675,6 +675,25 @@ static const struct mtk_mmc_compatible mt8516_compat = { .stop_dly_sel = 3, }; +static const struct mtk_mmc_compatible mt8189_compat = { + .clk_div_bits = 12, + .recheck_sdio_irq = false, + .hs400_tune = false, + .needs_top_base = true, + .pad_tune_reg = MSDC_PAD_TUNE0, + .async_fifo = true, + .data_tune = true, + .busy_check = true, + .stop_clk_fix = true, + .stop_dly_sel = 1, + .pop_en_cnt = 2, + .enhance_rx = true, + .support_64g = true, + .support_new_tx = true, + .support_new_rx = true, + .support_spm_res_release = true, +}; + static const struct mtk_mmc_compatible mt8196_compat = { .clk_div_bits = 12, .recheck_sdio_irq = false, @@ -705,6 +724,7 @@ static const struct of_device_id msdc_of_ids[] = { { .compatible = "mediatek,mt8135-mmc", .data = &mt8135_compat}, { .compatible = "mediatek,mt8173-mmc", .data = &mt8173_compat}, { .compatible = "mediatek,mt8183-mmc", .data = &mt8183_compat}, + { .compatible = "mediatek,mt8189-mmc", .data = &mt8189_compat}, { .compatible = "mediatek,mt8196-mmc", .data = &mt8196_compat}, { .compatible = "mediatek,mt8516-mmc", .data = &mt8516_compat}, -- cgit v1.2.3 From 8ceb70c9f970bfbdceb1e51578850a60b9de2236 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Thu, 11 Dec 2025 15:56:03 +0800 Subject: mmc: sdhci-esdhc-imx: wait for data transfer completion before reset On IMX7ULP platforms, certain SD cards (e.g. Kingston Canvas Go! Plus) cause system hangs and reboots during manual tuning. These cards exhibit large gaps (~16us) between tuning command response and data transmission. When cmd CRC errors occur during tuning, the code assumes data errors even tuning data hasn't been fully received and then reset host data circuit. Per IMX7ULP reference manual, reset operations (RESET_DATA/ALL) need to make sure no active data transfers. Previously, resetting while data was in-flight would clear data circuit, including ADMA/SDMA address, causing data to be transmitted to incorrect memory address. This patch adds polling for data transfer completion before executing resets. Signed-off-by: Luke Wang Reviewed-by: Bough Chen Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index a7a5df673b0f..97461e20425d 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -216,6 +216,8 @@ #define ESDHC_FLAG_DUMMY_PAD BIT(19) #define ESDHC_AUTO_TUNING_WINDOW 3 +/* 100ms timeout for data inhibit */ +#define ESDHC_DATA_INHIBIT_WAIT_US 100000 enum wp_types { ESDHC_WP_NONE, /* no WP, neither controller nor gpio */ @@ -1453,6 +1455,22 @@ static void esdhc_set_uhs_signaling(struct sdhci_host *host, unsigned timing) static void esdhc_reset(struct sdhci_host *host, u8 mask) { + u32 present_state; + int ret; + + /* + * For data or full reset, ensure any active data transfer completes + * before resetting to avoid system hang. + */ + if (mask & (SDHCI_RESET_DATA | SDHCI_RESET_ALL)) { + ret = readl_poll_timeout_atomic(host->ioaddr + ESDHC_PRSSTAT, present_state, + !(present_state & SDHCI_DATA_INHIBIT), 2, + ESDHC_DATA_INHIBIT_WAIT_US); + if (ret == -ETIMEDOUT) + dev_warn(mmc_dev(host->mmc), + "timeout waiting for data transfer completion\n"); + } + sdhci_and_cqhci_reset(host, mask); sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); -- cgit v1.2.3 From 182b650e4e9c3d68ac636048463db21f8375a057 Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 15 Dec 2025 15:27:27 -0600 Subject: dt-bindings: mmc: cdns,sdhci: Drop required "resets" on AMD Pensando ELBA The AMD Pensando ELBA DT defines no reset for the SDHCI, so it is obviously not required. Signed-off-by: Rob Herring (Arm) Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml index ac75d694611a..6c7317d13aa6 100644 --- a/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/cdns,sdhci.yaml @@ -134,8 +134,6 @@ allOf: items: - description: Host controller registers - description: Elba byte-lane enable register for writes - required: - - resets else: properties: reg: -- cgit v1.2.3 From acb52756e90c99648c993e4ea8a550e126d29d9d Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 15 Dec 2025 15:27:36 -0600 Subject: dt-bindings: mmc: brcm,iproc-sdhci: Allow "dma-coherent" and "iommus" properties The Broadcom iProc SDHCI controller is DMA coherent and/or behind an IOMMU on some Broadcom SoCs, so allow the dma-coherent and iommus properties. Signed-off-by: Rob Herring (Arm) Reviewed-by: Florian Fainelli Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml index 2f63f2cdeb71..65bb2f66f8cf 100644 --- a/Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/brcm,iproc-sdhci.yaml @@ -26,9 +26,14 @@ properties: reg: minItems: 1 + dma-coherent: true + interrupts: maxItems: 1 + iommus: + maxItems: 1 + clocks: maxItems: 1 description: -- cgit v1.2.3 From 5962f68603ea74b804f7bb01a475b08846932aad Mon Sep 17 00:00:00 2001 From: "Rob Herring (Arm)" Date: Mon, 15 Dec 2025 15:27:50 -0600 Subject: dt-bindings: mmc: arasan,sdhci: Allow "dma-coherent" property The Arasan SDHCI controller is DMA coherent on the APM merlin SoC, so allow the dma-coherent property. No reason implementations can't also be coherent and there's not an SoC specific compatible, so allow it on any platform. Signed-off-by: Rob Herring (Arm) Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml index 8e79de97b242..d6b6fa6dcffb 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml @@ -121,6 +121,8 @@ properties: - const: clk_ahb - const: gate + dma-coherent: true + interrupts: minItems: 1 maxItems: 2 -- cgit v1.2.3 From 9313c6c3dbd7aab645f1a37943f8187ea48edc6f Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:49:56 +0800 Subject: mmc: dw_mmc: Remove vqmmc_enabled from struct dw_mci and update the reset commit 51da2240906c ("mmc: dw_mmc: use mmc_regulator_get_supply to handle regulators") introduced tracking of vqmmc_enabled. Currently, mmc_regulator_enable_vqmmc() and mmc_regulator_disable_vqmmc() well record the status of vqmmc, so use these two helpers to remove vqmmc_enabled locally. With vqmmc_enabled gone, resetting controller on MMC_POWER_ON phase won't work as dw_mci_set_ios() will be called several times during enumerating which leads to reset the controller several times too. This messes up the status machine of controller. By looking into the commit d1f1dd86006c ("mmc: dw_mmc: Give a good reset after we give power"), it tried to solve failures on rk3288. The problem is probably because the vqmmc is used for IO block associated with dw controller. When SD is removed during I/O, cutting off vqmmc in MMC_POWER_OFF phase will confuse the controller as its status machine refers to several IO status, such as data_state_mc_busy and data_busy on SDMMC_STATUS reg. So the controller could run into an unexpected state and could not enumerate cards correctly the next time. Reset it on MMC_POWER_ON phase or MMC_POWER_OFF phase should work, let's do the latter. This patch is tested on RK3588s/RK3399/RK3576 EVB with TF cards with both vqmmc present or not. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 26 ++++---------------------- drivers/mmc/host/dw_mmc.h | 2 -- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 9c491cd5c368..6cb891aaa5c2 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1443,25 +1443,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) mci_writel(slot->host, PWREN, regs); break; case MMC_POWER_ON: - if (!slot->host->vqmmc_enabled) { - if (!IS_ERR(mmc->supply.vqmmc)) { - ret = regulator_enable(mmc->supply.vqmmc); - if (ret < 0) - dev_err(slot->host->dev, - "failed to enable vqmmc\n"); - else - slot->host->vqmmc_enabled = true; - - } else { - /* Keep track so we don't reset again */ - slot->host->vqmmc_enabled = true; - } - - /* Reset our state machine after powering on */ - dw_mci_ctrl_reset(slot->host, - SDMMC_CTRL_ALL_RESET_FLAGS); - } - + mmc_regulator_enable_vqmmc(mmc); /* Adjust clock / bus width after power is up */ dw_mci_setup_bus(slot, false); @@ -1473,13 +1455,13 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); - if (!IS_ERR(mmc->supply.vqmmc) && slot->host->vqmmc_enabled) - regulator_disable(mmc->supply.vqmmc); - slot->host->vqmmc_enabled = false; + mmc_regulator_disable_vqmmc(mmc); regs = mci_readl(slot->host, PWREN); regs &= ~(1 << slot->id); mci_writel(slot->host, PWREN, regs); + /* Reset our state machine after powering off */ + dw_mci_ctrl_reset(slot->host, SDMMC_CTRL_ALL_RESET_FLAGS); break; default: break; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index b4ceca0167c4..6faa63b3ce30 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -121,7 +121,6 @@ struct dw_mci_dma_slave { * @push_data: Pointer to FIFO push function. * @pull_data: Pointer to FIFO pull function. * @quirks: Set of quirks that apply to specific versions of the IP. - * @vqmmc_enabled: Status of vqmmc, should be true or false. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. * @sdio_id0: Number of slot0 in the SDIO interrupt registers. @@ -228,7 +227,6 @@ struct dw_mci { void (*pull_data)(struct dw_mci *host, void *buf, int cnt); u32 quirks; - bool vqmmc_enabled; unsigned long irq_flags; /* IRQ flags */ int irq; -- cgit v1.2.3 From 9edf45987d08c0115ea9a66887be8bc9f59cef27 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:49:57 +0800 Subject: mmc: dw_mmc: Remove check before calling mmc_regulator_set_ocr() mmc_regulator_set_ocr() already checks if vqmmc is correct. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 6cb891aaa5c2..b3baa9f1c7e3 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1427,15 +1427,10 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->power_mode) { case MMC_POWER_UP: - if (!IS_ERR(mmc->supply.vmmc)) { - ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, - ios->vdd); - if (ret) { - dev_err(slot->host->dev, - "failed to enable vmmc regulator\n"); - /*return, if failed turn on vmmc*/ - return; - } + ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); + if (ret) { + dev_err(slot->host->dev, "failed to enable vmmc regulator\n"); + return; } set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); regs = mci_readl(slot->host, PWREN); -- cgit v1.2.3 From 53f05821b7f7e096d9987ab78e7009847066bb80 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:49:58 +0800 Subject: mmc: dw_mmc: Remove unused header files and keep alphabetical order Some header files are not needed and they are out of order, this patch remove unused header files and re-order them alphabetically. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index b3baa9f1c7e3..1ede742c185a 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -7,35 +7,28 @@ * Copyright (C) 2009, 2010 Imagination Technologies Ltd. */ -#include +#include #include #include +#include #include #include #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include #include #include #include #include #include -#include -#include -#include #include +#include +#include +#include +#include +#include #include "dw_mmc.h" -- cgit v1.2.3 From 1a990def44fbb714e55794dc28d03e77bed66879 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:49:59 +0800 Subject: mmc: dw_mmc: Move struct mmc_host from struct dw_mci_slot to struct dw_mci Move struct mmc_host to struct dw_mci in preparation for removing dw_mci_slot. No functional change intended. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 2 +- drivers/mmc/host/dw_mmc-hi3798cv200.c | 2 +- drivers/mmc/host/dw_mmc-hi3798mv200.c | 2 +- drivers/mmc/host/dw_mmc-k3.c | 2 +- drivers/mmc/host/dw_mmc-rockchip.c | 2 +- drivers/mmc/host/dw_mmc-starfive.c | 2 +- drivers/mmc/host/dw_mmc.c | 62 +++++++++++++++++------------------ drivers/mmc/host/dw_mmc.h | 4 +-- 8 files changed, 38 insertions(+), 40 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 384609671a9a..067569b15138 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -534,7 +534,7 @@ static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode) { struct dw_mci *host = slot->host; struct dw_mci_exynos_priv_data *priv = host->priv; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; u8 start_smpl, smpl, candidates = 0; s8 found; int ret = 0; diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c index 0ccfae1b2dbe..03f7ed86c872 100644 --- a/drivers/mmc/host/dw_mmc-hi3798cv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798cv200.c @@ -72,7 +72,7 @@ static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot, clk_set_phase(priv->sample_clk, degrees[i]); mci_writel(host, RINTSTS, ALL_INT_CLR); - err = mmc_send_tuning(slot->mmc, opcode, NULL); + err = mmc_send_tuning(host->mmc, opcode, NULL); if (!err) found = 1; diff --git a/drivers/mmc/host/dw_mmc-hi3798mv200.c b/drivers/mmc/host/dw_mmc-hi3798mv200.c index 5791a975a944..3cc4bc28377b 100644 --- a/drivers/mmc/host/dw_mmc-hi3798mv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798mv200.c @@ -115,7 +115,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, * * Treat edge(flip) found as an error too. */ - err = mmc_send_tuning(slot->mmc, opcode, NULL); + err = mmc_send_tuning(host->mmc, opcode, NULL); regval = mci_readl(host, TUNING_CTRL); if (err || (regval & SDMMC_TUNING_FIND_EDGE)) err = 1; diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index ad6aa1aea549..4ef99c05305e 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -368,7 +368,7 @@ static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode) { int i = 0; struct dw_mci *host = slot->host; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; int smpl_phase = 0; u32 tuning_sample_flag = 0; int best_clksmpl = 0; diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index ac069d0c42b2..c8816c5872cd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -288,7 +288,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) { struct dw_mci *host = slot->host; struct dw_mci_rockchip_priv_data *priv = host->priv; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; int ret = 0; int i; bool v, prev_v = 0, first_v; diff --git a/drivers/mmc/host/dw_mmc-starfive.c b/drivers/mmc/host/dw_mmc-starfive.c index 34964b0dab21..d4ea289d0dce 100644 --- a/drivers/mmc/host/dw_mmc-starfive.c +++ b/drivers/mmc/host/dw_mmc-starfive.c @@ -65,7 +65,7 @@ static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, dw_mci_starfive_set_sample_phase(host, smpl_phase); mci_writel(host, RINTSTS, ALL_INT_CLR); - ret = mmc_send_tuning(slot->mmc, opcode, NULL); + ret = mmc_send_tuning(host->mmc, opcode, NULL); if (!ret && smpl_raise < 0) { smpl_raise = smpl_phase; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1ede742c185a..5b3478a4d10e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -160,8 +160,8 @@ DEFINE_SHOW_ATTRIBUTE(dw_mci_regs); static void dw_mci_init_debugfs(struct dw_mci_slot *slot) { - struct mmc_host *mmc = slot->mmc; struct dw_mci *host = slot->host; + struct mmc_host *mmc = host->mmc; struct dentry *root; root = mmc->debugfs_root; @@ -237,7 +237,7 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) if (readl_poll_timeout_atomic(host->regs + SDMMC_CMD, cmd_status, !(cmd_status & SDMMC_CMD_START), 1, 500 * USEC_PER_MSEC)) - dev_err(&slot->mmc->class_dev, + dev_err(&host->mmc->class_dev, "Timeout sending command (cmd %#x arg %#x status %#x)\n", cmd, arg, cmd_status); } @@ -473,7 +473,7 @@ static void dw_mci_dmac_complete_dma(void *arg) if ((host->use_dma == TRANS_MODE_EDMAC) && data && (data->flags & MMC_DATA_READ)) /* Invalidate cache after read */ - dma_sync_sg_for_cpu(mmc_dev(host->slot->mmc), + dma_sync_sg_for_cpu(mmc_dev(host->mmc), data->sg, data->sg_len, DMA_FROM_DEVICE); @@ -762,7 +762,7 @@ static int dw_mci_edmac_start_dma(struct dw_mci *host, /* Flush cache before write */ if (host->data->flags & MMC_DATA_WRITE) - dma_sync_sg_for_device(mmc_dev(host->slot->mmc), sgl, + dma_sync_sg_for_device(mmc_dev(host->mmc), sgl, sg_elems, DMA_TO_DEVICE); dma_async_issue_pending(host->dms->ch); @@ -1151,7 +1151,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) if (host->state == STATE_WAITING_CMD11_DONE) sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; - slot->mmc->actual_clock = 0; + host->mmc->actual_clock = 0; if (!clock) { mci_writel(host, CLKENA, 0); @@ -1172,7 +1172,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) force_clkinit) { /* Silent the verbose log if calling from PM context */ if (!force_clkinit) - dev_info(&slot->mmc->class_dev, + dev_info(&host->mmc->class_dev, "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", slot->id, host->bus_hz, clock, div ? ((host->bus_hz / div) >> 1) : @@ -1182,8 +1182,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) * If card is polling, display the message only * one time at boot time. */ - if (slot->mmc->caps & MMC_CAP_NEEDS_POLL && - slot->mmc->f_min == clock) + if (host->mmc->caps & MMC_CAP_NEEDS_POLL && + host->mmc->f_min == clock) set_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags); } @@ -1211,7 +1211,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* keep the last clock value that was requested from core */ slot->__clk_old = clock; - slot->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : + host->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : host->bus_hz; } @@ -1277,7 +1277,7 @@ static void __dw_mci_start_request(struct dw_mci *host, mci_writel(host, BLKSIZ, data->blksz); } - cmdflags = dw_mci_prepare_command(slot->mmc, cmd); + cmdflags = dw_mci_prepare_command(host->mmc, cmd); /* this is the first command, send the initialization clock */ if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags)) @@ -1327,13 +1327,13 @@ static void dw_mci_start_request(struct dw_mci *host, static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, struct mmc_request *mrq) { - dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n", + dev_vdbg(&host->mmc->class_dev, "queue request: state=%d\n", host->state); slot->mrq = mrq; if (host->state == STATE_WAITING_CMD11_DONE) { - dev_warn(&slot->mmc->class_dev, + dev_warn(&host->mmc->class_dev, "Voltage change didn't complete\n"); /* * this case isn't expected to happen, so we can @@ -1812,7 +1812,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) __acquires(&host->lock) { struct dw_mci_slot *slot; - struct mmc_host *prev_mmc = host->slot->mmc; + struct mmc_host *prev_mmc = host->mmc; WARN_ON(host->cmd || host->data); @@ -1823,7 +1823,7 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) struct dw_mci_slot, queue_node); list_del(&slot->queue_node); dev_vdbg(host->dev, "list not empty: %s is next\n", - mmc_hostname(slot->mmc)); + mmc_hostname(host->mmc)); host->state = STATE_SENDING_CMD; dw_mci_start_request(host, slot); } else { @@ -2719,9 +2719,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) static void dw_mci_handle_cd(struct dw_mci *host) { - struct dw_mci_slot *slot = host->slot; - - mmc_detect_change(slot->mmc, + mmc_detect_change(host->mmc, msecs_to_jiffies(host->pdata->detect_delay_ms)); } @@ -2834,7 +2832,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) mci_writel(host, RINTSTS, SDMMC_INT_SDIO(slot->sdio_id)); __dw_mci_enable_sdio_irq(slot, 0); - sdio_signal_irq(slot->mmc); + sdio_signal_irq(host->mmc); } } @@ -2870,7 +2868,7 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) { struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; - struct mmc_host *mmc = slot->mmc; + struct mmc_host *mmc = host->mmc; int ctrl_id; if (host->pdata->caps) @@ -2931,7 +2929,7 @@ static int dw_mci_init_slot(struct dw_mci *host) slot = mmc_priv(mmc); slot->id = 0; slot->sdio_id = host->sdio_id0 + slot->id; - slot->mmc = mmc; + host->mmc = mmc; slot->host = host; host->slot = slot; @@ -2993,7 +2991,7 @@ static int dw_mci_init_slot(struct dw_mci *host) static void dw_mci_cleanup_slot(struct dw_mci_slot *slot) { /* Debugfs stuff is cleaned up by mmc core */ - mmc_remove_host(slot->mmc); + mmc_remove_host(slot->host->mmc); slot->host->slot = NULL; } @@ -3265,10 +3263,10 @@ static void dw_mci_enable_cd(struct dw_mci *host) * No need for CD if all slots have a non-error GPIO * as well as broken card detection is found. */ - if (host->slot->mmc->caps & MMC_CAP_NEEDS_POLL) + if (host->mmc->caps & MMC_CAP_NEEDS_POLL) return; - if (mmc_gpio_get_cd(host->slot->mmc) < 0) { + if (mmc_gpio_get_cd(host->mmc) < 0) { spin_lock_irqsave(&host->irq_lock, irqflags); temp = mci_readl(host, INTMASK); temp |= SDMMC_INT_CD; @@ -3532,8 +3530,8 @@ int dw_mci_runtime_suspend(struct device *dev) clk_disable_unprepare(host->ciu_clk); if (host->slot && - (mmc_host_can_gpio_cd(host->slot->mmc) || - !mmc_card_is_removable(host->slot->mmc))) + (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc))) clk_disable_unprepare(host->biu_clk); return 0; @@ -3546,8 +3544,8 @@ int dw_mci_runtime_resume(struct device *dev) struct dw_mci *host = dev_get_drvdata(dev); if (host->slot && - (mmc_host_can_gpio_cd(host->slot->mmc) || - !mmc_card_is_removable(host->slot->mmc))) { + (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc))) { ret = clk_prepare_enable(host->biu_clk); if (ret) return ret; @@ -3583,14 +3581,14 @@ int dw_mci_runtime_resume(struct device *dev) mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); - if (host->slot && host->slot->mmc->pm_flags & MMC_PM_KEEP_POWER) - dw_mci_set_ios(host->slot->mmc, &host->slot->mmc->ios); + if (host->slot && host->mmc->pm_flags & MMC_PM_KEEP_POWER) + dw_mci_set_ios(host->mmc, &host->mmc->ios); /* Force setup bus to guarantee available clock output */ dw_mci_setup_bus(host->slot, true); /* Re-enable SDIO interrupts. */ - if (sdio_irq_claimed(host->slot->mmc)) + if (sdio_irq_claimed(host->mmc)) __dw_mci_enable_sdio_irq(host->slot, 1); /* Now that slots are all setup, we can enable card detect */ @@ -3600,8 +3598,8 @@ int dw_mci_runtime_resume(struct device *dev) err: if (host->slot && - (mmc_host_can_gpio_cd(host->slot->mmc) || - !mmc_card_is_removable(host->slot->mmc))) + (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc))) clk_disable_unprepare(host->biu_clk); return ret; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 6faa63b3ce30..b4efc5812083 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -127,6 +127,7 @@ struct dw_mci_dma_slave { * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. * @cto_timer: Timer for broken command transfer over scheme. * @dto_timer: Timer for broken data transfer over scheme. + * @mmc: The mmc_host representing this dw_mci. * * Locking * ======= @@ -240,6 +241,7 @@ struct dw_mci { struct fault_attr fail_data_crc; struct hrtimer fault_timer; #endif + struct mmc_host *mmc; }; /* DMA ops for Internal/External DMAC interface */ @@ -543,7 +545,6 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS /** * struct dw_mci_slot - MMC slot state - * @mmc: The mmc_host representing this slot. * @host: The MMC controller this slot is using. * @ctype: Card type for this slot. * @mrq: mmc_request currently being processed or waiting to be @@ -558,7 +559,6 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { - struct mmc_host *mmc; struct dw_mci *host; u32 ctype; -- cgit v1.2.3 From d7ab40c3bd2a40a57fa5a09a6f7e561f59c733a6 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:50:00 +0800 Subject: mmc: dw_mmc: Let variant drivers to use struct dw_mci as possible This patch changes the callbacks of switch_voltage() and execute_tuning() in order for variant drivers to avoid accessing to struct dw_mci_slot. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 3 +-- drivers/mmc/host/dw_mmc-hi3798cv200.c | 4 +--- drivers/mmc/host/dw_mmc-hi3798mv200.c | 15 +++++++-------- drivers/mmc/host/dw_mmc-k3.c | 19 +++++++------------ drivers/mmc/host/dw_mmc-rockchip.c | 3 +-- drivers/mmc/host/dw_mmc-starfive.c | 3 +-- drivers/mmc/host/dw_mmc.c | 4 ++-- drivers/mmc/host/dw_mmc.h | 4 ++-- 8 files changed, 22 insertions(+), 33 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 067569b15138..41069850e656 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -530,9 +530,8 @@ out: return loc; } -static int dw_mci_exynos_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_exynos_execute_tuning(struct dw_mci *host, u32 opcode) { - struct dw_mci *host = slot->host; struct dw_mci_exynos_priv_data *priv = host->priv; struct mmc_host *mmc = host->mmc; u8 start_smpl, smpl, candidates = 0; diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c index 03f7ed86c872..4b723ed3259c 100644 --- a/drivers/mmc/host/dw_mmc-hi3798cv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798cv200.c @@ -57,11 +57,9 @@ static void dw_mci_hi3798cv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) clk_set_phase(priv->drive_clk, 135); } -static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci_slot *slot, - u32 opcode) +static int dw_mci_hi3798cv200_execute_tuning(struct dw_mci *host, u32 opcode) { static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 }; - struct dw_mci *host = slot->host; struct hi3798cv200_priv *priv = host->priv; int raise_point = -1, fall_point = -1; int err, prev_err = -1; diff --git a/drivers/mmc/host/dw_mmc-hi3798mv200.c b/drivers/mmc/host/dw_mmc-hi3798mv200.c index 3cc4bc28377b..a64907e2e84c 100644 --- a/drivers/mmc/host/dw_mmc-hi3798mv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798mv200.c @@ -74,25 +74,24 @@ static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) } } -static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci_slot *slot) +static inline int dw_mci_hi3798mv200_enable_tuning(struct dw_mci *host) { - struct dw_mci_hi3798mv200_priv *priv = slot->host->priv; + struct dw_mci_hi3798mv200_priv *priv = host->priv; return regmap_clear_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE); } -static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci_slot *slot) +static inline int dw_mci_hi3798mv200_disable_tuning(struct dw_mci *host) { - struct dw_mci_hi3798mv200_priv *priv = slot->host->priv; + struct dw_mci_hi3798mv200_priv *priv = host->priv; return regmap_set_bits(priv->crg_reg, priv->sap_dll_offset, SAP_DLL_CTRL_DLLMODE); } -static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, +static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci *host, u32 opcode) { static const int degrees[] = { 0, 45, 90, 135, 180, 225, 270, 315 }; - struct dw_mci *host = slot->host; struct dw_mci_hi3798mv200_priv *priv = host->priv; int raise_point = -1, fall_point = -1, mid; int err, prev_err = -1; @@ -101,7 +100,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, int i; int ret; - ret = dw_mci_hi3798mv200_enable_tuning(slot); + ret = dw_mci_hi3798mv200_enable_tuning(host); if (ret < 0) return ret; @@ -136,7 +135,7 @@ static int dw_mci_hi3798mv200_execute_tuning_mix_mode(struct dw_mci_slot *slot, } tuning_out: - ret = dw_mci_hi3798mv200_disable_tuning(slot); + ret = dw_mci_hi3798mv200_disable_tuning(host); if (ret < 0) return ret; diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 4ef99c05305e..23c303106dca 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -138,15 +138,13 @@ static int dw_mci_hi6220_parse_dt(struct dw_mci *host) return 0; } -static int dw_mci_hi6220_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +static int dw_mci_hi6220_switch_voltage(struct dw_mci *host, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); struct k3_priv *priv; - struct dw_mci *host; + struct mmc_host *mmc = host->mmc; int min_uv, max_uv; int ret; - host = slot->host; priv = host->priv; if (!priv || !priv->reg) @@ -199,7 +197,7 @@ static void dw_mci_hi6220_set_ios(struct dw_mci *host, struct mmc_ios *ios) host->bus_hz = clk_get_rate(host->biu_clk); } -static int dw_mci_hi6220_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_hi6220_execute_tuning(struct dw_mci *host, u32 opcode) { return 0; } @@ -364,10 +362,9 @@ static int dw_mci_get_best_clksmpl(unsigned int sample_flag) return middle_range; } -static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_hi3660_execute_tuning(struct dw_mci *host, u32 opcode) { int i = 0; - struct dw_mci *host = slot->host; struct mmc_host *mmc = host->mmc; int smpl_phase = 0; u32 tuning_sample_flag = 0; @@ -398,15 +395,13 @@ static int dw_mci_hi3660_execute_tuning(struct dw_mci_slot *slot, u32 opcode) return 0; } -static int dw_mci_hi3660_switch_voltage(struct mmc_host *mmc, +static int dw_mci_hi3660_switch_voltage(struct dw_mci *host, struct mmc_ios *ios) { - int ret = 0; - struct dw_mci_slot *slot = mmc_priv(mmc); struct k3_priv *priv; - struct dw_mci *host; + struct mmc_host *mmc = host->mmc; + int ret = 0; - host = slot->host; priv = host->priv; if (!priv || !priv->reg) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index c8816c5872cd..922be2951da7 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -284,9 +284,8 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) #define TUNING_ITERATION_TO_PHASE(i, num_phases) \ (DIV_ROUND_UP((i) * 360, num_phases)) -static int dw_mci_rk3288_execute_tuning(struct dw_mci_slot *slot, u32 opcode) +static int dw_mci_rk3288_execute_tuning(struct dw_mci *host, u32 opcode) { - struct dw_mci *host = slot->host; struct dw_mci_rockchip_priv_data *priv = host->priv; struct mmc_host *mmc = host->mmc; int ret = 0; diff --git a/drivers/mmc/host/dw_mmc-starfive.c b/drivers/mmc/host/dw_mmc-starfive.c index d4ea289d0dce..11472a641cbe 100644 --- a/drivers/mmc/host/dw_mmc-starfive.c +++ b/drivers/mmc/host/dw_mmc-starfive.c @@ -53,11 +53,10 @@ static void dw_mci_starfive_set_sample_phase(struct dw_mci *host, u32 smpl_phase mdelay(1); } -static int dw_mci_starfive_execute_tuning(struct dw_mci_slot *slot, +static int dw_mci_starfive_execute_tuning(struct dw_mci *host, u32 opcode) { static const int grade = MAX_DELAY_CHAIN; - struct dw_mci *host = slot->host; int smpl_phase, smpl_raise = -1, smpl_fall = -1; int ret; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 5b3478a4d10e..fec8ca8ae23c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1483,7 +1483,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) int ret; if (drv_data && drv_data->switch_voltage) - return drv_data->switch_voltage(mmc, ios); + return drv_data->switch_voltage(host, ios); /* * Program the voltage. Note that some instances of dw_mmc may use @@ -1641,7 +1641,7 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) int err = -EINVAL; if (drv_data && drv_data->execute_tuning) - err = drv_data->execute_tuning(slot, opcode); + err = drv_data->execute_tuning(host, opcode); return err; } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index b4efc5812083..594c8f70aa25 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -604,10 +604,10 @@ struct dw_mci_drv_data { int (*init)(struct dw_mci *host); void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios); int (*parse_dt)(struct dw_mci *host); - int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode); + int (*execute_tuning)(struct dw_mci *host, u32 opcode); int (*prepare_hs400_tuning)(struct dw_mci *host, struct mmc_ios *ios); - int (*switch_voltage)(struct mmc_host *mmc, + int (*switch_voltage)(struct dw_mci *host, struct mmc_ios *ios); void (*set_data_timeout)(struct dw_mci *host, unsigned int timeout_ns); -- cgit v1.2.3 From 1a1936f808d791b24f9b8dc9c2171dbbd5f18653 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:50:01 +0800 Subject: mmc: dw_mmc: Move flags from struct dw_mci_slot to struct dw_mci With this, dw_mmc-exynos.c will not need to access slot. While at it, since the host->slot is always present when calling dw_mci_exynos_set_clksel_timing(), let's remove it. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-exynos.c | 4 ++-- drivers/mmc/host/dw_mmc.c | 26 +++++++++++++------------- drivers/mmc/host/dw_mmc.h | 14 +++++++------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index 41069850e656..261344d3a8cf 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -185,8 +185,8 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing) * HOLD register should be bypassed in case there is no phase shift * applied on CMD/DATA that is sent to the card. */ - if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel) && host->slot) - set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags); + if (!SDMMC_CLKSEL_GET_DRV_WD3(clksel)) + set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags); } static int dw_mci_exynos_runtime_resume(struct device *dev) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index fec8ca8ae23c..3ff1d2a37a9b 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -304,7 +304,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_DAT_WR; } - if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &slot->flags)) + if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags)) cmdr |= SDMMC_CMD_USE_HOLD_REG; return cmdr; @@ -343,7 +343,7 @@ static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) cmdr = stop->opcode | SDMMC_CMD_STOP | SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; - if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->slot->flags)) + if (!test_bit(DW_MMC_CARD_NO_USE_HOLD, &host->flags)) cmdr |= SDMMC_CMD_USE_HOLD_REG; return cmdr; @@ -896,7 +896,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc) || !mmc_card_is_removable(mmc))) { present = 1; - if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) { + if (!test_bit(DW_MMC_CARD_PRESENT, &host->flags)) { if (mmc->caps & MMC_CAP_NEEDS_POLL) { dev_info(&mmc->class_dev, "card is polling.\n"); @@ -904,7 +904,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc) dev_info(&mmc->class_dev, "card is non-removable.\n"); } - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); + set_bit(DW_MMC_CARD_PRESENT, &host->flags); } return present; @@ -915,10 +915,10 @@ static int dw_mci_get_cd(struct mmc_host *mmc) == 0 ? 1 : 0; spin_lock_bh(&host->lock); - if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &slot->flags)) + if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &host->flags)) dev_dbg(&mmc->class_dev, "card is present\n"); else if (!present && - !test_and_clear_bit(DW_MMC_CARD_PRESENT, &slot->flags)) + !test_and_clear_bit(DW_MMC_CARD_PRESENT, &host->flags)) dev_dbg(&mmc->class_dev, "card is not present\n"); spin_unlock_bh(&host->lock); @@ -1168,7 +1168,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; if ((clock != slot->__clk_old && - !test_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags)) || + !test_bit(DW_MMC_CARD_NEEDS_POLL, &host->flags)) || force_clkinit) { /* Silent the verbose log if calling from PM context */ if (!force_clkinit) @@ -1184,7 +1184,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) */ if (host->mmc->caps & MMC_CAP_NEEDS_POLL && host->mmc->f_min == clock) - set_bit(DW_MMC_CARD_NEEDS_POLL, &slot->flags); + set_bit(DW_MMC_CARD_NEEDS_POLL, &host->flags); } /* disable clock */ @@ -1202,7 +1202,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; - if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags)) + if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags)) clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; mci_writel(host, CLKENA, clk_en_a); @@ -1280,7 +1280,7 @@ static void __dw_mci_start_request(struct dw_mci *host, cmdflags = dw_mci_prepare_command(host->mmc, cmd); /* this is the first command, send the initialization clock */ - if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags)) + if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &host->flags)) cmdflags |= SDMMC_CMD_INIT; if (data) { @@ -1425,7 +1425,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) dev_err(slot->host->dev, "failed to enable vmmc regulator\n"); return; } - set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); + set_bit(DW_MMC_CARD_NEED_INIT, &slot->host->flags); regs = mci_readl(slot->host, PWREN); regs |= (1 << slot->id); mci_writel(slot->host, PWREN, regs); @@ -1578,10 +1578,10 @@ static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare) clk_en_a_old = mci_readl(host, CLKENA); if (prepare) { - set_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + set_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags); clk_en_a = clk_en_a_old & ~clken_low_pwr; } else { - clear_bit(DW_MMC_CARD_NO_LOW_PWR, &slot->flags); + clear_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags); clk_en_a = clk_en_a_old | clken_low_pwr; } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 594c8f70aa25..933d0a373f70 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -128,6 +128,7 @@ struct dw_mci_dma_slave { * @cto_timer: Timer for broken command transfer over scheme. * @dto_timer: Timer for broken data transfer over scheme. * @mmc: The mmc_host representing this dw_mci. + * @flags: Random state bits associated with the host. * * Locking * ======= @@ -242,6 +243,12 @@ struct dw_mci { struct hrtimer fault_timer; #endif struct mmc_host *mmc; + unsigned long flags; +#define DW_MMC_CARD_PRESENT 0 +#define DW_MMC_CARD_NEED_INIT 1 +#define DW_MMC_CARD_NO_LOW_PWR 2 +#define DW_MMC_CARD_NO_USE_HOLD 3 +#define DW_MMC_CARD_NEEDS_POLL 4 }; /* DMA ops for Internal/External DMAC interface */ @@ -554,7 +561,6 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS * @clock: Clock rate configured by set_ios(). Protected by host->lock. * @__clk_old: The last clock value that was requested from core. * Keeping track of this helps us to avoid spamming the console. - * @flags: Random state bits associated with the slot. * @id: Number of this slot. * @sdio_id: Number of this slot in the SDIO interrupt registers. */ @@ -569,12 +575,6 @@ struct dw_mci_slot { unsigned int clock; unsigned int __clk_old; - unsigned long flags; -#define DW_MMC_CARD_PRESENT 0 -#define DW_MMC_CARD_NEED_INIT 1 -#define DW_MMC_CARD_NO_LOW_PWR 2 -#define DW_MMC_CARD_NO_USE_HOLD 3 -#define DW_MMC_CARD_NEEDS_POLL 4 int id; int sdio_id; }; -- cgit v1.2.3 From b8cc1e80668aba7d1206a1f03c12908c078a7168 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:50:02 +0800 Subject: mmc: dw_mmc: Remove id and ctype from dw_mci_slot There is only one slot support, id should be zero. So no need to keep it in track as long as ctype associated. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 41 ++++++++++++++++++++--------------------- drivers/mmc/host/dw_mmc.h | 7 ++----- 2 files changed, 22 insertions(+), 26 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 3ff1d2a37a9b..201d19ad3a92 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -282,7 +282,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) * until the voltage change is all done. */ clk_en_a = mci_readl(host, CLKENA); - clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id); + clk_en_a &= ~SDMMC_CLKEN_LOW_PWR; mci_writel(host, CLKENA, clk_en_a); mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); @@ -911,7 +911,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc) } else if (gpio_cd >= 0) present = gpio_cd; else - present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) + present = (mci_readl(slot->host, CDETECT) & BIT(0)) == 0 ? 1 : 0; spin_lock_bh(&host->lock); @@ -1173,8 +1173,8 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* Silent the verbose log if calling from PM context */ if (!force_clkinit) dev_info(&host->mmc->class_dev, - "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", - slot->id, host->bus_hz, clock, + "Bus speed = %dHz (slot req %dHz, actual %dHZ div = %d)\n", + host->bus_hz, clock, div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); @@ -1201,9 +1201,9 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_send_cmd(slot, sdmmc_cmd_bits, 0); /* enable clock; only low power if no SDIO */ - clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; + clk_en_a = SDMMC_CLKEN_ENABLE; if (!test_bit(DW_MMC_CARD_NO_LOW_PWR, &host->flags)) - clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; + clk_en_a |= SDMMC_CLKEN_LOW_PWR; mci_writel(host, CLKENA, clk_en_a); /* inform CIU */ @@ -1218,7 +1218,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) host->current_speed = clock; /* Set the current slot bus width */ - mci_writel(host, CTYPE, (slot->ctype << slot->id)); + mci_writel(host, CTYPE, host->ctype); } static void dw_mci_set_data_timeout(struct dw_mci *host, @@ -1386,14 +1386,14 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) switch (ios->bus_width) { case MMC_BUS_WIDTH_4: - slot->ctype = SDMMC_CTYPE_4BIT; + slot->host->ctype = SDMMC_CTYPE_4BIT; break; case MMC_BUS_WIDTH_8: - slot->ctype = SDMMC_CTYPE_8BIT; + slot->host->ctype = SDMMC_CTYPE_8BIT; break; default: /* set default 1 bit mode */ - slot->ctype = SDMMC_CTYPE_1BIT; + slot->host->ctype = SDMMC_CTYPE_1BIT; } regs = mci_readl(slot->host, UHS_REG); @@ -1402,9 +1402,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->timing == MMC_TIMING_MMC_DDR52 || ios->timing == MMC_TIMING_UHS_DDR50 || ios->timing == MMC_TIMING_MMC_HS400) - regs |= ((0x1 << slot->id) << 16); + regs |= BIT(16); else - regs &= ~((0x1 << slot->id) << 16); + regs &= ~BIT(16); mci_writel(slot->host, UHS_REG, regs); slot->host->timing = ios->timing; @@ -1427,7 +1427,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } set_bit(DW_MMC_CARD_NEED_INIT, &slot->host->flags); regs = mci_readl(slot->host, PWREN); - regs |= (1 << slot->id); + regs |= BIT(0); mci_writel(slot->host, PWREN, regs); break; case MMC_POWER_ON: @@ -1446,7 +1446,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) mmc_regulator_disable_vqmmc(mmc); regs = mci_readl(slot->host, PWREN); - regs &= ~(1 << slot->id); + regs &= ~BIT(0); mci_writel(slot->host, PWREN, regs); /* Reset our state machine after powering off */ dw_mci_ctrl_reset(slot->host, SDMMC_CTRL_ALL_RESET_FLAGS); @@ -1479,7 +1479,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; u32 uhs; - u32 v18 = SDMMC_UHS_18V << slot->id; + u32 v18 = SDMMC_UHS_18V; int ret; if (drv_data && drv_data->switch_voltage) @@ -1521,7 +1521,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) read_only = gpio_ro; else read_only = - mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0; + mci_readl(slot->host, WRTPRT) & BIT(0) ? 1 : 0; dev_dbg(&mmc->class_dev, "card is %s\n", read_only ? "read-only" : "read-write"); @@ -1555,10 +1555,10 @@ static void dw_mci_hw_reset(struct mmc_host *mmc) * tRSTH >= 1us: RST_n high period */ reset = mci_readl(host, RST_N); - reset &= ~(SDMMC_RST_HWACTIVE << slot->id); + reset &= ~SDMMC_RST_HWACTIVE; mci_writel(host, RST_N, reset); usleep_range(1, 2); - reset |= SDMMC_RST_HWACTIVE << slot->id; + reset |= SDMMC_RST_HWACTIVE; mci_writel(host, RST_N, reset); usleep_range(200, 300); } @@ -1566,7 +1566,7 @@ static void dw_mci_hw_reset(struct mmc_host *mmc) static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare) { struct dw_mci *host = slot->host; - const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; + const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR; u32 clk_en_a_old; u32 clk_en_a; @@ -2927,8 +2927,7 @@ static int dw_mci_init_slot(struct dw_mci *host) return -ENOMEM; slot = mmc_priv(mmc); - slot->id = 0; - slot->sdio_id = host->sdio_id0 + slot->id; + slot->sdio_id = host->sdio_id0; host->mmc = mmc; slot->host = host; host->slot = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 933d0a373f70..7f6efb6e6245 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -129,6 +129,7 @@ struct dw_mci_dma_slave { * @dto_timer: Timer for broken data transfer over scheme. * @mmc: The mmc_host representing this dw_mci. * @flags: Random state bits associated with the host. + * @ctype: Card type for this host. * * Locking * ======= @@ -249,6 +250,7 @@ struct dw_mci { #define DW_MMC_CARD_NO_LOW_PWR 2 #define DW_MMC_CARD_NO_USE_HOLD 3 #define DW_MMC_CARD_NEEDS_POLL 4 + u32 ctype; }; /* DMA ops for Internal/External DMAC interface */ @@ -553,7 +555,6 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS /** * struct dw_mci_slot - MMC slot state * @host: The MMC controller this slot is using. - * @ctype: Card type for this slot. * @mrq: mmc_request currently being processed or waiting to be * processed, or NULL when the slot is idle. * @queue_node: List node for placing this node in the @queue list of @@ -561,21 +562,17 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS * @clock: Clock rate configured by set_ios(). Protected by host->lock. * @__clk_old: The last clock value that was requested from core. * Keeping track of this helps us to avoid spamming the console. - * @id: Number of this slot. * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { struct dw_mci *host; - u32 ctype; - struct mmc_request *mrq; struct list_head queue_node; unsigned int clock; unsigned int __clk_old; - int id; int sdio_id; }; -- cgit v1.2.3 From a1513290091078206f0d2af199104bf11cf8c9a9 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:50:03 +0800 Subject: mmc: dw_mmc: Remove sdio_id from struct dw_mci_slot There is only one slot support, the sdio_id is used to indicate the SDIO slot and where is the irq located. So it's pointless now, remove it. Given sdio_id0 is only used by Rockchip to inform dwc core the irq is located with a offset, rename sdio_id0 to sdio_irq to reflect the fact. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-rockchip.c | 4 ++-- drivers/mmc/host/dw_mmc.c | 9 ++++----- drivers/mmc/host/dw_mmc.h | 7 ++----- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 922be2951da7..76995415bc4c 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -475,8 +475,8 @@ static int dw_mci_rockchip_init(struct dw_mci *host) struct dw_mci_rockchip_priv_data *priv = host->priv; int ret, i; - /* It is slot 8 on Rockchip SoCs */ - host->sdio_id0 = 8; + /* SDIO irq is the 8th on Rockchip SoCs */ + host->sdio_irq = 8; if (of_device_is_compatible(host->dev->of_node, "rockchip,rk3288-dw-mshc")) { host->bus_hz /= RK3288_CLKGEN_DIV; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 201d19ad3a92..4dbc5417c7be 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1603,9 +1603,9 @@ static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb) /* Enable/disable Slot Specific SDIO interrupt */ int_mask = mci_readl(host, INTMASK); if (enb) - int_mask |= SDMMC_INT_SDIO(slot->sdio_id); + int_mask |= SDMMC_INT_SDIO(host->sdio_irq); else - int_mask &= ~SDMMC_INT_SDIO(slot->sdio_id); + int_mask &= ~SDMMC_INT_SDIO(host->sdio_irq); mci_writel(host, INTMASK, int_mask); spin_unlock_irqrestore(&host->irq_lock, irqflags); @@ -2828,9 +2828,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) dw_mci_handle_cd(host); } - if (pending & SDMMC_INT_SDIO(slot->sdio_id)) { + if (pending & SDMMC_INT_SDIO(host->sdio_irq)) { mci_writel(host, RINTSTS, - SDMMC_INT_SDIO(slot->sdio_id)); + SDMMC_INT_SDIO(host->sdio_irq)); __dw_mci_enable_sdio_irq(slot, 0); sdio_signal_irq(host->mmc); } @@ -2927,7 +2927,6 @@ static int dw_mci_init_slot(struct dw_mci *host) return -ENOMEM; slot = mmc_priv(mmc); - slot->sdio_id = host->sdio_id0; host->mmc = mmc; slot->host = host; host->slot = slot; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 7f6efb6e6245..3a2e1a046b3e 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -123,7 +123,7 @@ struct dw_mci_dma_slave { * @quirks: Set of quirks that apply to specific versions of the IP. * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. - * @sdio_id0: Number of slot0 in the SDIO interrupt registers. + * @sdio_irq: SDIO interrupt bit in interrupt registers. * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. * @cto_timer: Timer for broken command transfer over scheme. * @dto_timer: Timer for broken data transfer over scheme. @@ -233,7 +233,7 @@ struct dw_mci { unsigned long irq_flags; /* IRQ flags */ int irq; - int sdio_id0; + int sdio_irq; struct timer_list cmd11_timer; struct timer_list cto_timer; @@ -562,7 +562,6 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS * @clock: Clock rate configured by set_ios(). Protected by host->lock. * @__clk_old: The last clock value that was requested from core. * Keeping track of this helps us to avoid spamming the console. - * @sdio_id: Number of this slot in the SDIO interrupt registers. */ struct dw_mci_slot { struct dw_mci *host; @@ -572,8 +571,6 @@ struct dw_mci_slot { unsigned int clock; unsigned int __clk_old; - - int sdio_id; }; /** -- cgit v1.2.3 From 027e1688bc4a77134f3bd9a2b0cac031e743cded Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:50:04 +0800 Subject: mmc: dw_mmc: Move clock rate stuff from struct dw_mci_slot to struct dw_mci Except for moving them, this patch also renames __clk_old to clk_old. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 8 ++++---- drivers/mmc/host/dw_mmc.h | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4dbc5417c7be..18c44a23a698 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1142,7 +1142,7 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot->host; - unsigned int clock = slot->clock; + unsigned int clock = host->clock; u32 div; u32 clk_en_a; u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT; @@ -1167,7 +1167,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; - if ((clock != slot->__clk_old && + if ((clock != host->clk_old && !test_bit(DW_MMC_CARD_NEEDS_POLL, &host->flags)) || force_clkinit) { /* Silent the verbose log if calling from PM context */ @@ -1210,7 +1210,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_send_cmd(slot, sdmmc_cmd_bits, 0); /* keep the last clock value that was requested from core */ - slot->__clk_old = clock; + host->clk_old = clock; host->mmc->actual_clock = div ? ((host->bus_hz / div) >> 1) : host->bus_hz; } @@ -1413,7 +1413,7 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * Use mirror of ios->clock to prevent race with mmc * core ios update when finding the minimum. */ - slot->clock = ios->clock; + slot->host->clock = ios->clock; if (drv_data && drv_data->set_ios) drv_data->set_ios(slot->host, ios); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 3a2e1a046b3e..99a69dacb3cd 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -130,6 +130,8 @@ struct dw_mci_dma_slave { * @mmc: The mmc_host representing this dw_mci. * @flags: Random state bits associated with the host. * @ctype: Card type for this host. + * @clock: Clock rate configured by set_ios(). Protected by host->lock. + * @clk_old: The last clock value that was requested from core. * * Locking * ======= @@ -251,6 +253,8 @@ struct dw_mci { #define DW_MMC_CARD_NO_USE_HOLD 3 #define DW_MMC_CARD_NEEDS_POLL 4 u32 ctype; + unsigned int clock; + unsigned int clk_old; }; /* DMA ops for Internal/External DMAC interface */ @@ -559,8 +563,6 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS * processed, or NULL when the slot is idle. * @queue_node: List node for placing this node in the @queue list of * &struct dw_mci. - * @clock: Clock rate configured by set_ios(). Protected by host->lock. - * @__clk_old: The last clock value that was requested from core. * Keeping track of this helps us to avoid spamming the console. */ struct dw_mci_slot { @@ -568,9 +570,6 @@ struct dw_mci_slot { struct mmc_request *mrq; struct list_head queue_node; - - unsigned int clock; - unsigned int __clk_old; }; /** -- cgit v1.2.3 From 78983c4d608b552de2e59e798c31230e03ca5a2d Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:50:05 +0800 Subject: mmc: dw_mmc: Remove mrq from struct dw_mci_slot struct dw_mci has already kept mrq, so removing keeping mrq again from struct dw_mci_slot. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 11 +++++------ drivers/mmc/host/dw_mmc.h | 4 +--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 18c44a23a698..02a88826928d 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -108,7 +108,7 @@ static int dw_mci_req_show(struct seq_file *s, void *v) /* Make sure we get a consistent snapshot */ spin_lock_bh(&slot->host->lock); - mrq = slot->mrq; + mrq = slot->host->mrq; if (mrq) { cmd = mrq->cmd; @@ -1260,7 +1260,7 @@ static void __dw_mci_start_request(struct dw_mci *host, struct mmc_data *data; u32 cmdflags; - mrq = slot->mrq; + mrq = host->mrq; host->mrq = mrq; @@ -1316,7 +1316,7 @@ static void __dw_mci_start_request(struct dw_mci *host, static void dw_mci_start_request(struct dw_mci *host, struct dw_mci_slot *slot) { - struct mmc_request *mrq = slot->mrq; + struct mmc_request *mrq = host->mrq; struct mmc_command *cmd; cmd = mrq->sbc ? mrq->sbc : mrq->cmd; @@ -1330,7 +1330,7 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, dev_vdbg(&host->mmc->class_dev, "queue request: state=%d\n", host->state); - slot->mrq = mrq; + host->mrq = mrq; if (host->state == STATE_WAITING_CMD11_DONE) { dev_warn(&host->mmc->class_dev, @@ -1356,7 +1356,7 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci *host = slot->host; - WARN_ON(slot->mrq); + WARN_ON(host->mrq); /* * The check for card presence and queueing of the request must be @@ -1816,7 +1816,6 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) WARN_ON(host->cmd || host->data); - host->slot->mrq = NULL; host->mrq = NULL; if (!list_empty(&host->queue)) { slot = list_entry(host->queue.next, diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 99a69dacb3cd..f91f5fcb9422 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -64,7 +64,7 @@ struct dw_mci_dma_slave { * @fifo_reg: Pointer to MMIO registers for data FIFO * @sg: Scatterlist entry currently being processed by PIO code, if any. * @sg_miter: PIO mapping scatterlist iterator. - * @mrq: The request currently being processed on @slot, + * @mrq: The request currently being processed on @host, * or NULL if the controller is idle. * @cmd: The command currently being sent to the card, or NULL. * @data: The data currently being transferred, or NULL if no data @@ -559,7 +559,6 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS /** * struct dw_mci_slot - MMC slot state * @host: The MMC controller this slot is using. - * @mrq: mmc_request currently being processed or waiting to be * processed, or NULL when the slot is idle. * @queue_node: List node for placing this node in the @queue list of * &struct dw_mci. @@ -568,7 +567,6 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS struct dw_mci_slot { struct dw_mci *host; - struct mmc_request *mrq; struct list_head queue_node; }; -- cgit v1.2.3 From 3449b31eb18f9439dcebfd999c51c47bed148d3b Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 16 Dec 2025 19:50:06 +0800 Subject: mmc: dw_mmc: Remove queue from dw_mci It's pointless to use a queue if only one slot is supported. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 23 ++++------------------- drivers/mmc/host/dw_mmc.h | 9 ++------- 2 files changed, 6 insertions(+), 26 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 02a88826928d..4ec60740b0b4 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1346,8 +1346,6 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, if (host->state == STATE_IDLE) { host->state = STATE_SENDING_CMD; dw_mci_start_request(host, slot); - } else { - list_add_tail(&slot->queue_node, &host->queue); } } @@ -1811,28 +1809,16 @@ static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) __releases(&host->lock) __acquires(&host->lock) { - struct dw_mci_slot *slot; struct mmc_host *prev_mmc = host->mmc; WARN_ON(host->cmd || host->data); host->mrq = NULL; - if (!list_empty(&host->queue)) { - slot = list_entry(host->queue.next, - struct dw_mci_slot, queue_node); - list_del(&slot->queue_node); - dev_vdbg(host->dev, "list not empty: %s is next\n", - mmc_hostname(host->mmc)); - host->state = STATE_SENDING_CMD; - dw_mci_start_request(host, slot); - } else { - dev_vdbg(host->dev, "list empty\n"); - if (host->state == STATE_SENDING_CMD11) - host->state = STATE_WAITING_CMD11_DONE; - else - host->state = STATE_IDLE; - } + if (host->state == STATE_SENDING_CMD11) + host->state = STATE_WAITING_CMD11_DONE; + else + host->state = STATE_IDLE; spin_unlock(&host->lock); mmc_request_done(prev_mmc, mrq); @@ -3353,7 +3339,6 @@ int dw_mci_probe(struct dw_mci *host) spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); - INIT_LIST_HEAD(&host->queue); dw_mci_init_fault(host); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index f91f5fcb9422..d5e4e6d67812 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -96,7 +96,6 @@ struct dw_mci_dma_slave { * @completed_events: Bitmask of events which the state machine has * processed. * @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. * @current_speed: Configured rate of the controller. @@ -136,12 +135,12 @@ struct dw_mci_dma_slave { * Locking * ======= * - * @lock is a softirq-safe spinlock protecting @queue as well as + * @lock is a softirq-safe spinlock protecting as well as * @slot, @mrq and @state. These must always be updated * at the same time while holding @lock. * The @mrq field of struct dw_mci_slot is also protected by @lock, * and must always be written at the same time as the slot is added to - * @queue. + * @host. * * @irq_lock is an irq-safe spinlock protecting the INTMASK register * to allow the interrupt handler to modify it directly. Held for only long @@ -203,7 +202,6 @@ struct dw_mci { unsigned long pending_events; unsigned long completed_events; enum dw_mci_state state; - struct list_head queue; u32 bus_hz; u32 current_speed; @@ -560,14 +558,11 @@ static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTS * struct dw_mci_slot - MMC slot state * @host: The MMC controller this slot is using. * processed, or NULL when the slot is idle. - * @queue_node: List node for placing this node in the @queue list of * &struct dw_mci. * Keeping track of this helps us to avoid spamming the console. */ struct dw_mci_slot { struct dw_mci *host; - - struct list_head queue_node; }; /** -- cgit v1.2.3 From c72be4c3066d5f8bee2c699e5c5ff6df5896f74e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Sat, 20 Dec 2025 13:22:05 +0800 Subject: mmc: dw_mmc: Introduce dw_mci_alloc_host() This helper is used for variant drivers to allocate struct dw_mci and set struct dw_mci as private data of struct mmc_host. Signed-off-by: Shawn Lin Tested-by: Marek Szyprowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 17 +++++++++++++++++ drivers/mmc/host/dw_mmc.h | 1 + 2 files changed, 18 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4ec60740b0b4..ecb32dc2a8f0 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3258,6 +3258,23 @@ static void dw_mci_enable_cd(struct dw_mci *host) } } +struct dw_mci *dw_mci_alloc_host(struct device *dev) +{ + struct mmc_host *mmc; + struct dw_mci *host; + + mmc = devm_mmc_alloc_host(dev, sizeof(struct dw_mci)); + if (!mmc) + return ERR_PTR(-ENOMEM); + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dev = dev; + + return host; +} +EXPORT_SYMBOL(dw_mci_alloc_host); + int dw_mci_probe(struct dw_mci *host) { const struct dw_mci_drv_data *drv_data = host->drv_data; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index d5e4e6d67812..892b5451eb69 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -544,6 +544,7 @@ static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value) #define __raw_readq(__reg) (*(volatile u64 __force *)(__reg)) #endif +extern struct dw_mci *dw_mci_alloc_host(struct device *device); extern int dw_mci_probe(struct dw_mci *host); extern void dw_mci_remove(struct dw_mci *host); #ifdef CONFIG_PM -- cgit v1.2.3 From 5e9f849d8d2303e95569124444837afa2ff812ba Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Sat, 20 Dec 2025 13:22:06 +0800 Subject: mmc: dw_mmc: Remove struct dw_mci_slot Use dw_mci_alloc_host() helper to allocate struct dw_mci for dw_mmc-pci.c and dw_mmc-pltfm.c. With that, we could get rid of struct dw_mci_slot and remove it everywhere. Signed-off-by: Shawn Lin Tested-by: Marek Szyprowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pci.c | 7 +- drivers/mmc/host/dw_mmc-pltfm.c | 7 +- drivers/mmc/host/dw_mmc.c | 227 +++++++++++++++++----------------------- drivers/mmc/host/dw_mmc.h | 19 +--- 4 files changed, 103 insertions(+), 157 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 092cc99175af..24f403693711 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -41,13 +41,12 @@ static int dw_mci_pci_probe(struct pci_dev *pdev, if (ret) return ret; - host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); - if (!host) - return -ENOMEM; + host = dw_mci_alloc_host(&pdev->dev); + if (IS_ERR(host)) + return PTR_ERR(host); host->irq = pdev->irq; host->irq_flags = IRQF_SHARED; - host->dev = &pdev->dev; host->pdata = &pci_board_data; ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev)); diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index de820ffd2133..29f2c200d244 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -33,16 +33,15 @@ int dw_mci_pltfm_register(struct platform_device *pdev, struct dw_mci *host; struct resource *regs; - host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL); - if (!host) - return -ENOMEM; + host = dw_mci_alloc_host(&pdev->dev); + if (IS_ERR(host)) + return PTR_ERR(host); host->irq = platform_get_irq(pdev, 0); if (host->irq < 0) return host->irq; host->drv_data = drv_data; - host->dev = &pdev->dev; host->irq_flags = 0; host->pdata = pdev->dev.platform_data; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index ecb32dc2a8f0..5c7859a86dcc 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -100,15 +100,15 @@ struct idmac_desc { #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) { - struct dw_mci_slot *slot = s->private; + struct dw_mci *host = s->private; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_command *stop; struct mmc_data *data; /* Make sure we get a consistent snapshot */ - spin_lock_bh(&slot->host->lock); - mrq = slot->host->mrq; + spin_lock_bh(&host->lock); + mrq = host->mrq; if (mrq) { cmd = mrq->cmd; @@ -133,7 +133,7 @@ static int dw_mci_req_show(struct seq_file *s, void *v) stop->resp[2], stop->error); } - spin_unlock_bh(&slot->host->lock); + spin_unlock_bh(&host->lock); return 0; } @@ -158,9 +158,8 @@ static int dw_mci_regs_show(struct seq_file *s, void *v) } DEFINE_SHOW_ATTRIBUTE(dw_mci_regs); -static void dw_mci_init_debugfs(struct dw_mci_slot *slot) +static void dw_mci_init_debugfs(struct dw_mci *host) { - struct dw_mci *host = slot->host; struct mmc_host *mmc = host->mmc; struct dentry *root; @@ -169,7 +168,7 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot) return; debugfs_create_file("regs", 0400, root, host, &dw_mci_regs_fops); - debugfs_create_file("req", 0400, root, slot, &dw_mci_req_fops); + debugfs_create_file("req", 0400, root, host, &dw_mci_req_fops); debugfs_create_u32("state", 0400, root, &host->state); debugfs_create_xul("pending_events", 0400, root, &host->pending_events); @@ -224,9 +223,8 @@ static void dw_mci_wait_while_busy(struct dw_mci *host, u32 cmd_flags) } } -static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) +static void mci_send_cmd(struct dw_mci *host, u32 cmd, u32 arg) { - struct dw_mci *host = slot->host; unsigned int cmd_status = 0; mci_writel(host, CMDARG, arg); @@ -244,8 +242,7 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); u32 cmdr; cmd->error = -EINPROGRESS; @@ -267,8 +264,8 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) cmdr |= SDMMC_CMD_VOLT_SWITCH; /* Change state to continue to handle CMD11 weirdness */ - WARN_ON(slot->host->state != STATE_SENDING_CMD); - slot->host->state = STATE_SENDING_CMD11; + WARN_ON(host->state != STATE_SENDING_CMD); + host->state = STATE_SENDING_CMD11; /* * We need to disable low power mode (automatic clock stop) @@ -284,7 +281,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) clk_en_a = mci_readl(host, CLKENA); clk_en_a &= ~SDMMC_CLKEN_LOW_PWR; mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | + mci_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } @@ -852,16 +849,16 @@ static int dw_mci_pre_dma_transfer(struct dw_mci *host, static void dw_mci_pre_req(struct mmc_host *mmc, struct mmc_request *mrq) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - if (!slot->host->use_dma || !data) + if (!host->use_dma || !data) return; /* This data might be unmapped at this time */ data->host_cookie = COOKIE_UNMAPPED; - if (dw_mci_pre_dma_transfer(slot->host, mrq->data, + if (dw_mci_pre_dma_transfer(host, mrq->data, COOKIE_PRE_MAPPED) < 0) data->host_cookie = COOKIE_UNMAPPED; } @@ -870,14 +867,14 @@ static void dw_mci_post_req(struct mmc_host *mmc, struct mmc_request *mrq, int err) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); struct mmc_data *data = mrq->data; - if (!slot->host->use_dma || !data) + if (!host->use_dma || !data) return; if (data->host_cookie != COOKIE_UNMAPPED) - dma_unmap_sg(slot->host->dev, + dma_unmap_sg(host->dev, data->sg, data->sg_len, mmc_get_dma_dir(data)); @@ -887,8 +884,7 @@ static void dw_mci_post_req(struct mmc_host *mmc, static int dw_mci_get_cd(struct mmc_host *mmc) { int present; - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); int gpio_cd = mmc_gpio_get_cd(mmc); /* Use platform get_cd function, else try onboard card detect */ @@ -911,7 +907,7 @@ static int dw_mci_get_cd(struct mmc_host *mmc) } else if (gpio_cd >= 0) present = gpio_cd; else - present = (mci_readl(slot->host, CDETECT) & BIT(0)) + present = (mci_readl(host, CDETECT) & BIT(0)) == 0 ? 1 : 0; spin_lock_bh(&host->lock); @@ -1139,9 +1135,8 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) } } -static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) +static void dw_mci_setup_bus(struct dw_mci *host, bool force_clkinit) { - struct dw_mci *host = slot->host; unsigned int clock = host->clock; u32 div; u32 clk_en_a; @@ -1155,7 +1150,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) if (!clock) { mci_writel(host, CLKENA, 0); - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); } else if (clock != host->current_speed || force_clkinit) { div = host->bus_hz / clock; if (host->bus_hz % clock && host->bus_hz > clock) @@ -1173,7 +1168,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) /* Silent the verbose log if calling from PM context */ if (!force_clkinit) dev_info(&host->mmc->class_dev, - "Bus speed = %dHz (slot req %dHz, actual %dHZ div = %d)\n", + "Bus speed = %dHz (req %dHz, actual %dHZ div = %d)\n", host->bus_hz, clock, div ? ((host->bus_hz / div) >> 1) : host->bus_hz, div); @@ -1192,13 +1187,13 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKSRC, 0); /* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); /* set clock to desired speed */ mci_writel(host, CLKDIV, div); /* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); /* enable clock; only low power if no SDIO */ clk_en_a = SDMMC_CLKEN_ENABLE; @@ -1207,7 +1202,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) mci_writel(host, CLKENA, clk_en_a); /* inform CIU */ - mci_send_cmd(slot, sdmmc_cmd_bits, 0); + mci_send_cmd(host, sdmmc_cmd_bits, 0); /* keep the last clock value that was requested from core */ host->clk_old = clock; @@ -1217,7 +1212,7 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) host->current_speed = clock; - /* Set the current slot bus width */ + /* Set the current bus width */ mci_writel(host, CTYPE, host->ctype); } @@ -1253,7 +1248,6 @@ static void dw_mci_set_data_timeout(struct dw_mci *host, } static void __dw_mci_start_request(struct dw_mci *host, - struct dw_mci_slot *slot, struct mmc_command *cmd) { struct mmc_request *mrq; @@ -1313,19 +1307,17 @@ static void __dw_mci_start_request(struct dw_mci *host, host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); } -static void dw_mci_start_request(struct dw_mci *host, - struct dw_mci_slot *slot) +static void dw_mci_start_request(struct dw_mci *host) { struct mmc_request *mrq = host->mrq; struct mmc_command *cmd; cmd = mrq->sbc ? mrq->sbc : mrq->cmd; - __dw_mci_start_request(host, slot, cmd); + __dw_mci_start_request(host, cmd); } /* must be called with host->lock held */ -static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, - struct mmc_request *mrq) +static void dw_mci_queue_request(struct dw_mci *host, struct mmc_request *mrq) { dev_vdbg(&host->mmc->class_dev, "queue request: state=%d\n", host->state); @@ -1345,14 +1337,13 @@ static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, if (host->state == STATE_IDLE) { host->state = STATE_SENDING_CMD; - dw_mci_start_request(host, slot); + dw_mci_start_request(host); } } static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); WARN_ON(host->mrq); @@ -1370,31 +1361,31 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_lock_bh(&host->lock); - dw_mci_queue_request(host, slot, mrq); + dw_mci_queue_request(host, mrq); spin_unlock_bh(&host->lock); } static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); - const struct dw_mci_drv_data *drv_data = slot->host->drv_data; + struct dw_mci *host = mmc_priv(mmc); + const struct dw_mci_drv_data *drv_data = host->drv_data; u32 regs; int ret; switch (ios->bus_width) { case MMC_BUS_WIDTH_4: - slot->host->ctype = SDMMC_CTYPE_4BIT; + host->ctype = SDMMC_CTYPE_4BIT; break; case MMC_BUS_WIDTH_8: - slot->host->ctype = SDMMC_CTYPE_8BIT; + host->ctype = SDMMC_CTYPE_8BIT; break; default: /* set default 1 bit mode */ - slot->host->ctype = SDMMC_CTYPE_1BIT; + host->ctype = SDMMC_CTYPE_1BIT; } - regs = mci_readl(slot->host, UHS_REG); + regs = mci_readl(host, UHS_REG); /* DDR mode set */ if (ios->timing == MMC_TIMING_MMC_DDR52 || @@ -1404,77 +1395,76 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else regs &= ~BIT(16); - mci_writel(slot->host, UHS_REG, regs); - slot->host->timing = ios->timing; + mci_writel(host, UHS_REG, regs); + host->timing = ios->timing; /* * Use mirror of ios->clock to prevent race with mmc * core ios update when finding the minimum. */ - slot->host->clock = ios->clock; + host->clock = ios->clock; if (drv_data && drv_data->set_ios) - drv_data->set_ios(slot->host, ios); + drv_data->set_ios(host, ios); switch (ios->power_mode) { case MMC_POWER_UP: ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); if (ret) { - dev_err(slot->host->dev, "failed to enable vmmc regulator\n"); + dev_err(host->dev, "failed to enable vmmc regulator\n"); return; } - set_bit(DW_MMC_CARD_NEED_INIT, &slot->host->flags); - regs = mci_readl(slot->host, PWREN); + set_bit(DW_MMC_CARD_NEED_INIT, &host->flags); + regs = mci_readl(host, PWREN); regs |= BIT(0); - mci_writel(slot->host, PWREN, regs); + mci_writel(host, PWREN, regs); break; case MMC_POWER_ON: mmc_regulator_enable_vqmmc(mmc); /* Adjust clock / bus width after power is up */ - dw_mci_setup_bus(slot, false); + dw_mci_setup_bus(host, false); break; case MMC_POWER_OFF: /* Turn clock off before power goes down */ - dw_mci_setup_bus(slot, false); + dw_mci_setup_bus(host, false); if (!IS_ERR(mmc->supply.vmmc)) mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); mmc_regulator_disable_vqmmc(mmc); - regs = mci_readl(slot->host, PWREN); + regs = mci_readl(host, PWREN); regs &= ~BIT(0); - mci_writel(slot->host, PWREN, regs); + mci_writel(host, PWREN, regs); /* Reset our state machine after powering off */ - dw_mci_ctrl_reset(slot->host, SDMMC_CTRL_ALL_RESET_FLAGS); + dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS); break; default: break; } - if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) - slot->host->state = STATE_IDLE; + if (host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) + host->state = STATE_IDLE; } static int dw_mci_card_busy(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); u32 status; /* * Check the busy bit which is low when DAT[3:0] * (the data lines) are 0000 */ - status = mci_readl(slot->host, STATUS); + status = mci_readl(host, STATUS); return !!(status & SDMMC_STATUS_BUSY); } static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; u32 uhs; u32 v18 = SDMMC_UHS_18V; @@ -1511,7 +1501,7 @@ static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) static int dw_mci_get_ro(struct mmc_host *mmc) { int read_only; - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); int gpio_ro = mmc_gpio_get_ro(mmc); /* Use platform get_ro function, else try on board write protect */ @@ -1519,7 +1509,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) read_only = gpio_ro; else read_only = - mci_readl(slot->host, WRTPRT) & BIT(0) ? 1 : 0; + mci_readl(host, WRTPRT) & BIT(0) ? 1 : 0; dev_dbg(&mmc->class_dev, "card is %s\n", read_only ? "read-only" : "read-write"); @@ -1529,8 +1519,7 @@ static int dw_mci_get_ro(struct mmc_host *mmc) static void dw_mci_hw_reset(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; int reset; @@ -1561,9 +1550,8 @@ static void dw_mci_hw_reset(struct mmc_host *mmc) usleep_range(200, 300); } -static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare) +static void dw_mci_prepare_sdio_irq(struct dw_mci *host, bool prepare) { - struct dw_mci *host = slot->host; const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR; u32 clk_en_a_old; u32 clk_en_a; @@ -1585,14 +1573,13 @@ static void dw_mci_prepare_sdio_irq(struct dw_mci_slot *slot, bool prepare) if (clk_en_a != clk_en_a_old) { mci_writel(host, CLKENA, clk_en_a); - mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, + mci_send_cmd(host, SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT, 0); } } -static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb) +static void __dw_mci_enable_sdio_irq(struct dw_mci *host, int enb) { - struct dw_mci *host = slot->host; unsigned long irqflags; u32 int_mask; @@ -1611,11 +1598,10 @@ static void __dw_mci_enable_sdio_irq(struct dw_mci_slot *slot, int enb) static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); - dw_mci_prepare_sdio_irq(slot, enb); - __dw_mci_enable_sdio_irq(slot, enb); + dw_mci_prepare_sdio_irq(host, enb); + __dw_mci_enable_sdio_irq(host, enb); /* Avoid runtime suspending the device when SDIO IRQ is enabled */ if (enb) @@ -1626,15 +1612,14 @@ static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) static void dw_mci_ack_sdio_irq(struct mmc_host *mmc) { - struct dw_mci_slot *slot = mmc_priv(mmc); + struct dw_mci *host = mmc_priv(mmc); - __dw_mci_enable_sdio_irq(slot, 1); + __dw_mci_enable_sdio_irq(host, 1); } static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; int err = -EINVAL; @@ -1646,8 +1631,7 @@ static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) static int dw_mci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios) { - struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci *host = slot->host; + struct dw_mci *host = mmc_priv(mmc); const struct dw_mci_drv_data *drv_data = host->drv_data; if (drv_data && drv_data->prepare_hs400_tuning) @@ -1718,7 +1702,7 @@ static bool dw_mci_reset(struct dw_mci *host) ciu_out: /* After a CTRL reset we need to have CIU set clock registers */ - mci_send_cmd(host->slot, SDMMC_CMD_UPD_CLK, 0); + mci_send_cmd(host, SDMMC_CMD_UPD_CLK, 0); return ret; } @@ -1996,8 +1980,7 @@ static void dw_mci_work_func(struct work_struct *t) set_bit(EVENT_CMD_COMPLETE, &host->completed_events); err = dw_mci_command_complete(host, cmd); if (cmd == mrq->sbc && !err) { - __dw_mci_start_request(host, host->slot, - mrq->cmd); + __dw_mci_start_request(host, mrq->cmd); goto unlock; } @@ -2712,7 +2695,6 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) { struct dw_mci *host = dev_id; u32 pending; - struct dw_mci_slot *slot = host->slot; pending = mci_readl(host, MINTSTS); /* read-only mask reg */ @@ -2816,7 +2798,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_SDIO(host->sdio_irq)) { mci_writel(host, RINTSTS, SDMMC_INT_SDIO(host->sdio_irq)); - __dw_mci_enable_sdio_irq(slot, 0); + __dw_mci_enable_sdio_irq(host, 0); sdio_signal_irq(host->mmc); } @@ -2849,9 +2831,8 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) +static int dw_mci_init_host_caps(struct dw_mci *host) { - struct dw_mci *host = slot->host; const struct dw_mci_drv_data *drv_data = host->drv_data; struct mmc_host *mmc = host->mmc; int ctrl_id; @@ -2901,21 +2882,11 @@ static int dw_mci_init_slot_caps(struct dw_mci_slot *slot) return 0; } -static int dw_mci_init_slot(struct dw_mci *host) +static int dw_mci_init_host(struct dw_mci *host) { - struct mmc_host *mmc; - struct dw_mci_slot *slot; + struct mmc_host *mmc = host->mmc; int ret; - mmc = devm_mmc_alloc_host(host->dev, sizeof(*slot)); - if (!mmc) - return -ENOMEM; - - slot = mmc_priv(mmc); - host->mmc = mmc; - slot->host = host; - host->slot = slot; - mmc->ops = &dw_mci_ops; /*if there are external regulators, get them*/ @@ -2930,7 +2901,7 @@ static int dw_mci_init_slot(struct dw_mci *host) if (ret) return ret; - ret = dw_mci_init_slot_caps(slot); + ret = dw_mci_init_host_caps(host); if (ret) return ret; @@ -2965,17 +2936,16 @@ static int dw_mci_init_slot(struct dw_mci *host) return ret; #if defined(CONFIG_DEBUG_FS) - dw_mci_init_debugfs(slot); + dw_mci_init_debugfs(host); #endif return 0; } -static void dw_mci_cleanup_slot(struct dw_mci_slot *slot) +static void dw_mci_cleanup_host(struct dw_mci *host) { /* Debugfs stuff is cleaned up by mmc core */ - mmc_remove_host(slot->host->mmc); - slot->host->slot = NULL; + mmc_remove_host(host->mmc); } static void dw_mci_init_dma(struct dw_mci *host) @@ -3243,7 +3213,7 @@ static void dw_mci_enable_cd(struct dw_mci *host) u32 temp; /* - * No need for CD if all slots have a non-error GPIO + * No need for CD if host has a non-error GPIO * as well as broken card detection is found. */ if (host->mmc->caps & MMC_CAP_NEEDS_POLL) @@ -3465,14 +3435,13 @@ int dw_mci_probe(struct dw_mci *host) "DW MMC controller at irq %d,%d bit host data width,%u deep fifo\n", host->irq, width, fifo_size); - /* We need at least one slot to succeed */ - ret = dw_mci_init_slot(host); + ret = dw_mci_init_host(host); if (ret) { - dev_dbg(host->dev, "slot %d init failed\n", i); + dev_dbg(host->dev, "host init failed\n"); goto err_dmaunmap; } - /* Now that slots are all setup, we can enable card detect */ + /* Now that host is setup, we can enable card detect */ dw_mci_enable_cd(host); return 0; @@ -3495,9 +3464,8 @@ EXPORT_SYMBOL(dw_mci_probe); void dw_mci_remove(struct dw_mci *host) { - dev_dbg(host->dev, "remove slot\n"); - if (host->slot) - dw_mci_cleanup_slot(host->slot); + dev_dbg(host->dev, "remove host\n"); + dw_mci_cleanup_host(host); mci_writel(host, RINTSTS, 0xFFFFFFFF); mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ @@ -3516,8 +3484,6 @@ void dw_mci_remove(struct dw_mci *host) } EXPORT_SYMBOL(dw_mci_remove); - - #ifdef CONFIG_PM int dw_mci_runtime_suspend(struct device *dev) { @@ -3528,9 +3494,8 @@ int dw_mci_runtime_suspend(struct device *dev) clk_disable_unprepare(host->ciu_clk); - if (host->slot && - (mmc_host_can_gpio_cd(host->mmc) || - !mmc_card_is_removable(host->mmc))) + if (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc)) clk_disable_unprepare(host->biu_clk); return 0; @@ -3542,9 +3507,8 @@ int dw_mci_runtime_resume(struct device *dev) int ret = 0; struct dw_mci *host = dev_get_drvdata(dev); - if (host->slot && - (mmc_host_can_gpio_cd(host->mmc) || - !mmc_card_is_removable(host->mmc))) { + if (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc)) { ret = clk_prepare_enable(host->biu_clk); if (ret) return ret; @@ -3580,25 +3544,24 @@ int dw_mci_runtime_resume(struct device *dev) mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); - if (host->slot && host->mmc->pm_flags & MMC_PM_KEEP_POWER) + if (host->mmc->pm_flags & MMC_PM_KEEP_POWER) dw_mci_set_ios(host->mmc, &host->mmc->ios); /* Force setup bus to guarantee available clock output */ - dw_mci_setup_bus(host->slot, true); + dw_mci_setup_bus(host, true); /* Re-enable SDIO interrupts. */ if (sdio_irq_claimed(host->mmc)) - __dw_mci_enable_sdio_irq(host->slot, 1); + __dw_mci_enable_sdio_irq(host, 1); - /* Now that slots are all setup, we can enable card detect */ + /* Now that host is setup, we can enable card detect */ dw_mci_enable_cd(host); return 0; err: - if (host->slot && - (mmc_host_can_gpio_cd(host->mmc) || - !mmc_card_is_removable(host->mmc))) + if (mmc_host_can_gpio_cd(host->mmc) || + !mmc_card_is_removable(host->mmc)) clk_disable_unprepare(host->biu_clk); return ret; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 892b5451eb69..26efe1a8a41b 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -57,7 +57,7 @@ struct dw_mci_dma_slave { }; /** - * struct dw_mci - MMC controller state shared between all slots + * struct dw_mci - MMC controller state * @lock: Spinlock protecting the queue and associated data. * @irq_lock: Spinlock protecting the INTMASK setting. * @regs: Pointer to MMIO registers. @@ -108,7 +108,6 @@ struct dw_mci_dma_slave { * @priv: Implementation defined private data. * @biu_clk: Pointer to bus interface unit clock instance. * @ciu_clk: Pointer to card interface unit clock instance. - * @slot: Slots sharing this MMC controller. * @fifo_depth: depth of FIFO. * @data_addr_override: override fifo reg offset with this value. * @wm_aligned: force fifo watermark equal with data length in PIO mode. @@ -136,11 +135,8 @@ struct dw_mci_dma_slave { * ======= * * @lock is a softirq-safe spinlock protecting as well as - * @slot, @mrq and @state. These must always be updated + * @mrq and @state. These must always be updated * at the same time while holding @lock. - * The @mrq field of struct dw_mci_slot is also protected by @lock, - * and must always be written at the same time as the slot is added to - * @host. * * @irq_lock is an irq-safe spinlock protecting the INTMASK register * to allow the interrupt handler to modify it directly. Held for only long @@ -555,17 +551,6 @@ static inline int dw_mci_runtime_suspend(struct device *device) { return -EOPNOT static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTSUPP; } #endif -/** - * struct dw_mci_slot - MMC slot state - * @host: The MMC controller this slot is using. - * processed, or NULL when the slot is idle. - * &struct dw_mci. - * Keeping track of this helps us to avoid spamming the console. - */ -struct dw_mci_slot { - struct dw_mci *host; -}; - /** * dw_mci driver data - dw-mshc implementation specific driver data. * @caps: mmc subsystem specified capabilities of the controller(s). -- cgit v1.2.3 From 4231325cfb87f712fcbe1fcbeea4186d18c78513 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 23 Dec 2025 10:24:49 +0800 Subject: dt-bindings: mmc: spacemit,sdhci: add reset support The SpacemiT SDHCI controller has two reset lines, one connect to AXI bus which shared by all controllers, while another one connect to individual controller separately. Signed-off-by: Yixun Lan Reviewed-by: Javier Martinez Canillas Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml index 13d9382058fb..de4e9efeb666 100644 --- a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml @@ -32,6 +32,16 @@ properties: - const: core - const: io + resets: + items: + - description: axi reset, connect to AXI bus, shared by all controllers + - description: sdh reset, connect to individual controller separately + + reset-names: + items: + - const: axi + - const: sdh + required: - compatible - reg -- cgit v1.2.3 From 658b716c048684ad13d78280d69b883f181251da Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 23 Dec 2025 10:24:50 +0800 Subject: mmc: sdhci-of-k1: add reset support The SDHCI controller of SpacemiT K1 SoC requires two resets, add support to explicitly request the reset line and deassert during initialization phase. Still using devm_xx_get_optional() API to make the request optional. Signed-off-by: Yixun Lan Reviewed-by: Javier Martinez Canillas Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-k1.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c index 0cc97e23a2f9..a160e1d5d9bd 100644 --- a/drivers/mmc/host/sdhci-of-k1.c +++ b/drivers/mmc/host/sdhci-of-k1.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "sdhci.h" @@ -223,6 +224,21 @@ static inline int spacemit_sdhci_get_clocks(struct device *dev, return 0; } +static inline int spacemit_sdhci_get_resets(struct device *dev) +{ + struct reset_control *rst; + + rst = devm_reset_control_get_optional_shared_deasserted(dev, "axi"); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + rst = devm_reset_control_get_optional_exclusive_deasserted(dev, "sdh"); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + return 0; +} + static const struct sdhci_ops spacemit_sdhci_ops = { .get_max_clock = spacemit_sdhci_clk_get_max_clock, .reset = spacemit_sdhci_reset, @@ -284,6 +300,10 @@ static int spacemit_sdhci_probe(struct platform_device *pdev) if (ret) goto err_pltfm; + ret = spacemit_sdhci_get_resets(dev); + if (ret) + goto err_pltfm; + ret = sdhci_add_host(host); if (ret) goto err_pltfm; -- cgit v1.2.3 From 0621d52b1ddfcdcee8216beec18646d23ed3f165 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Mon, 22 Dec 2025 11:03:33 +0100 Subject: mmc: cavium: Use clamp to simplify cvm_mmc_set_clock Use clamp() to simplify cvm_mmc_set_clock() and improve its readability. Signed-off-by: Thorsten Blum Signed-off-by: Ulf Hansson --- drivers/mmc/host/cavium.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/host/cavium.c b/drivers/mmc/host/cavium.c index 9a55db0e657c..37a88f2a0c86 100644 --- a/drivers/mmc/host/cavium.c +++ b/drivers/mmc/host/cavium.c @@ -905,9 +905,7 @@ static void cvm_mmc_set_clock(struct cvm_mmc_slot *slot, unsigned int clock) { struct mmc_host *mmc = slot->mmc; - clock = min(clock, mmc->f_max); - clock = max(clock, mmc->f_min); - slot->clock = clock; + slot->clock = clamp(clock, mmc->f_min, mmc->f_max); } static int cvm_mmc_init_lowlevel(struct cvm_mmc_slot *slot) -- cgit v1.2.3 From e8c23b466bcc3eae2528ef1e070a759079f092bf Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 24 Dec 2025 13:44:32 +0100 Subject: mmc: atmel-mci: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Nicolas Ferre Signed-off-by: Ulf Hansson --- drivers/mmc/host/atmel-mci.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index fdf6926ea468..3b4928f5b9b2 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -629,14 +629,13 @@ static int atmci_of_init(struct atmel_mci *host) { struct device *dev = host->dev; struct device_node *np = dev->of_node; - struct device_node *cnp; u32 slot_id; int err; if (!np) return dev_err_probe(dev, -EINVAL, "device node not found\n"); - for_each_child_of_node(np, cnp) { + for_each_child_of_node_scoped(np, cnp) { if (of_property_read_u32(cnp, "reg", &slot_id)) { dev_warn(dev, "reg property is missing for %pOF\n", cnp); continue; @@ -645,7 +644,6 @@ static int atmci_of_init(struct atmel_mci *host) if (slot_id >= ATMCI_MAX_NR_SLOTS) { dev_warn(dev, "can't have more than %d slots\n", ATMCI_MAX_NR_SLOTS); - of_node_put(cnp); break; } @@ -658,10 +656,8 @@ static int atmci_of_init(struct atmel_mci *host) "cd", GPIOD_IN, "cd-gpios"); err = PTR_ERR_OR_ZERO(host->pdata[slot_id].detect_pin); if (err) { - if (err != -ENOENT) { - of_node_put(cnp); + if (err != -ENOENT) return err; - } host->pdata[slot_id].detect_pin = NULL; } @@ -673,10 +669,8 @@ static int atmci_of_init(struct atmel_mci *host) "wp", GPIOD_IN, "wp-gpios"); err = PTR_ERR_OR_ZERO(host->pdata[slot_id].wp_pin); if (err) { - if (err != -ENOENT) { - of_node_put(cnp); + if (err != -ENOENT) return err; - } host->pdata[slot_id].wp_pin = NULL; } } -- cgit v1.2.3 From 4e709d5d62f3cc6d520a2a1a7ae9d2c74fcd95ac Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 24 Dec 2025 13:44:33 +0100 Subject: mmc: cavium-octeon: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/cavium-octeon.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/cavium-octeon.c b/drivers/mmc/host/cavium-octeon.c index 0592f356b1e5..8a0daddd9200 100644 --- a/drivers/mmc/host/cavium-octeon.c +++ b/drivers/mmc/host/cavium-octeon.c @@ -148,7 +148,7 @@ static void octeon_mmc_dmar_fixup_done(struct cvm_mmc_host *host) static int octeon_mmc_probe(struct platform_device *pdev) { - struct device_node *cn, *node = pdev->dev.of_node; + struct device_node *node = pdev->dev.of_node; struct cvm_mmc_host *host; void __iomem *base; int mmc_irq[9]; @@ -268,7 +268,7 @@ static int octeon_mmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); i = 0; - for_each_child_of_node(node, cn) { + for_each_child_of_node_scoped(node, cn) { host->slot_pdev[i] = of_platform_device_create(cn, NULL, &pdev->dev); if (!host->slot_pdev[i]) { @@ -279,7 +279,6 @@ static int octeon_mmc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Error populating slots\n"); octeon_mmc_set_shared_power(host, 0); - of_node_put(cn); goto error; } i++; -- cgit v1.2.3 From f6b3889812e4f37fe5f1eeca9741eb7d7e39a764 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 24 Dec 2025 13:44:34 +0100 Subject: mmc: jz4740: Fix Wvoid-pointer-to-enum-cast warning "jz4740" is an enum, thus cast of pointer on 64-bit compile test with clang W=1 causes: jz4740_mmc.c:1055:18: error: cast to smaller integer type 'enum jz4740_mmc_version' from 'const void *' [-Werror,-Wvoid-pointer-to-enum-cast] Signed-off-by: Krzysztof Kozlowski Reviewed-by: Paul Cercueil Signed-off-by: Ulf Hansson --- drivers/mmc/host/jz4740_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index 6a0d0250d47b..6a3c26b7c82d 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -1052,7 +1052,7 @@ static int jz4740_mmc_probe(struct platform_device* pdev) host = mmc_priv(mmc); /* Default if no match is JZ4740 */ - host->version = (enum jz4740_mmc_version)device_get_match_data(&pdev->dev); + host->version = (unsigned long)device_get_match_data(&pdev->dev); ret = mmc_of_parse(mmc); if (ret) -- cgit v1.2.3 From 6fba00c98eb9da19118f4d1f50fe0d790ef79341 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 24 Dec 2025 13:44:35 +0100 Subject: mmc: sdhci-of-aspeed: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to make code a bit simpler. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-aspeed.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index ca97b01996b1..4296def69436 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -519,7 +519,7 @@ static struct platform_driver aspeed_sdhci_driver = { static int aspeed_sdc_probe(struct platform_device *pdev) { - struct device_node *parent, *child; + struct device_node *parent; struct aspeed_sdc *sdc; int ret; @@ -548,12 +548,11 @@ static int aspeed_sdc_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, sdc); parent = pdev->dev.of_node; - for_each_available_child_of_node(parent, child) { + for_each_available_child_of_node_scoped(parent, child) { struct platform_device *cpdev; cpdev = of_platform_device_create(child, NULL, &pdev->dev); if (!cpdev) { - of_node_put(child); ret = -ENODEV; goto err_clk; } -- cgit v1.2.3 From c740532de087009b18981ab14be42c45494c246d Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Thu, 25 Dec 2025 20:16:15 +0200 Subject: dt-bindings: mmc: arm,pl18x: Do not use plural form of a proper noun PrimeCell As a proper noun PrimeCell is a single entity and it can not have a plural form, fix the typo. Signed-off-by: Vladimir Zapolskiy Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/arm,pl18x.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml index f90fd73904a2..8d62be4355a0 100644 --- a/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml +++ b/Documentation/devicetree/bindings/mmc/arm,pl18x.yaml @@ -11,7 +11,7 @@ maintainers: - Ulf Hansson description: - The ARM PrimeCells MMCI PL180 and PL181 provides an interface for + The ARM PrimeCell MMCI PL180 and PL181 provides an interface for reading and writing to MultiMedia and SD cards alike. Over the years vendors have use the VHDL code from ARM to create derivative MMC/SD/SDIO host controllers with very similar characteristics. -- cgit v1.2.3 From 4db2a6c8576f75f16b253c1fd15c821c40662aa2 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:16:52 +0800 Subject: mmc: dw_mmc: Check return value of dma_ops->init() in resume dma_ops->init() may fail, should check the return value. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 5c7859a86dcc..e437a142a76e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3524,8 +3524,11 @@ int dw_mci_runtime_resume(struct device *dev) goto err; } - if (host->use_dma && host->dma_ops->init) - host->dma_ops->init(host); + if (host->use_dma && host->dma_ops->init) { + ret = host->dma_ops->init(host); + if (ret) + return ret; + } /* * Restore the initial value at FIFOTH register -- cgit v1.2.3 From 57cc962430a1026f68bcda583d0bdd02dd564298 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:16:53 +0800 Subject: mmc: dw_mmc: Remove dma_ops from struct dw_mci_board It does take dma_ops from struct dw_mci_board, but we immediately re-assign it from either dw_mci_idmac_ops or dw_mci_edmac_ops in dw_mci_init_dma(). That means it's never used now, or has been broken for a long time without noticed. It seems no drivers need it, so remove it now. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 1 - drivers/mmc/host/dw_mmc.h | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e437a142a76e..4d4a727b8b3f 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3366,7 +3366,6 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_ciu; } - host->dma_ops = host->pdata->dma_ops; dw_mci_init_dma(host); /* Clear the interrupts for the host controller */ diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 26efe1a8a41b..bf3566ac40d9 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -78,7 +78,7 @@ struct dw_mci_dma_slave { * @dma_64bit_address: Whether DMA supports 64-bit address mode or not. * @sg_dma: Bus address of DMA buffer. * @sg_cpu: Virtual address of DMA buffer. - * @dma_ops: Pointer to platform-specific DMA callbacks. + * @dma_ops: Pointer to DMA callbacks. * @cmd_status: Snapshot of SR taken upon completion of the current * @ring_size: Buffer size for idma descriptors. * command. Only valid when EVENT_CMD_COMPLETE is pending. @@ -280,7 +280,6 @@ struct dw_mci_board { u32 detect_delay_ms; struct reset_control *rstc; - struct dw_mci_dma_ops *dma_ops; }; /* Support for longer data read timeout */ -- cgit v1.2.3 From 137cf0a4d63322f4be50954da8378bf389493f2e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:16:54 +0800 Subject: mmc: dw_mmc: Remove SDMMC_INT_ERROR SDMMC_INT_ERROR is defined as 0xbfc2, which looks like a magic number and it's never used, so remove it. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index bf3566ac40d9..0ff301cda4dc 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -399,7 +399,6 @@ struct dw_mci_board { #define SDMMC_INT_CMD_DONE BIT(2) #define SDMMC_INT_RESP_ERR BIT(1) #define SDMMC_INT_CD BIT(0) -#define SDMMC_INT_ERROR 0xbfc2 /* Command register defines */ #define SDMMC_CMD_START BIT(31) #define SDMMC_CMD_USE_HOLD_REG BIT(29) -- cgit v1.2.3 From fedf31e20405db12d1019f3df7b27fb7c92d2c61 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:16:55 +0800 Subject: mmc: dw_mmc: Remove assignment of pdata in dw_mci_pltfm_register() No one using dw_mci_pltfm_register() passes in platform_data when searching host and arch directories, remove it. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pltfm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 29f2c200d244..c7d727350596 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -43,7 +43,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev, host->drv_data = drv_data; host->irq_flags = 0; - host->pdata = pdev->dev.platform_data; host->regs = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); if (IS_ERR(host->regs)) -- cgit v1.2.3 From cea6e1a151bf4e97f76620441d56e97ef38a2dbd Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:16:56 +0800 Subject: mmc: dw_mmc: Remove caps2 and pm_caps from struct dw_mci_board Nobody uses them. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 6 ------ drivers/mmc/host/dw_mmc.h | 2 -- 2 files changed, 8 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4d4a727b8b3f..d917b30598c5 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2840,9 +2840,6 @@ static int dw_mci_init_host_caps(struct dw_mci *host) if (host->pdata->caps) mmc->caps = host->pdata->caps; - if (host->pdata->pm_caps) - mmc->pm_caps = host->pdata->pm_caps; - if (drv_data) mmc->caps |= drv_data->common_caps; @@ -2863,9 +2860,6 @@ static int dw_mci_init_host_caps(struct dw_mci *host) mmc->caps |= drv_data->caps[ctrl_id]; } - if (host->pdata->caps2) - mmc->caps2 = host->pdata->caps2; - /* if host has set a minimum_freq, we should respect it */ if (host->minimum_speed) mmc->f_min = host->minimum_speed; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 0ff301cda4dc..47775dd8f6a3 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -267,8 +267,6 @@ struct dw_mci_board { unsigned int bus_hz; /* Clock speed at the cclk_in pad */ u32 caps; /* Capabilities */ - u32 caps2; /* More capabilities */ - u32 pm_caps; /* PM capabilities */ /* * Override fifo depth. If 0, autodetect it from the FIFOTH register, * but note that this may not be reliable after a bootloader has used -- cgit v1.2.3 From d917f35c33fd0abe49f5a93a85489d96b8a9af3b Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:16:57 +0800 Subject: mmc: dw_mmc: Move rstc from struct dw_mci_board to struct dw_mci Nobody using dw_mci_board passes in rstc, move it to the common struct dw_mci needed by all users. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 16 ++++++++-------- drivers/mmc/host/dw_mmc.h | 6 ++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index d917b30598c5..4a0b9a6b45f1 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3166,9 +3166,9 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) return ERR_PTR(-ENOMEM); /* find reset controller when exist */ - pdata->rstc = devm_reset_control_get_optional_exclusive(dev, "reset"); - if (IS_ERR(pdata->rstc)) - return ERR_CAST(pdata->rstc); + host->rstc = devm_reset_control_get_optional_exclusive(dev, "reset"); + if (IS_ERR(host->rstc)) + return ERR_CAST(host->rstc); if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth)) dev_info(dev, @@ -3299,10 +3299,10 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_ciu; } - if (host->pdata->rstc) { - reset_control_assert(host->pdata->rstc); + if (host->rstc) { + reset_control_assert(host->rstc); usleep_range(10, 50); - reset_control_deassert(host->pdata->rstc); + reset_control_deassert(host->rstc); } if (drv_data && drv_data->init) { @@ -3443,7 +3443,7 @@ err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - reset_control_assert(host->pdata->rstc); + reset_control_assert(host->rstc); err_clk_ciu: clk_disable_unprepare(host->ciu_clk); @@ -3470,7 +3470,7 @@ void dw_mci_remove(struct dw_mci *host) if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - reset_control_assert(host->pdata->rstc); + reset_control_assert(host->rstc); clk_disable_unprepare(host->ciu_clk); clk_disable_unprepare(host->biu_clk); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 47775dd8f6a3..3cac7ce7c070 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -130,6 +130,8 @@ struct dw_mci_dma_slave { * @ctype: Card type for this host. * @clock: Clock rate configured by set_ios(). Protected by host->lock. * @clk_old: The last clock value that was requested from core. + * @pdev: platform_device registered + * @rstc: Reset controller for this host. * * Locking * ======= @@ -249,6 +251,8 @@ struct dw_mci { u32 ctype; unsigned int clock; unsigned int clk_old; + struct platform_device *pdev; + struct reset_control *rstc; }; /* DMA ops for Internal/External DMAC interface */ @@ -276,8 +280,6 @@ struct dw_mci_board { /* delay in mS before detecting cards after interrupt */ u32 detect_delay_ms; - - struct reset_control *rstc; }; /* Support for longer data read timeout */ -- cgit v1.2.3 From dc1f8aacc9941150be504048a46dd94c111717bc Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:16:58 +0800 Subject: mmc: dw_mmc: Remove fifo_depth from struct dw_mci_board struct dw_mci already keeps one, so remove it from struct dw_mci_board. Now, as dw_mmc-pci still provide struct dw_mci_board, so host->fifo_depth will not be overwritten. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pci.c | 2 +- drivers/mmc/host/dw_mmc.c | 6 +++--- drivers/mmc/host/dw_mmc.h | 6 ------ 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 24f403693711..89ad4993e4e1 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -28,7 +28,6 @@ static struct dw_mci_board pci_board_data = { .caps = DW_MCI_CAPABILITIES, .bus_hz = 33 * 1000 * 1000, .detect_delay_ms = 200, - .fifo_depth = 32, }; static int dw_mci_pci_probe(struct pci_dev *pdev, @@ -48,6 +47,7 @@ static int dw_mci_pci_probe(struct pci_dev *pdev, host->irq = pdev->irq; host->irq_flags = IRQF_SHARED; host->pdata = &pci_board_data; + host->fifo_depth = 32; ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev)); if (ret) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4a0b9a6b45f1..98549a260e37 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3170,7 +3170,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) if (IS_ERR(host->rstc)) return ERR_CAST(host->rstc); - if (device_property_read_u32(dev, "fifo-depth", &pdata->fifo_depth)) + if (device_property_read_u32(dev, "fifo-depth", &host->fifo_depth)) dev_info(dev, "fifo-depth property not found, using value of FIFOTH register as default\n"); @@ -3373,7 +3373,7 @@ int dw_mci_probe(struct dw_mci *host) * FIFO threshold settings RxMark = fifo_size / 2 - 1, * Tx Mark = fifo_size / 2 DMA Size = 8 */ - if (!host->pdata->fifo_depth) { + if (!host->fifo_depth) { /* * Power-on value of RX_WMark is FIFO_DEPTH-1, but this may * have been overwritten by the bootloader, just like we're @@ -3383,7 +3383,7 @@ int dw_mci_probe(struct dw_mci *host) fifo_size = mci_readl(host, FIFOTH); fifo_size = 1 + ((fifo_size >> 16) & 0xfff); } else { - fifo_size = host->pdata->fifo_depth; + fifo_size = host->fifo_depth; } host->fifo_depth = fifo_size; host->fifoth_val = diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 3cac7ce7c070..bc82d1df75f0 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -271,12 +271,6 @@ struct dw_mci_board { unsigned int bus_hz; /* Clock speed at the cclk_in pad */ u32 caps; /* Capabilities */ - /* - * Override fifo depth. If 0, autodetect it from the FIFOTH register, - * but note that this may not be reliable after a bootloader has used - * it. - */ - unsigned int fifo_depth; /* delay in mS before detecting cards after interrupt */ u32 detect_delay_ms; -- cgit v1.2.3 From d3fcd6362aafe5f30d85c2f1639cdcb7288ca4bf Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:16:59 +0800 Subject: mmc: dw_mmc: Move detect_delay_ms from struct dw_mci_board to struct dw_mci Now, as dw_mmc-pci still provide struct dw_mci_board, so host->detect_delay_ms will not be overwritten. So it's fine to move it. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pci.c | 2 +- drivers/mmc/host/dw_mmc.c | 4 ++-- drivers/mmc/host/dw_mmc.h | 5 ++--- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 89ad4993e4e1..616804a07821 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -27,7 +27,6 @@ static struct dw_mci_board pci_board_data = { .caps = DW_MCI_CAPABILITIES, .bus_hz = 33 * 1000 * 1000, - .detect_delay_ms = 200, }; static int dw_mci_pci_probe(struct pci_dev *pdev, @@ -48,6 +47,7 @@ static int dw_mci_pci_probe(struct pci_dev *pdev, host->irq_flags = IRQF_SHARED; host->pdata = &pci_board_data; host->fifo_depth = 32; + host->detect_delay_ms = 200; ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev)); if (ret) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 98549a260e37..e67d785a2ff6 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2688,7 +2688,7 @@ static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) static void dw_mci_handle_cd(struct dw_mci *host) { mmc_detect_change(host->mmc, - msecs_to_jiffies(host->pdata->detect_delay_ms)); + msecs_to_jiffies(host->detect_delay_ms)); } static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) @@ -3175,7 +3175,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) "fifo-depth property not found, using value of FIFOTH register as default\n"); device_property_read_u32(dev, "card-detect-delay", - &pdata->detect_delay_ms); + &host->detect_delay_ms); device_property_read_u32(dev, "data-addr", &host->data_addr_override); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index bc82d1df75f0..e364b1696c17 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -132,6 +132,7 @@ struct dw_mci_dma_slave { * @clk_old: The last clock value that was requested from core. * @pdev: platform_device registered * @rstc: Reset controller for this host. + * @detect_delay_ms: Delay in mS before detecting cards after interrupt. * * Locking * ======= @@ -253,6 +254,7 @@ struct dw_mci { unsigned int clk_old; struct platform_device *pdev; struct reset_control *rstc; + u32 detect_delay_ms; }; /* DMA ops for Internal/External DMAC interface */ @@ -271,9 +273,6 @@ struct dw_mci_board { unsigned int bus_hz; /* Clock speed at the cclk_in pad */ u32 caps; /* Capabilities */ - - /* delay in mS before detecting cards after interrupt */ - u32 detect_delay_ms; }; /* Support for longer data read timeout */ -- cgit v1.2.3 From ec6d8bb39dc8d8ee976d65349290411cab209764 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:17:00 +0800 Subject: mmc: dw_mmc: Remove bus_hz from struct dw_mci_board struct dw_mci already keeps one, reuse it. Now, as dw_mmc-pci still provide struct dw_mci_board, so host->bus_hz will not be overwritten. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pci.c | 2 +- drivers/mmc/host/dw_mmc.c | 10 ++++------ drivers/mmc/host/dw_mmc.h | 2 -- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 616804a07821..95e91f816ccf 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -26,7 +26,6 @@ static struct dw_mci_board pci_board_data = { .caps = DW_MCI_CAPABILITIES, - .bus_hz = 33 * 1000 * 1000, }; static int dw_mci_pci_probe(struct pci_dev *pdev, @@ -48,6 +47,7 @@ static int dw_mci_pci_probe(struct pci_dev *pdev, host->pdata = &pci_board_data; host->fifo_depth = 32; host->detect_delay_ms = 200; + host->bus_hz = 33 * 1000 * 1000; ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev)); if (ret) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e67d785a2ff6..a5a7ef696d52 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3183,7 +3183,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) host->wm_aligned = true; if (!device_property_read_u32(dev, "clock-frequency", &clock_frequency)) - pdata->bus_hz = clock_frequency; + host->bus_hz = clock_frequency; if (drv_data && drv_data->parse_dt) { ret = drv_data->parse_dt(host); @@ -3273,8 +3273,6 @@ int dw_mci_probe(struct dw_mci *host) ret = PTR_ERR(host->ciu_clk); if (ret == -EPROBE_DEFER) goto err_clk_biu; - - host->bus_hz = host->pdata->bus_hz; } else { ret = clk_prepare_enable(host->ciu_clk); if (ret) { @@ -3282,12 +3280,12 @@ int dw_mci_probe(struct dw_mci *host) goto err_clk_biu; } - if (host->pdata->bus_hz) { - ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz); + if (host->bus_hz) { + ret = clk_set_rate(host->ciu_clk, host->bus_hz); if (ret) dev_warn(host->dev, "Unable to set bus rate to %uHz\n", - host->pdata->bus_hz); + host->bus_hz); } host->bus_hz = clk_get_rate(host->ciu_clk); } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index e364b1696c17..cb0e06b01d22 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -270,8 +270,6 @@ struct dw_mci_dma_ops { /* Board platform data */ struct dw_mci_board { - unsigned int bus_hz; /* Clock speed at the cclk_in pad */ - u32 caps; /* Capabilities */ }; -- cgit v1.2.3 From 792bd24b6eb6625e9b7ea926597c5c15a5554266 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:17:01 +0800 Subject: mmc: dw_mmc: Remove struct dw_mci_board The only user of dw_mci_board is dw_pci-pci, now we can provide the caps from dw_mci_drv_data, so we could let dw_pci-pci use dw_mci_drv_data and remove caps from struct dw_mci_board. With that, struct dw_mci_board is no longer needed, we can remove it. Then we should check all settings in dw_mci_parse_dt in order not to overwrite them if provided from variant drivers. Also, without CONFIG_OF support, dw_mmc doesn' work as host->pdata is always ERR_PTR(-EINVAL), we could remove it together. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pci.c | 6 +++--- drivers/mmc/host/dw_mmc.c | 45 ++++++++++++++----------------------------- drivers/mmc/host/dw_mmc.h | 7 ------- 3 files changed, 17 insertions(+), 41 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 95e91f816ccf..c82c23e4ea9a 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -24,8 +24,8 @@ MMC_CAP_SD_HIGHSPEED | MMC_CAP_8_BIT_DATA |\ MMC_CAP_SDIO_IRQ) -static struct dw_mci_board pci_board_data = { - .caps = DW_MCI_CAPABILITIES, +static const struct dw_mci_drv_data pci_drv_data = { + .common_caps = DW_MCI_CAPABILITIES, }; static int dw_mci_pci_probe(struct pci_dev *pdev, @@ -44,10 +44,10 @@ static int dw_mci_pci_probe(struct pci_dev *pdev, host->irq = pdev->irq; host->irq_flags = IRQF_SHARED; - host->pdata = &pci_board_data; host->fifo_depth = 32; host->detect_delay_ms = 200; host->bus_hz = 33 * 1000 * 1000; + host->drv_data = &pci_drv_data; ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev)); if (ret) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index a5a7ef696d52..2b3f95017dba 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2837,9 +2837,6 @@ static int dw_mci_init_host_caps(struct dw_mci *host) struct mmc_host *mmc = host->mmc; int ctrl_id; - if (host->pdata->caps) - mmc->caps = host->pdata->caps; - if (drv_data) mmc->caps |= drv_data->common_caps; @@ -3152,54 +3149,43 @@ exit: spin_unlock_irqrestore(&host->irq_lock, irqflags); } -#ifdef CONFIG_OF -static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) +static int dw_mci_parse_dt(struct dw_mci *host) { - struct dw_mci_board *pdata; struct device *dev = host->dev; const struct dw_mci_drv_data *drv_data = host->drv_data; int ret; u32 clock_frequency; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - /* find reset controller when exist */ host->rstc = devm_reset_control_get_optional_exclusive(dev, "reset"); if (IS_ERR(host->rstc)) - return ERR_CAST(host->rstc); + return PTR_ERR(host->rstc); - if (device_property_read_u32(dev, "fifo-depth", &host->fifo_depth)) + if (!host->fifo_depth && device_property_read_u32(dev, "fifo-depth", &host->fifo_depth)) dev_info(dev, "fifo-depth property not found, using value of FIFOTH register as default\n"); - device_property_read_u32(dev, "card-detect-delay", - &host->detect_delay_ms); + if (!host->detect_delay_ms) + device_property_read_u32(dev, "card-detect-delay", + &host->detect_delay_ms); - device_property_read_u32(dev, "data-addr", &host->data_addr_override); + if (!host->data_addr_override) + device_property_read_u32(dev, "data-addr", &host->data_addr_override); if (device_property_present(dev, "fifo-watermark-aligned")) host->wm_aligned = true; - if (!device_property_read_u32(dev, "clock-frequency", &clock_frequency)) + if (!host->bus_hz && !device_property_read_u32(dev, "clock-frequency", &clock_frequency)) host->bus_hz = clock_frequency; if (drv_data && drv_data->parse_dt) { ret = drv_data->parse_dt(host); if (ret) - return ERR_PTR(ret); + return ret; } - return pdata; -} - -#else /* CONFIG_OF */ -static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) -{ - return ERR_PTR(-EINVAL); + return 0; } -#endif /* CONFIG_OF */ static void dw_mci_enable_cd(struct dw_mci *host) { @@ -3245,12 +3231,9 @@ int dw_mci_probe(struct dw_mci *host) int width, i, ret = 0; u32 fifo_size; - if (!host->pdata) { - host->pdata = dw_mci_parse_dt(host); - if (IS_ERR(host->pdata)) - return dev_err_probe(host->dev, PTR_ERR(host->pdata), - "platform data not available\n"); - } + ret = dw_mci_parse_dt(host); + if (ret) + return dev_err_probe(host->dev, ret, "parse dt failed\n"); host->biu_clk = devm_clk_get(host->dev, "biu"); if (IS_ERR(host->biu_clk)) { diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index cb0e06b01d22..76bf8a72b6bd 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -103,7 +103,6 @@ struct dw_mci_dma_slave { * @fifoth_val: The value of FIFOTH register. * @verid: Denote Version ID. * @dev: Device associated with the MMC controller. - * @pdata: Platform data associated with the MMC controller. * @drv_data: Driver specific data for identified variant of the controller * @priv: Implementation defined private data. * @biu_clk: Pointer to bus interface unit clock instance. @@ -208,7 +207,6 @@ struct dw_mci { u32 fifoth_val; u16 verid; struct device *dev; - struct dw_mci_board *pdata; const struct dw_mci_drv_data *drv_data; void *priv; struct clk *biu_clk; @@ -268,11 +266,6 @@ struct dw_mci_dma_ops { void (*exit)(struct dw_mci *host); }; -/* Board platform data */ -struct dw_mci_board { - u32 caps; /* Capabilities */ -}; - /* Support for longer data read timeout */ #define DW_MMC_QUIRK_EXTENDED_TMOUT BIT(0) /* Force 32-bit access to the FIFO */ -- cgit v1.2.3 From e453a08b3a1199809b27a2c6982261710abe0170 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:17:02 +0800 Subject: mmc: dw_mmc: Remove redundant struct mmc_data forward declaration The header file linux/mmc/core.h, which is already included in dw_mmc.h, contains the forward declaration of struct mmc_data. There's no need to redeclare it here. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 76bf8a72b6bd..5c17bcc85bf5 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -43,8 +43,6 @@ enum dw_mci_cookie { COOKIE_MAPPED, /* mapped by prepare_data() of dwmmc */ }; -struct mmc_data; - enum { TRANS_MODE_PIO = 0, TRANS_MODE_IDMAC, -- cgit v1.2.3 From 29d3f6a64376eff2d44e1f181d7770434b05274b Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:17:03 +0800 Subject: mmc: dw_mmc: Remove DW_MCI_SEND_STATUS and DW_MCI_RECV_STATUS macros Use MMC_DATA_READ and MMC_DATA_WRITE defined by MMC core instead. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 2b3f95017dba..e563998b846c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -40,8 +40,6 @@ SDMMC_INT_RESP_ERR | SDMMC_INT_HLE) #define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \ DW_MCI_CMD_ERROR_FLAGS) -#define DW_MCI_SEND_STATUS 1 -#define DW_MCI_RECV_STATUS 2 #define DW_MCI_DMA_THRESHOLD 16 #define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ @@ -1085,9 +1083,9 @@ static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) host->data = data; if (data->flags & MMC_DATA_READ) - host->dir_status = DW_MCI_RECV_STATUS; + host->dir_status = MMC_DATA_READ; else - host->dir_status = DW_MCI_SEND_STATUS; + host->dir_status = MMC_DATA_WRITE; dw_mci_ctrl_thld(host, data); @@ -1853,7 +1851,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) data->error = -EILSEQ; } else if (status & SDMMC_INT_EBE) { if (host->dir_status == - DW_MCI_SEND_STATUS) { + MMC_DATA_WRITE) { /* * No data CRC status was returned. * The number of bytes transferred @@ -1862,7 +1860,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) data->bytes_xfered = 0; data->error = -ETIMEDOUT; } else if (host->dir_status == - DW_MCI_RECV_STATUS) { + MMC_DATA_READ) { data->error = -EILSEQ; } } else { @@ -2007,7 +2005,7 @@ static void dw_mci_work_func(struct work_struct *t) * avoids races and keeps things simple. */ if (err != -ETIMEDOUT && - host->dir_status == DW_MCI_RECV_STATUS) { + host->dir_status == MMC_DATA_READ) { state = STATE_SENDING_DATA; continue; } @@ -2051,7 +2049,7 @@ static void dw_mci_work_func(struct work_struct *t) * If all data-related interrupts don't come * within the given time in reading data state. */ - if (host->dir_status == DW_MCI_RECV_STATUS) + if (host->dir_status == MMC_DATA_READ) dw_mci_set_drto(host); break; } @@ -2091,7 +2089,7 @@ static void dw_mci_work_func(struct work_struct *t) * interrupt doesn't come within the given time. * in reading data state. */ - if (host->dir_status == DW_MCI_RECV_STATUS) + if (host->dir_status == MMC_DATA_READ) dw_mci_set_drto(host); break; } @@ -2759,7 +2757,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (!host->data_status) host->data_status = pending; smp_wmb(); /* drain writebuffer */ - if (host->dir_status == DW_MCI_RECV_STATUS) { + if (host->dir_status == MMC_DATA_READ) { if (host->sg != NULL) dw_mci_read_data_pio(host, true); } @@ -2771,13 +2769,13 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_RXDR) { mci_writel(host, RINTSTS, SDMMC_INT_RXDR); - if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) + if (host->dir_status == MMC_DATA_READ && host->sg) dw_mci_read_data_pio(host, false); } if (pending & SDMMC_INT_TXDR) { mci_writel(host, RINTSTS, SDMMC_INT_TXDR); - if (host->dir_status == DW_MCI_SEND_STATUS && host->sg) + if (host->dir_status == MMC_DATA_WRITE && host->sg) dw_mci_write_data_pio(host); } -- cgit v1.2.3 From 7597d68cea6827f721e66b69d85e50e8ba820b09 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:17:04 +0800 Subject: mmc: dw_mmc: Improve dw_mci_get_cd() The current dw_mci_get_cd() implementation maintains a DW_MMC_CARD_PRESENT flag primarily for logging purposes, which adds unnecessary complexity. Additionally, the if-else-elif control flow does not align with the Linux kernel coding style. This commit simplifies the function by: - Removing the redundant card presence flag - Replacing the conditional chain with a cleaner implementation - Improving code readability while maintaining functionality The change reduces code complexity without affecting the actual card detection behavior. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 44 ++++++++++++++------------------------------ drivers/mmc/host/dw_mmc.h | 9 ++++----- 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index e563998b846c..1d085b41920e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -881,42 +881,21 @@ static void dw_mci_post_req(struct mmc_host *mmc, static int dw_mci_get_cd(struct mmc_host *mmc) { - int present; struct dw_mci *host = mmc_priv(mmc); int gpio_cd = mmc_gpio_get_cd(mmc); - /* Use platform get_cd function, else try onboard card detect */ - if (((mmc->caps & MMC_CAP_NEEDS_POLL) - || !mmc_card_is_removable(mmc))) { - present = 1; + if (mmc->caps & MMC_CAP_NEEDS_POLL) + return 1; - if (!test_bit(DW_MMC_CARD_PRESENT, &host->flags)) { - if (mmc->caps & MMC_CAP_NEEDS_POLL) { - dev_info(&mmc->class_dev, - "card is polling.\n"); - } else { - dev_info(&mmc->class_dev, - "card is non-removable.\n"); - } - set_bit(DW_MMC_CARD_PRESENT, &host->flags); - } - - return present; - } else if (gpio_cd >= 0) - present = gpio_cd; - else - present = (mci_readl(host, CDETECT) & BIT(0)) - == 0 ? 1 : 0; + if (!mmc_card_is_removable(mmc)) + return 1; - spin_lock_bh(&host->lock); - if (present && !test_and_set_bit(DW_MMC_CARD_PRESENT, &host->flags)) - dev_dbg(&mmc->class_dev, "card is present\n"); - else if (!present && - !test_and_clear_bit(DW_MMC_CARD_PRESENT, &host->flags)) - dev_dbg(&mmc->class_dev, "card is not present\n"); - spin_unlock_bh(&host->lock); + /* Try slot gpio detection */ + if (gpio_cd >= 0) + return !!gpio_cd; - return present; + /* Host native card detect */ + return !(mci_readl(host, CDETECT) & BIT(0)); } static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) @@ -2918,6 +2897,11 @@ static int dw_mci_init_host(struct dw_mci *host) mmc->max_seg_size = mmc->max_req_size; } + if (mmc->caps & MMC_CAP_NEEDS_POLL) + dev_info(&mmc->class_dev, "card is polling.\n"); + else if (!mmc_card_is_removable(mmc)) + dev_info(&mmc->class_dev, "card is non-removable.\n"); + dw_mci_get_cd(mmc); ret = mmc_add_host(mmc); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 5c17bcc85bf5..6800e7ce1d7f 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -240,11 +240,10 @@ struct dw_mci { #endif struct mmc_host *mmc; unsigned long flags; -#define DW_MMC_CARD_PRESENT 0 -#define DW_MMC_CARD_NEED_INIT 1 -#define DW_MMC_CARD_NO_LOW_PWR 2 -#define DW_MMC_CARD_NO_USE_HOLD 3 -#define DW_MMC_CARD_NEEDS_POLL 4 +#define DW_MMC_CARD_NEED_INIT 0 +#define DW_MMC_CARD_NO_LOW_PWR 1 +#define DW_MMC_CARD_NO_USE_HOLD 2 +#define DW_MMC_CARD_NEEDS_POLL 3 u32 ctype; unsigned int clock; unsigned int clk_old; -- cgit v1.2.3 From c6969a13f6d3e8706bc9c0df23576c1884d6f7fb Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:17:05 +0800 Subject: mmc: dw_mmc: Remove unused register access macros The mci_readw/mci_writew/mci_readq/mci_writeq macros were added to provide 16-bit and 64-bit register access operations, but they have remained unused since their introduction. Remove these dead code. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.h | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 6800e7ce1d7f..a4d5f4fa9375 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -488,32 +488,7 @@ static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value) #define mci_writel(dev, reg, value) \ writel_relaxed((value), (dev)->regs + SDMMC_##reg) -/* 16-bit FIFO access macros */ -#define mci_readw(dev, reg) \ - readw_relaxed((dev)->regs + SDMMC_##reg) -#define mci_writew(dev, reg, value) \ - writew_relaxed((value), (dev)->regs + SDMMC_##reg) - -/* 64-bit FIFO access macros */ -#ifdef readq -#define mci_readq(dev, reg) \ - readq_relaxed((dev)->regs + SDMMC_##reg) -#define mci_writeq(dev, reg, value) \ - writeq_relaxed((value), (dev)->regs + SDMMC_##reg) -#else -/* - * Dummy readq implementation for architectures that don't define it. - * - * We would assume that none of these architectures would configure - * the IP block with a 64bit FIFO width, so this code will never be - * executed on those machines. Defining these macros here keeps the - * rest of the code free from ifdefs. - */ -#define mci_readq(dev, reg) \ - (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg)) -#define mci_writeq(dev, reg, value) \ - (*(volatile u64 __force *)((dev)->regs + SDMMC_##reg) = (value)) - +#ifndef readq #define __raw_writeq(__value, __reg) \ (*(volatile u64 __force *)(__reg) = (__value)) #define __raw_readq(__reg) (*(volatile u64 __force *)(__reg)) -- cgit v1.2.3 From 765f4836ec6620212004dc7ce6b40a88dba71c30 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:17:07 +0800 Subject: mmc: dw_mmc-pci: Use BAR_2 and pcim_iomap_region() PCIe endpoint framework defines standard BAR enum for EP drivers to use, so remove PCI_BAR_NO here. Then, pcim_iomap_regions has been deprecated for a long time, replace it with pcim_iomap_region(); Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pci.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index c82c23e4ea9a..66d2edc30600 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -10,13 +10,13 @@ #include #include #include +#include #include #include #include #include #include "dw_mmc.h" -#define PCI_BAR_NO 2 #define SYNOPSYS_DW_MCI_VENDOR_ID 0x700 #define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107 /* Defining the Capabilities */ @@ -49,11 +49,9 @@ static int dw_mci_pci_probe(struct pci_dev *pdev, host->bus_hz = 33 * 1000 * 1000; host->drv_data = &pci_drv_data; - ret = pcim_iomap_regions(pdev, 1 << PCI_BAR_NO, pci_name(pdev)); - if (ret) - return ret; - - host->regs = pcim_iomap_table(pdev)[PCI_BAR_NO]; + host->regs = pcim_iomap_region(pdev, BAR_2, pci_name(pdev)); + if (IS_ERR(host->regs)) + return PTR_ERR(host->regs); pci_set_master(pdev); -- cgit v1.2.3 From 2c7fe99a71f65d61b677f1c7d5cf5e9bd859c5ee Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 6 Jan 2026 10:17:08 +0800 Subject: mmc: dw_mmc-pltfm: use modern PM macros Use the modern PM macros for the suspend and resume functions to be automatically dropped by the compiler when CONFIG_PM or CONFIG_PM_SLEEP are disabled, without having to use #ifdef guards. When this's done, remove all variant drivers' local pm definition and replace it with dw_mci_pltfm_pmops. Signed-off-by: Shawn Lin [Ulf: Fixed conflict by dropping changes for dw_mmc-rockchip] Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-bluefield.c | 2 +- drivers/mmc/host/dw_mmc-k3.c | 7 +------ drivers/mmc/host/dw_mmc-pci.c | 8 ++------ drivers/mmc/host/dw_mmc-pltfm.c | 4 ++-- drivers/mmc/host/dw_mmc.c | 2 -- drivers/mmc/host/dw_mmc.h | 5 ----- 6 files changed, 6 insertions(+), 22 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c index 3cf526ab0387..ed6dea42b18e 100644 --- a/drivers/mmc/host/dw_mmc-bluefield.c +++ b/drivers/mmc/host/dw_mmc-bluefield.c @@ -73,7 +73,7 @@ static struct platform_driver dw_mci_bluefield_pltfm_driver = { .name = "dwmmc_bluefield", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_bluefield_match, - .pm = &dw_mci_pltfm_pmops, + .pm = pm_ptr(&dw_mci_pltfm_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 23c303106dca..8cf0487b952c 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -455,11 +455,6 @@ static int dw_mci_k3_probe(struct platform_device *pdev) return dw_mci_pltfm_register(pdev, drv_data); } -static const struct dev_pm_ops dw_mci_k3_dev_pm_ops = { - SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) - RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL) -}; - static struct platform_driver dw_mci_k3_pltfm_driver = { .probe = dw_mci_k3_probe, .remove = dw_mci_pltfm_remove, @@ -467,7 +462,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = { .name = "dwmmc_k3", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_k3_match, - .pm = pm_ptr(&dw_mci_k3_dev_pm_ops), + .pm = pm_ptr(&dw_mci_pltfm_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index 66d2edc30600..e046674c9748 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -16,6 +16,7 @@ #include #include #include "dw_mmc.h" +#include "dw_mmc-pltfm.h" #define SYNOPSYS_DW_MCI_VENDOR_ID 0x700 #define SYNOPSYS_DW_MCI_DEVICE_ID 0x1107 @@ -71,11 +72,6 @@ static void dw_mci_pci_remove(struct pci_dev *pdev) dw_mci_remove(host); } -static const struct dev_pm_ops dw_mci_pci_dev_pm_ops = { - SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) - RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL) -}; - static const struct pci_device_id dw_mci_pci_id[] = { { PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) }, {} @@ -88,7 +84,7 @@ static struct pci_driver dw_mci_pci_driver = { .probe = dw_mci_pci_probe, .remove = dw_mci_pci_remove, .driver = { - .pm = pm_ptr(&dw_mci_pci_dev_pm_ops), + .pm = pm_ptr(&dw_mci_pltfm_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index c7d727350596..fde465a4bf5d 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -57,9 +57,9 @@ int dw_mci_pltfm_register(struct platform_device *pdev, EXPORT_SYMBOL_GPL(dw_mci_pltfm_register); const struct dev_pm_ops dw_mci_pltfm_pmops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend, + RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL) }; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1d085b41920e..d7c5b13e48f9 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3440,7 +3440,6 @@ void dw_mci_remove(struct dw_mci *host) } EXPORT_SYMBOL(dw_mci_remove); -#ifdef CONFIG_PM int dw_mci_runtime_suspend(struct device *dev) { struct dw_mci *host = dev_get_drvdata(dev); @@ -3526,7 +3525,6 @@ err: return ret; } EXPORT_SYMBOL(dw_mci_runtime_resume); -#endif /* CONFIG_PM */ static int __init dw_mci_init(void) { diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index a4d5f4fa9375..9a27d778f362 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -497,13 +497,8 @@ static inline void mci_fifo_l_writeq(void __iomem *addr, u64 value) extern struct dw_mci *dw_mci_alloc_host(struct device *device); extern int dw_mci_probe(struct dw_mci *host); extern void dw_mci_remove(struct dw_mci *host); -#ifdef CONFIG_PM extern int dw_mci_runtime_suspend(struct device *device); extern int dw_mci_runtime_resume(struct device *device); -#else -static inline int dw_mci_runtime_suspend(struct device *device) { return -EOPNOTSUPP; } -static inline int dw_mci_runtime_resume(struct device *device) { return -EOPNOTSUPP; } -#endif /** * dw_mci driver data - dw-mshc implementation specific driver data. -- cgit v1.2.3 From c0b68bc25efeaca8a1ef3a0cfab5fbf5f81d002d Mon Sep 17 00:00:00 2001 From: Jeff Chen Date: Tue, 13 Jan 2026 11:15:17 +0800 Subject: mmc: sdio: add NXP vendor and IW61x device IDs Add NXP's SDIO vendor ID (0x0471) and IW61x device ID (0x0205) to sdio_ids.h for future support of NXP Wi-Fi chips over SDIO. Signed-off-by: Jeff Chen Signed-off-by: Ulf Hansson --- include/linux/mmc/sdio_ids.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h index 673cbdf43453..39ac2b612e4a 100644 --- a/include/linux/mmc/sdio_ids.h +++ b/include/linux/mmc/sdio_ids.h @@ -116,6 +116,9 @@ #define SDIO_VENDOR_ID_MICROCHIP_WILC 0x0296 #define SDIO_DEVICE_ID_MICROCHIP_WILC1000 0x5347 +#define SDIO_VENDOR_ID_NXP 0x0471 +#define SDIO_DEVICE_ID_NXP_IW61X 0x0205 + #define SDIO_VENDOR_ID_REALTEK 0x024c #define SDIO_DEVICE_ID_REALTEK_RTW8723BS 0xb723 #define SDIO_DEVICE_ID_REALTEK_RTW8821BS 0xb821 -- cgit v1.2.3 From 366b5941566cebdb8a972ff296f4f889f3887741 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 20 Jan 2026 14:05:50 +0100 Subject: mmc: sdhci: Stop advertising the driver in dmesg As much as we have grown used to seeing this message on every kernel boot, it does not add any technical value. Drop all messages from sdhci_drv_init(), and drop the module_init() and module_exit() calls as well since they now become empty. The modules becomes a pure library module, meaning it will get pulled in by modprobe() when the symbols inside it are needed, or compiled in if any users are compiled in. Signed-off-by: Linus Walleij Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ac7e11f37af7..1ff15fa9b042 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -4997,22 +4997,6 @@ EXPORT_SYMBOL_GPL(sdhci_remove_host); * * \*****************************************************************************/ -static int __init sdhci_drv_init(void) -{ - pr_info(DRIVER_NAME - ": Secure Digital Host Controller Interface driver\n"); - pr_info(DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); - - return 0; -} - -static void __exit sdhci_drv_exit(void) -{ -} - -module_init(sdhci_drv_init); -module_exit(sdhci_drv_exit); - module_param(debug_quirks, uint, 0444); module_param(debug_quirks2, uint, 0444); -- cgit v1.2.3 From b4206966e2d48883f04d5a2b2ae6c46b528245d3 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 22 Jan 2026 17:37:30 +0800 Subject: dt-bindings: mmc: spacemit,sdhci: add support for K3 SoC The SDHCI controller found on SpacemiT K3 SoC share the same IP with K1 generation, while fixed the broken 64BIT DMA issue. Introduce a compatible string to enable support for it. Acked-by: Rob Herring (Arm) Signed-off-by: Yixun Lan Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml index de4e9efeb666..9a055d963a7f 100644 --- a/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/spacemit,sdhci.yaml @@ -14,7 +14,9 @@ allOf: properties: compatible: - const: spacemit,k1-sdhci + enum: + - spacemit,k1-sdhci + - spacemit,k3-sdhci reg: maxItems: 1 -- cgit v1.2.3 From 1e9f43a1dbefd3de45b97545e5773d2b52dc7f02 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Thu, 22 Jan 2026 17:37:31 +0800 Subject: mmc: sdhci-of-k1: spacemit: Add support for K3 SoC The SDHCI controller found on SpacemiT K3 SoC share the same IP with K1 generation and introduce a compatible data to denote the change that broken 64BIT DMA issue has been fixed. Signed-off-by: Yixun Lan Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-k1.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-k1.c b/drivers/mmc/host/sdhci-of-k1.c index a160e1d5d9bd..455656f9842d 100644 --- a/drivers/mmc/host/sdhci-of-k1.c +++ b/drivers/mmc/host/sdhci-of-k1.c @@ -259,8 +259,20 @@ static const struct sdhci_pltfm_data spacemit_sdhci_k1_pdata = { SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }; +static const struct sdhci_pltfm_data spacemit_sdhci_k3_pdata = { + .ops = &spacemit_sdhci_ops, + .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_32BIT_ADMA_SIZE | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, +}; + static const struct of_device_id spacemit_sdhci_of_match[] = { - { .compatible = "spacemit,k1-sdhci" }, + { .compatible = "spacemit,k1-sdhci", .data = &spacemit_sdhci_k1_pdata }, + { .compatible = "spacemit,k3-sdhci", .data = &spacemit_sdhci_k3_pdata }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, spacemit_sdhci_of_match); @@ -271,10 +283,13 @@ static int spacemit_sdhci_probe(struct platform_device *pdev) struct spacemit_sdhci_host *sdhst; struct sdhci_pltfm_host *pltfm_host; struct sdhci_host *host; + const struct sdhci_pltfm_data *data; struct mmc_host_ops *mops; int ret; - host = sdhci_pltfm_init(pdev, &spacemit_sdhci_k1_pdata, sizeof(*sdhst)); + data = of_device_get_match_data(&pdev->dev); + + host = sdhci_pltfm_init(pdev, data, sizeof(*sdhst)); if (IS_ERR(host)) return PTR_ERR(host); -- cgit v1.2.3 From d5159623162cc3d462ba1e661f8362c181756d65 Mon Sep 17 00:00:00 2001 From: Albert Yang Date: Fri, 23 Jan 2026 17:53:37 +0800 Subject: dt-bindings: mmc: add binding for BST DWCMSHC SDHCI controller Add device tree bindings for the Black Sesame Technologies DWCMSHC SDHCI controller used in C1200 SoC. The binding describes a Synopsys DesignWare Cores Mobile Storage Host Controller with BST-specific extensions including: - Two register regions (core SDHCI and CRM registers) - Optional memory-region for bounce buffer support - Fixed clock input Signed-off-by: Ge Gordon Signed-off-by: Albert Yang Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/bst,c1200-sdhci.yaml | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml diff --git a/Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml b/Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml new file mode 100644 index 000000000000..8358bb70c333 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/bst,c1200-sdhci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Black Sesame Technologies DWCMSHC SDHCI Controller + +maintainers: + - Ge Gordon + +allOf: + - $ref: sdhci-common.yaml# + +properties: + compatible: + const: bst,c1200-sdhci + + reg: + items: + - description: Core SDHCI registers + - description: CRM registers + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: core + + memory-region: + maxItems: 1 + + dma-coherent: true + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + #include + + bus { + #address-cells = <2>; + #size-cells = <2>; + + mmc@22200000 { + compatible = "bst,c1200-sdhci"; + reg = <0x0 0x22200000 0x0 0x1000>, + <0x0 0x23006000 0x0 0x1000>; + interrupts = ; + clocks = <&clk_mmc>; + clock-names = "core"; + memory-region = <&mmc0_reserved>; + max-frequency = <200000000>; + bus-width = <8>; + non-removable; + dma-coherent; + }; + }; -- cgit v1.2.3 From ef7eb1a7094dbb5042ea1bed34193c1415fb9844 Mon Sep 17 00:00:00 2001 From: Albert Yang Date: Fri, 23 Jan 2026 17:53:38 +0800 Subject: mmc: sdhci: allow drivers to pre-allocate bounce buffer Allow platform drivers to pre-allocate bounce buffer by checking if host->bounce_buffer is already set before attempting allocation. This enables platforms with specific DMA constraints (such as 32-bit DMA on controllers that cannot access high memory) to use their own reserved memory regions for the bounce buffer. Suggested-by: Adrian Hunter Signed-off-by: Albert Yang Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 1ff15fa9b042..b1a3cd574c84 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -4193,6 +4193,12 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) unsigned int bounce_size; int ret; + /* Drivers may have already allocated the buffer */ + if (host->bounce_buffer) { + bounce_size = host->bounce_buffer_size; + max_blocks = bounce_size / 512; + goto out; + } /* * Cap the bounce buffer at 64KB. Using a bigger bounce buffer * has diminishing returns, this is probably because SD/MMC @@ -4241,6 +4247,7 @@ static void sdhci_allocate_bounce_buffer(struct sdhci_host *host) host->bounce_buffer_size = bounce_size; +out: /* Lie about this since we're bouncing */ mmc->max_segs = max_blocks; mmc->max_seg_size = bounce_size; -- cgit v1.2.3 From 695824f45629dfad8834f432d78f8b60fdcbe3d8 Mon Sep 17 00:00:00 2001 From: Albert Yang Date: Fri, 23 Jan 2026 17:53:39 +0800 Subject: mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver Add SDHCI controller driver for Black Sesame Technologies C1200 SoC. This driver supports the DWCMSHC SDHCI controller with BST-specific enhancements including: - Custom clock management and tuning - Power management support - BST-specific register configurations - Support for eMMC and SD card interfaces - Hardware limitation workaround for 32-bit DMA addressing The driver addresses specific hardware constraints where: - System memory uses 64-bit bus, eMMC controller uses 32-bit bus - eMMC controller cannot access memory through SMMU due to hardware bug - All system DRAM is configured outside 4GB boundary (ZONE_DMA32) - Uses SRAM-based bounce buffer within 32-bit address space Signed-off-by: Ge Gordon Signed-off-by: Albert Yang Acked-by: Arnd Bergmann Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 14 ++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-of-bst.c | 521 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 536 insertions(+) create mode 100644 drivers/mmc/host/sdhci-of-bst.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 6d79cc9a79e2..b37e6e014416 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -429,6 +429,20 @@ config MMC_SDHCI_BCM_KONA If you have a controller with this interface, say Y or M here. +config MMC_SDHCI_BST + tristate "SDHCI support for Black Sesame Technologies BST C1200 controller" + depends on ARCH_BST || COMPILE_TEST + depends on MMC_SDHCI_PLTFM + depends on OF + help + This selects the Secure Digital Host Controller Interface (SDHCI) + for Black Sesame Technologies BST C1200 SoC. The controller is + based on Synopsys DesignWare Cores Mobile Storage Controller but + requires platform-specific workarounds for hardware limitations. + + If you have a controller with this interface, say Y or M here. + If unsure, say N. + config MMC_SDHCI_F_SDH30 tristate "SDHCI support for Fujitsu Semiconductor F_SDH30" depends on MMC_SDHCI_PLTFM diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 5057fea8afb6..ee412e6b84d6 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_UHS2) += sdhci-uhs2.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o +obj-$(CONFIG_MMC_SDHCI_BST) += sdhci-of-bst.o sdhci-pci-y += sdhci-pci-core.o sdhci-pci-o2micro.o sdhci-pci-arasan.o \ sdhci-pci-dwc-mshc.o sdhci-pci-gli.o obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o diff --git a/drivers/mmc/host/sdhci-of-bst.c b/drivers/mmc/host/sdhci-of-bst.c new file mode 100644 index 000000000000..c124990a64f4 --- /dev/null +++ b/drivers/mmc/host/sdhci-of-bst.c @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SDHCI driver for Black Sesame Technologies C1200 controller + * + * Copyright (c) 2025 Black Sesame Technologies + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sdhci.h" +#include "sdhci-pltfm.h" + +/* SDHCI register extensions */ +#define SDHCI_CLOCK_PLL_EN 0x0008 +#define SDHCI_VENDOR_PTR_R 0xE8 + +/* BST-specific tuning parameters */ +#define BST_TUNING_COUNT 0x20 + +/* Synopsys vendor specific registers */ +#define SDHC_EMMC_CTRL_R_OFFSET 0x2C +#define MBIU_CTRL 0x510 + +/* MBIU burst control bits */ +#define BURST_INCR16_EN BIT(3) +#define BURST_INCR8_EN BIT(2) +#define BURST_INCR4_EN BIT(1) +#define BURST_EN (BURST_INCR16_EN | BURST_INCR8_EN | BURST_INCR4_EN) +#define MBIU_BURST_MASK GENMASK(3, 0) + +/* CRM (Clock/Reset/Management) register offsets */ +#define SDEMMC_CRM_BCLK_DIV_CTRL 0x08 +#define SDEMMC_CRM_TIMER_DIV_CTRL 0x0C +#define SDEMMC_CRM_RX_CLK_CTRL 0x14 +#define SDEMMC_CRM_VOL_CTRL 0x1C +#define REG_WR_PROTECT 0x88 +#define DELAY_CHAIN_SEL 0x94 + +/* CRM register values and bit definitions */ +#define REG_WR_PROTECT_KEY 0x1234abcd +#define BST_VOL_STABLE_ON BIT(7) +#define BST_TIMER_DIV_MASK GENMASK(7, 0) +#define BST_TIMER_DIV_VAL 0x20 +#define BST_TIMER_LOAD_BIT BIT(8) +#define BST_BCLK_EN_BIT BIT(10) +#define BST_RX_UPDATE_BIT BIT(11) +#define BST_EMMC_CTRL_RST_N BIT(2) /* eMMC card reset control */ + +/* Clock frequency limits */ +#define BST_DEFAULT_MAX_FREQ 200000000UL /* 200 MHz */ +#define BST_DEFAULT_MIN_FREQ 400000UL /* 400 kHz */ + +/* Clock control bit definitions */ +#define BST_CLOCK_DIV_MASK GENMASK(7, 0) +#define BST_CLOCK_DIV_SHIFT 8 +#define BST_BCLK_DIV_MASK GENMASK(9, 0) + +/* Clock frequency thresholds */ +#define BST_CLOCK_THRESHOLD_LOW 1500 + +/* Clock stability polling parameters */ +#define BST_CLK_STABLE_POLL_US 1000 /* Poll interval in microseconds */ +#define BST_CLK_STABLE_TIMEOUT_US 20000 /* Timeout for internal clock stabilization (us) */ + +struct sdhci_bst_priv { + void __iomem *crm_reg_base; +}; + +union sdhci_bst_rx_ctrl { + struct { + u32 rx_revert:1, + rx_clk_sel_sec:1, + rx_clk_div:4, + rx_clk_phase_inner:2, + rx_clk_sel_first:1, + rx_clk_phase_out:2, + rx_clk_en:1, + res0:20; + }; + u32 reg; +}; + +static u32 sdhci_bst_crm_read(struct sdhci_pltfm_host *pltfm_host, u32 offset) +{ + struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host); + + return readl(priv->crm_reg_base + offset); +} + +static void sdhci_bst_crm_write(struct sdhci_pltfm_host *pltfm_host, u32 offset, u32 value) +{ + struct sdhci_bst_priv *priv = sdhci_pltfm_priv(pltfm_host); + + writel(value, priv->crm_reg_base + offset); +} + +static int sdhci_bst_wait_int_clk(struct sdhci_host *host) +{ + u16 clk; + + if (read_poll_timeout(sdhci_readw, clk, (clk & SDHCI_CLOCK_INT_STABLE), + BST_CLK_STABLE_POLL_US, BST_CLK_STABLE_TIMEOUT_US, false, + host, SDHCI_CLOCK_CONTROL)) + return -EBUSY; + return 0; +} + +static unsigned int sdhci_bst_get_max_clock(struct sdhci_host *host) +{ + return BST_DEFAULT_MAX_FREQ; +} + +static unsigned int sdhci_bst_get_min_clock(struct sdhci_host *host) +{ + return BST_DEFAULT_MIN_FREQ; +} + +static void sdhci_bst_enable_clk(struct sdhci_host *host, unsigned int clk) +{ + struct sdhci_pltfm_host *pltfm_host; + unsigned int div; + u32 val; + union sdhci_bst_rx_ctrl rx_reg; + + pltfm_host = sdhci_priv(host); + + /* Calculate clock divider based on target frequency */ + if (clk == 0) { + div = 0; + } else if (clk < BST_DEFAULT_MIN_FREQ) { + /* Below minimum: use max divider to get closest to min freq */ + div = BST_DEFAULT_MAX_FREQ / BST_DEFAULT_MIN_FREQ; + } else if (clk <= BST_DEFAULT_MAX_FREQ) { + /* Normal range: calculate divider directly */ + div = BST_DEFAULT_MAX_FREQ / clk; + } else { + /* Above maximum: no division needed */ + div = 1; + } + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk &= ~SDHCI_CLOCK_PLL_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL); + val &= ~BST_TIMER_LOAD_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL); + val &= ~BST_TIMER_DIV_MASK; + val |= BST_TIMER_DIV_VAL; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL); + val |= BST_TIMER_LOAD_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_TIMER_DIV_CTRL, val); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + val &= ~BST_RX_UPDATE_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val); + + rx_reg.reg = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + + rx_reg.rx_revert = 0; + rx_reg.rx_clk_sel_sec = 1; + rx_reg.rx_clk_div = 4; + rx_reg.rx_clk_phase_inner = 2; + rx_reg.rx_clk_sel_first = 0; + rx_reg.rx_clk_phase_out = 2; + + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, rx_reg.reg); + + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + val |= BST_RX_UPDATE_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val); + + /* Disable clock first */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val &= ~BST_BCLK_EN_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* Setup clock divider */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val &= ~BST_BCLK_DIV_MASK; + val |= div; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* Enable clock */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val |= BST_BCLK_EN_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* RMW the clock divider bits to avoid clobbering other fields */ + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk &= ~(BST_CLOCK_DIV_MASK << BST_CLOCK_DIV_SHIFT); + clk |= (div & BST_CLOCK_DIV_MASK) << BST_CLOCK_DIV_SHIFT; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + clk |= SDHCI_CLOCK_PLL_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk |= SDHCI_CLOCK_CARD_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); + + clk |= SDHCI_CLOCK_INT_EN; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +} + +static void sdhci_bst_set_clock(struct sdhci_host *host, unsigned int clock) +{ + /* Turn off card/internal/PLL clocks when clock==0 to avoid idle power */ + u32 clk_reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + + if (!clock) { + clk_reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN | SDHCI_CLOCK_PLL_EN); + sdhci_writew(host, clk_reg, SDHCI_CLOCK_CONTROL); + return; + } + sdhci_bst_enable_clk(host, clock); +} + +/* + * sdhci_bst_reset - Reset the SDHCI host controller with special + * handling for eMMC card reset control. + */ +static void sdhci_bst_reset(struct sdhci_host *host, u8 mask) +{ + u16 vendor_ptr, emmc_ctrl_reg; + u32 reg; + + if (host->mmc->caps2 & MMC_CAP2_NO_SD) { + vendor_ptr = sdhci_readw(host, SDHCI_VENDOR_PTR_R); + emmc_ctrl_reg = vendor_ptr + SDHC_EMMC_CTRL_R_OFFSET; + + reg = sdhci_readw(host, emmc_ctrl_reg); + reg &= ~BST_EMMC_CTRL_RST_N; + sdhci_writew(host, reg, emmc_ctrl_reg); + sdhci_reset(host, mask); + usleep_range(10, 20); + reg = sdhci_readw(host, emmc_ctrl_reg); + reg |= BST_EMMC_CTRL_RST_N; + sdhci_writew(host, reg, emmc_ctrl_reg); + } else { + sdhci_reset(host, mask); + } +} + +/* Set timeout control register to maximum value (0xE) */ +static void sdhci_bst_set_timeout(struct sdhci_host *host, struct mmc_command *cmd) +{ + sdhci_writeb(host, 0xE, SDHCI_TIMEOUT_CONTROL); +} + +/* + * sdhci_bst_set_power - Set power mode and voltage, also configures + * MBIU burst mode control based on power state. + */ +static void sdhci_bst_set_power(struct sdhci_host *host, unsigned char mode, + unsigned short vdd) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + u32 reg; + u32 val; + + sdhci_set_power(host, mode, vdd); + + if (mode == MMC_POWER_OFF) { + /* Disable MBIU burst mode */ + reg = sdhci_readw(host, MBIU_CTRL); + reg &= ~BURST_EN; /* Clear all burst enable bits */ + sdhci_writew(host, reg, MBIU_CTRL); + + /* Disable CRM BCLK */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL); + val &= ~BST_BCLK_EN_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_BCLK_DIV_CTRL, val); + + /* Disable RX clock */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL); + val &= ~BST_RX_UPDATE_BIT; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_RX_CLK_CTRL, val); + + /* Turn off voltage stable power */ + val = sdhci_bst_crm_read(pltfm_host, SDEMMC_CRM_VOL_CTRL); + val &= ~BST_VOL_STABLE_ON; + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, val); + } else { + /* Configure burst mode only when powered on */ + reg = sdhci_readw(host, MBIU_CTRL); + reg &= ~MBIU_BURST_MASK; /* Clear burst related bits */ + reg |= BURST_EN; /* Enable burst mode for better bandwidth */ + sdhci_writew(host, reg, MBIU_CTRL); + } +} + +/* + * sdhci_bst_execute_tuning - Execute tuning procedure by trying different + * delay chain values and selecting the optimal one. + */ +static int sdhci_bst_execute_tuning(struct sdhci_host *host, u32 opcode) +{ + struct sdhci_pltfm_host *pltfm_host; + int ret = 0, error; + int first_start = -1, first_end = -1, best = 0; + int second_start = -1, second_end = -1, has_failure = 0; + int i; + + pltfm_host = sdhci_priv(host); + + for (i = 0; i < BST_TUNING_COUNT; i++) { + /* Protected write */ + sdhci_bst_crm_write(pltfm_host, REG_WR_PROTECT, REG_WR_PROTECT_KEY); + /* Write tuning value */ + sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << i) - 1); + + /* Wait for internal clock stable before tuning */ + if (sdhci_bst_wait_int_clk(host)) { + dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n"); + return -EBUSY; + } + + ret = mmc_send_tuning(host->mmc, opcode, &error); + if (ret != 0) { + has_failure = 1; + } else { + if (has_failure == 0) { + if (first_start == -1) + first_start = i; + first_end = i; + } else { + if (second_start == -1) + second_start = i; + second_end = i; + } + } + } + + /* Calculate best tuning value */ + if (first_end - first_start >= second_end - second_start) + best = ((first_end - first_start) >> 1) + first_start; + else + best = ((second_end - second_start) >> 1) + second_start; + + if (best < 0) + best = 0; + + sdhci_bst_crm_write(pltfm_host, DELAY_CHAIN_SEL, (1ul << best) - 1); + /* Confirm internal clock stable after setting best tuning value */ + if (sdhci_bst_wait_int_clk(host)) { + dev_err(mmc_dev(host->mmc), "Internal clock never stabilised\n"); + return -EBUSY; + } + + return 0; +} + +/* Enable voltage stable power for voltage switch */ +static void sdhci_bst_voltage_switch(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + /* Enable voltage stable power */ + sdhci_bst_crm_write(pltfm_host, SDEMMC_CRM_VOL_CTRL, BST_VOL_STABLE_ON); +} + +static const struct sdhci_ops sdhci_bst_ops = { + .set_clock = sdhci_bst_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = sdhci_set_uhs_signaling, + .get_min_clock = sdhci_bst_get_min_clock, + .get_max_clock = sdhci_bst_get_max_clock, + .reset = sdhci_bst_reset, + .set_power = sdhci_bst_set_power, + .set_timeout = sdhci_bst_set_timeout, + .platform_execute_tuning = sdhci_bst_execute_tuning, + .voltage_switch = sdhci_bst_voltage_switch, +}; + +static const struct sdhci_pltfm_data sdhci_bst_pdata = { + .ops = &sdhci_bst_ops, + .quirks = SDHCI_QUIRK_BROKEN_ADMA | + SDHCI_QUIRK_DELAY_AFTER_POWER | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT, + .quirks2 = SDHCI_QUIRK2_BROKEN_DDR50 | + SDHCI_QUIRK2_TUNING_WORK_AROUND | + SDHCI_QUIRK2_ACMD23_BROKEN, +}; + +static void sdhci_bst_free_bounce_buffer(struct sdhci_host *host) +{ + if (host->bounce_buffer) { + dma_free_coherent(mmc_dev(host->mmc), host->bounce_buffer_size, + host->bounce_buffer, host->bounce_addr); + host->bounce_buffer = NULL; + } + of_reserved_mem_device_release(mmc_dev(host->mmc)); +} + +static int sdhci_bst_alloc_bounce_buffer(struct sdhci_host *host) +{ + struct mmc_host *mmc = host->mmc; + unsigned int bounce_size; + int ret; + + /* Fixed SRAM bounce size to 32KB: verified config under 32-bit DMA addressing limit */ + bounce_size = SZ_32K; + + ret = of_reserved_mem_device_init_by_idx(mmc_dev(mmc), mmc_dev(mmc)->of_node, 0); + if (ret) { + dev_err(mmc_dev(mmc), "Failed to initialize reserved memory\n"); + return ret; + } + + host->bounce_buffer = dma_alloc_coherent(mmc_dev(mmc), bounce_size, + &host->bounce_addr, GFP_KERNEL); + if (!host->bounce_buffer) + return -ENOMEM; + + host->bounce_buffer_size = bounce_size; + + return 0; +} + +static int sdhci_bst_probe(struct platform_device *pdev) +{ + struct sdhci_pltfm_host *pltfm_host; + struct sdhci_host *host; + struct sdhci_bst_priv *priv; + int err; + + host = sdhci_pltfm_init(pdev, &sdhci_bst_pdata, sizeof(struct sdhci_bst_priv)); + if (IS_ERR(host)) + return PTR_ERR(host); + + pltfm_host = sdhci_priv(host); + priv = sdhci_pltfm_priv(pltfm_host); /* Get platform private data */ + + err = mmc_of_parse(host->mmc); + if (err) + return err; + + sdhci_get_of_property(pdev); + + /* Get CRM registers from the second reg entry */ + priv->crm_reg_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(priv->crm_reg_base)) { + err = PTR_ERR(priv->crm_reg_base); + return err; + } + + /* + * Silicon constraints for BST C1200: + * - System RAM base is 0x800000000 (above 32-bit addressable range) + * - The eMMC controller DMA engine is limited to 32-bit addressing + * - SMMU cannot be used on this path due to hardware design flaws + * - These are fixed in silicon and cannot be changed in software + * + * Bus/controller mapping: + * - No registers are available to reprogram the address mapping + * - The 32-bit DMA limit is a hard constraint of the controller IP + * + * Given these constraints, an SRAM-based bounce buffer in the 32-bit + * address space is required to enable eMMC DMA on this platform. + */ + err = sdhci_bst_alloc_bounce_buffer(host); + if (err) { + dev_err(&pdev->dev, "Failed to allocate bounce buffer: %d\n", err); + return err; + } + + err = sdhci_add_host(host); + if (err) + goto err_free_bounce_buffer; + + return 0; + +err_free_bounce_buffer: + sdhci_bst_free_bounce_buffer(host); + + return err; +} + +static void sdhci_bst_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + + sdhci_bst_free_bounce_buffer(host); + sdhci_pltfm_remove(pdev); +} + +static const struct of_device_id sdhci_bst_ids[] = { + { .compatible = "bst,c1200-sdhci" }, + {} +}; +MODULE_DEVICE_TABLE(of, sdhci_bst_ids); + +static struct platform_driver sdhci_bst_driver = { + .driver = { + .name = "sdhci-bst", + .of_match_table = sdhci_bst_ids, + }, + .probe = sdhci_bst_probe, + .remove = sdhci_bst_remove, +}; +module_platform_driver(sdhci_bst_driver); + +MODULE_DESCRIPTION("Black Sesame Technologies SDHCI driver (BST)"); +MODULE_AUTHOR("Black Sesame Technologies Co., Ltd."); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 1e618364767cd03350dd6cba01411c68803044c5 Mon Sep 17 00:00:00 2001 From: Albert Yang Date: Fri, 23 Jan 2026 17:53:42 +0800 Subject: MAINTAINERS: add MMC files to BST entry Add the MMC device tree binding and driver files to the existing ARM/BST SOC SUPPORT maintainer entry. Signed-off-by: Albert Yang Signed-off-by: Ulf Hansson --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 55af015174a5..27fefd92744c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2646,7 +2646,9 @@ R: BST Linux Kernel Upstream Group L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/arm/bst.yaml +F: Documentation/devicetree/bindings/mmc/bst,c1200-sdhci.yaml F: arch/arm64/boot/dts/bst/ +F: drivers/mmc/host/sdhci-of-bst.c ARM/CALXEDA HIGHBANK ARCHITECTURE M: Andre Przywara -- cgit v1.2.3 From 6aaa6c561fe6e4a9011534ed050ca50fa6491f2f Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 23 Jan 2026 20:24:07 +0800 Subject: arm64: dts: hisilicon: hikey960/970: Convert to use standard mmc alias Convert the long-deprecated mshc alias to standard mmc alias. Signed-off-by: Shawn Lin Reviewed-by: Wei Xu Signed-off-by: Ulf Hansson --- arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts | 4 ++-- arch/arm64/boot/dts/hisilicon/hi3670-hikey970.dts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts index ed84ab92fb19..c6056a85ce80 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts +++ b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts @@ -20,8 +20,8 @@ compatible = "hisilicon,hi3660-hikey960", "hisilicon,hi3660"; aliases { - mshc1 = &dwmmc1; - mshc2 = &dwmmc2; + mmc1 = &dwmmc1; + mmc2 = &dwmmc2; serial0 = &uart0; serial1 = &uart1; serial2 = &uart2; diff --git a/arch/arm64/boot/dts/hisilicon/hi3670-hikey970.dts b/arch/arm64/boot/dts/hisilicon/hi3670-hikey970.dts index 7c32f5fd5cc5..65764b38d2e6 100644 --- a/arch/arm64/boot/dts/hisilicon/hi3670-hikey970.dts +++ b/arch/arm64/boot/dts/hisilicon/hi3670-hikey970.dts @@ -19,8 +19,8 @@ compatible = "hisilicon,hi3670-hikey970", "hisilicon,hi3670"; aliases { - mshc1 = &dwmmc1; - mshc2 = &dwmmc2; + mmc1 = &dwmmc1; + mmc2 = &dwmmc2; serial0 = &uart0; serial1 = &uart1; serial2 = &uart2; -- cgit v1.2.3 From 9b541ceaf5b462f852f35245add4f2d3afd579f4 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 23 Jan 2026 20:24:08 +0800 Subject: mmc: dw_mmc-k3: Remove mshc alias support Remove the long-deprecated mshc alias support, as the mmc core already provides alias functionality through the standard mmc alias. This eliminates the redundant dual-alias system. The driver now obtains the controller ID from struct mmc_host::index(supplied by mmc alias) instead of the legacy mshc alias. dw_mci_hi6220_parse_dt() which parse mshc alias is used for hi6220, hi3660 and hi3670. Given hi6220 never assigned mshc alias on the DTS files, and hi3360 and hi3670 share the same code, so with it removed, add a return value to dw_mci_hs_set_timing() and let dw_mci_hi3660_init() check if index exceeds TIMING_MODE in the firs place to bail out early, the same as before. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-k3.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 8cf0487b952c..8b1b4cf016ab 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -53,7 +53,6 @@ #define USE_DLY_MAX_SMPL (14) struct k3_priv { - int ctrl_id; u32 cur_speed; struct regmap *reg; }; @@ -127,13 +126,6 @@ static int dw_mci_hi6220_parse_dt(struct dw_mci *host) if (IS_ERR(priv->reg)) priv->reg = NULL; - priv->ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); - if (priv->ctrl_id < 0) - priv->ctrl_id = 0; - - if (priv->ctrl_id >= TIMING_MODE) - return -EINVAL; - host->priv = priv; return 0; } @@ -211,7 +203,7 @@ static const struct dw_mci_drv_data hi6220_data = { .execute_tuning = dw_mci_hi6220_execute_tuning, }; -static void dw_mci_hs_set_timing(struct dw_mci *host, int timing, +static int dw_mci_hs_set_timing(struct dw_mci *host, int timing, int smpl_phase) { u32 drv_phase; @@ -220,10 +212,10 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing, u32 enable_shift = 0; u32 reg_value; int ctrl_id; - struct k3_priv *priv; - priv = host->priv; - ctrl_id = priv->ctrl_id; + ctrl_id = host->mmc->index; + if (ctrl_id >= TIMING_MODE) + return -EINVAL; drv_phase = hs_timing_cfg[ctrl_id][timing].drv_phase; smpl_dly = hs_timing_cfg[ctrl_id][timing].smpl_dly; @@ -260,6 +252,8 @@ static void dw_mci_hs_set_timing(struct dw_mci *host, int timing, /* We should delay 1ms wait for timing setting finished. */ usleep_range(1000, 2000); + + return 0; } static int dw_mci_hi3660_init(struct dw_mci *host) @@ -267,10 +261,9 @@ static int dw_mci_hi3660_init(struct dw_mci *host) mci_writel(host, CDTHRCTL, SDMMC_SET_THLD(SDCARD_RD_THRESHOLD, SDMMC_CARD_RD_THR_EN)); - dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1); host->bus_hz /= (GENCLK_DIV + 1); - return 0; + return dw_mci_hs_set_timing(host, MMC_TIMING_LEGACY, -1); } static int dw_mci_set_sel18(struct dw_mci *host, bool set) @@ -407,7 +400,7 @@ static int dw_mci_hi3660_switch_voltage(struct dw_mci *host, if (!priv || !priv->reg) return 0; - if (priv->ctrl_id == DWMMC_SDIO_ID) + if (mmc->index == DWMMC_SDIO_ID) return 0; if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) -- cgit v1.2.3 From 8ea84b50a95f4529a78161afd9b6bc7fa9f0fc55 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 23 Jan 2026 20:24:09 +0800 Subject: mmc: dw_mmc: Remove mshc alias support Remove the long-deprecated mshc alias support, as the mmc core already provides alias functionality through the standard mmc alias. This eliminates the redundant dual-alias system. The driver now obtains the controller ID from struct mmc_host::index(supplied by mmc alias) instead of the legacy mshc alias. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index d7c5b13e48f9..d9a0ea41ef58 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2817,13 +2817,10 @@ static int dw_mci_init_host_caps(struct dw_mci *host) if (drv_data) mmc->caps |= drv_data->common_caps; - if (host->dev->of_node) { - ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); - if (ctrl_id < 0) - ctrl_id = 0; - } else { + if (host->dev->of_node) + ctrl_id = mmc->index; + else ctrl_id = to_platform_device(host->dev)->id; - } if (drv_data && drv_data->caps) { if (ctrl_id >= drv_data->num_caps) { -- cgit v1.2.3 From 4cd4e8231652872823ac5ed0be66df59ce7c778f Mon Sep 17 00:00:00 2001 From: Andre Korol Date: Thu, 29 Jan 2026 00:59:17 -0300 Subject: mmc: loongson2-mmc: drop redundant memset after dma_alloc_coherent() dma_alloc_coherent() returns zeroed memory, so the memset() immediately after allocation is redundant. Signed-off-by: Andre Korol Reviewed-by: Binbin Zhou Signed-off-by: Ulf Hansson --- drivers/mmc/host/loongson2-mmc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c index da3daab5f3d6..026cbd80f114 100644 --- a/drivers/mmc/host/loongson2-mmc.c +++ b/drivers/mmc/host/loongson2-mmc.c @@ -846,7 +846,6 @@ static int ls2k2000_mmc_set_internal_dma(struct loongson2_mmc_host *host, if (!host->sg_cpu) return -ENOMEM; - memset(host->sg_cpu, 0, PAGE_SIZE); return 0; } -- cgit v1.2.3 From f3b9683bf5432aaada84bb4a7f815f3eb3148a90 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 4 Feb 2026 14:00:19 +0100 Subject: mmc: dw_mmc: move pmops into core driver Since the platform power management structure is now shared with the PCI front-end, there is a link failure if only the PCI variant is enabled: arm-linux-gnueabi/bin/arm-linux-gnueabi-ld: drivers/mmc/host/dw_mmc-pci.o:(.data+0x74): undefined reference to `dw_mci_pltfm_pmops' This could be fixed by selecting the platform driver from the PCI one in Kconfig, or by reverting the change to the PCI driver, but since this is now used by all dw_mmc variants, just move the structure into the core code as well. Signed-off-by: Arnd Bergmann Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pltfm.c | 9 --------- drivers/mmc/host/dw_mmc.c | 6 ++++++ 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index fde465a4bf5d..079d0cc97766 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -56,15 +56,6 @@ int dw_mci_pltfm_register(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(dw_mci_pltfm_register); -const struct dev_pm_ops dw_mci_pltfm_pmops = { - SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - RUNTIME_PM_OPS(dw_mci_runtime_suspend, - dw_mci_runtime_resume, - NULL) -}; -EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); - static int dw_mci_socfpga_priv_init(struct dw_mci *host) { struct device_node *np = host->dev->of_node; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index d9a0ea41ef58..1fd507b3142e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3523,6 +3523,12 @@ err: } EXPORT_SYMBOL(dw_mci_runtime_resume); +const struct dev_pm_ops dw_mci_pltfm_pmops = { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); + static int __init dw_mci_init(void) { pr_info("Synopsys Designware Multimedia Card Interface Driver\n"); -- cgit v1.2.3 From d6bf2e64dec87322f2b11565ddb59c0e967f96e3 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 4 Feb 2026 11:40:03 +0800 Subject: mmc: core: Optimize time for secure erase/trim for some Kingston eMMCs Kingston eMMC IY2964 and IB2932 takes a fixed ~2 seconds for each secure erase/trim operation regardless of size - that is, a single secure erase/trim operation of 1MB takes the same time as 1GB. With default calculated 3.5MB max discard size, secure erase 1GB requires ~300 separate operations taking ~10 minutes total. Add a card quirk, MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME, to set maximum secure erase size for those devices. This allows 1GB secure erase to complete in a single operation, reducing time from 10 minutes to just 2 seconds. Signed-off-by: Luke Wang Cc: stable@vger.kernel.org Signed-off-by: Ulf Hansson --- drivers/mmc/core/card.h | 5 +++++ drivers/mmc/core/queue.c | 9 +++++++-- drivers/mmc/core/quirks.h | 9 +++++++++ include/linux/mmc/card.h | 1 + 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/card.h b/drivers/mmc/core/card.h index a9619dd45270..a7c364d0030a 100644 --- a/drivers/mmc/core/card.h +++ b/drivers/mmc/core/card.h @@ -311,4 +311,9 @@ static inline int mmc_card_broken_mdt(const struct mmc_card *c) return c->quirks & MMC_QUIRK_BROKEN_MDT; } +static inline int mmc_card_fixed_secure_erase_trim_time(const struct mmc_card *c) +{ + return c->quirks & MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME; +} + #endif diff --git a/drivers/mmc/core/queue.c b/drivers/mmc/core/queue.c index 13000fc57e2e..39fcb662c43f 100644 --- a/drivers/mmc/core/queue.c +++ b/drivers/mmc/core/queue.c @@ -184,8 +184,13 @@ static void mmc_queue_setup_discard(struct mmc_card *card, return; lim->max_hw_discard_sectors = max_discard; - if (mmc_card_can_secure_erase_trim(card)) - lim->max_secure_erase_sectors = max_discard; + if (mmc_card_can_secure_erase_trim(card)) { + if (mmc_card_fixed_secure_erase_trim_time(card)) + lim->max_secure_erase_sectors = UINT_MAX >> card->erase_shift; + else + lim->max_secure_erase_sectors = max_discard; + } + if (mmc_card_can_trim(card) && card->erased_byte == 0) lim->max_write_zeroes_sectors = max_discard; diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index f5e8a0f6d11b..6f727b4a60a5 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -153,6 +153,15 @@ static const struct mmc_fixup __maybe_unused mmc_blk_fixups[] = { MMC_FIXUP("M62704", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, MMC_QUIRK_TRIM_BROKEN), + /* + * On Some Kingston eMMCs, secure erase/trim time is independent + * of erase size, fixed at approximately 2 seconds. + */ + MMC_FIXUP("IY2964", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, + MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME), + MMC_FIXUP("IB2932", CID_MANFID_KINGSTON, 0x0100, add_quirk_mmc, + MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME), + END_FIXUP }; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 4722dd7e46ce..9dc4750296af 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -330,6 +330,7 @@ struct mmc_card { #define MMC_QUIRK_BROKEN_SD_POWEROFF_NOTIFY (1<<17) /* Disable broken SD poweroff notify support */ #define MMC_QUIRK_NO_UHS_DDR50_TUNING (1<<18) /* Disable DDR50 tuning */ #define MMC_QUIRK_BROKEN_MDT (1<<19) /* Wrong manufacturing year */ +#define MMC_QUIRK_FIXED_SECURE_ERASE_TRIM_TIME (1<<20) /* Secure erase/trim time is fixed regardless of size */ bool written_flag; /* Indicates eMMC has been written since power on */ bool reenable_cmdq; /* Re-enable Command Queue */ -- cgit v1.2.3 From 97720432b093e28123c78d39030c8680114b885d Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 30 Jan 2026 18:00:38 -0800 Subject: mmc: host: Remove unnecessary module_init/exit functions Three MMC drivers have unnecessary module_init and module_exit functions that are empty or just print a message. Remove them. Note that if a module_init function exists, a module_exit function must also exist; otherwise, the module cannot be unloaded. Signed-off-by: Ethan Nelson-Moore Acked-by: Shawn Lin Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 13 ------------- drivers/mmc/host/sdhci-pltfm.c | 13 ------------- drivers/mmc/host/sdhci-uhs2.c | 13 +------------ 3 files changed, 1 insertion(+), 38 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1fd507b3142e..d670c4be7342 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3529,19 +3529,6 @@ const struct dev_pm_ops dw_mci_pltfm_pmops = { }; EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); -static int __init dw_mci_init(void) -{ - pr_info("Synopsys Designware Multimedia Card Interface Driver\n"); - return 0; -} - -static void __exit dw_mci_exit(void) -{ -} - -module_init(dw_mci_init); -module_exit(dw_mci_exit); - MODULE_DESCRIPTION("DW Multimedia Card Interface driver"); MODULE_AUTHOR("NXP Semiconductor VietNam"); MODULE_AUTHOR("Imagination Technologies Ltd"); diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 7f6ac636f040..d4fb60c1ef69 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -215,19 +215,6 @@ const struct dev_pm_ops sdhci_pltfm_pmops = { }; EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops); -static int __init sdhci_pltfm_drv_init(void) -{ - pr_info("sdhci-pltfm: SDHCI platform and OF driver helper\n"); - - return 0; -} -module_init(sdhci_pltfm_drv_init); - -static void __exit sdhci_pltfm_drv_exit(void) -{ -} -module_exit(sdhci_pltfm_drv_exit); - MODULE_DESCRIPTION("SDHCI platform and OF driver helper"); MODULE_AUTHOR("Intel Corporation"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-uhs2.c b/drivers/mmc/host/sdhci-uhs2.c index c459a08d01da..41e49c6cc751 100644 --- a/drivers/mmc/host/sdhci-uhs2.c +++ b/drivers/mmc/host/sdhci-uhs2.c @@ -1126,7 +1126,7 @@ static irqreturn_t sdhci_uhs2_thread_irq(int irq, void *dev_id) /*****************************************************************************\ * * - * Driver init/exit * + * Driver init * * * \*****************************************************************************/ @@ -1138,17 +1138,6 @@ static int sdhci_uhs2_host_ops_init(struct sdhci_host *host) return 0; } -static int __init sdhci_uhs2_mod_init(void) -{ - return 0; -} -module_init(sdhci_uhs2_mod_init); - -static void __exit sdhci_uhs2_mod_exit(void) -{ -} -module_exit(sdhci_uhs2_mod_exit); - /*****************************************************************************\ * * Device allocation/registration * -- cgit v1.2.3 From 7d608bea4adab7869450442e7985b09fdc84117e Mon Sep 17 00:00:00 2001 From: SriNavmani A Date: Fri, 6 Feb 2026 16:23:07 +0800 Subject: dt-bindings: mmc: arasan,sdhci: Add Axiado AX3000 SoC Add compatible strings for Axiado AX3000 SoC eMMC controller which is based on Arasan eMMC controller. Signed-off-by: SriNavmani A Signed-off-by: Tzu-Hao Wei Acked-by: Rob Herring (Arm) Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml index d6b6fa6dcffb..f343fb78e114 100644 --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.yaml @@ -106,6 +106,9 @@ properties: description: For this device it is strongly suggested to include arasan,soc-ctl-syscon. + - items: + - const: axiado,ax3000-sdhci-5.1-emmc # Axiado AX3000 eMMC controller + - const: arasan,sdhci-5.1 reg: maxItems: 1 -- cgit v1.2.3 From 0b02521bbc77b36980ccd77a7f058c333de19754 Mon Sep 17 00:00:00 2001 From: SriNavmani A Date: Fri, 6 Feb 2026 16:23:08 +0800 Subject: mmc: sdhci-of-arasan: add support on Axiado AX3000 SoC Axiado AX3000 SoC eMMC controller is based on Arasan eMMC 5.1 host controller IP. Signed-off-by: SriNavmani A Signed-off-by: Tzu-Hao Wei Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index ab7f0ffe7b4f..caf97238a58b 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1512,6 +1512,17 @@ static struct sdhci_arasan_of_data intel_keembay_sdio_data = { .clk_ops = &arasan_clk_ops, }; +static const struct sdhci_pltfm_data sdhci_arasan_axiado_pdata = { + .ops = &sdhci_arasan_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_CQE, +}; + +static struct sdhci_arasan_of_data sdhci_arasan_axiado_data = { + .pdata = &sdhci_arasan_axiado_pdata, + .clk_ops = &arasan_clk_ops, +}; + static const struct of_device_id sdhci_arasan_of_match[] = { /* SoC-specific compatible strings w/ soc_ctl_map */ { @@ -1538,6 +1549,10 @@ static const struct of_device_id sdhci_arasan_of_match[] = { .compatible = "intel,keembay-sdhci-5.1-sdio", .data = &intel_keembay_sdio_data, }, + { + .compatible = "axiado,ax3000-sdhci-5.1-emmc", + .data = &sdhci_arasan_axiado_data, + }, /* Generic compatible below here */ { .compatible = "arasan,sdhci-8.9a", -- cgit v1.2.3 From 517b1e3c9455698b5ce7fc479aa0b91731e5d9fa Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 7 Feb 2026 10:10:41 -0300 Subject: dt-bindings: mmc: rockchip-dw-mshc: Add RV1103B compatible The RV1103B uses the DesignWare MSHC controller compatible with the existing Rockchip RK3288 variant. Add the rockchip,rv1103b-dw-mshc compatible string. Signed-off-by: Fabio Estevam Reviewed-by: Heiko Stuebner Acked-by: Krzysztof Kozlowski Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml index acb9fb9a92cd..a75209bd2710 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml @@ -43,6 +43,7 @@ properties: - rockchip,rk3562-dw-mshc - rockchip,rk3568-dw-mshc - rockchip,rk3588-dw-mshc + - rockchip,rv1103b-dw-mshc - rockchip,rv1108-dw-mshc - rockchip,rv1126-dw-mshc - const: rockchip,rk3288-dw-mshc -- cgit v1.2.3 From ad1aec8f15bf966d9f1d3fbca9fa878ff6d824a4 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 11 Feb 2026 22:00:34 +0800 Subject: mmc: dw_mmc: Rename dw_mci_pltfm_pmops to dw_mci_pmops Since it's moved to dw_mmc core and resued by others variant host drivers, so drop the pltfm notation. Suggested-by: Ulf Hansson Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-bluefield.c | 2 +- drivers/mmc/host/dw_mmc-k3.c | 2 +- drivers/mmc/host/dw_mmc-pci.c | 2 +- drivers/mmc/host/dw_mmc-pltfm.c | 2 +- drivers/mmc/host/dw_mmc-pltfm.h | 2 +- drivers/mmc/host/dw_mmc.c | 4 ++-- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-bluefield.c b/drivers/mmc/host/dw_mmc-bluefield.c index ed6dea42b18e..81dc072d0e3f 100644 --- a/drivers/mmc/host/dw_mmc-bluefield.c +++ b/drivers/mmc/host/dw_mmc-bluefield.c @@ -73,7 +73,7 @@ static struct platform_driver dw_mci_bluefield_pltfm_driver = { .name = "dwmmc_bluefield", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_bluefield_match, - .pm = pm_ptr(&dw_mci_pltfm_pmops), + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c index 8b1b4cf016ab..59b802c9f2b8 100644 --- a/drivers/mmc/host/dw_mmc-k3.c +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -455,7 +455,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = { .name = "dwmmc_k3", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_k3_match, - .pm = pm_ptr(&dw_mci_pltfm_pmops), + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index e046674c9748..a2e1b4d75a5e 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -84,7 +84,7 @@ static struct pci_driver dw_mci_pci_driver = { .probe = dw_mci_pci_probe, .remove = dw_mci_pci_remove, .driver = { - .pm = pm_ptr(&dw_mci_pltfm_pmops), + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 079d0cc97766..68770aaff8d9 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -125,7 +125,7 @@ static struct platform_driver dw_mci_pltfm_driver = { .name = "dw_mmc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = dw_mci_pltfm_match, - .pm = &dw_mci_pltfm_pmops, + .pm = pm_ptr(&dw_mci_pmops), }, }; diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h index 64cf7522a3d4..ef1b05d484c3 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.h +++ b/drivers/mmc/host/dw_mmc-pltfm.h @@ -11,6 +11,6 @@ extern int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data); extern void dw_mci_pltfm_remove(struct platform_device *pdev); -extern const struct dev_pm_ops dw_mci_pltfm_pmops; +extern const struct dev_pm_ops dw_mci_pmops; #endif /* _DW_MMC_PLTFM_H_ */ diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index d670c4be7342..edea9f4a46cf 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -3523,11 +3523,11 @@ err: } EXPORT_SYMBOL(dw_mci_runtime_resume); -const struct dev_pm_ops dw_mci_pltfm_pmops = { +const struct dev_pm_ops dw_mci_pmops = { SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) RUNTIME_PM_OPS(dw_mci_runtime_suspend, dw_mci_runtime_resume, NULL) }; -EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops); +EXPORT_SYMBOL_GPL(dw_mci_pmops); MODULE_DESCRIPTION("DW Multimedia Card Interface driver"); MODULE_AUTHOR("NXP Semiconductor VietNam"); -- cgit v1.2.3 From b13b3203be1a9b5eabe407c1027ea16688e07cb5 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 22 Feb 2026 18:39:13 -0500 Subject: mmc: sdhci-pic32: add SPDX license identifier Add the missing SPDX license identifier to the top of the file, and drop the boiler plate license text. Signed-off-by: Brian Masney Cc: linux-spdx@vger.kernel.org Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pic32.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-pic32.c b/drivers/mmc/host/sdhci-pic32.c index 2cc632e91fe4..6899cf6c65e7 100644 --- a/drivers/mmc/host/sdhci-pic32.c +++ b/drivers/mmc/host/sdhci-pic32.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Support of SDHCI platform devices for Microchip PIC32. * @@ -5,10 +6,6 @@ * Andrei Pistirica, Paul Thacker * * Inspired by sdhci-pltfm.c - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include -- cgit v1.2.3 From 26fd3865e42f3bd3f17f6c3ca36b40d2b90a7254 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Sun, 22 Feb 2026 18:39:14 -0500 Subject: mmc: sdhci-pic32: allow driver to be compiled with COMPILE_TEST This driver currently only supports builds against a PIC32 target. Now that commit d6618d277c1a ("mmc: sdhci-pic32: update include to use pic32.h from platform_data") is merged, it's possible to compile this driver on other architectures. To avoid future breakage of this driver in the future, let's update the Kconfig so that it can be built with COMPILE_TEST enabled on all architectures. Signed-off-by: Brian Masney Acked-by: Adrian Hunter 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 b37e6e014416..4f060d3e5636 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -1058,7 +1058,7 @@ config MMC_MTK config MMC_SDHCI_MICROCHIP_PIC32 tristate "Microchip PIC32MZDA SDHCI support" - depends on MMC_SDHCI && PIC32MZDA && MMC_SDHCI_PLTFM + depends on MMC_SDHCI && MMC_SDHCI_PLTFM && (PIC32MZDA || COMPILE_TEST) help This selects the Secure Digital Host Controller Interface (SDHCI) for PIC32MZDA platform. -- cgit v1.2.3 From 1e31a3e867ca3d3bb345f9524670256cc8b62c25 Mon Sep 17 00:00:00 2001 From: Ricky Wu Date: Fri, 30 Jan 2026 11:05:01 +0800 Subject: mmc: rtsx_pci_sdmmc: simplify voltage switch handling after card_busy() Now that the Realtek PCIe SDMMC host implements the card_busy() callback, the MMC core performs the required DAT[3:0] validation during the signal voltage switch sequence. As a result, the driver-side voltage stabilization helpers sd_wait_voltage_stable_1() and sd_wait_voltage_stable_2() become redundant. These helpers duplicate DAT line checks that are now handled centrally by the MMC core via card_busy(). Remove the two stabilization helpers and simplify sdmmc_switch_voltage() accordingly, keeping only the minimal clock control needed around the voltage switch. No functional changes and intended. Signed-off-by: Ricky Wu Signed-off-by: Ulf Hansson --- drivers/mmc/host/rtsx_pci_sdmmc.c | 88 +++------------------------------------ 1 file changed, 6 insertions(+), 82 deletions(-) diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 4db3328f46df..8dfbc62f165b 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1181,79 +1181,6 @@ static int sdmmc_get_cd(struct mmc_host *mmc) return cd; } -static int sd_wait_voltage_stable_1(struct realtek_pci_sdmmc *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - u8 stat; - - /* Reference to Signal Voltage Switch Sequence in SD spec. - * Wait for a period of time so that the card can drive SD_CMD and - * SD_DAT[3:0] to low after sending back CMD11 response. - */ - mdelay(1); - - /* SD_CMD, SD_DAT[3:0] should be driven to low by card; - * If either one of SD_CMD,SD_DAT[3:0] is not low, - * abort the voltage switch sequence; - */ - err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); - if (err < 0) - return err; - - if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS)) - return -EINVAL; - - /* Stop toggle SD clock */ - err = rtsx_pci_write_register(pcr, SD_BUS_STAT, - 0xFF, SD_CLK_FORCE_STOP); - if (err < 0) - return err; - - return 0; -} - -static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host) -{ - struct rtsx_pcr *pcr = host->pcr; - int err; - u8 stat, mask, val; - - /* Wait 1.8V output of voltage regulator in card stable */ - msleep(50); - - /* Toggle SD clock again */ - err = rtsx_pci_write_register(pcr, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN); - if (err < 0) - return err; - - /* Wait for a period of time so that the card can drive - * SD_DAT[3:0] to high at 1.8V - */ - msleep(20); - - /* SD_CMD, SD_DAT[3:0] should be pulled high by host */ - err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); - if (err < 0) - return err; - - mask = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS; - val = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | - SD_DAT1_STATUS | SD_DAT0_STATUS; - if ((stat & mask) != val) { - dev_dbg(sdmmc_dev(host), - "%s: SD_BUS_STAT = 0x%x\n", __func__, stat); - rtsx_pci_write_register(pcr, SD_BUS_STAT, - SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); - rtsx_pci_write_register(pcr, CARD_CLK_EN, 0xFF, 0); - return -EINVAL; - } - - return 0; -} - static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) { struct realtek_pci_sdmmc *host = mmc_priv(mmc); @@ -1281,7 +1208,9 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) voltage = OUTPUT_1V8; if (voltage == OUTPUT_1V8) { - err = sd_wait_voltage_stable_1(host); + /* Stop toggle SD clock */ + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + 0xFF, SD_CLK_FORCE_STOP); if (err < 0) goto out; } @@ -1290,16 +1219,11 @@ static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) if (err < 0) goto out; - if (voltage == OUTPUT_1V8) { - err = sd_wait_voltage_stable_2(host); - if (err < 0) - goto out; - } - out: /* Stop toggle SD clock in idle */ - err = rtsx_pci_write_register(pcr, SD_BUS_STAT, - SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); + if (err < 0) + rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); mutex_unlock(&pcr->pcr_mutex); -- cgit v1.2.3 From 5f7ac24ba232180caf77e9ddd6ccad61b9948706 Mon Sep 17 00:00:00 2001 From: Huan He Date: Thu, 26 Feb 2026 17:26:14 +0800 Subject: dt-bindings: mmc: dwcmshc-sdhci: Fix resets array validation The binding defines tuple-style reset-names items for some compatibles, which implicitly enforces a fixed array length via JSON Schema. Defining global maxItems for resets and reset-names causes these constraints to be intersected via allOf, resulting in an effective minItems equal to the global maxItems. This leads to dtbs_check failures reporting reset arrays as too short, even when the DTS provides the correct number of entries. Fixes: 30009a21f257 ("dt-bindings: mmc: sdhci-of-dwcmshc: Add Eswin EIC7700") Co-developed-by: Pritesh Patel Signed-off-by: Pritesh Patel Signed-off-by: Huan He Acked-by: Conor Dooley Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml index 7e7c55dc2440..5cebe5eb1efb 100644 --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml @@ -50,9 +50,11 @@ properties: maxItems: 1 resets: + minItems: 4 maxItems: 5 reset-names: + minItems: 4 maxItems: 5 rockchip,txclk-tapnum: @@ -146,6 +148,7 @@ allOf: else: properties: resets: + minItems: 5 maxItems: 5 reset-names: items: -- cgit v1.2.3 From b6376ccdcb4bf7163fc4cbe5f83e265bc1660d4f Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:09 +0200 Subject: phy: can-transceiver: rename temporary helper function to avoid conflict Rename the temporary devm_mux_state_get_optional function to avoid conflict with upcoming implementation in multiplexer subsystem. Signed-off-by: Josua Mayer Acked-by: Vinod Koul Reviewed-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/phy/phy-can-transceiver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index 330356706ad7..fcbca9d2bded 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -128,7 +128,7 @@ MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); /* Temporary wrapper until the multiplexer subsystem supports optional muxes */ static inline struct mux_state * -devm_mux_state_get_optional(struct device *dev, const char *mux_name) +can_transceiver_phy_mux_state_get_optional(struct device *dev, const char *mux_name) { if (!of_property_present(dev->of_node, "mux-states")) return NULL; @@ -183,7 +183,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) priv->num_ch = num_ch; platform_set_drvdata(pdev, priv); - mux_state = devm_mux_state_get_optional(dev, NULL); + mux_state = can_transceiver_phy_mux_state_get_optional(dev, NULL); if (IS_ERR(mux_state)) return PTR_ERR(mux_state); -- cgit v1.2.3 From 80f3df6e254d860cc7a41d5f2ae37fe717644098 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:10 +0200 Subject: phy: renesas: rcar-gen3-usb2: rename local mux helper to avoid conflict Rename the temporary devm_mux_state_get_optional function to avoid conflict with upcoming implementation in multiplexer subsystem. Signed-off-by: Josua Mayer Reviewed-by: Wolfram Sang Acked-by: Vinod Koul Signed-off-by: Ulf Hansson --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index cfc2a8d9028d..1155b111420a 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -941,7 +941,7 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha /* Temporary wrapper until the multiplexer subsystem supports optional muxes */ static inline struct mux_state * -devm_mux_state_get_optional(struct device *dev, const char *mux_name) +rcar_gen3_phy_mux_state_get_optional(struct device *dev, const char *mux_name) { if (!of_property_present(dev->of_node, "mux-states")) return NULL; @@ -1036,7 +1036,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]); } - mux_state = devm_mux_state_get_optional(dev, NULL); + mux_state = rcar_gen3_phy_mux_state_get_optional(dev, NULL); if (IS_ERR(mux_state)) return PTR_ERR(mux_state); if (mux_state) { -- cgit v1.2.3 From 993bcaf32c494014f56357f6cdd87fdfaa4e4d11 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:11 +0200 Subject: mux: Add helper functions for getting optional and selected mux-state In-tree phy-can-transceiver and phy_rcar_gen3_usb2 have already implemented local versions of devm_mux_state_get_optional. The omap-i2c driver gets and selects an optional mux in its probe function without using any helper. Add new helper functions covering both aforementioned use-cases: - mux_control_get_optional: Get a mux-control if specified in dt, return NULL otherwise. - devm_mux_state_get_optional: Get a mux-state if specified in dt, return NULL otherwise. - devm_mux_state_get_selected: Get and select a mux-state specified in dt, return error otherwise. - devm_mux_state_get_optional_selected: Get and select a mux-state if specified in dt, return error or NULL. Existing mux_get helper function is changed to take an extra argument indicating whether the mux is optional. In this case no error is printed, and NULL returned in case of ENOENT. Calling code is adapted to handle NULL return case, and to pass optional argument as required. To support automatic deselect for _selected helper, a new structure is created storing an exit pointer similar to clock core which is called on release. To facilitate code sharing between optional/mandatory/selected helpers, a new internal helper function is added to handle quiet (optional) and verbose (mandatory) errors, as well as storing the correct callback for devm release: __devm_mux_state_get Due to this structure devm_mux_state_get_*_selected can no longer print a useful error message when select fails. Instead callers should print errors where needed. Commit e153fdea9db04 ("phy: can-transceiver: Re-instate "mux-states" property presence check") noted that "mux_get() always prints an error message in case of an error, including when the property is not present, confusing the user." The first error message covers the case that a mux name is not matched in dt. The second error message is based on of_parse_phandle_with_args return value. In optional case no error is printed and NULL is returned. This ensures that the new helper functions will not confuse the user either. With the addition of optional helper functions it became clear that drivers should compile and link even if CONFIG_MULTIPLEXER was not enabled. Add stubs for all symbols exported by mux core. Acked-by: Wolfram Sang Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- drivers/mux/core.c | 194 +++++++++++++++++++++++++++++++++++++------ include/linux/mux/consumer.h | 108 +++++++++++++++++++++++- 2 files changed, 271 insertions(+), 31 deletions(-) diff --git a/drivers/mux/core.c b/drivers/mux/core.c index f09ee8782e3d..23538de2c91b 100644 --- a/drivers/mux/core.c +++ b/drivers/mux/core.c @@ -46,6 +46,16 @@ static const struct class mux_class = { .name = "mux", }; +/** + * struct devm_mux_state_state - Tracks managed resources for mux-state objects. + * @mstate: Pointer to a mux state. + * @exit: An optional callback to execute before free. + */ +struct devm_mux_state_state { + struct mux_state *mstate; + int (*exit)(struct mux_state *mstate); +}; + static DEFINE_IDA(mux_ida); static int __init mux_init(void) @@ -516,17 +526,19 @@ static struct mux_chip *of_find_mux_chip_by_node(struct device_node *np) return dev ? to_mux_chip(dev) : NULL; } -/* +/** * mux_get() - Get the mux-control for a device. * @dev: The device that needs a mux-control. * @mux_name: The name identifying the mux-control. * @state: Pointer to where the requested state is returned, or NULL when * the required multiplexer states are handled by other means. + * @optional: Whether to return NULL and silence errors when mux doesn't exist. * - * Return: A pointer to the mux-control, or an ERR_PTR with a negative errno. + * Return: Pointer to the mux-control on success, an ERR_PTR with a negative + * errno on error, or NULL if optional is true and mux doesn't exist. */ static struct mux_control *mux_get(struct device *dev, const char *mux_name, - unsigned int *state) + unsigned int *state, bool optional) { struct device_node *np = dev->of_node; struct of_phandle_args args; @@ -542,7 +554,9 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, else index = of_property_match_string(np, "mux-control-names", mux_name); - if (index < 0) { + if (index < 0 && optional) { + return NULL; + } else if (index < 0) { dev_err(dev, "mux controller '%s' not found\n", mux_name); return ERR_PTR(index); @@ -558,8 +572,12 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, "mux-controls", "#mux-control-cells", index, &args); if (ret) { + if (optional && ret == -ENOENT) + return NULL; + dev_err(dev, "%pOF: failed to get mux-%s %s(%i)\n", - np, state ? "state" : "control", mux_name ?: "", index); + np, state ? "state" : "control", + mux_name ?: "", index); return ERR_PTR(ret); } @@ -617,10 +635,29 @@ static struct mux_control *mux_get(struct device *dev, const char *mux_name, */ struct mux_control *mux_control_get(struct device *dev, const char *mux_name) { - return mux_get(dev, mux_name, NULL); + struct mux_control *mux = mux_get(dev, mux_name, NULL, false); + + if (!mux) + return ERR_PTR(-ENOENT); + + return mux; } EXPORT_SYMBOL_GPL(mux_control_get); +/** + * mux_control_get_optional() - Get the optional mux-control for a device. + * @dev: The device that needs a mux-control. + * @mux_name: The name identifying the mux-control. + * + * Return: Pointer to the mux-control on success, an ERR_PTR with a negative + * errno on error, or NULL if mux doesn't exist. + */ +struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name) +{ + return mux_get(dev, mux_name, NULL, true); +} +EXPORT_SYMBOL_GPL(mux_control_get_optional); + /** * mux_control_put() - Put away the mux-control for good. * @mux: The mux-control to put away. @@ -670,14 +707,16 @@ struct mux_control *devm_mux_control_get(struct device *dev, } EXPORT_SYMBOL_GPL(devm_mux_control_get); -/* +/** * mux_state_get() - Get the mux-state for a device. * @dev: The device that needs a mux-state. * @mux_name: The name identifying the mux-state. + * @optional: Whether to return NULL and silence errors when mux doesn't exist. * - * Return: A pointer to the mux-state, or an ERR_PTR with a negative errno. + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if optional is true and mux doesn't exist. */ -static struct mux_state *mux_state_get(struct device *dev, const char *mux_name) +static struct mux_state *mux_state_get(struct device *dev, const char *mux_name, bool optional) { struct mux_state *mstate; @@ -685,12 +724,15 @@ static struct mux_state *mux_state_get(struct device *dev, const char *mux_name) if (!mstate) return ERR_PTR(-ENOMEM); - mstate->mux = mux_get(dev, mux_name, &mstate->state); + mstate->mux = mux_get(dev, mux_name, &mstate->state, optional); if (IS_ERR(mstate->mux)) { int err = PTR_ERR(mstate->mux); kfree(mstate); return ERR_PTR(err); + } else if (!mstate->mux) { + kfree(mstate); + return optional ? NULL : ERR_PTR(-ENOENT); } return mstate; @@ -710,9 +752,66 @@ static void mux_state_put(struct mux_state *mstate) static void devm_mux_state_release(struct device *dev, void *res) { - struct mux_state *mstate = *(struct mux_state **)res; + struct devm_mux_state_state *devm_state = res; + + if (devm_state->exit) + devm_state->exit(devm_state->mstate); + + mux_state_put(devm_state->mstate); +} + +/** + * __devm_mux_state_get() - Get the optional mux-state for a device, + * with resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * @optional: Whether to return NULL and silence errors when mux doesn't exist. + * @init: Optional function pointer for mux-state object initialisation. + * @exit: Optional function pointer for mux-state object cleanup on release. + * + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if optional is true and mux doesn't exist. + */ +static struct mux_state *__devm_mux_state_get(struct device *dev, const char *mux_name, + bool optional, + int (*init)(struct mux_state *mstate), + int (*exit)(struct mux_state *mstate)) +{ + struct devm_mux_state_state *devm_state; + struct mux_state *mstate; + int ret; + + mstate = mux_state_get(dev, mux_name, optional); + if (IS_ERR(mstate)) + return ERR_CAST(mstate); + else if (optional && !mstate) + return NULL; + else if (!mstate) + return ERR_PTR(-ENOENT); + + devm_state = devres_alloc(devm_mux_state_release, sizeof(*devm_state), GFP_KERNEL); + if (!devm_state) { + ret = -ENOMEM; + goto err_devres_alloc; + } + + if (init) { + ret = init(mstate); + if (ret) + goto err_mux_state_init; + } + + devm_state->mstate = mstate; + devm_state->exit = exit; + devres_add(dev, devm_state); + return mstate; + +err_mux_state_init: + devres_free(devm_state); +err_devres_alloc: mux_state_put(mstate); + return ERR_PTR(ret); } /** @@ -722,28 +821,69 @@ static void devm_mux_state_release(struct device *dev, void *res) * @mux_name: The name identifying the mux-control. * * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno. + * + * The mux-state will automatically be freed on release. */ -struct mux_state *devm_mux_state_get(struct device *dev, - const char *mux_name) +struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name) { - struct mux_state **ptr, *mstate; - - ptr = devres_alloc(devm_mux_state_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + return __devm_mux_state_get(dev, mux_name, false, NULL, NULL); +} +EXPORT_SYMBOL_GPL(devm_mux_state_get); - mstate = mux_state_get(dev, mux_name); - if (IS_ERR(mstate)) { - devres_free(ptr); - return mstate; - } +/** + * devm_mux_state_get_optional() - Get the optional mux-state for a device, + * with resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if mux doesn't exist. + * + * The mux-state will automatically be freed on release. + */ +struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name) +{ + return __devm_mux_state_get(dev, mux_name, true, NULL, NULL); +} +EXPORT_SYMBOL_GPL(devm_mux_state_get_optional); - *ptr = mstate; - devres_add(dev, ptr); +/** + * devm_mux_state_get_selected() - Get the mux-state for a device, with + * resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: Pointer to the mux-state, or an ERR_PTR with a negative errno. + * + * The returned mux-state (if valid) is already selected. + * + * The mux-state will automatically be deselected and freed on release. + */ +struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name) +{ + return __devm_mux_state_get(dev, mux_name, false, mux_state_select, mux_state_deselect); +} +EXPORT_SYMBOL_GPL(devm_mux_state_get_selected); - return mstate; +/** + * devm_mux_state_get_optional_selected() - Get the optional mux-state for + * a device, with resource management. + * @dev: The device that needs a mux-state. + * @mux_name: The name identifying the mux-state. + * + * Return: Pointer to the mux-state on success, an ERR_PTR with a negative + * errno on error, or NULL if mux doesn't exist. + * + * The returned mux-state (if valid) is already selected. + * + * The mux-state will automatically be deselected and freed on release. + */ +struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, + const char *mux_name) +{ + return __devm_mux_state_get(dev, mux_name, true, mux_state_select, mux_state_deselect); } -EXPORT_SYMBOL_GPL(devm_mux_state_get); +EXPORT_SYMBOL_GPL(devm_mux_state_get_optional_selected); /* * Using subsys_initcall instead of module_init here to try to ensure - for diff --git a/include/linux/mux/consumer.h b/include/linux/mux/consumer.h index 2e25c838f831..a961861a503b 100644 --- a/include/linux/mux/consumer.h +++ b/include/linux/mux/consumer.h @@ -16,6 +16,8 @@ struct device; struct mux_control; struct mux_state; +#if IS_ENABLED(CONFIG_MULTIPLEXER) + unsigned int mux_control_states(struct mux_control *mux); int __must_check mux_control_select_delay(struct mux_control *mux, unsigned int state, @@ -54,11 +56,109 @@ int mux_control_deselect(struct mux_control *mux); int mux_state_deselect(struct mux_state *mstate); struct mux_control *mux_control_get(struct device *dev, const char *mux_name); +struct mux_control *mux_control_get_optional(struct device *dev, const char *mux_name); void mux_control_put(struct mux_control *mux); -struct mux_control *devm_mux_control_get(struct device *dev, - const char *mux_name); -struct mux_state *devm_mux_state_get(struct device *dev, - const char *mux_name); +struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get_optional(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get_selected(struct device *dev, const char *mux_name); +struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, const char *mux_name); + +#else + +static inline unsigned int mux_control_states(struct mux_control *mux) +{ + return 0; +} +static inline int __must_check mux_control_select_delay(struct mux_control *mux, + unsigned int state, unsigned int delay_us) +{ + return -EOPNOTSUPP; +} +static inline int __must_check mux_state_select_delay(struct mux_state *mstate, + unsigned int delay_us) +{ + return -EOPNOTSUPP; +} +static inline int __must_check mux_control_try_select_delay(struct mux_control *mux, + unsigned int state, + unsigned int delay_us) +{ + return -EOPNOTSUPP; +} +static inline int __must_check mux_state_try_select_delay(struct mux_state *mstate, + unsigned int delay_us) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_control_select(struct mux_control *mux, + unsigned int state) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_state_select(struct mux_state *mstate) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_control_try_select(struct mux_control *mux, + unsigned int state) +{ + return -EOPNOTSUPP; +} + +static inline int __must_check mux_state_try_select(struct mux_state *mstate) +{ + return -EOPNOTSUPP; +} + +static inline int mux_control_deselect(struct mux_control *mux) +{ + return -EOPNOTSUPP; +} +static inline int mux_state_deselect(struct mux_state *mstate) +{ + return -EOPNOTSUPP; +} + +static inline struct mux_control *mux_control_get(struct device *dev, const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_control *mux_control_get_optional(struct device *dev, + const char *mux_name) +{ + return NULL; +} +static inline void mux_control_put(struct mux_control *mux) {} + +static inline struct mux_control *devm_mux_control_get(struct device *dev, const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_state *devm_mux_state_get(struct device *dev, const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_state *devm_mux_state_get_optional(struct device *dev, + const char *mux_name) +{ + return NULL; +} +static inline struct mux_state *devm_mux_state_get_selected(struct device *dev, + const char *mux_name) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline struct mux_state *devm_mux_state_get_optional_selected(struct device *dev, + const char *mux_name) +{ + return NULL; +} + +#endif /* CONFIG_MULTIPLEXER */ #endif /* _LINUX_MUX_CONSUMER_H */ -- cgit v1.2.3 From d76e0c54ed8405469375177ddc7e4b863b59d0ae Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:12 +0200 Subject: phy: can-transceiver: drop temporary helper getting optional mux-state Multiplexer subsystem has now added helpers for getting managed optional mux-state. Switch to the new devm_mux_state_get_optional helper. This change is only compile-tested. Acked-by: Vinod Koul Reviewed-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- drivers/phy/phy-can-transceiver.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index fcbca9d2bded..2b52e47f247a 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -126,16 +126,6 @@ static const struct of_device_id can_transceiver_phy_ids[] = { }; MODULE_DEVICE_TABLE(of, can_transceiver_phy_ids); -/* Temporary wrapper until the multiplexer subsystem supports optional muxes */ -static inline struct mux_state * -can_transceiver_phy_mux_state_get_optional(struct device *dev, const char *mux_name) -{ - if (!of_property_present(dev->of_node, "mux-states")) - return NULL; - - return devm_mux_state_get(dev, mux_name); -} - static struct phy *can_transceiver_phy_xlate(struct device *dev, const struct of_phandle_args *args) { @@ -183,7 +173,7 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) priv->num_ch = num_ch; platform_set_drvdata(pdev, priv); - mux_state = can_transceiver_phy_mux_state_get_optional(dev, NULL); + mux_state = devm_mux_state_get_optional(dev, NULL); if (IS_ERR(mux_state)) return PTR_ERR(mux_state); -- cgit v1.2.3 From 602236a78291c0ead8667be2c7a9199d2c290479 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:13 +0200 Subject: phy: renesas: rcar-gen3-usb2: drop helper getting optional mux-state Multiplexer subsystem has now added helpers for getting managed optional mux-state. Switch to the new devm_mux_state_get_optional_selected helper. This change is only compile-tested. Signed-off-by: Josua Mayer Acked-by: Vinod Koul Reviewed-by: Wolfram Sang Signed-off-by: Ulf Hansson --- drivers/phy/renesas/phy-rcar-gen3-usb2.c | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 1155b111420a..79e820e2fe55 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -939,21 +939,6 @@ static int rcar_gen3_phy_usb2_vbus_regulator_register(struct rcar_gen3_chan *cha return rcar_gen3_phy_usb2_vbus_regulator_get_exclusive_enable(channel, enable); } -/* Temporary wrapper until the multiplexer subsystem supports optional muxes */ -static inline struct mux_state * -rcar_gen3_phy_mux_state_get_optional(struct device *dev, const char *mux_name) -{ - if (!of_property_present(dev->of_node, "mux-states")) - return NULL; - - return devm_mux_state_get(dev, mux_name); -} - -static void rcar_gen3_phy_mux_state_deselect(void *data) -{ - mux_state_deselect(data); -} - static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1036,20 +1021,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]); } - mux_state = rcar_gen3_phy_mux_state_get_optional(dev, NULL); + mux_state = devm_mux_state_get_optional_selected(dev, NULL); if (IS_ERR(mux_state)) - return PTR_ERR(mux_state); - if (mux_state) { - ret = mux_state_select(mux_state); - if (ret) - return dev_err_probe(dev, ret, "Failed to select USB mux\n"); - - ret = devm_add_action_or_reset(dev, rcar_gen3_phy_mux_state_deselect, - mux_state); - if (ret) - return dev_err_probe(dev, ret, - "Failed to register USB mux state deselect\n"); - } + return dev_err_probe(dev, PTR_ERR(mux_state), "Failed to get USB mux\n"); if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel) { ret = rcar_gen3_phy_usb2_vbus_regulator_register(channel); -- cgit v1.2.3 From 43c00f2bcff7c6e235f0c1b1eb18a6db0aa23815 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:14 +0200 Subject: i2c: omap: switch to new generic helper for getting selected mux-state Multiplexer subsystem has added generic helper functions for getting an already selected mux-state object. Replace existing logic in probe with the equivalent helper function. There is a functional difference in that the mux is now automatically deselected on release, replacing the explicit mux_state_deselect call. This change is only compile-tested. Reviewed-by: Geert Uytterhoeven Reviewed-by: Andreas Kemnade Reviewed-by: Wolfram Sang Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- drivers/i2c/busses/i2c-omap.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index d9f590f0c384..f02d294db42a 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1453,27 +1453,16 @@ omap_i2c_probe(struct platform_device *pdev) (1000 * omap->speed / 8); } - if (of_property_present(node, "mux-states")) { - struct mux_state *mux_state; - - mux_state = devm_mux_state_get(&pdev->dev, NULL); - if (IS_ERR(mux_state)) { - r = PTR_ERR(mux_state); - dev_dbg(&pdev->dev, "failed to get I2C mux: %d\n", r); - goto err_put_pm; - } - omap->mux_state = mux_state; - r = mux_state_select(omap->mux_state); - if (r) { - dev_err(&pdev->dev, "failed to select I2C mux: %d\n", r); - goto err_put_pm; - } + omap->mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL); + if (IS_ERR(omap->mux_state)) { + r = PTR_ERR(omap->mux_state); + goto err_put_pm; } /* reset ASAP, clearing any IRQs */ r = omap_i2c_init(omap); if (r) - goto err_mux_state_deselect; + goto err_put_pm; if (omap->rev < OMAP_I2C_OMAP1_REV_2) r = devm_request_irq(&pdev->dev, omap->irq, omap_i2c_omap1_isr, @@ -1515,9 +1504,6 @@ omap_i2c_probe(struct platform_device *pdev) err_unuse_clocks: omap_i2c_write_reg(omap, OMAP_I2C_CON_REG, 0); -err_mux_state_deselect: - if (omap->mux_state) - mux_state_deselect(omap->mux_state); err_put_pm: pm_runtime_put_sync(omap->dev); err_disable_pm: -- cgit v1.2.3 From bc0d3adf2f47439e8b7ffd228154d71b8acb50e6 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:15 +0200 Subject: dt-bindings: mmc: renesas,sdhi: Add mux-states property Add mux controller support for data or control lines that are muxed between a host and multiple cards. There are several devices supporting a choice of eMMC or SD on a single board by both dip switch and gpio, e.g. Renesas RZ/G2L SMARC SoM and SolidRun RZ/G2L SoM. In-tree dts for the Renesas boards currently rely on preprocessor macros and gpio hogs to describe the respective cards. By adding mux-states property to sdhi controller description, boards can correctly describe the mux that already exists in hardware - and drivers can coordinate between mux selection and probing for cards. Acked-by: Rob Herring (Arm) Reviewed-by: Wolfram Sang Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index c754ea71f51f..64fac0d11329 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -106,6 +106,11 @@ properties: iommus: maxItems: 1 + mux-states: + description: + mux controller node to route the SD/SDIO/eMMC signals from SoC to cards. + maxItems: 1 + power-domains: maxItems: 1 @@ -275,6 +280,7 @@ examples: max-frequency = <195000000>; power-domains = <&sysc R8A7790_PD_ALWAYS_ON>; resets = <&cpg 314>; + mux-states = <&mux 0>; }; sdhi1: mmc@ee120000 { -- cgit v1.2.3 From ce5c7c17e70640fc5635fd2252d0bdf4664d452b Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:16 +0200 Subject: mux: add visible config symbol to enable multiplexer subsystem The multiplexer subsystem was initially designed to be completely hidden, relying on consumers to "select MULTIPLEXER" explicitly. Drivers implementing multiplexers depend on this hidden symbol. This prevents users from manually enabling both the mux core and any of the multiplexer drivers. All multiplexer drivers in drivers/mux/ can operate standalone without a consumer. This is particularly useful in a device-tree, where a default state can be set through the idle-state property. Over time, several drivers have added "select MULTIPLEXER" dependencies, some of which require a mux and some consider it optional. v7.0-rc1 shows 15 such occurrences in Kconfig files, in a variety of subsystems. The natural step forward to allow enabling mux core and drivers would be adding a prompt and help text to the existing symbol. This violates the general Kbuild advice to avoid selecting visible symbols for all existing consumers of the mux core. Add the new config symbol MUX_CORE with a prompt and help text as a wrapper for users to enable manually. This avoids existing consumers automatically selecting a visible symbol. Change the MULTIPLEXER symbol from tristate to bool. This avoids complex dependencies if users were to attempt a configuration where the mux is a module but one of its consumers is built-in, as well as difficulties keeping the state of visible and invisible symbols in sync. Further convert the "menu ... depends on ..." structure to "if ... menu ... endmenu endif". These are functionally equivalent, but the new structure is more efficient and can support future source statements within the conditional block. Signed-off-by: Josua Mayer Signed-off-by: Ulf Hansson --- drivers/mux/Kconfig | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/mux/Kconfig b/drivers/mux/Kconfig index c68132e38138..6d17dfa25dad 100644 --- a/drivers/mux/Kconfig +++ b/drivers/mux/Kconfig @@ -4,10 +4,21 @@ # config MULTIPLEXER - tristate + bool + +config MUX_CORE + bool "Generic Multiplexer Support" + select MULTIPLEXER + help + This framework is designed to abstract multiplexer handling for + devices via various GPIO-, MMIO/Regmap or specific multiplexer + controller chips. + + If unsure, say no. + +if MULTIPLEXER menu "Multiplexer drivers" - depends on MULTIPLEXER config MUX_ADG792A tristate "Analog Devices ADG792A/ADG792G Multiplexers" @@ -60,3 +71,5 @@ config MUX_MMIO be called mux-mmio. endmenu + +endif # MULTIPLEXER -- cgit v1.2.3 From 3c080bb2639d7b17bf83ca561f60252774a77b37 Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Thu, 26 Feb 2026 15:21:17 +0200 Subject: mmc: host: renesas_sdhi_core: support selecting an optional mux Some hardware designs route data or control signals through a mux to support multiple devices on a single sdhi controller. In particular SolidRun RZ/G2L/G2LC/V2L System on Module use a mux for switching between soldered eMMC and an optional microSD on a carrier board, e.g. for development or provisioning. SD/SDIO/eMMC are not well suited for runtime switching between different cards, however boot-time selection is possible and useful - in particular considering dt overlays. Add support for an optional SD/SDIO/eMMC mux defined in dt, and select it during probe. Similar functionality already exists in other places, e.g. i2c-omap. Signed-off-by: Josua Mayer Reviewed-by: Wolfram Sang Tested-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_core.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 2a310a145785..f9ec78d699f4 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1062,6 +1063,7 @@ int renesas_sdhi_probe(struct platform_device *pdev, struct regulator_dev *rdev; struct renesas_sdhi_dma *dma_priv; struct device *dev = &pdev->dev; + struct mux_state *mux_state; struct tmio_mmc_host *host; struct renesas_sdhi *priv; int num_irqs, irq, ret, i; @@ -1116,6 +1118,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, "state_uhs"); } + mux_state = devm_mux_state_get_optional_selected(&pdev->dev, NULL); + if (IS_ERR(mux_state)) + return PTR_ERR(mux_state); + host = tmio_mmc_host_alloc(pdev, mmc_data); if (IS_ERR(host)) return PTR_ERR(host); -- cgit v1.2.3 From 670f524faedccb1749beeb445c833ecae8c7409b Mon Sep 17 00:00:00 2001 From: Felix Gu Date: Mon, 2 Mar 2026 21:47:12 +0800 Subject: mmc: sdhci-of-bst: Fix memory leak in sdhci_bst_alloc_bounce_buffer() In sdhci_bst_alloc_bounce_buffer(), if dma_alloc_coherent() fails, the function immediately returns -ENOMEM without releasing the reserved memory, which results in a memory leak. Add the missing of_reserved_mem_device_release() call before returning -ENOMEM to properly clean up the reserved memory. Fixes: 695824f45629 ("mmc: sdhci: add Black Sesame Technologies BST C1200 controller driver") Signed-off-by: Felix Gu Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-bst.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-of-bst.c b/drivers/mmc/host/sdhci-of-bst.c index c124990a64f4..f8d3df715e1a 100644 --- a/drivers/mmc/host/sdhci-of-bst.c +++ b/drivers/mmc/host/sdhci-of-bst.c @@ -425,8 +425,10 @@ static int sdhci_bst_alloc_bounce_buffer(struct sdhci_host *host) host->bounce_buffer = dma_alloc_coherent(mmc_dev(mmc), bounce_size, &host->bounce_addr, GFP_KERNEL); - if (!host->bounce_buffer) + if (!host->bounce_buffer) { + of_reserved_mem_device_release(mmc_dev(mmc)); return -ENOMEM; + } host->bounce_buffer_size = bounce_size; -- cgit v1.2.3 From f1ccb2b0955293766c5f50002299202bf49a4142 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 3 Mar 2026 19:27:38 +0800 Subject: dt-bindings: mmc: loongson,ls2k0500-mmc: Add compatible for Loongson-2K0300 Add "loongson,ls2k0300-mmc" dedicated compatible to represent the eMMC/SD/SDIO controller interface of the Loongson-2K0300 chip. Its hardware design is similar to that of the Loongson-2K2000, but it suffers from hardware defects such as missing CMD48 interrupts. Signed-off-by: Binbin Zhou Acked-by: Conor Dooley Reviewed-by: Huacai Chen Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml b/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml index c142421bc723..b3e8d3f13592 100644 --- a/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/loongson,ls2k0500-mmc.yaml @@ -22,6 +22,7 @@ allOf: properties: compatible: enum: + - loongson,ls2k0300-mmc - loongson,ls2k0500-mmc - loongson,ls2k1000-mmc - loongson,ls2k2000-mmc -- cgit v1.2.3 From 1d1519bc582c4700c37b6ba7bb5cde237060159b Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 3 Mar 2026 19:27:39 +0800 Subject: mmc: loongson2: Gathering all SoCs private data together More Loongson SoCs will be added, gathering all SoC private data (`loongson2_mmc_pdata`) together to make the code clearer. No functional change intended. Signed-off-by: Binbin Zhou Reviewed-by: Huacai Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/loongson2-mmc.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c index 026cbd80f114..333bed60df3b 100644 --- a/drivers/mmc/host/loongson2-mmc.c +++ b/drivers/mmc/host/loongson2-mmc.c @@ -703,14 +703,6 @@ static int ls2k0500_mmc_set_external_dma(struct loongson2_mmc_host *host, return 0; } -static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = { - .regmap_config = &ls2k0500_mmc_regmap_config, - .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, - .setting_dma = ls2k0500_mmc_set_external_dma, - .prepare_dma = loongson2_mmc_prepare_external_dma, - .release_dma = loongson2_mmc_release_external_dma, -}; - static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host, struct platform_device *pdev) { @@ -735,14 +727,6 @@ static int ls2k1000_mmc_set_external_dma(struct loongson2_mmc_host *host, return 0; } -static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = { - .regmap_config = &ls2k0500_mmc_regmap_config, - .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, - .setting_dma = ls2k1000_mmc_set_external_dma, - .prepare_dma = loongson2_mmc_prepare_external_dma, - .release_dma = loongson2_mmc_release_external_dma, -}; - static const struct regmap_config ls2k2000_mmc_regmap_config = { .reg_bits = 32, .val_bits = 32, @@ -855,6 +839,22 @@ static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host, dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); } +static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = { + .regmap_config = &ls2k0500_mmc_regmap_config, + .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, + .setting_dma = ls2k0500_mmc_set_external_dma, + .prepare_dma = loongson2_mmc_prepare_external_dma, + .release_dma = loongson2_mmc_release_external_dma, +}; + +static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = { + .regmap_config = &ls2k0500_mmc_regmap_config, + .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, + .setting_dma = ls2k1000_mmc_set_external_dma, + .prepare_dma = loongson2_mmc_prepare_external_dma, + .release_dma = loongson2_mmc_release_external_dma, +}; + static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = { .regmap_config = &ls2k2000_mmc_regmap_config, .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data, -- cgit v1.2.3 From bdc1eb80b9b9793e28f625a790b0ae0387bb5b17 Mon Sep 17 00:00:00 2001 From: Binbin Zhou Date: Tue, 3 Mar 2026 19:27:40 +0800 Subject: mmc: loongson2: Add Loongson-2K0300 SD/SDIO/eMMC controller driver This patch describes the two MMC controllers of the Loongson-2K0300 SoC, one providing an eMMC interface and the other exporting an SD/SDIO interface. Its hardware design is similar to that of the Loongson-2K2000, but it suffers from hardware defects such as missing CMD48 interrupts. Signed-off-by: Binbin Zhou Reviewed-by: Huacai Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/loongson2-mmc.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/mmc/host/loongson2-mmc.c b/drivers/mmc/host/loongson2-mmc.c index 333bed60df3b..f553e92fd9e5 100644 --- a/drivers/mmc/host/loongson2-mmc.c +++ b/drivers/mmc/host/loongson2-mmc.c @@ -189,6 +189,12 @@ #define LOONGSON2_MMC_DLLVAL_TIMEOUT_US 4000 #define LOONGSON2_MMC_TXFULL_TIMEOUT_US 500 +/* + * Due to a hardware design flaw, the Loongson-2K0300 may fail to recognize the + * CMD48 (SD_READ_EXTR_SINGLE) interrupt. + */ +#define LOONGSON2_MMC_CMD48_QUIRK BIT(0) + /* Loongson-2K1000 SDIO2 DMA routing register */ #define LS2K1000_SDIO_DMA_MASK GENMASK(17, 15) #define LS2K1000_DMA0_CONF 0x0 @@ -245,6 +251,7 @@ struct loongson2_mmc_host { }; struct loongson2_mmc_pdata { + u32 flags; const struct regmap_config *regmap_config; void (*reorder_cmd_data)(struct loongson2_mmc_host *host, struct mmc_command *cmd); void (*fix_data_timeout)(struct loongson2_mmc_host *host, struct mmc_command *cmd); @@ -568,6 +575,12 @@ static void loongson2_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct loongson2_mmc_host *host = mmc_priv(mmc); + if ((host->pdata->flags & LOONGSON2_MMC_CMD48_QUIRK) && + mrq->cmd->opcode == SD_READ_EXTR_SINGLE) { + mmc_request_done(mmc, mrq); + return; + } + host->cmd_is_stop = 0; host->mrq = mrq; loongson2_mmc_send_request(mmc); @@ -839,7 +852,18 @@ static void loongson2_mmc_release_internal_dma(struct loongson2_mmc_host *host, dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); } +static struct loongson2_mmc_pdata ls2k0300_mmc_pdata = { + .flags = LOONGSON2_MMC_CMD48_QUIRK, + .regmap_config = &ls2k2000_mmc_regmap_config, + .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data, + .fix_data_timeout = ls2k2000_mmc_fix_data_timeout, + .setting_dma = ls2k2000_mmc_set_internal_dma, + .prepare_dma = loongson2_mmc_prepare_internal_dma, + .release_dma = loongson2_mmc_release_internal_dma, +}; + static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = { + .flags = 0, .regmap_config = &ls2k0500_mmc_regmap_config, .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, .setting_dma = ls2k0500_mmc_set_external_dma, @@ -848,6 +872,7 @@ static struct loongson2_mmc_pdata ls2k0500_mmc_pdata = { }; static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = { + .flags = 0, .regmap_config = &ls2k0500_mmc_regmap_config, .reorder_cmd_data = ls2k0500_mmc_reorder_cmd_data, .setting_dma = ls2k1000_mmc_set_external_dma, @@ -856,6 +881,7 @@ static struct loongson2_mmc_pdata ls2k1000_mmc_pdata = { }; static struct loongson2_mmc_pdata ls2k2000_mmc_pdata = { + .flags = 0, .regmap_config = &ls2k2000_mmc_regmap_config, .reorder_cmd_data = ls2k2000_mmc_reorder_cmd_data, .fix_data_timeout = ls2k2000_mmc_fix_data_timeout, @@ -984,6 +1010,7 @@ static void loongson2_mmc_remove(struct platform_device *pdev) } static const struct of_device_id loongson2_mmc_of_ids[] = { + { .compatible = "loongson,ls2k0300-mmc", .data = &ls2k0300_mmc_pdata }, { .compatible = "loongson,ls2k0500-mmc", .data = &ls2k0500_mmc_pdata }, { .compatible = "loongson,ls2k1000-mmc", .data = &ls2k1000_mmc_pdata }, { .compatible = "loongson,ls2k2000-mmc", .data = &ls2k2000_mmc_pdata }, -- cgit v1.2.3 From 4cbdda11fd016689edd5f200726190701bfc7f2a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 5 Mar 2026 10:08:24 +0100 Subject: mmc: renesas_sdhi_sys_dmac: Convert to DEFINE_RUNTIME_DEV_PM_OPS() Convert the Renesas SDHI SD/SDIO controller driver using SYS-DMAC from an open-coded dev_pm_ops structure to DEFINE_RUNTIME_DEV_PM_OPS() and pm_ptr(). This simplifies the code, and reduces kernel size in case CONFIG_PM is disabled. Signed-off-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/mmc/host/renesas_sdhi_sys_dmac.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/renesas_sdhi_sys_dmac.c b/drivers/mmc/host/renesas_sdhi_sys_dmac.c index 543ad1d0ed1c..9215600f03a2 100644 --- a/drivers/mmc/host/renesas_sdhi_sys_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_sys_dmac.c @@ -456,19 +456,15 @@ static int renesas_sdhi_sys_dmac_probe(struct platform_device *pdev) of_device_get_match_data(&pdev->dev), NULL); } -static const struct dev_pm_ops renesas_sdhi_sys_dmac_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend, - tmio_mmc_host_runtime_resume, - NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(renesas_sdhi_sys_dmac_dev_pm_ops, + tmio_mmc_host_runtime_suspend, + tmio_mmc_host_runtime_resume, NULL); static struct platform_driver renesas_sys_dmac_sdhi_driver = { .driver = { .name = "sh_mobile_sdhi", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &renesas_sdhi_sys_dmac_dev_pm_ops, + .pm = pm_ptr(&renesas_sdhi_sys_dmac_dev_pm_ops), .of_match_table = renesas_sdhi_sys_dmac_of_match, }, .probe = renesas_sdhi_sys_dmac_probe, -- cgit v1.2.3 From 6c9b6c635148e3dc39ab1411fa5d9762596831ac Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 5 Mar 2026 13:25:48 +0100 Subject: mmc: sdio: Use min3() to simplify sdio_set_block_size() Use min3() to simplify sdio_set_block_size(). Signed-off-by: Thorsten Blum Signed-off-by: Ulf Hansson --- drivers/mmc/core/sdio_io.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/sdio_io.c b/drivers/mmc/core/sdio_io.c index b774bf51981d..12716ec0e35d 100644 --- a/drivers/mmc/core/sdio_io.c +++ b/drivers/mmc/core/sdio_io.c @@ -163,10 +163,8 @@ int sdio_set_block_size(struct sdio_func *func, unsigned blksz) if (blksz > func->card->host->max_blk_size) return -EINVAL; - if (blksz == 0) { - blksz = min(func->max_blksize, func->card->host->max_blk_size); - blksz = min(blksz, 512u); - } + if (blksz == 0) + blksz = min3(func->max_blksize, func->card->host->max_blk_size, 512u); ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE, -- cgit v1.2.3 From 46ab50745309c3f230f6ae4328f280607ef2c69b Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 5 Mar 2026 13:25:49 +0100 Subject: mmc: tifm_sd: Use min3() to simplify tifm_sd_transfer_data() Use min3() to simplify tifm_sd_transfer_data(). Signed-off-by: Thorsten Blum Signed-off-by: Ulf Hansson --- drivers/mmc/host/tifm_sd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index 2cd69c9e9571..c1f7d5b37911 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -193,9 +193,7 @@ static void tifm_sd_transfer_data(struct tifm_sd *host) pg = sg_page(&sg[host->sg_pos]) + (off >> PAGE_SHIFT); p_off = offset_in_page(off); - p_cnt = PAGE_SIZE - p_off; - p_cnt = min(p_cnt, cnt); - p_cnt = min(p_cnt, t_size); + p_cnt = min3(PAGE_SIZE - p_off, cnt, t_size); if (r_data->flags & MMC_DATA_READ) tifm_sd_read_fifo(host, pg, p_off, p_cnt); -- cgit v1.2.3 From 0f961114e4aca744597390d78fbe462a846ac332 Mon Sep 17 00:00:00 2001 From: Ciprian Marian Costea Date: Mon, 9 Mar 2026 15:34:06 +0100 Subject: dt-bindings: mmc: fsl-imx-esdhc: add S32N79 support Add compatible string "nxp,s32n79-usdhc" for the uSDHC controller found in NXP S32N79 series automotive SoCs. Co-developed-by: Larisa Grigore Signed-off-by: Larisa Grigore Signed-off-by: Ciprian Marian Costea Acked-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml index b98a84f93277..014b049baeb6 100644 --- a/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml +++ b/Documentation/devicetree/bindings/mmc/fsl-imx-esdhc.yaml @@ -35,6 +35,7 @@ properties: - fsl,imx8mm-usdhc - fsl,imxrt1050-usdhc - nxp,s32g2-usdhc + - nxp,s32n79-usdhc - items: - const: fsl,imx50-esdhc - const: fsl,imx53-esdhc -- cgit v1.2.3 From 9652a49a17bf6d5978259318bc8c7bb91f3fb6c9 Mon Sep 17 00:00:00 2001 From: Ciprian Marian Costea Date: Mon, 9 Mar 2026 15:34:08 +0100 Subject: mmc: sdhci-esdhc-imx: add NXP S32N79 support Add support for the uSDHC controller found in NXP S32N79 automotive SoCs, which reuse the existing sdhci-esdhc-imx driver with slice difference. Compared with s32g2/s32g3, needn't set ESDHC_FLAG_SKIP_CD_WAKE flag because s32n79 does not have this limitation. Co-developed-by: Larisa Grigore Signed-off-by: Larisa Grigore Signed-off-by: Ciprian Marian Costea Reviewed-by: Haibo Chen Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 97461e20425d..743d842b440e 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -323,6 +323,14 @@ static struct esdhc_soc_data usdhc_s32g2_data = { .quirks = SDHCI_QUIRK_NO_LED, }; +static struct esdhc_soc_data usdhc_s32n79_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, + .quirks = SDHCI_QUIRK_NO_LED, +}; + static struct esdhc_soc_data usdhc_imx7ulp_data = { .flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING | ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 @@ -410,6 +418,7 @@ static const struct of_device_id imx_esdhc_dt_ids[] = { { .compatible = "fsl,imx95-usdhc", .data = &usdhc_imx95_data, }, { .compatible = "fsl,imxrt1050-usdhc", .data = &usdhc_imxrt1050_data, }, { .compatible = "nxp,s32g2-usdhc", .data = &usdhc_s32g2_data, }, + { .compatible = "nxp,s32n79-usdhc", .data = &usdhc_s32n79_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_esdhc_dt_ids); -- cgit v1.2.3 From 0d944576c99ebaacadb07ceb7e34bfbcc67a32d6 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 9 Mar 2026 11:29:01 +0800 Subject: mmc: dw_mmc: Add parsing mmc_clk_phase_map support The dw_mmc library already assists in invoking mmc_of_parse() to provide unified parsing for variant drivers. We can also call mmc_of_parse_clk_phase() to help variant drivers achieve unified parsing. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 2 ++ drivers/mmc/host/dw_mmc.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index edea9f4a46cf..bbdd7594ef01 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -2866,6 +2866,8 @@ static int dw_mci_init_host(struct dw_mci *host) if (ret) return ret; + mmc_of_parse_clk_phase(host->dev, &host->phase_map); + ret = dw_mci_init_host_caps(host); if (ret) return ret; diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 9a27d778f362..42e58be74ce0 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -130,6 +130,7 @@ struct dw_mci_dma_slave { * @pdev: platform_device registered * @rstc: Reset controller for this host. * @detect_delay_ms: Delay in mS before detecting cards after interrupt. + * @phase_map: The map for recording in and out phases for each timing * * Locking * ======= @@ -250,6 +251,7 @@ struct dw_mci { struct platform_device *pdev; struct reset_control *rstc; u32 detect_delay_ms; + struct mmc_clk_phase_map phase_map; }; /* DMA ops for Internal/External DMAC interface */ -- cgit v1.2.3 From 8750929d971386c530ca1e131e50a56187fa5f73 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 9 Mar 2026 11:29:02 +0800 Subject: mmc: dw_mmc-hi3798mv200: Using phase map from dw_mmc core dw_mmc core helps parse phase map now, so reuse it. No functional changes intended. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-hi3798mv200.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-hi3798mv200.c b/drivers/mmc/host/dw_mmc-hi3798mv200.c index a64907e2e84c..fda417888be9 100644 --- a/drivers/mmc/host/dw_mmc-hi3798mv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798mv200.c @@ -30,13 +30,12 @@ struct dw_mci_hi3798mv200_priv { struct clk *drive_clk; struct regmap *crg_reg; u32 sap_dll_offset; - struct mmc_clk_phase_map phase_map; }; static void dw_mci_hi3798mv200_set_ios(struct dw_mci *host, struct mmc_ios *ios) { struct dw_mci_hi3798mv200_priv *priv = host->priv; - struct mmc_clk_phase phase = priv->phase_map.phase[ios->timing]; + struct mmc_clk_phase phase = host->phase_map.phase[ios->timing]; u32 val; val = mci_readl(host, ENABLE_SHIFT); @@ -158,9 +157,9 @@ tuning_out: * We don't care what timing we are tuning for, * simply use the same phase for all timing needs tuning. */ - priv->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid]; - priv->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid]; - priv->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid]; + host->phase_map.phase[MMC_TIMING_MMC_HS200].in_deg = degrees[mid]; + host->phase_map.phase[MMC_TIMING_MMC_HS400].in_deg = degrees[mid]; + host->phase_map.phase[MMC_TIMING_UHS_SDR104].in_deg = degrees[mid]; clk_set_phase(priv->sample_clk, degrees[mid]); dev_dbg(host->dev, "Tuning clk_sample[%d, %d], set[%d]\n", @@ -185,8 +184,6 @@ static int dw_mci_hi3798mv200_init(struct dw_mci *host) if (!priv) return -ENOMEM; - mmc_of_parse_clk_phase(host->dev, &priv->phase_map); - priv->sample_clk = devm_clk_get_enabled(host->dev, "ciu-sample"); if (IS_ERR(priv->sample_clk)) return dev_err_probe(host->dev, PTR_ERR(priv->sample_clk), -- cgit v1.2.3 From cc1060a18e0464a7b03c06fb64889935d27acee0 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 9 Mar 2026 11:29:03 +0800 Subject: mmc: dw_mmc-rockchip: Add phase map support Multiple boards require different phase settings, rendering the default phase policy unscalable. Therefore, we introduce phase map to address this limitation. To preserve backward compatibility, the default_sample_phase and original drv phase for different modes are retained, with phase map values taking precedence when available. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-rockchip.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 76995415bc4c..c6eece4ec3fd 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -179,7 +179,8 @@ static int rockchip_mmc_set_phase(struct dw_mci *host, bool sample, int degrees) static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) { struct dw_mci_rockchip_priv_data *priv = host->priv; - int ret; + struct mmc_clk_phase phase = host->phase_map.phase[ios->timing]; + int ret, sample_phase, drv_phase; unsigned int cclkin; u32 bus_hz; @@ -213,8 +214,15 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) } /* Make sure we use phases which we can enumerate with */ - if (!IS_ERR(priv->sample_clk) && ios->timing <= MMC_TIMING_SD_HS) - rockchip_mmc_set_phase(host, true, priv->default_sample_phase); + if (!IS_ERR(priv->sample_clk)) { + /* Keep backward compatibility */ + if (ios->timing <= MMC_TIMING_SD_HS) { + sample_phase = phase.valid ? phase.in_deg : priv->default_sample_phase; + rockchip_mmc_set_phase(host, true, sample_phase); + } else if (phase.valid) { + rockchip_mmc_set_phase(host, true, phase.in_deg); + } + } /* * Set the drive phase offset based on speed mode to achieve hold times. @@ -243,15 +251,13 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * same results, for instance). */ if (!IS_ERR(priv->drv_clk)) { - int phase; - /* * In almost all cases a 90 degree phase offset will provide * sufficient hold times across all valid input clock rates * assuming delay_o is not absurd for a given SoC. We'll use * that as a default. */ - phase = 90; + drv_phase = 90; switch (ios->timing) { case MMC_TIMING_MMC_DDR52: @@ -261,7 +267,7 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * to get the same timings. */ if (ios->bus_width == MMC_BUS_WIDTH_8) - phase = 180; + drv_phase = 180; break; case MMC_TIMING_UHS_SDR104: case MMC_TIMING_MMC_HS200: @@ -273,11 +279,14 @@ static void dw_mci_rk3288_set_ios(struct dw_mci *host, struct mmc_ios *ios) * SoCs measured this seems to be OK, but it doesn't * hurt to give margin here, so we use 180. */ - phase = 180; + drv_phase = 180; break; } - rockchip_mmc_set_phase(host, false, phase); + /* Use out phase from phase map first */ + if (phase.valid) + drv_phase = phase.out_deg; + rockchip_mmc_set_phase(host, false, drv_phase); } } -- cgit v1.2.3 From e98f926e5a2d8023a74ec2ba7a973b5d76610f4e Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Tue, 10 Mar 2026 09:35:52 +0800 Subject: mmc: core: Validate UHS/DDR/HS200 timing selection for 1-bit bus width UHS/DDR/HS200 modes require at least 4-bit bus support. Host controllers that lack relevant capability registers rely on paring properties provided by firmware, which may incorrectly set these modes. Now that mmc_validate_host_caps() has been introduced to validate such configuration violations, let's also add checks for UHS/DDR/HS200 modes. This fixes an issue where, if the HS200/HS400 property is set while only a 1-bit bus width is used, mmc_select_hs200() returns 0 without actually performing the mode switch. Consequently, mmc_select_timing() proceeds without falling back to mmc_select_hs(), leaving the eMMC device operating in legacy mode (26 MHz) instead of switching to High Speed mode (52 MHz). Signed-off-by: Luke Wang [Shawn: reword the commit msg and rework the code] Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 88c95dbfd9cf..a457c88fdcbc 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -624,12 +624,24 @@ static int mmc_validate_host_caps(struct mmc_host *host) return -EINVAL; } + /* UHS/DDR/HS200 modes require at least 4-bit bus */ + if (!(caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)) && + ((caps & (MMC_CAP_UHS | MMC_CAP_DDR)) || (caps2 & MMC_CAP2_HS200))) { + dev_warn(dev, "drop UHS/DDR/HS200 support since 1-bit bus only\n"); + caps &= ~(MMC_CAP_UHS | MMC_CAP_DDR); + caps2 &= ~MMC_CAP2_HS200; + } + + /* HS400 and HS400ES modes require 8-bit bus */ if (caps2 & (MMC_CAP2_HS400_ES | MMC_CAP2_HS400) && !(caps & MMC_CAP_8_BIT_DATA) && !(caps2 & MMC_CAP2_NO_MMC)) { dev_warn(dev, "drop HS400 support since no 8-bit bus\n"); - host->caps2 = caps2 & ~MMC_CAP2_HS400_ES & ~MMC_CAP2_HS400; + caps2 &= ~(MMC_CAP2_HS400_ES | MMC_CAP2_HS400); } + host->caps = caps; + host->caps2 = caps2; + return 0; } -- cgit v1.2.3 From ca43d891b6da359260c94b74fe29799f381eb782 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 10 Mar 2026 09:35:53 +0800 Subject: mmc: core: Remove checking MMC_CAP_4_BIT_DATA from mmc_host_can_uhs() The bus width support for UHS mode is now validated in mmc_validate_host_caps(). Therefore, we can safely remove the explicit MMC_CAP_4_BIT_DATA check from mmc_host_can_uhs(). As part of this cleanup, simplify the condition by using the consolidated MMC_CAP_UHS flag. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/mmc/core/host.h b/drivers/mmc/core/host.h index 5941d68ff989..6bce5a4798e2 100644 --- a/drivers/mmc/core/host.h +++ b/drivers/mmc/core/host.h @@ -56,11 +56,7 @@ static inline int mmc_host_can_access_boot(struct mmc_host *host) static inline int mmc_host_can_uhs(struct mmc_host *host) { - return host->caps & - (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | - MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | - MMC_CAP_UHS_DDR50) && - host->caps & MMC_CAP_4_BIT_DATA; + return host->caps & MMC_CAP_UHS; } static inline bool mmc_card_hs200(struct mmc_card *card) -- cgit v1.2.3 From 941717214d0be9f0be5fb0b46a38e429b72f8719 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Wed, 11 Mar 2026 15:15:48 +0530 Subject: dt-bindings: mmc: sdhci-msm: add IPQ5210 compatible The IPQ5210 supports eMMC with an SDHCI controller. Add the appropriate compatible to the documentation. Signed-off-by: Kathiravan Thirumoorthy Acked-by: Krzysztof Kozlowski 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 938be8228d66..fd1d5b04e755 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml @@ -38,6 +38,7 @@ properties: - items: - enum: - qcom,ipq5018-sdhci + - qcom,ipq5210-sdhci - qcom,ipq5332-sdhci - qcom,ipq5424-sdhci - qcom,ipq6018-sdhci -- cgit v1.2.3 From 60ed2f96c9842cd4e85e3229fdc6bec2a0fa9cfa Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 11 Mar 2026 17:50:07 +0800 Subject: mmc: sdhci-esdhc-imx: add 1-bit bus width support Add sdhci_get_property() call to parse common SDHCI DT properties, including "bus-width = <1>" which sets SDHCI_QUIRK_FORCE_1_BIT_DATA quirk for 1-bit data bus width configuration. Remove the duplicate "no-1-8-v" property parsing since sdhci_get_property() already handles it. Signed-off-by: Luke Wang Acked-by: Adrian Hunter Reviewed-by: Haibo Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 743d842b440e..37e0ae3e6db1 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1822,8 +1822,6 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, of_property_read_u32(np, "fsl,strobe-dll-delay-target", &boarddata->strobe_dll_delay_target); - if (of_property_read_bool(np, "no-1-8-v")) - host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; if (of_property_read_u32(np, "fsl,delay-line", &boarddata->delay_line)) boarddata->delay_line = 0; @@ -1842,6 +1840,8 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, if (ret) return ret; + sdhci_get_property(pdev); + /* HS400/HS400ES require 8 bit bus */ if (!(host->mmc->caps & MMC_CAP_8_BIT_DATA)) host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); -- cgit v1.2.3 From 5cfc804ba9d30322288cdb7ffc42d8c851dc6439 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 11 Mar 2026 17:50:08 +0800 Subject: mmc: sdhci-esdhc-imx: remove duplicate HS400 bus width validation mmc_validate_host_caps() already validates that HS400/HS400ES requires 8-bit bus width. Remove the duplicate validation. Signed-off-by: Luke Wang Acked-by: Adrian Hunter Reviewed-by: Haibo Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-esdhc-imx.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index 37e0ae3e6db1..18ecddd6df6f 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -1842,10 +1842,6 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, sdhci_get_property(pdev); - /* HS400/HS400ES require 8 bit bus */ - if (!(host->mmc->caps & MMC_CAP_8_BIT_DATA)) - host->mmc->caps2 &= ~(MMC_CAP2_HS400 | MMC_CAP2_HS400_ES); - if (mmc_gpio_get_cd(host->mmc) >= 0) host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; -- cgit v1.2.3 From d75c6c7d8e9855e4dfa28d17c4ab9ad4f29a40a9 Mon Sep 17 00:00:00 2001 From: Luke Wang Date: Wed, 11 Mar 2026 17:50:09 +0800 Subject: mmc: sdhci-pltfm: remove duplicate DTS property parsing The "keep-power-in-suspend", "wakeup-source" and "enable-sdio-wakeup" properties are already parsed in mmc_of_parse(). All sdhci drivers that call sdhci_get_property() also call mmc_of_parse(). The only exception is sdhci-of-hlwd, which does not call mmc_of_parse(), but its devicetree does not use these properties anyway. Signed-off-by: Luke Wang Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pltfm.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index d4fb60c1ef69..933fafe0a0ef 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -95,13 +95,6 @@ void sdhci_get_property(struct platform_device *pdev) sdhci_get_compatibility(pdev); device_property_read_u32(dev, "clock-frequency", &pltfm_host->clock); - - if (device_property_present(dev, "keep-power-in-suspend")) - host->mmc->pm_caps |= MMC_PM_KEEP_POWER; - - if (device_property_read_bool(dev, "wakeup-source") || - device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */ - host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; } EXPORT_SYMBOL_GPL(sdhci_get_property); -- cgit v1.2.3 From fd78e2b582a05ff3217016bed9c8a3cc632ee61b Mon Sep 17 00:00:00 2001 From: Neeraj Soni Date: Fri, 13 Mar 2026 13:45:48 +0530 Subject: mmc: sdhci-msm: Add support for wrapped keys Add the wrapped key support for sdhci-msm by implementing the needed methods in struct blk_crypto_ll_ops and setting the appropriate flag in blk_crypto_profile::key_types_supported. Tested on SC7280 eMMC variant. How to test: Use the "v1.3.0" tag from https://github.com/google/fscryptctl and build fscryptctl that supports generating wrapped keys. Enable the following config options: CONFIG_BLK_INLINE_ENCRYPTION=y CONFIG_QCOM_INLINE_CRYPTO_ENGINE=y CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y CONFIG_MMC_CRYPTO=y Enable "qcom_ice.use_wrapped_keys" via kernel command line. $ mkfs.ext4 -F -O encrypt,stable_inodes /dev/disk/by-partlabel/vm-data $ mount /dev/disk/by-partlabel/vm-data -o inlinecrypt /mnt $ fscryptctl generate_hw_wrapped_key /dev/disk/by-partlabel/vm-data > /mnt/key.longterm $ fscryptctl prepare_hw_wrapped_key /dev/disk/by-partlabel/vm-data < /mnt/key.longterm > /tmp/key.ephemeral $ KEYID=$(fscryptctl add_key --hw-wrapped-key < /tmp/key.ephemeral /mnt) $ rm -rf /mnt/dir $ mkdir /mnt/dir $ fscryptctl set_policy --iv-ino-lblk-32 "$KEYID" /mnt/dir $ dmesg > /mnt/dir/test.txt $ sync Reboot the board $ mount /dev/disk/by-partlabel/vm-data -o inlinecrypt /mnt $ ls /mnt/dir # File should be encrypted $ fscryptctl prepare_hw_wrapped_key /dev/disk/by-partlabel/vm-data < /mnt/key.longterm > /tmp/key.ephemeral $ KEYID=$(fscryptctl add_key --hw-wrapped-key < /tmp/key.ephemeral /mnt) $ fscryptctl set_policy --iv-ino-lblk-32 "$KEYID" /mnt/dir $ cat /mnt/dir/test.txt # File should now be decrypted Tested-by: Wenjia Zhang Signed-off-by: Neeraj Soni Acked-by: Adrian Hunter Reviewed-by: Eric Biggers Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index da356627d9de..b4131b12df56 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1944,7 +1944,7 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, profile->ll_ops = sdhci_msm_crypto_ops; profile->max_dun_bytes_supported = 4; - profile->key_types_supported = BLK_CRYPTO_KEY_TYPE_RAW; + profile->key_types_supported = qcom_ice_get_supported_key_type(ice); profile->dev = dev; /* @@ -2024,6 +2024,42 @@ static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile, return qcom_ice_evict_key(msm_host->ice, slot); } +static int sdhci_msm_ice_derive_sw_secret(struct blk_crypto_profile *profile, + const u8 *eph_key, size_t eph_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_derive_sw_secret(msm_host->ice, eph_key, eph_key_size, + sw_secret); +} + +static int sdhci_msm_ice_import_key(struct blk_crypto_profile *profile, + const u8 *raw_key, size_t raw_key_size, + u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_import_key(msm_host->ice, raw_key, raw_key_size, lt_key); +} + +static int sdhci_msm_ice_generate_key(struct blk_crypto_profile *profile, + u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_generate_key(msm_host->ice, lt_key); +} + +static int sdhci_msm_ice_prepare_key(struct blk_crypto_profile *profile, + const u8 *lt_key, size_t lt_key_size, + u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct sdhci_msm_host *msm_host = sdhci_msm_host_from_crypto_profile(profile); + + return qcom_ice_prepare_key(msm_host->ice, lt_key, lt_key_size, eph_key); +} + static void sdhci_msm_non_cqe_ice_init(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -2089,6 +2125,10 @@ static void sdhci_msm_request(struct mmc_host *mmc, struct mmc_request *mrq) static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = { .keyslot_program = sdhci_msm_ice_keyslot_program, .keyslot_evict = sdhci_msm_ice_keyslot_evict, + .derive_sw_secret = sdhci_msm_ice_derive_sw_secret, + .import_key = sdhci_msm_ice_import_key, + .generate_key = sdhci_msm_ice_generate_key, + .prepare_key = sdhci_msm_ice_prepare_key, }; #else /* CONFIG_MMC_CRYPTO */ -- cgit v1.2.3 From 7ee65cdf54fc9eb4eaf2ffc5c5d1409a90ff4efa Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 13 Mar 2026 10:08:35 -0300 Subject: dt-bindings: mmc: rockchip-dw-mshc: Fix the RV1103B compatible RV1103B uses the same DesignWare MSHC controller IP version as RK3576. They have no "ciu-drive" nor "ciu-sample" clocks and use the phase tuning inside the controller. Fix it accordingly. Fixes: 517b1e3c9455 ("dt-bindings: mmc: rockchip-dw-mshc: Add RV1103B compatible") Suggested-by: Shawn Lin Signed-off-by: Fabio Estevam Reviewed-by: Heiko Stuebner Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml index a75209bd2710..4965bb518c54 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml @@ -43,11 +43,14 @@ properties: - rockchip,rk3562-dw-mshc - rockchip,rk3568-dw-mshc - rockchip,rk3588-dw-mshc - - rockchip,rv1103b-dw-mshc - rockchip,rv1108-dw-mshc - rockchip,rv1126-dw-mshc - const: rockchip,rk3288-dw-mshc # for Rockchip RK3576 with phase tuning inside the controller + - items: + - enum: + - rockchip,rv1103b-dw-mshc + - const: rockchip,rk3576-dw-mshc - const: rockchip,rk3576-dw-mshc reg: -- cgit v1.2.3 From 3f1628baa51e78c3f0cba6383f00405e5a8c175e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 17 Mar 2026 10:14:52 +0800 Subject: mmc: dw_mmc-pltfm: Use phase_map for SoCFPGA clock phase configuration This change aligns the SoCFPGA driver with the current dw_mmc core, which now manages clock phases through host->phase_map. The phase values are still scaled by SOCFPGA_DW_MMC_CLK_PHASE_STEP before being written to the system manager registers. No functional changes intended. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc-pltfm.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 68770aaff8d9..cf38bb118dc2 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -59,12 +59,13 @@ EXPORT_SYMBOL_GPL(dw_mci_pltfm_register); static int dw_mci_socfpga_priv_init(struct dw_mci *host) { struct device_node *np = host->dev->of_node; + struct mmc_clk_phase phase; struct regmap *sys_mgr_base_addr; - u32 clk_phase[2] = {0}, reg_offset, reg_shift; - int i, rc, hs_timing; + u32 reg_offset, reg_shift; + int hs_timing; - rc = of_property_read_variable_u32_array(np, "clk-phase-sd-hs", &clk_phase[0], 2, 0); - if (rc < 0) + phase = host->phase_map.phase[MMC_TIMING_SD_HS]; + if (!phase.valid) return 0; sys_mgr_base_addr = altr_sysmgr_regmap_lookup_by_phandle(np, "altr,sysmgr-syscon"); @@ -76,10 +77,10 @@ static int dw_mci_socfpga_priv_init(struct dw_mci *host) of_property_read_u32_index(np, "altr,sysmgr-syscon", 1, ®_offset); of_property_read_u32_index(np, "altr,sysmgr-syscon", 2, ®_shift); - for (i = 0; i < ARRAY_SIZE(clk_phase); i++) - clk_phase[i] /= SOCFPGA_DW_MMC_CLK_PHASE_STEP; + phase.in_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP; + phase.out_deg /= SOCFPGA_DW_MMC_CLK_PHASE_STEP; - hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1], reg_shift); + hs_timing = SYSMGR_SDMMC_CTRL_SET(phase.in_deg, phase.out_deg, reg_shift); regmap_write(sys_mgr_base_addr, reg_offset, hs_timing); return 0; -- cgit v1.2.3 From e65a413a2d4505012fdba2974dca613ac1779d84 Mon Sep 17 00:00:00 2001 From: Nick Hawkins Date: Mon, 16 Mar 2026 10:01:14 -0500 Subject: dt-bindings: mmc: snps,dwcmshc-sdhci: add HPE GSC dwcmshc compatible Add the 'hpe,gsc-dwcmshc' compatible string for the HPE GSC (ARM64 Cortex-A53) BMC SoC eMMC controller. The HPE GSC requires access to the MSHCCS register in the SoC system register block to configure SCG sync disable for HS200 RX delay-line phase selection. The required 'hpe,gxp-sysreg' property takes a phandle to the existing 'hpe,gxp-sysreg' syscon and the MSHCCS register offset within that block. The HPE GSC eMMC interface only exposes a single 'core' clock (no bus clock), so clocks/clock-names are constrained to a single item. Signed-off-by: Nick Hawkins Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml index 5cebe5eb1efb..539fa3b54b5b 100644 --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml @@ -23,6 +23,7 @@ properties: - const: sophgo,sg2044-dwcmshc - const: sophgo,sg2042-dwcmshc - enum: + - hpe,gsc-dwcmshc - rockchip,rk3568-dwcmshc - rockchip,rk3588-dwcmshc - snps,dwcmshc-sdhci @@ -79,6 +80,17 @@ properties: description: Specifies the drive impedance in Ohm. enum: [33, 40, 50, 66, 100] + hpe,gxp-sysreg: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to HPE GXP SoC system register block (syscon) + - description: offset of the MSHCCS register within the syscon block + description: + Phandle to the HPE GXP SoC system register block (syscon) and + offset of the MSHCCS register used to configure clock + synchronisation for HS200 tuning. + required: - compatible - reg @@ -89,6 +101,26 @@ required: allOf: - $ref: mmc-controller.yaml# + - if: + properties: + compatible: + contains: + const: hpe,gsc-dwcmshc + + then: + properties: + clocks: + items: + - description: core clock + clock-names: + items: + - const: core + required: + - hpe,gxp-sysreg + else: + properties: + hpe,gxp-sysreg: false + - if: properties: compatible: -- cgit v1.2.3 From e6375787bfe8f69fa17b5778f201ba7291ebcf15 Mon Sep 17 00:00:00 2001 From: Nick Hawkins Date: Mon, 16 Mar 2026 10:01:15 -0500 Subject: mmc: sdhci-of-dwcmshc: Add HPE GSC eMMC support Add support for the eMMC controller integrated in the HPE GSC (ARM64 Cortex-A53) BMC SoC under the new 'hpe,gsc-dwcmshc' compatible string. The HPE GSC eMMC controller is based on the DesignWare Cores MSHC IP but requires several platform-specific adjustments: Clock mux (dwcmshc_hpe_set_clock): The GSC SoC wires SDHCI_CLOCK_CONTROL.freq_sel directly to a clock mux rather than a divider. Forcing freq_sel = 1 when the requested clock is 200 MHz (HS200) selects the correct high-speed clock source. Using the generic sdhci_set_clock() would otherwise leave the mux on the wrong source after tuning. Auto-tuning / vendor config (dwcmshc_hpe_vendor_specific): Disables the command-conflict check (DWCMSHC_HOST_CTRL3 BIT(0)) and programs the ATCTRL register using existing AT_CTRL_* macros: AT_CTRL_AT_EN auto-tuning circuit enable AT_CTRL_SWIN_TH_EN sampling window threshold enable AT_CTRL_TUNE_CLK_STOP_EN tune-clock-stop enable PRE_CHANGE_DLY = 3 pre-change delay POST_CHANGE_DLY = 3 post-change delay SWIN_TH_VAL = 2 sampling window threshold This combination is required for reliable HS200 signal integrity on the GSC PCB trace topology. eMMC mode (dwcmshc_hpe_set_emmc): Helper that sets DWCMSHC_CARD_IS_EMMC unconditionally. Called from both the reset and UHS-signaling paths. Reset (dwcmshc_hpe_reset): Calls dwcmshc_reset(), re-applies the vendor config above via dwcmshc_hpe_vendor_specific(), and then calls dwcmshc_hpe_set_emmc(). The GSC controller clears the CARD_IS_EMMC bit on every reset; leaving it clear causes card-detect mis-identification on an eMMC-only slot. UHS signaling (dwcmshc_hpe_set_uhs_signaling): Wraps dwcmshc_set_uhs_signaling() and calls dwcmshc_hpe_set_emmc() to ensure CARD_IS_EMMC is set for all timing modes, not just HS400. Init (dwcmshc_hpe_gsc_init): Obtains the SoC register block and MSHCCS offset via the 'hpe,gxp-sysreg' syscon phandle argument and sets SCGSyncDis (BIT(18)) to allow the HS200 RX delay lines to settle while the card clock is stopped during auto-tuning. Enables SDHCI v4 mode. Quirks: SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN: base clock not advertised in capabilities; must be obtained from the DTS 'clocks' property. SDHCI_QUIRK2_PRESET_VALUE_BROKEN: preset-value registers are not populated in the GSC ROM. All HPE-specific code is isolated to the new hpe_gsc_init / hpe_ops / hpe_gsc_pdata symbols. No existing platform (Rockchip, T-Head, sg2042, etc.) is affected. Signed-off-by: Nick Hawkins Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 146 ++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 2b75a36c096b..9dc7498d422d 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -40,7 +40,10 @@ #define DWCMSHC_AREA1_MASK GENMASK(11, 0) /* Offset inside the vendor area 1 */ #define DWCMSHC_HOST_CTRL3 0x8 +#define DWCMSHC_HOST_CTRL3_CMD_CONFLICT BIT(0) #define DWCMSHC_EMMC_CONTROL 0x2c +/* HPE GSC SoC MSHCCS register */ +#define HPE_GSC_MSHCCS_SCGSYNCDIS BIT(18) #define DWCMSHC_CARD_IS_EMMC BIT(0) #define DWCMSHC_ENHANCED_STROBE BIT(8) #define DWCMSHC_EMMC_ATCTRL 0x40 @@ -1245,6 +1248,126 @@ static int sg2042_init(struct device *dev, struct sdhci_host *host, ARRAY_SIZE(clk_ids), clk_ids); } +/* + * HPE GSC-specific vendor configuration: disable command conflict check + * and program Auto-Tuning Control register. + */ +static void dwcmshc_hpe_vendor_specific(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 atctrl; + u8 extra; + + extra = sdhci_readb(host, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); + extra &= ~DWCMSHC_HOST_CTRL3_CMD_CONFLICT; + sdhci_writeb(host, extra, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); + + atctrl = AT_CTRL_AT_EN | AT_CTRL_SWIN_TH_EN | AT_CTRL_TUNE_CLK_STOP_EN | + FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, 3) | + FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY) | + FIELD_PREP(AT_CTRL_SWIN_TH_VAL_MASK, 2); + sdhci_writel(host, atctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); +} + +static void dwcmshc_hpe_set_emmc(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u16 ctrl; + + ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + ctrl |= DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); +} + +static void dwcmshc_hpe_reset(struct sdhci_host *host, u8 mask) +{ + dwcmshc_reset(host, mask); + dwcmshc_hpe_vendor_specific(host); + dwcmshc_hpe_set_emmc(host); +} + +static void dwcmshc_hpe_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) +{ + dwcmshc_set_uhs_signaling(host, timing); + dwcmshc_hpe_set_emmc(host); +} + +/* + * HPE GSC eMMC controller clock setup. + * + * The GSC SoC wires the freq_sel field of SDHCI_CLOCK_CONTROL directly to a + * clock mux rather than a divider. Force freq_sel = 1 when running at + * 200 MHz (HS200) so the mux selects the correct clock source. + */ +static void dwcmshc_hpe_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + + host->mmc->actual_clock = 0; + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + if (clock == 0) + return; + + clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); + + if (host->mmc->actual_clock == 200000000) + clk |= (1 << SDHCI_DIVIDER_SHIFT); + + sdhci_enable_clk(host, clk); +} + +/* + * HPE GSC eMMC controller init. + * + * The GSC SoC requires configuring MSHCCS. Bit 18 (SCGSyncDis) disables clock + * synchronisation for phase-select values going to the HS200 RX delay lines, + * allowing the card clock to be stopped while the delay selection settles and + * the phase shift is applied. This must be used together with the ATCTRL + * settings programmed in dwcmshc_hpe_vendor_specific(): + * AT_CTRL_R.TUNE_CLK_STOP_EN = 0x1 + * AT_CTRL_R.POST_CHANGE_DLY = 0x3 + * AT_CTRL_R.PRE_CHANGE_DLY = 0x3 + * + * The DTS node provides a syscon phandle ('hpe,gxp-sysreg') with the + * MSHCCS register offset as an argument. + */ +static int dwcmshc_hpe_gsc_init(struct device *dev, struct sdhci_host *host, + struct dwcmshc_priv *dwc_priv) +{ + unsigned int reg_offset; + struct regmap *soc_ctrl; + int ret; + + /* Disable cmd conflict check and configure auto-tuning */ + dwcmshc_hpe_vendor_specific(host); + + /* Look up the GXP sysreg syscon and MSHCCS offset */ + soc_ctrl = syscon_regmap_lookup_by_phandle_args(dev->of_node, + "hpe,gxp-sysreg", + 1, ®_offset); + if (IS_ERR(soc_ctrl)) { + dev_err(dev, "failed to get hpe,gxp-sysreg syscon\n"); + return PTR_ERR(soc_ctrl); + } + + /* Set SCGSyncDis (bit 18) to disable sync on HS200 RX delay lines */ + ret = regmap_update_bits(soc_ctrl, reg_offset, + HPE_GSC_MSHCCS_SCGSYNCDIS, + HPE_GSC_MSHCCS_SCGSYNCDIS); + if (ret) { + dev_err(dev, "failed to set SCGSyncDis in MSHCCS\n"); + return ret; + } + + sdhci_enable_v4_mode(host); + + return 0; +} + static void sdhci_eic7700_set_clock(struct sdhci_host *host, unsigned int clock) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -1834,6 +1957,25 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = { .init = eic7700_init, }; +static const struct sdhci_ops sdhci_dwcmshc_hpe_ops = { + .set_clock = dwcmshc_hpe_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_hpe_set_uhs_signaling, + .get_max_clock = dwcmshc_get_max_clock, + .reset = dwcmshc_hpe_reset, + .adma_write_desc = dwcmshc_adma_write_desc, + .irq = dwcmshc_cqe_irq_handler, +}; + +static const struct dwcmshc_pltfm_data sdhci_dwcmshc_hpe_gsc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_hpe_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, + }, + .init = dwcmshc_hpe_gsc_init, +}; + static const struct cqhci_host_ops dwcmshc_cqhci_ops = { .enable = dwcmshc_sdhci_cqe_enable, .disable = sdhci_cqe_disable, @@ -1942,6 +2084,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { .compatible = "eswin,eic7700-dwcmshc", .data = &sdhci_dwcmshc_eic7700_pdata, }, + { + .compatible = "hpe,gsc-dwcmshc", + .data = &sdhci_dwcmshc_hpe_gsc_pdata, + }, {}, }; MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids); -- cgit v1.2.3 From a137e03f81e241656a4c70ea2a54833e9256de16 Mon Sep 17 00:00:00 2001 From: Jiayu Du Date: Thu, 19 Mar 2026 22:07:03 +0800 Subject: dt-bindings: mmc: Add sdhci support for Canaan k230 The Canaan k230 uses the SDHCI from Synopsys. Add compatible strings to the k230. The k230 has two controllers. MMC0 supports eMMC, while MMC1 supports SDIO. Signed-off-by: Jiayu Du Reviewed-by: Conor Dooley Signed-off-by: Ulf Hansson --- .../bindings/mmc/snps,dwcmshc-sdhci.yaml | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml index 539fa3b54b5b..cd823a3ef213 100644 --- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml @@ -23,6 +23,8 @@ properties: - const: sophgo,sg2044-dwcmshc - const: sophgo,sg2042-dwcmshc - enum: + - canaan,k230-emmc + - canaan,k230-sdio - hpe,gsc-dwcmshc - rockchip,rk3568-dwcmshc - rockchip,rk3588-dwcmshc @@ -58,6 +60,11 @@ properties: minItems: 4 maxItems: 5 + canaan,usb-phy: + $ref: /schemas/types.yaml#/definitions/phandle + description: Phandle to the Canaan K230 USB PHY node required for + k230-emmc/sdio. + rockchip,txclk-tapnum: description: Specify the number of delay for tx sampling. $ref: /schemas/types.yaml#/definitions/uint8 @@ -101,6 +108,27 @@ required: allOf: - $ref: mmc-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - canaan,k230-emmc + - canaan,k230-sdio + then: + properties: + clocks: + minItems: 5 + clock-names: + items: + - const: core + - const: bus + - const: axi + - const: block + - const: timer + required: + - canaan,usb-phy + - if: properties: compatible: -- cgit v1.2.3 From 4095694c09b99533c7300829c0df993d1d57ed4b Mon Sep 17 00:00:00 2001 From: Jiayu Du Date: Thu, 19 Mar 2026 22:07:04 +0800 Subject: mmc: sdhci-dwcmshc: Add Canaan K230 DWCMSHC controller support Add SDHCI controller driver for Canaan k230 SoC. Implement custom sdhci_ops for set_clock, phy init, init and reset. Tested-by: Junhui Liu Signed-off-by: Jiayu Du Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 260 ++++++++++++++++++++++++++++++++++++ 1 file changed, 260 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 9dc7498d422d..a91b3e7e4d16 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -131,9 +131,11 @@ #define PHY_CNFG_PHY_PWRGOOD_MASK BIT_MASK(1) /* bit [1] */ #define PHY_CNFG_PAD_SP_MASK GENMASK(19, 16) /* bits [19:16] */ #define PHY_CNFG_PAD_SP 0x0c /* PMOS TX drive strength */ +#define PHY_CNFG_PAD_SP_k230 0x09 /* PMOS TX drive strength for k230 */ #define PHY_CNFG_PAD_SP_SG2042 0x09 /* PMOS TX drive strength for SG2042 */ #define PHY_CNFG_PAD_SN_MASK GENMASK(23, 20) /* bits [23:20] */ #define PHY_CNFG_PAD_SN 0x0c /* NMOS TX drive strength */ +#define PHY_CNFG_PAD_SN_k230 0x08 /* NMOS TX drive strength for k230 */ #define PHY_CNFG_PAD_SN_SG2042 0x08 /* NMOS TX drive strength for SG2042 */ /* PHY command/response pad settings */ @@ -156,14 +158,21 @@ #define PHY_PAD_RXSEL_3V3 0x2 /* Receiver type select for 3.3V */ #define PHY_PAD_WEAKPULL_MASK GENMASK(4, 3) /* bits [4:3] */ +#define PHY_PAD_WEAKPULL_DISABLED 0x0 /* Weak pull up and pull down disabled */ #define PHY_PAD_WEAKPULL_PULLUP 0x1 /* Weak pull up enabled */ #define PHY_PAD_WEAKPULL_PULLDOWN 0x2 /* Weak pull down enabled */ #define PHY_PAD_TXSLEW_CTRL_P_MASK GENMASK(8, 5) /* bits [8:5] */ #define PHY_PAD_TXSLEW_CTRL_P 0x3 /* Slew control for P-Type pad TX */ +#define PHY_PAD_TXSLEW_CTRL_P_k230 0x2 /* Slew control for P-Type pad TX for k230 */ #define PHY_PAD_TXSLEW_CTRL_N_MASK GENMASK(12, 9) /* bits [12:9] */ #define PHY_PAD_TXSLEW_CTRL_N 0x3 /* Slew control for N-Type pad TX */ #define PHY_PAD_TXSLEW_CTRL_N_SG2042 0x2 /* Slew control for N-Type pad TX for SG2042 */ +#define PHY_PAD_TXSLEW_CTRL_N_k230 0x2 /* Slew control for N-Type pad TX for k230 */ + +/* PHY Common DelayLine config settings */ +#define PHY_COMMDL_CNFG (DWC_MSHC_PTR_PHY_R + 0x1c) +#define PHY_COMMDL_CNFG_DLSTEP_SEL BIT(0) /* DelayLine outputs on PAD enabled */ /* PHY CLK delay line settings */ #define PHY_SDCLKDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x1d) @@ -177,7 +186,10 @@ #define PHY_SDCLKDL_DC_HS400 0x18 /* delay code for HS400 mode */ #define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20) +#define PHY_SMPLDL_CNFG_EXTDLY_EN BIT(0) #define PHY_SMPLDL_CNFG_BYPASS_EN BIT(1) +#define PHY_SMPLDL_CNFG_INPSEL_MASK GENMASK(3, 2) /* bits [3:2] */ +#define PHY_SMPLDL_CNFG_INPSEL 0x3 /* delay line input source */ /* PHY drift_cclk_rx delay line configuration setting */ #define PHY_ATDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x21) @@ -227,9 +239,20 @@ SDHCI_TRNS_BLK_CNT_EN | \ SDHCI_TRNS_DMA) +#define to_pltfm_data(priv, name) \ + container_of((priv)->dwcmshc_pdata, struct name##_pltfm_data, dwcmshc_pdata) + /* SMC call for BlueField-3 eMMC RST_N */ #define BLUEFIELD_SMC_SET_EMMC_RST_N 0x82000007 +/* Canaan specific Registers */ +#define SD0_CTRL 0x00 +#define SD0_HOST_REG_VOL_STABLE BIT(4) +#define SD0_CARD_WRITE_PROT BIT(6) +#define SD1_CTRL 0x08 +#define SD1_HOST_REG_VOL_STABLE BIT(0) +#define SD1_CARD_WRITE_PROT BIT(2) + /* Eswin specific Registers */ #define EIC7700_CARD_CLK_STABLE BIT(28) #define EIC7700_INT_BCLK_STABLE BIT(16) @@ -271,6 +294,11 @@ struct eic7700_priv { unsigned int drive_impedance; }; +struct k230_priv { + /* Canaan k230 specific */ + struct regmap *hi_sys_regmap; +}; + #define DWCMSHC_MAX_OTHER_CLKS 3 struct dwcmshc_priv { @@ -281,6 +309,7 @@ struct dwcmshc_priv { int num_other_clks; struct clk_bulk_data other_clks[DWCMSHC_MAX_OTHER_CLKS]; + const struct dwcmshc_pltfm_data *dwcmshc_pdata; void *priv; /* pointer to SoC private stuff */ u16 delay_line; u16 flags; @@ -293,6 +322,14 @@ struct dwcmshc_pltfm_data { void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); }; +struct k230_pltfm_data { + struct dwcmshc_pltfm_data dwcmshc_pdata; + bool is_emmc; + u32 ctrl_reg; + u32 vol_stable_bit; + u32 write_prot_bit; +}; + static void dwcmshc_enable_card_clk(struct sdhci_host *host) { u16 ctrl; @@ -1779,6 +1816,181 @@ static int eic7700_init(struct device *dev, struct sdhci_host *host, struct dwcm return 0; } +static void dwcmshc_k230_sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + u16 clk; + + sdhci_set_clock(host, clock); + + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + /* + * It is necessary to enable SDHCI_PROG_CLOCK_MODE. This is a + * vendor-specific quirk. If this is not done, the eMMC will be + * unable to read or write. + */ + clk |= SDHCI_PROG_CLOCK_MODE; + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); +} + +static void sdhci_k230_config_phy_delay(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 val; + + sdhci_writeb(host, PHY_COMMDL_CNFG_DLSTEP_SEL, PHY_COMMDL_CNFG); + sdhci_writeb(host, 0x0, PHY_SDCLKDL_CNFG_R); + sdhci_writeb(host, PHY_SDCLKDL_DC_INITIAL, PHY_SDCLKDL_DC_R); + + val = PHY_SMPLDL_CNFG_EXTDLY_EN; + val |= FIELD_PREP(PHY_SMPLDL_CNFG_INPSEL_MASK, PHY_SMPLDL_CNFG_INPSEL); + sdhci_writeb(host, val, PHY_SMPLDL_CNFG_R); + + sdhci_writeb(host, FIELD_PREP(PHY_ATDL_CNFG_INPSEL_MASK, PHY_ATDL_CNFG_INPSEL), + PHY_ATDL_CNFG_R); + + val = sdhci_readl(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + val |= AT_CTRL_TUNE_CLK_STOP_EN; + val |= FIELD_PREP(AT_CTRL_PRE_CHANGE_DLY_MASK, AT_CTRL_PRE_CHANGE_DLY); + val |= FIELD_PREP(AT_CTRL_POST_CHANGE_DLY_MASK, AT_CTRL_POST_CHANGE_DLY); + sdhci_writel(host, val, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_ATCTRL); + sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_AT_STAT); +} + +static int dwcmshc_k230_phy_init(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + u32 rxsel; + u32 val; + u32 reg; + int ret; + + /* reset phy */ + sdhci_writew(host, 0, PHY_CNFG_R); + + /* Disable the clock */ + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + + rxsel = dwc_priv->flags & FLAG_IO_FIXED_1V8 ? PHY_PAD_RXSEL_1V8 : PHY_PAD_RXSEL_3V3; + + val = rxsel; + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230); + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP); + + sdhci_writew(host, val, PHY_CMDPAD_CNFG_R); + sdhci_writew(host, val, PHY_DATAPAD_CNFG_R); + sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R); + + val = rxsel; + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230); + sdhci_writew(host, val, PHY_CLKPAD_CNFG_R); + + val = rxsel; + val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P_k230); + val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N_k230); + sdhci_writew(host, val, PHY_STBPAD_CNFG_R); + + sdhci_k230_config_phy_delay(host); + + /* Wait max 150 ms */ + ret = read_poll_timeout(sdhci_readl, reg, + (reg & FIELD_PREP(PHY_CNFG_PHY_PWRGOOD_MASK, 1)), + 10, 150000, false, host, PHY_CNFG_R); + if (ret) { + dev_err(mmc_dev(host->mmc), "READ PHY PWRGOOD timeout!\n"); + return -ETIMEDOUT; + } + + reg = FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN_k230) | + FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP_k230); + sdhci_writel(host, reg, PHY_CNFG_R); + + /* de-assert the phy */ + reg |= PHY_CNFG_RSTN_DEASSERT; + sdhci_writel(host, reg, PHY_CNFG_R); + + return 0; +} + +static void dwcmshc_k230_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230); + u8 emmc_ctrl; + + dwcmshc_reset(host, mask); + + if (mask != SDHCI_RESET_ALL) + return; + + emmc_ctrl = sdhci_readw(host, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + sdhci_writeb(host, emmc_ctrl, dwc_priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + + if (k230_pdata->is_emmc) + dwcmshc_k230_phy_init(host); + else + sdhci_writel(host, 0x0, dwc_priv->vendor_specific_area1 + DWCMSHC_HOST_CTRL3); +} + +static int dwcmshc_k230_init(struct device *dev, struct sdhci_host *host, + struct dwcmshc_priv *dwc_priv) +{ + const struct k230_pltfm_data *k230_pdata = to_pltfm_data(dwc_priv, k230); + static const char * const clk_ids[] = {"block", "timer", "axi"}; + struct device_node *usb_phy_node; + struct k230_priv *k230_priv; + u32 data; + int ret; + + k230_priv = devm_kzalloc(dev, sizeof(struct k230_priv), GFP_KERNEL); + if (!k230_priv) + return -ENOMEM; + + dwc_priv->priv = k230_priv; + + usb_phy_node = of_parse_phandle(dev->of_node, "canaan,usb-phy", 0); + if (!usb_phy_node) + return dev_err_probe(dev, -ENODEV, "Failed to find canaan,usb-phy phandle\n"); + + k230_priv->hi_sys_regmap = device_node_to_regmap(usb_phy_node); + of_node_put(usb_phy_node); + + if (IS_ERR(k230_priv->hi_sys_regmap)) + return dev_err_probe(dev, PTR_ERR(k230_priv->hi_sys_regmap), + "Failed to get k230-usb-phy regmap\n"); + + ret = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv, + ARRAY_SIZE(clk_ids), clk_ids); + if (ret) + return dev_err_probe(dev, ret, "Failed to get/enable k230 mmc other clocks\n"); + + if (k230_pdata->is_emmc) { + host->flags &= ~SDHCI_SIGNALING_330; + dwc_priv->flags |= FLAG_IO_FIXED_1V8; + } else { + host->mmc->caps |= MMC_CAP_SD_HIGHSPEED; + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + } + + ret = regmap_read(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, &data); + if (ret) + return dev_err_probe(dev, ret, "Failed to read control reg 0x%x\n", + k230_pdata->ctrl_reg); + + data |= k230_pdata->write_prot_bit | k230_pdata->vol_stable_bit; + ret = regmap_write(k230_priv->hi_sys_regmap, k230_pdata->ctrl_reg, data); + if (ret) + return dev_err_probe(dev, ret, "Failed to write control reg 0x%x\n", + k230_pdata->ctrl_reg); + + return 0; +} + static const struct sdhci_ops sdhci_dwcmshc_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -1866,6 +2078,15 @@ static const struct sdhci_ops sdhci_dwcmshc_eic7700_ops = { .platform_execute_tuning = sdhci_eic7700_executing_tuning, }; +static const struct sdhci_ops sdhci_dwcmshc_k230_ops = { + .set_clock = dwcmshc_k230_sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .set_uhs_signaling = dwcmshc_set_uhs_signaling, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .reset = dwcmshc_k230_sdhci_reset, + .adma_write_desc = dwcmshc_adma_write_desc, +}; + static const struct dwcmshc_pltfm_data sdhci_dwcmshc_pdata = { .pdata = { .ops = &sdhci_dwcmshc_ops, @@ -1957,6 +2178,36 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_eic7700_pdata = { .init = eic7700_init, }; +static const struct k230_pltfm_data k230_emmc_data = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_k230_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, + }, + .init = dwcmshc_k230_init, + }, + .is_emmc = true, + .ctrl_reg = SD0_CTRL, + .vol_stable_bit = SD0_HOST_REG_VOL_STABLE, + .write_prot_bit = SD0_CARD_WRITE_PROT, +}; + +static const struct k230_pltfm_data k230_sdio_data = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_k230_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, + }, + .init = dwcmshc_k230_init, + }, + .is_emmc = false, + .ctrl_reg = SD1_CTRL, + .vol_stable_bit = SD1_HOST_REG_VOL_STABLE, + .write_prot_bit = SD1_CARD_WRITE_PROT, +}; + static const struct sdhci_ops sdhci_dwcmshc_hpe_ops = { .set_clock = dwcmshc_hpe_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -2048,6 +2299,14 @@ dsbl_cqe_caps: } static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { + { + .compatible = "canaan,k230-emmc", + .data = &k230_emmc_data.dwcmshc_pdata, + }, + { + .compatible = "canaan,k230-sdio", + .data = &k230_sdio_data.dwcmshc_pdata, + }, { .compatible = "rockchip,rk3588-dwcmshc", .data = &sdhci_dwcmshc_rk35xx_pdata, @@ -2134,6 +2393,7 @@ static int dwcmshc_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); priv = sdhci_pltfm_priv(pltfm_host); + priv->dwcmshc_pdata = pltfm_data; if (dev->of_node) { pltfm_host->clk = devm_clk_get(dev, "core"); -- cgit v1.2.3 From 50d349cb70cdb6db352dec114e6381a31870b4ff Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Mar 2026 23:02:21 +0100 Subject: mmc: sdhci-pci: Drop unused include This driver includes the legacy header but does not use any symbols from it. Drop the inclusion. Signed-off-by: Andy Shevchenko Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index 47a0a738862b..c347fac24515 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include -- cgit v1.2.3 From 918d627c091683203a76badc88c2cd58b420f107 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Tue, 24 Mar 2026 09:58:49 +0800 Subject: dt-bindings: mmc: sdhci-of-aspeed: Add AST2700 compatible AST2700 SDHCI controller is fully compatible with AST2600. However, it is necessary to take the AST2700 SD controller out of reset, so require the 'resets' property. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ryan Chen Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/aspeed,sdhci.yaml | 41 +++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml b/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml index d24950ccea95..e4a9c2810893 100644 --- a/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml +++ b/Documentation/devicetree/bindings/mmc/aspeed,sdhci.yaml @@ -22,10 +22,15 @@ description: |+ properties: compatible: - enum: - - aspeed,ast2400-sd-controller - - aspeed,ast2500-sd-controller - - aspeed,ast2600-sd-controller + oneOf: + - enum: + - aspeed,ast2400-sd-controller + - aspeed,ast2500-sd-controller + - aspeed,ast2600-sd-controller + - items: + - const: aspeed,ast2700-sd-controller + - const: aspeed,ast2600-sd-controller + reg: maxItems: 1 description: Common configuration registers @@ -38,6 +43,9 @@ properties: maxItems: 1 description: The SD/SDIO controller clock gate + resets: + maxItems: 1 + patternProperties: "^sdhci@[0-9a-f]+$": type: object @@ -46,10 +54,15 @@ patternProperties: properties: compatible: - enum: - - aspeed,ast2400-sdhci - - aspeed,ast2500-sdhci - - aspeed,ast2600-sdhci + oneOf: + - enum: + - aspeed,ast2400-sdhci + - aspeed,ast2500-sdhci + - aspeed,ast2600-sdhci + - items: + - const: aspeed,ast2700-sdhci + - const: aspeed,ast2600-sdhci + reg: maxItems: 1 description: The SDHCI registers @@ -78,6 +91,18 @@ required: - ranges - clocks +if: + properties: + compatible: + contains: + const: aspeed,ast2700-sd-controller +then: + required: + - resets +else: + properties: + resets: false + examples: - | #include -- cgit v1.2.3 From 4f7cfb94be61677e8bfb7cbc909496f67bb6a08f Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Tue, 24 Mar 2026 09:58:50 +0800 Subject: mmc: sdhci-of-aspeed: Handle optional controller reset Get the optional reset line for the ASPEED SD controller during probe by using devm_reset_control_get_optional_exclusive_deasserted(). This allows platforms such as AST2700, which require the SD controller to be taken out of reset before use, to work with the existing driver. Acked-by: Adrian Hunter Signed-off-by: Ryan Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-aspeed.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/mmc/host/sdhci-of-aspeed.c b/drivers/mmc/host/sdhci-of-aspeed.c index 4296def69436..f5d973783cbe 100644 --- a/drivers/mmc/host/sdhci-of-aspeed.c +++ b/drivers/mmc/host/sdhci-of-aspeed.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "sdhci-pltfm.h" @@ -519,6 +520,7 @@ static struct platform_driver aspeed_sdhci_driver = { static int aspeed_sdc_probe(struct platform_device *pdev) { + struct reset_control *reset; struct device_node *parent; struct aspeed_sdc *sdc; int ret; @@ -529,6 +531,10 @@ static int aspeed_sdc_probe(struct platform_device *pdev) spin_lock_init(&sdc->lock); + reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, NULL); + if (IS_ERR(reset)) + return dev_err_probe(&pdev->dev, PTR_ERR(reset), "unable to acquire reset\n"); + sdc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(sdc->clk)) return PTR_ERR(sdc->clk); -- cgit v1.2.3 From a767c98c89653a1acf3ecf427e76a738aa2dd9c2 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 24 Mar 2026 16:42:16 +0800 Subject: mmc: sdhci-of-arasan: Use standard mmc_clk_phase_map infrastructure Convert the Arasan SDHCI driver to use the mainline standard mmc_clk_phase_map infrastructure instead of custom clk_phase_in/out arrays as well as arasan_dt_read_clk_phase(). The phase values for ZynqMP, Versal, and Versal-NET platforms are still initialized from the predefined tables. Signed-off-by: Shawn Lin Reviewed-by: Sai Krishna Potthuri Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-arasan.c | 73 +++++++------------------------------- 1 file changed, 12 insertions(+), 61 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index caf97238a58b..785d3acb18c5 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -152,8 +152,7 @@ struct sdhci_arasan_clk_ops { * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw. * @sampleclk_hw: Struct for the clock we might provide to a PHY. * @sampleclk: Pointer to normal 'struct clock' for sampleclk_hw. - * @clk_phase_in: Array of Input Clock Phase Delays for all speed modes - * @clk_phase_out: Array of Output Clock Phase Delays for all speed modes + * @phase_map: Struct for mmc_clk_phase_map provided. * @set_clk_delays: Function pointer for setting Clock Delays * @clk_of_data: Platform specific runtime clock data storage pointer */ @@ -162,8 +161,7 @@ struct sdhci_arasan_clk_data { struct clk *sdcardclk; struct clk_hw sampleclk_hw; struct clk *sampleclk; - int clk_phase_in[MMC_TIMING_MMC_HS400 + 1]; - int clk_phase_out[MMC_TIMING_MMC_HS400 + 1]; + struct mmc_clk_phase_map phase_map; void (*set_clk_delays)(struct sdhci_host *host); void *clk_of_data; }; @@ -1249,36 +1247,9 @@ static void sdhci_arasan_set_clk_delays(struct sdhci_host *host) struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data; clk_set_phase(clk_data->sampleclk, - clk_data->clk_phase_in[host->timing]); + clk_data->phase_map.phase[host->timing].in_deg); clk_set_phase(clk_data->sdcardclk, - clk_data->clk_phase_out[host->timing]); -} - -static void arasan_dt_read_clk_phase(struct device *dev, - struct sdhci_arasan_clk_data *clk_data, - unsigned int timing, const char *prop) -{ - struct device_node *np = dev->of_node; - - u32 clk_phase[2] = {0}; - int ret; - - /* - * Read Tap Delay values from DT, if the DT does not contain the - * Tap Values then use the pre-defined values. - */ - ret = of_property_read_variable_u32_array(np, prop, &clk_phase[0], - 2, 0); - if (ret < 0) { - dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n", - prop, clk_data->clk_phase_in[timing], - clk_data->clk_phase_out[timing]); - return; - } - - /* The values read are Input and Output Clock Delays in order */ - clk_data->clk_phase_in[timing] = clk_phase[0]; - clk_data->clk_phase_out[timing] = clk_phase[1]; + clk_data->phase_map.phase[host->timing].out_deg); } /** @@ -1315,8 +1286,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev, } for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { - clk_data->clk_phase_in[i] = zynqmp_iclk_phase[i]; - clk_data->clk_phase_out[i] = zynqmp_oclk_phase[i]; + clk_data->phase_map.phase[i].in_deg = zynqmp_iclk_phase[i]; + clk_data->phase_map.phase[i].out_deg = zynqmp_oclk_phase[i]; } } @@ -1327,8 +1298,8 @@ static void arasan_dt_parse_clk_phases(struct device *dev, VERSAL_OCLK_PHASE; for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { - clk_data->clk_phase_in[i] = versal_iclk_phase[i]; - clk_data->clk_phase_out[i] = versal_oclk_phase[i]; + clk_data->phase_map.phase[i].in_deg = versal_iclk_phase[i]; + clk_data->phase_map.phase[i].out_deg = versal_oclk_phase[i]; } } if (of_device_is_compatible(dev->of_node, "xlnx,versal-net-emmc")) { @@ -1338,32 +1309,12 @@ static void arasan_dt_parse_clk_phases(struct device *dev, VERSAL_NET_EMMC_OCLK_PHASE; for (i = 0; i <= MMC_TIMING_MMC_HS400; i++) { - clk_data->clk_phase_in[i] = versal_net_iclk_phase[i]; - clk_data->clk_phase_out[i] = versal_net_oclk_phase[i]; + clk_data->phase_map.phase[i].in_deg = versal_net_iclk_phase[i]; + clk_data->phase_map.phase[i].out_deg = versal_net_oclk_phase[i]; } } - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY, - "clk-phase-legacy"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS, - "clk-phase-mmc-hs"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS, - "clk-phase-sd-hs"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12, - "clk-phase-uhs-sdr12"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25, - "clk-phase-uhs-sdr25"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50, - "clk-phase-uhs-sdr50"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104, - "clk-phase-uhs-sdr104"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50, - "clk-phase-uhs-ddr50"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52, - "clk-phase-mmc-ddr52"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200, - "clk-phase-mmc-hs200"); - arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400, - "clk-phase-mmc-hs400"); + + mmc_of_parse_clk_phase(dev, &clk_data->phase_map); } static const struct sdhci_pltfm_data sdhci_arasan_pdata = { -- cgit v1.2.3 From 2ce454acfc41d145a0977fdeead9076f84c7b692 Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Thu, 26 Mar 2026 10:59:13 +0100 Subject: dt-bindings: mmc: amlogic: Add compatible for T7 mmc Add amlogic,t7-mmc compatible string, falling back to amlogic,meson-axg-mmc as the T7 MMC controller is compatible with the AXG implementation. Acked-by: Conor Dooley Acked-by: Rob Herring (Arm) Reviewed-by: Xianwei Zhao Signed-off-by: Ronald Claveau Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml index 57646575a13f..976f36de2091 100644 --- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml @@ -19,6 +19,10 @@ allOf: properties: compatible: oneOf: + - items: + - enum: + - amlogic,t7-mmc + - const: amlogic,meson-axg-mmc - const: amlogic,meson-axg-mmc - items: - const: amlogic,meson-gx-mmc -- cgit v1.2.3 From c7c6d4f5103864f73ee3a78bfd6da241f84197dd Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Wed, 25 Mar 2026 08:49:47 -0500 Subject: mmc: block: use single block write in retry Due to errata i2493[0], multi-block write would still fail in retries. With i2493, the MMC interface has the potential of write failures when issuing multi-block writes operating in HS200 mode with excessive IO supply noise. While the errata provides guidance in hardware design and layout to minimize the IO supply noise, in theory the write failure cannot be resolved in hardware. The software solution to ensure the data integrity is to add minimum 5us delay between block writes. Single-block write is the practical way to introduce the delay. This patch reuses recovery_mode flag, and switches to single-block write in retry when multi-block write fails. It covers both CQE and non-CQE cases. [0] https://www.ti.com/lit/pdf/sprz582 Cc: stable@vger.kernel.org Suggested-by: Jens Axboe Signed-off-by: Bin Liu Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 12 ++++++++++-- drivers/mmc/core/queue.h | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 05ee76cb0a08..db8c99c73a61 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1401,6 +1401,9 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq, rq_data_dir(req) == WRITE && (md->flags & MMC_BLK_REL_WR); + if (mqrq->flags & MQRQ_XFER_SINGLE_BLOCK) + recovery_mode = 1; + memset(brq, 0, sizeof(struct mmc_blk_request)); mmc_crypto_prepare_req(mqrq); @@ -1540,10 +1543,13 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req) err = 0; if (err) { - if (mqrq->retries++ < MMC_CQE_RETRIES) + if (mqrq->retries++ < MMC_CQE_RETRIES) { + if (rq_data_dir(req) == WRITE) + mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK; blk_mq_requeue_request(req, true); - else + } else { blk_mq_end_request(req, BLK_STS_IOERR); + } } else if (mrq->data) { if (blk_update_request(req, BLK_STS_OK, mrq->data->bytes_xfered)) blk_mq_requeue_request(req, true); @@ -2085,6 +2091,8 @@ static void mmc_blk_mq_complete_rq(struct mmc_queue *mq, struct request *req) } else if (!blk_rq_bytes(req)) { __blk_mq_end_request(req, BLK_STS_IOERR); } else if (mqrq->retries++ < MMC_MAX_RETRIES) { + if (rq_data_dir(req) == WRITE) + mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK; blk_mq_requeue_request(req, true); } else { if (mmc_card_removed(mq->card)) diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h index 1498840a4ea0..c254e6580afd 100644 --- a/drivers/mmc/core/queue.h +++ b/drivers/mmc/core/queue.h @@ -61,6 +61,8 @@ enum mmc_drv_op { MMC_DRV_OP_GET_EXT_CSD, }; +#define MQRQ_XFER_SINGLE_BLOCK BIT(0) + struct mmc_queue_req { struct mmc_blk_request brq; struct scatterlist *sg; @@ -69,6 +71,7 @@ struct mmc_queue_req { void *drv_op_data; unsigned int ioc_count; int retries; + u32 flags; }; struct mmc_queue { -- cgit v1.2.3 From 19fe6c02f480e86962f928e298c3f51040da5411 Mon Sep 17 00:00:00 2001 From: Kathiravan Thirumoorthy Date: Wed, 25 Mar 2026 17:10:19 +0530 Subject: dt-bindings: mmc: sdhci-msm: add IPQ9650 compatible The IPQ9650 supports eMMC with an SDHCI controller. Add the appropriate compatible to the documentation. Signed-off-by: Kathiravan Thirumoorthy Acked-by: Krzysztof Kozlowski 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 fd1d5b04e755..695a95e8f35d 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml @@ -43,6 +43,7 @@ properties: - qcom,ipq5424-sdhci - qcom,ipq6018-sdhci - qcom,ipq9574-sdhci + - qcom,ipq9650-sdhci - qcom,kaanapali-sdhci - qcom,milos-sdhci - qcom,qcm2290-sdhci -- cgit v1.2.3 From 49eb823cbe648dba791cfe1c16e4f6fcfd32f5ca Mon Sep 17 00:00:00 2001 From: Bhargav Joshi Date: Thu, 26 Mar 2026 04:24:38 +0530 Subject: dt-bindings: mmc: hisilicon,hi3660-dw-mshc: Convert to DT schema Convert the Hisilicon DesignWare Mobile Storage Host Controller (dw-mshc) bindings from text format to DT schema. As part of this conversion, the binding file is renamed from k3-dw-mshc.txt to hisilicon,hi3660-dw-mshc.yaml to align with compatible string naming conventions. Examples have been updated to pass schema validation. Note: synopsys-dw-mshc binding specifies clock names as "biu" followed by "ciu". However, this Hisilicon binding reverses the order to 'ciu' then 'biu' to match both the legacy text binding and in-kernel Hisilicon DTS board files. Signed-off-by: Bhargav Joshi Acked-by: Zhangfei Gao Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ulf Hansson --- .../bindings/mmc/hisilicon,hi3660-dw-mshc.yaml | 117 +++++++++++++++++++++ .../devicetree/bindings/mmc/k3-dw-mshc.txt | 73 ------------- 2 files changed, 117 insertions(+), 73 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml delete mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt diff --git a/Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml new file mode 100644 index 000000000000..296bd776488e --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/hisilicon,hi3660-dw-mshc.yaml @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/hisilicon,hi3660-dw-mshc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Hisilicon specific extensions to the Synopsys Designware Mobile Storage Host Controller + +maintainers: + - Zhangfei Gao + +description: + The Synopsys designware mobile storage host controller is used to interface + a SoC with storage medium such as eMMC or SD/MMC cards. This file documents + differences between the core Synopsys dw mshc controller properties described + by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific + extensions to the Synopsys Designware Mobile Storage Host Controller. + +allOf: + - $ref: /schemas/mmc/synopsys-dw-mshc-common.yaml# + +properties: + compatible: + oneOf: + - enum: + - hisilicon,hi3660-dw-mshc + - hisilicon,hi4511-dw-mshc + - hisilicon,hi6220-dw-mshc + - items: + - const: hisilicon,hi3670-dw-mshc + - const: hisilicon,hi3660-dw-mshc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: card interface unit clock + - description: bus interface unit clock + + clock-names: + items: + - const: ciu + - const: biu + + hisilicon,peripheral-syscon: + $ref: /schemas/types.yaml#/definitions/phandle + description: phandle of syscon used to control peripheral. + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + + mmc@fcd03000 { + compatible = "hisilicon,hi4511-dw-mshc"; + reg = <0xfcd03000 0x1000>; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>; + clock-names = "ciu", "biu"; + vmmc-supply = <&ldo12>; + fifo-depth = <0x100>; + pinctrl-names = "default"; + pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>; + bus-width = <4>; + disable-wp; + cd-gpios = <&gpio10 3 GPIO_ACTIVE_HIGH>; + cap-mmc-highspeed; + cap-sd-highspeed; + }; + + - | + #include + #include + #include + + soc { + #address-cells = <2>; + #size-cells = <2>; + + mmc@f723e000 { + compatible = "hisilicon,hi6220-dw-mshc"; + reg = <0x0 0xf723e000 0x0 0x1000>; + interrupts = ; + clocks = <&clock_sys HI6220_MMC1_CIUCLK>, + <&clock_sys HI6220_MMC1_CLK>; + clock-names = "ciu", "biu"; + bus-width = <4>; + disable-wp; + cap-sd-highspeed; + sd-uhs-sdr12; + sd-uhs-sdr25; + card-detect-delay = <200>; + hisilicon,peripheral-syscon = <&ao_ctrl>; + cd-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>; + pinctrl-names = "default", "idle"; + pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>; + pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>; + vqmmc-supply = <&ldo7>; + vmmc-supply = <&ldo10>; + }; + }; diff --git a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt deleted file mode 100644 index 36c4bea675d5..000000000000 --- a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt +++ /dev/null @@ -1,73 +0,0 @@ -* Hisilicon specific extensions to the Synopsys Designware Mobile - Storage Host Controller - -Read synopsys-dw-mshc.txt for more details - -The Synopsys designware mobile storage host controller is used to interface -a SoC with storage medium such as eMMC or SD/MMC cards. This file documents -differences between the core Synopsys dw mshc controller properties described -by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific -extensions to the Synopsys Designware Mobile Storage Host Controller. - -Required Properties: - -* compatible: should be one of the following. - - "hisilicon,hi3660-dw-mshc": for controllers with hi3660 specific extensions. - - "hisilicon,hi3670-dw-mshc", "hisilicon,hi3660-dw-mshc": for controllers - with hi3670 specific extensions. - - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extensions. - - "hisilicon,hi6220-dw-mshc": for controllers with hi6220 specific extensions. - -Optional Properties: -- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral. - -Example: - - /* for Hi3620 */ - - /* SoC portion */ - dwmmc_0: dwmmc0@fcd03000 { - compatible = "hisilicon,hi4511-dw-mshc"; - reg = <0xfcd03000 0x1000>; - interrupts = <0 16 4>; - #address-cells = <1>; - #size-cells = <0>; - clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>; - clock-names = "ciu", "biu"; - }; - - /* Board portion */ - dwmmc0@fcd03000 { - vmmc-supply = <&ldo12>; - fifo-depth = <0x100>; - pinctrl-names = "default"; - pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>; - bus-width = <4>; - disable-wp; - cd-gpios = <&gpio10 3 0>; - cap-mmc-highspeed; - cap-sd-highspeed; - }; - - /* for Hi6220 */ - - dwmmc_1: dwmmc1@f723e000 { - compatible = "hisilicon,hi6220-dw-mshc"; - bus-width = <0x4>; - disable-wp; - cap-sd-highspeed; - sd-uhs-sdr12; - sd-uhs-sdr25; - card-detect-delay = <200>; - hisilicon,peripheral-syscon = <&ao_ctrl>; - reg = <0x0 0xf723e000 0x0 0x1000>; - interrupts = <0x0 0x49 0x4>; - clocks = <&clock_sys HI6220_MMC1_CIUCLK>, <&clock_sys HI6220_MMC1_CLK>; - clock-names = "ciu", "biu"; - cd-gpios = <&gpio1 0 1>; - pinctrl-names = "default", "idle"; - pinctrl-0 = <&sd_pmx_func &sd_clk_cfg_func &sd_cfg_func>; - pinctrl-1 = <&sd_pmx_idle &sd_clk_cfg_idle &sd_cfg_idle>; - vqmmc-supply = <&ldo7>; - vmmc-supply = <&ldo10>; - }; -- cgit v1.2.3 From 59b0efd867fbbcad6d378e8aa08dda44d1f48651 Mon Sep 17 00:00:00 2001 From: Cathy Xu Date: Thu, 26 Mar 2026 11:05:16 +0800 Subject: mmc: mtk-sd: disable new_tx/rx and modify related settings for mt8189 Disable new_tx/rx to avoid data transmission instability, and adjust .data_tune, .stop_dly_sel, and .pop_en_cnt to fit the overall configuration after disabling new_tx/rx, making it more compatible with mt8189. Fixes: 846a3a2fdff5 ("mmc: mtk-sd: add support for MT8189 SoC") Tested-by: Louis-Alexis Eyraud Signed-off-by: Cathy Xu Signed-off-by: Ulf Hansson --- drivers/mmc/host/mtk-sd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 302ac8529c4f..b2680cc054bd 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -682,15 +682,15 @@ static const struct mtk_mmc_compatible mt8189_compat = { .needs_top_base = true, .pad_tune_reg = MSDC_PAD_TUNE0, .async_fifo = true, - .data_tune = true, + .data_tune = false, .busy_check = true, .stop_clk_fix = true, - .stop_dly_sel = 1, - .pop_en_cnt = 2, + .stop_dly_sel = 3, + .pop_en_cnt = 8, .enhance_rx = true, .support_64g = true, - .support_new_tx = true, - .support_new_rx = true, + .support_new_tx = false, + .support_new_rx = false, .support_spm_res_release = true, }; -- cgit v1.2.3 From c3126dccfd7b8e61dc6b6eb093f57dc44a92eb22 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 26 Mar 2026 19:58:49 -0700 Subject: mmc: mmc_test: use kzalloc_flex Simplifies allocations by using a flexible array member in these structs. Add __counted_by to get extra runtime analysis. Signed-off-by: Rosen Penev Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_test.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index 43fdb67d5407..f5c542855037 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -51,12 +51,12 @@ struct mmc_test_pages { /** * struct mmc_test_mem - allocated memory. - * @arr: array of allocations * @cnt: number of allocations + * @arr: array of allocations */ struct mmc_test_mem { - struct mmc_test_pages *arr; unsigned int cnt; + struct mmc_test_pages arr[] __counted_by(cnt); }; /** @@ -135,21 +135,22 @@ struct mmc_test_dbgfs_file { * struct mmc_test_card - test information. * @card: card under test * @scratch: transfer buffer - * @buffer: transfer buffer * @highmem: buffer for highmem tests * @area: information for performance tests * @gr: pointer to results of current testcase + * @buffer: transfer buffer */ struct mmc_test_card { struct mmc_card *card; u8 scratch[BUFFER_SIZE]; - u8 *buffer; #ifdef CONFIG_HIGHMEM struct page *highmem; #endif struct mmc_test_area area; struct mmc_test_general_result *gr; + + u8 buffer[]; }; enum mmc_test_prep_media { @@ -315,7 +316,6 @@ static void mmc_test_free_mem(struct mmc_test_mem *mem) while (mem->cnt--) __free_pages(mem->arr[mem->cnt].page, mem->arr[mem->cnt].order); - kfree(mem->arr); kfree(mem); } @@ -348,14 +348,10 @@ static struct mmc_test_mem *mmc_test_alloc_mem(unsigned long min_sz, if (max_segs > max_page_cnt) max_segs = max_page_cnt; - mem = kzalloc_obj(*mem); + mem = kzalloc_flex(*mem, arr, max_segs); if (!mem) return NULL; - mem->arr = kzalloc_objs(*mem->arr, max_segs); - if (!mem->arr) - goto out_free; - while (max_page_cnt) { struct page *page; unsigned int order; @@ -3099,7 +3095,7 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf, if (ret) return ret; - test = kzalloc_obj(*test); + test = kzalloc_flex(*test, buffer, BUFFER_SIZE); if (!test) return -ENOMEM; @@ -3111,7 +3107,6 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf, test->card = card; - test->buffer = kzalloc(BUFFER_SIZE, GFP_KERNEL); #ifdef CONFIG_HIGHMEM test->highmem = alloc_pages(GFP_KERNEL | __GFP_HIGHMEM, BUFFER_ORDER); if (!test->highmem) { @@ -3120,17 +3115,14 @@ static ssize_t mtf_test_write(struct file *file, const char __user *buf, } #endif - if (test->buffer) { - mutex_lock(&mmc_test_lock); - mmc_test_run(test, testcase); - mutex_unlock(&mmc_test_lock); - } + mutex_lock(&mmc_test_lock); + mmc_test_run(test, testcase); + mutex_unlock(&mmc_test_lock); #ifdef CONFIG_HIGHMEM __free_pages(test->highmem, BUFFER_ORDER); free_test_buffer: #endif - kfree(test->buffer); kfree(test); return count; -- cgit v1.2.3 From 3c3b759be1e11868a1224623657fad8761d91640 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 27 Mar 2026 12:07:29 +0800 Subject: mmc: core: Remove legacy 'enable-sdio-wakeup' DT property support The 'enable-sdio-wakeup' device tree property was marked as legacy and superseded by the standard 'wakeup-source' property in commit 71a0151c5c82 ("Documentation: devicetree: fix reference to legacy wakeup properties") back in 2015. Since it has been a decade and the migration to the standard property has long been completed, remove this obsolete legacy support. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index a457c88fdcbc..4e1514bda65b 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -379,8 +379,7 @@ int mmc_of_parse(struct mmc_host *host) host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND; if (device_property_read_bool(dev, "keep-power-in-suspend")) host->pm_caps |= MMC_PM_KEEP_POWER; - if (device_property_read_bool(dev, "wakeup-source") || - device_property_read_bool(dev, "enable-sdio-wakeup")) /* legacy */ + if (device_property_read_bool(dev, "wakeup-source")) host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; if (device_property_read_bool(dev, "mmc-ddr-3_3v")) host->caps |= MMC_CAP_3_3V_DDR; -- cgit v1.2.3 From 0aa7a5723c96d23a21291b5683ce90a13e2d3046 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Fri, 27 Mar 2026 12:11:23 +0800 Subject: mmc: core: Switch to use pm_ptr() for mmc_host_class_dev_pm_ops Currently, the mmc_host_class_dev_pm_ops and its callback functions are wrapped in #ifdef CONFIG_PM_SLEEP to handle the conditional compilation when PM support is disabled. Replace this #ifdef usage with the standard pm_ptr() helpers. This allows the compiler to automatically optimize away the unused code paths when CONFIG_PM_SLEEP is not selected, resulting in cleaner and more maintainable code. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/host.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 4e1514bda65b..b7ce3137d452 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -33,7 +33,6 @@ static DEFINE_IDA(mmc_host_ida); -#ifdef CONFIG_PM_SLEEP static int mmc_host_class_prepare(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); @@ -60,15 +59,10 @@ static void mmc_host_class_complete(struct device *dev) } static const struct dev_pm_ops mmc_host_class_dev_pm_ops = { - .prepare = mmc_host_class_prepare, - .complete = mmc_host_class_complete, + .prepare = pm_sleep_ptr(mmc_host_class_prepare), + .complete = pm_sleep_ptr(mmc_host_class_complete), }; -#define MMC_HOST_CLASS_DEV_PM_OPS (&mmc_host_class_dev_pm_ops) -#else -#define MMC_HOST_CLASS_DEV_PM_OPS NULL -#endif - static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); @@ -90,7 +84,7 @@ static const struct class mmc_host_class = { .name = "mmc_host", .dev_release = mmc_host_classdev_release, .shutdown_pre = mmc_host_classdev_shutdown, - .pm = MMC_HOST_CLASS_DEV_PM_OPS, + .pm = pm_ptr(&mmc_host_class_dev_pm_ops), }; int mmc_register_host_class(void) -- cgit v1.2.3 From 257373c25ed7115f824588f20b3363d9e95640c4 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Sat, 28 Mar 2026 08:29:22 +0800 Subject: mmc: sdhci-dwcmshc: Refactor Rockchip platform data for controller revisions The driver previously used an enum (dwcmshc_rk_type) to distinguish platforms like the RK3568 and RK3588 based on DT compatible names. This approach is inflexible, scales poorly for future revisions or mixed-revision platforms, and conflates SoC names with controller revisions. One example is RK3576 which lists "rockchip,rk3588-dwcmshc" as a secondary compatible string just in order to claim it uses the same controller revision as RK3588. This is confusing and makes it error-prone to add new SoC support. Introduces a new struct rockchip_pltfm_data containing a dedicated revision field. The old enum is removed, and all code paths are updated to use the revision-based data. Signed-off-by: Shawn Lin Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 92 +++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index a91b3e7e4d16..6139516c6488 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -278,14 +278,8 @@ #define PHY_DELAY_CODE_EMMC 0x17 #define PHY_DELAY_CODE_SD 0x55 -enum dwcmshc_rk_type { - DWCMSHC_RK3568, - DWCMSHC_RK3588, -}; - struct rk35xx_priv { struct reset_control *reset; - enum dwcmshc_rk_type devtype; u8 txclk_tapnum; }; @@ -330,6 +324,16 @@ struct k230_pltfm_data { u32 write_prot_bit; }; +struct rockchip_pltfm_data { + struct dwcmshc_pltfm_data dwcmshc_pdata; + /* + * The controller hardware has two known revisions documented internally: + * - Revision 0: Exclusively used by RK3566 and RK3568 SoCs. + * - Revision 1: Implemented in all other Rockchip SoCs, including RK3576, RK3588, etc. + */ + int revision; +}; + static void dwcmshc_enable_card_clk(struct sdhci_host *host) { u16 ctrl; @@ -749,6 +753,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); struct rk35xx_priv *priv = dwc_priv->priv; + const struct rockchip_pltfm_data *rockchip_pdata = to_pltfm_data(dwc_priv, rockchip); u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT; u32 extra, reg; int err; @@ -816,7 +821,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock * we must set it in higher speed mode. */ extra = DWCMSHC_EMMC_DLL_DLYENA; - if (priv->devtype == DWCMSHC_RK3568) + if (rockchip_pdata->revision == 0) extra |= DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); @@ -842,7 +847,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock host->mmc->ios.timing == MMC_TIMING_MMC_HS400) txclk_tapnum = priv->txclk_tapnum; - if ((priv->devtype == DWCMSHC_RK3588) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { + if (rockchip_pdata->revision == 1 && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES; extra = DLL_CMDOUT_SRC_CLK_NEG | @@ -898,11 +903,6 @@ static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host, if (!priv) return -ENOMEM; - if (of_device_is_compatible(dev->of_node, "rockchip,rk3588-dwcmshc")) - priv->devtype = DWCMSHC_RK3588; - else - priv->devtype = DWCMSHC_RK3568; - priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc)); if (IS_ERR(priv->reset)) { err = PTR_ERR(priv->reset); @@ -2115,30 +2115,52 @@ static const struct cqhci_host_ops rk35xx_cqhci_ops = { .set_tran_desc = dwcmshc_set_tran_desc, }; -static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { - .pdata = { - .ops = &sdhci_dwcmshc_rk35xx_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, +static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3568_pdata = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .cqhci_host_ops = &rk35xx_cqhci_ops, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk35xx_postinit, }, - .cqhci_host_ops = &rk35xx_cqhci_ops, - .init = dwcmshc_rk35xx_init, - .postinit = dwcmshc_rk35xx_postinit, + .revision = 0, }; -static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk3576_pdata = { - .pdata = { - .ops = &sdhci_dwcmshc_rk35xx_ops, - .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, - .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | - SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, +static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3576_pdata = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .cqhci_host_ops = &rk35xx_cqhci_ops, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk3576_postinit, + }, + .revision = 1, +}; + +static const struct rockchip_pltfm_data sdhci_dwcmshc_rk3588_pdata = { + .dwcmshc_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, + }, + .cqhci_host_ops = &rk35xx_cqhci_ops, + .init = dwcmshc_rk35xx_init, + .postinit = dwcmshc_rk35xx_postinit, }, - .cqhci_host_ops = &rk35xx_cqhci_ops, - .init = dwcmshc_rk35xx_init, - .postinit = dwcmshc_rk3576_postinit, + .revision = 1, }; static const struct dwcmshc_pltfm_data sdhci_dwcmshc_th1520_pdata = { @@ -2309,7 +2331,7 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { }, { .compatible = "rockchip,rk3588-dwcmshc", - .data = &sdhci_dwcmshc_rk35xx_pdata, + .data = &sdhci_dwcmshc_rk3588_pdata, }, { .compatible = "rockchip,rk3576-dwcmshc", @@ -2317,7 +2339,7 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { }, { .compatible = "rockchip,rk3568-dwcmshc", - .data = &sdhci_dwcmshc_rk35xx_pdata, + .data = &sdhci_dwcmshc_rk3568_pdata, }, { .compatible = "snps,dwcmshc-sdhci", -- cgit v1.2.3 From a38ad7e173bc68c54f7afae7f669a824e313b1bb Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 30 Mar 2026 11:28:29 +0800 Subject: mmc: core: Replace the hard-coded shift value 9 with SECTOR_SHIFT These shift-by-9 operations are for converting between bytes and sectors. Use the SECTOR_SHIFT macro to improve code readability and maintainability. No functional changes intended. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 8 ++++---- drivers/mmc/core/core.c | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index db8c99c73a61..5032f7a8ead0 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1458,7 +1458,7 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq, * sectors can be read successfully. */ if (recovery_mode) - brq->data.blocks = queue_physical_block_size(mq->queue) >> 9; + brq->data.blocks = queue_physical_block_size(mq->queue) >> SECTOR_SHIFT; /* * Some controllers have HW issues while operating @@ -1976,7 +1976,7 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req) } if (rq_data_dir(req) == READ && brq->data.blocks > - queue_physical_block_size(mq->queue) >> 9) { + queue_physical_block_size(mq->queue) >> SECTOR_SHIFT) { /* Read one (native) sector at a time */ mmc_blk_read_single(mq, req); return; @@ -3025,14 +3025,14 @@ static int mmc_blk_alloc_parts(struct mmc_card *card, struct mmc_blk_data *md) */ ret = mmc_blk_alloc_rpmb_part(card, md, card->part[idx].part_cfg, - card->part[idx].size >> 9, + card->part[idx].size >> SECTOR_SHIFT, card->part[idx].name); if (ret) return ret; } else if (card->part[idx].size) { ret = mmc_blk_alloc_part(card, md, card->part[idx].part_cfg, - card->part[idx].size >> 9, + card->part[idx].size >> SECTOR_SHIFT, card->part[idx].force_ro, card->part[idx].name, card->part[idx].area_type); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 860378bea557..29e80e5f928e 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -97,7 +97,8 @@ static void mmc_should_fail_request(struct mmc_host *host, return; data->error = data_errors[get_random_u32_below(ARRAY_SIZE(data_errors))]; - data->bytes_xfered = get_random_u32_below(data->bytes_xfered >> 9) << 9; + data->bytes_xfered = get_random_u32_below(data->bytes_xfered >> SECTOR_SHIFT) + << SECTOR_SHIFT; } #else /* CONFIG_FAIL_MMC_REQUEST */ -- cgit v1.2.3 From 554b0674f22feb76d9198f2f1fa23b610f548548 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 30 Mar 2026 11:28:30 +0800 Subject: mmc: block: Convert to use DEFINE_SIMPLE_DEV_PM_OPS() Convert to use DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() to drop the CONFIG_PM_SLEEP to handle the conditional compilation when PM support is disabled. This allows the compiler to automatically optimize away the unused code paths when CONFIG_PM_SLEEP is not selected. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 5032f7a8ead0..53c1b04f1916 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -3362,7 +3362,6 @@ static void mmc_blk_shutdown(struct mmc_card *card) _mmc_blk_suspend(card); } -#ifdef CONFIG_PM_SLEEP static int mmc_blk_suspend(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); @@ -3388,14 +3387,13 @@ static int mmc_blk_resume(struct device *dev) } return 0; } -#endif -static SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(mmc_blk_pm_ops, mmc_blk_suspend, mmc_blk_resume); static struct mmc_driver mmc_driver = { .drv = { .name = "mmcblk", - .pm = &mmc_blk_pm_ops, + .pm = pm_sleep_ptr(&mmc_blk_pm_ops), }, .probe = mmc_blk_probe, .remove = mmc_blk_remove, -- cgit v1.2.3 From 058dbcf3ec2446a4deb094e0e22f2c345cd9816e Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 30 Mar 2026 11:28:31 +0800 Subject: mmc: mmc_test: Replace hard-coded values with macros and consolidate test parameters Replacing hard-coded values with standardized macros to improve code clarity, simplify future maintenance. Meanwhile, introduce global bs and sg_len arrays for block sizes and SG lengths, eliminating redundant local definitions in multiple test functions. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc_test.c | 83 ++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 47 deletions(-) diff --git a/drivers/mmc/core/mmc_test.c b/drivers/mmc/core/mmc_test.c index f5c542855037..ab38e4c45a8d 100644 --- a/drivers/mmc/core/mmc_test.c +++ b/drivers/mmc/core/mmc_test.c @@ -37,7 +37,7 @@ * Limit the test area size to the maximum MMC HC erase group size. Note that * the maximum SD allocation unit size is just 4MiB. */ -#define TEST_AREA_MAX_SIZE (128 * 1024 * 1024) +#define TEST_AREA_MAX_SIZE SZ_128M /** * struct mmc_test_pages - pages allocated by 'alloc_pages()'. @@ -169,6 +169,11 @@ struct mmc_test_multiple_rw { enum mmc_test_prep_media prepare; }; +static unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, + 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; + +static unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, + 1 << 7, 1 << 8, 1 << 9}; /*******************************************************************/ /* General helper functions */ /*******************************************************************/ @@ -502,7 +507,7 @@ static unsigned int mmc_test_rate(uint64_t bytes, struct timespec64 *ts) uint64_t ns; ns = timespec64_to_ns(ts); - bytes *= 1000000000; + bytes *= NSEC_PER_SEC; while (ns > UINT_MAX) { bytes >>= 1; @@ -548,7 +553,7 @@ static void mmc_test_save_transfer_result(struct mmc_test_card *test, static void mmc_test_print_rate(struct mmc_test_card *test, uint64_t bytes, struct timespec64 *ts1, struct timespec64 *ts2) { - unsigned int rate, iops, sectors = bytes >> 9; + unsigned int rate, iops, sectors = bytes >> SECTOR_SHIFT; struct timespec64 ts; ts = timespec64_sub(*ts2, *ts1); @@ -573,7 +578,7 @@ static void mmc_test_print_avg_rate(struct mmc_test_card *test, uint64_t bytes, unsigned int count, struct timespec64 *ts1, struct timespec64 *ts2) { - unsigned int rate, iops, sectors = bytes >> 9; + unsigned int rate, iops, sectors = bytes >> SECTOR_SHIFT; uint64_t tot = bytes * count; struct timespec64 ts; @@ -1374,7 +1379,7 @@ static int mmc_test_area_map(struct mmc_test_card *test, unsigned long sz, int err; unsigned int sg_len = 0; - t->blocks = sz >> 9; + t->blocks = sz >> SECTOR_SHIFT; if (max_scatter) { err = mmc_test_map_sg_max_scatter(t->mem, sz, t->sg, @@ -1457,7 +1462,7 @@ static int mmc_test_area_io_seq(struct mmc_test_card *test, unsigned long sz, else for (i = 0; i < count && ret == 0; i++) { ret = mmc_test_area_transfer(test, dev_addr, write); - dev_addr += sz >> 9; + dev_addr += sz >> SECTOR_SHIFT; } if (ret) @@ -1500,7 +1505,7 @@ static int mmc_test_area_erase(struct mmc_test_card *test) if (!mmc_card_can_erase(test->card)) return 0; - return mmc_erase(test->card, t->dev_addr, t->max_sz >> 9, + return mmc_erase(test->card, t->dev_addr, t->max_sz >> SECTOR_SHIFT, MMC_ERASE_ARG); } @@ -1528,7 +1533,7 @@ static int mmc_test_area_cleanup(struct mmc_test_card *test) static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) { struct mmc_test_area *t = &test->area; - unsigned long min_sz = 64 * 1024, sz; + unsigned long min_sz = SZ_64K, sz; int ret; ret = mmc_test_set_blksize(test, 512); @@ -1536,9 +1541,9 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) return ret; /* Make the test area size about 4MiB */ - sz = (unsigned long)test->card->pref_erase << 9; + sz = (unsigned long)test->card->pref_erase << SECTOR_SHIFT; t->max_sz = sz; - while (t->max_sz < 4 * 1024 * 1024) + while (t->max_sz < SZ_4M) t->max_sz += sz; while (t->max_sz > TEST_AREA_MAX_SIZE && t->max_sz > sz) t->max_sz -= sz; @@ -1548,8 +1553,8 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) t->max_seg_sz -= t->max_seg_sz % 512; t->max_tfr = t->max_sz; - if (t->max_tfr >> 9 > test->card->host->max_blk_count) - t->max_tfr = test->card->host->max_blk_count << 9; + if (t->max_tfr >> SECTOR_SHIFT > test->card->host->max_blk_count) + t->max_tfr = test->card->host->max_blk_count << SECTOR_SHIFT; if (t->max_tfr > test->card->host->max_req_size) t->max_tfr = test->card->host->max_req_size; if (t->max_tfr / t->max_seg_sz > t->max_segs) @@ -1579,7 +1584,7 @@ static int mmc_test_area_init(struct mmc_test_card *test, int erase, int fill) } t->dev_addr = mmc_test_capacity(test->card) / 2; - t->dev_addr -= t->dev_addr % (t->max_sz >> 9); + t->dev_addr -= t->dev_addr % (t->max_sz >> SECTOR_SHIFT); if (erase) { ret = mmc_test_area_erase(test); @@ -1684,7 +1689,7 @@ static int mmc_test_profile_read_perf(struct mmc_test_card *test) int ret; for (sz = 512; sz < t->max_tfr; sz <<= 1) { - dev_addr = t->dev_addr + (sz >> 9); + dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT); ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 1); if (ret) return ret; @@ -1708,7 +1713,7 @@ static int mmc_test_profile_write_perf(struct mmc_test_card *test) if (ret) return ret; for (sz = 512; sz < t->max_tfr; sz <<= 1) { - dev_addr = t->dev_addr + (sz >> 9); + dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT); ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 1); if (ret) return ret; @@ -1739,9 +1744,9 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test) return RESULT_UNSUP_HOST; for (sz = 512; sz < t->max_sz; sz <<= 1) { - dev_addr = t->dev_addr + (sz >> 9); + dev_addr = t->dev_addr + (sz >> SECTOR_SHIFT); ktime_get_ts64(&ts1); - ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG); + ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG); if (ret) return ret; ktime_get_ts64(&ts2); @@ -1749,7 +1754,7 @@ static int mmc_test_profile_trim_perf(struct mmc_test_card *test) } dev_addr = t->dev_addr; ktime_get_ts64(&ts1); - ret = mmc_erase(test->card, dev_addr, sz >> 9, MMC_TRIM_ARG); + ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG); if (ret) return ret; ktime_get_ts64(&ts2); @@ -1771,7 +1776,7 @@ static int mmc_test_seq_read_perf(struct mmc_test_card *test, unsigned long sz) ret = mmc_test_area_io(test, sz, dev_addr, 0, 0, 0); if (ret) return ret; - dev_addr += (sz >> 9); + dev_addr += (sz >> SECTOR_SHIFT); } ktime_get_ts64(&ts2); mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2); @@ -1813,7 +1818,7 @@ static int mmc_test_seq_write_perf(struct mmc_test_card *test, unsigned long sz) ret = mmc_test_area_io(test, sz, dev_addr, 1, 0, 0); if (ret) return ret; - dev_addr += (sz >> 9); + dev_addr += (sz >> SECTOR_SHIFT); } ktime_get_ts64(&ts2); mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2); @@ -1866,11 +1871,11 @@ static int mmc_test_profile_seq_trim_perf(struct mmc_test_card *test) dev_addr = t->dev_addr; ktime_get_ts64(&ts1); for (i = 0; i < cnt; i++) { - ret = mmc_erase(test->card, dev_addr, sz >> 9, + ret = mmc_erase(test->card, dev_addr, sz >> SECTOR_SHIFT, MMC_TRIM_ARG); if (ret) return ret; - dev_addr += (sz >> 9); + dev_addr += (sz >> SECTOR_SHIFT); } ktime_get_ts64(&ts2); mmc_test_print_avg_rate(test, sz, cnt, &ts1, &ts2); @@ -1897,7 +1902,7 @@ static int mmc_test_rnd_perf(struct mmc_test_card *test, int write, int print, struct timespec64 ts1, ts2, ts; int ret; - ssz = sz >> 9; + ssz = sz >> SECTOR_SHIFT; rnd_addr = mmc_test_capacity(test->card) / 4; range1 = rnd_addr / test->card->pref_erase; @@ -2013,10 +2018,10 @@ static int mmc_test_seq_perf(struct mmc_test_card *test, int write, sz = max_tfr; } - ssz = sz >> 9; + ssz = sz >> SECTOR_SHIFT; dev_addr = mmc_test_capacity(test->card) / 4; - if (tot_sz > dev_addr << 9) - tot_sz = dev_addr << 9; + if (tot_sz > dev_addr << SECTOR_SHIFT) + tot_sz = dev_addr << SECTOR_SHIFT; cnt = tot_sz / sz; dev_addr &= 0xffff0000; /* Round to 64MiB boundary */ @@ -2040,17 +2045,17 @@ static int mmc_test_large_seq_perf(struct mmc_test_card *test, int write) int ret, i; for (i = 0; i < 10; i++) { - ret = mmc_test_seq_perf(test, write, 10 * 1024 * 1024, 1); + ret = mmc_test_seq_perf(test, write, 10 * SZ_1M, 1); if (ret) return ret; } for (i = 0; i < 5; i++) { - ret = mmc_test_seq_perf(test, write, 100 * 1024 * 1024, 1); + ret = mmc_test_seq_perf(test, write, 100 * SZ_1M, 1); if (ret) return ret; } for (i = 0; i < 3; i++) { - ret = mmc_test_seq_perf(test, write, 1000 * 1024 * 1024, 1); + ret = mmc_test_seq_perf(test, write, 1000 * SZ_1M, 1); if (ret) return ret; } @@ -2153,7 +2158,7 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test, int i; for (i = 0 ; i < rw->len && ret == 0; i++) { - ret = mmc_test_rw_multiple(test, rw, 512 * 1024, rw->size, + ret = mmc_test_rw_multiple(test, rw, SZ_512K, rw->size, rw->sg_len[i]); if (ret) break; @@ -2166,8 +2171,6 @@ static int mmc_test_rw_multiple_sg_len(struct mmc_test_card *test, */ static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test) { - unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, - 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; struct mmc_test_multiple_rw test_data = { .bs = bs, .size = TEST_AREA_MAX_SIZE, @@ -2185,8 +2188,6 @@ static int mmc_test_profile_mult_write_blocking_perf(struct mmc_test_card *test) */ static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test) { - unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, - 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; struct mmc_test_multiple_rw test_data = { .bs = bs, .size = TEST_AREA_MAX_SIZE, @@ -2204,8 +2205,6 @@ static int mmc_test_profile_mult_write_nonblock_perf(struct mmc_test_card *test) */ static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test) { - unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, - 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; struct mmc_test_multiple_rw test_data = { .bs = bs, .size = TEST_AREA_MAX_SIZE, @@ -2223,8 +2222,6 @@ static int mmc_test_profile_mult_read_blocking_perf(struct mmc_test_card *test) */ static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test) { - unsigned int bs[] = {1 << 12, 1 << 13, 1 << 14, 1 << 15, 1 << 16, - 1 << 17, 1 << 18, 1 << 19, 1 << 20, 1 << 22}; struct mmc_test_multiple_rw test_data = { .bs = bs, .size = TEST_AREA_MAX_SIZE, @@ -2242,8 +2239,6 @@ static int mmc_test_profile_mult_read_nonblock_perf(struct mmc_test_card *test) */ static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test) { - unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, - 1 << 7, 1 << 8, 1 << 9}; struct mmc_test_multiple_rw test_data = { .sg_len = sg_len, .size = TEST_AREA_MAX_SIZE, @@ -2261,8 +2256,6 @@ static int mmc_test_profile_sglen_wr_blocking_perf(struct mmc_test_card *test) */ static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test) { - unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, - 1 << 7, 1 << 8, 1 << 9}; struct mmc_test_multiple_rw test_data = { .sg_len = sg_len, .size = TEST_AREA_MAX_SIZE, @@ -2280,8 +2273,6 @@ static int mmc_test_profile_sglen_wr_nonblock_perf(struct mmc_test_card *test) */ static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test) { - unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, - 1 << 7, 1 << 8, 1 << 9}; struct mmc_test_multiple_rw test_data = { .sg_len = sg_len, .size = TEST_AREA_MAX_SIZE, @@ -2299,8 +2290,6 @@ static int mmc_test_profile_sglen_r_blocking_perf(struct mmc_test_card *test) */ static int mmc_test_profile_sglen_r_nonblock_perf(struct mmc_test_card *test) { - unsigned int sg_len[] = {1, 1 << 3, 1 << 4, 1 << 5, 1 << 6, - 1 << 7, 1 << 8, 1 << 9}; struct mmc_test_multiple_rw test_data = { .sg_len = sg_len, .size = TEST_AREA_MAX_SIZE, @@ -2452,7 +2441,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test, if (ret) goto out_free; - if (repeat_cmd && (t->blocks + 1) << 9 > t->max_tfr) + if (repeat_cmd && (t->blocks + 1) << SECTOR_SHIFT > t->max_tfr) pr_info("%s: %d commands completed during transfer of %u blocks\n", mmc_hostname(test->card->host), count, t->blocks); -- cgit v1.2.3 From d34124edffdbd88ce21ed49c84d984b1c34e4670 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Mon, 30 Mar 2026 11:28:32 +0800 Subject: mmc: block: Use MQRQ_XFER_SINGLE_BLOCK for both read and write recovery Currently, the code uses the MQRQ_XFER_SINGLE_BLOCK flag to handle write failures by retrying with single-block transfers. However, read failures bypass this mechanism and instead use a dedicated legacy path mmc_blk_read_single() that performs sector-by-sector retries. Extend the MQRQ_XFER_SINGLE_BLOCK logic to cover multi-block read failures as well. By doing so, we can remove the redundant and complex mmc_blk_read_single() function, unifying the retry logic for both read and write operations under a single, consistent, easier-to-maintain mechanism. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/block.c | 70 ++---------------------------------------------- 1 file changed, 2 insertions(+), 68 deletions(-) diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index 53c1b04f1916..0274e8d07660 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -1544,8 +1544,7 @@ static void mmc_blk_cqe_complete_rq(struct mmc_queue *mq, struct request *req) if (err) { if (mqrq->retries++ < MMC_CQE_RETRIES) { - if (rq_data_dir(req) == WRITE) - mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK; + mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK; blk_mq_requeue_request(req, true); } else { blk_mq_end_request(req, BLK_STS_IOERR); @@ -1782,63 +1781,6 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req) return err; } -#define MMC_READ_SINGLE_RETRIES 2 - -/* Single (native) sector read during recovery */ -static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req) -{ - struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req); - struct mmc_request *mrq = &mqrq->brq.mrq; - struct mmc_card *card = mq->card; - struct mmc_host *host = card->host; - blk_status_t error = BLK_STS_OK; - size_t bytes_per_read = queue_physical_block_size(mq->queue); - - do { - u32 status; - int err; - int retries = 0; - - while (retries++ <= MMC_READ_SINGLE_RETRIES) { - mmc_blk_rw_rq_prep(mqrq, card, 1, mq); - - mmc_wait_for_req(host, mrq); - - err = mmc_send_status(card, &status); - if (err) - goto error_exit; - - if (!mmc_host_is_spi(host) && - !mmc_ready_for_data(status)) { - err = mmc_blk_fix_state(card, req); - if (err) - goto error_exit; - } - - if (!mrq->cmd->error) - break; - } - - if (mrq->cmd->error || - mrq->data->error || - (!mmc_host_is_spi(host) && - (mrq->cmd->resp[0] & CMD_ERRORS || status & CMD_ERRORS))) - error = BLK_STS_IOERR; - else - error = BLK_STS_OK; - - } while (blk_update_request(req, error, bytes_per_read)); - - return; - -error_exit: - mrq->data->bytes_xfered = 0; - blk_update_request(req, BLK_STS_IOERR, bytes_per_read); - /* Let it try the remaining request again */ - if (mqrq->retries > MMC_MAX_RETRIES - 1) - mqrq->retries = MMC_MAX_RETRIES - 1; -} - static inline bool mmc_blk_oor_valid(struct mmc_blk_request *brq) { return !!brq->mrq.sbc; @@ -1974,13 +1916,6 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req) mqrq->retries = MMC_MAX_RETRIES - MMC_DATA_RETRIES; return; } - - if (rq_data_dir(req) == READ && brq->data.blocks > - queue_physical_block_size(mq->queue) >> SECTOR_SHIFT) { - /* Read one (native) sector at a time */ - mmc_blk_read_single(mq, req); - return; - } } static inline bool mmc_blk_rq_error(struct mmc_blk_request *brq) @@ -2091,8 +2026,7 @@ static void mmc_blk_mq_complete_rq(struct mmc_queue *mq, struct request *req) } else if (!blk_rq_bytes(req)) { __blk_mq_end_request(req, BLK_STS_IOERR); } else if (mqrq->retries++ < MMC_MAX_RETRIES) { - if (rq_data_dir(req) == WRITE) - mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK; + mqrq->flags |= MQRQ_XFER_SINGLE_BLOCK; blk_mq_requeue_request(req, true); } else { if (mmc_card_removed(mq->card)) -- cgit v1.2.3 From 0801cebde7b9a65c3ec081f4c7220bbfc1e351b9 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 31 Mar 2026 15:54:49 +0800 Subject: mmc: dw_mmc: Inline dw_mci_queue_request() into dw_mci_request() With the removal of queue support, the function dw_mci_queue_request() is now just a wrapper with a confusing name that doesn't suggest anything about queue. Removes the function and moves its body into dw_mci_request(). No functional changes intended. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 46 +++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 27 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index bbdd7594ef01..8e78faf6bb35 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1293,31 +1293,6 @@ static void dw_mci_start_request(struct dw_mci *host) __dw_mci_start_request(host, cmd); } -/* must be called with host->lock held */ -static void dw_mci_queue_request(struct dw_mci *host, struct mmc_request *mrq) -{ - dev_vdbg(&host->mmc->class_dev, "queue request: state=%d\n", - host->state); - - host->mrq = mrq; - - if (host->state == STATE_WAITING_CMD11_DONE) { - dev_warn(&host->mmc->class_dev, - "Voltage change didn't complete\n"); - /* - * this case isn't expected to happen, so we can - * either crash here or just try to continue on - * in the closest possible state - */ - host->state = STATE_IDLE; - } - - if (host->state == STATE_IDLE) { - host->state = STATE_SENDING_CMD; - dw_mci_start_request(host); - } -} - static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct dw_mci *host = mmc_priv(mmc); @@ -1329,7 +1304,6 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) * atomic, otherwise the card could be removed in between and the * request wouldn't fail until another card was inserted. */ - if (!dw_mci_get_cd(mmc)) { mrq->cmd->error = -ENOMEDIUM; mmc_request_done(mmc, mrq); @@ -1338,7 +1312,25 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) spin_lock_bh(&host->lock); - dw_mci_queue_request(host, mrq); + dev_vdbg(&host->mmc->class_dev, "request: state=%d\n", + host->state); + + host->mrq = mrq; + if (host->state == STATE_WAITING_CMD11_DONE) { + dev_warn(&host->mmc->class_dev, + "Voltage change didn't complete\n"); + /* + * this case isn't expected to happen, so we can + * either crash here or just try to continue on + * in the closest possible state + */ + host->state = STATE_IDLE; + } + + if (host->state == STATE_IDLE) { + host->state = STATE_SENDING_CMD; + dw_mci_start_request(host); + } spin_unlock_bh(&host->lock); } -- cgit v1.2.3 From e89e7d2fff89ac5b7da5521c239e1417d3977af3 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Tue, 31 Mar 2026 15:54:50 +0800 Subject: mmc: dw_mmc: Remove dw_mci_start_request wrapper and rename core function The function dw_mci_start_request() was just a thin wrapper around __dw_mci_start_request(). Since it serves almost no functional purpose, remove the wrapper to simplify the code flow. Consequently, rename __dw_mci_start_request() to dw_mci_start_request() so that the core implementation uses the primary name. Signed-off-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 8e78faf6bb35..20193ee7b73e 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1224,8 +1224,7 @@ static void dw_mci_set_data_timeout(struct dw_mci *host, timeout_ns, tmout >> 8); } -static void __dw_mci_start_request(struct dw_mci *host, - struct mmc_command *cmd) +static void dw_mci_start_request(struct dw_mci *host, struct mmc_command *cmd) { struct mmc_request *mrq; struct mmc_data *data; @@ -1284,18 +1283,10 @@ static void __dw_mci_start_request(struct dw_mci *host, host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); } -static void dw_mci_start_request(struct dw_mci *host) -{ - struct mmc_request *mrq = host->mrq; - struct mmc_command *cmd; - - cmd = mrq->sbc ? mrq->sbc : mrq->cmd; - __dw_mci_start_request(host, cmd); -} - static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct dw_mci *host = mmc_priv(mmc); + struct mmc_command *cmd; WARN_ON(host->mrq); @@ -1329,7 +1320,8 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) if (host->state == STATE_IDLE) { host->state = STATE_SENDING_CMD; - dw_mci_start_request(host); + cmd = mrq->sbc ? mrq->sbc : mrq->cmd; + dw_mci_start_request(host, cmd); } spin_unlock_bh(&host->lock); @@ -1949,7 +1941,7 @@ static void dw_mci_work_func(struct work_struct *t) set_bit(EVENT_CMD_COMPLETE, &host->completed_events); err = dw_mci_command_complete(host, cmd); if (cmd == mrq->sbc && !err) { - __dw_mci_start_request(host, mrq->cmd); + dw_mci_start_request(host, mrq->cmd); goto unlock; } -- cgit v1.2.3 From 5b8b35d6f4fa758dd5e8ae18526ea1c73f6787e0 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2026 11:52:07 +0100 Subject: mmc: vub300: rename probe error labels Error labels should be named after what they do. Rename the probe error labels. Signed-off-by: Johan Hovold Signed-off-by: Ulf Hansson --- drivers/mmc/host/vub300.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 3c9df27f9fa7..3057a69ff8c4 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2107,19 +2107,19 @@ static int vub300_probe(struct usb_interface *interface, command_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_out_urb) { retval = -ENOMEM; - goto error0; + goto err_put_udev; } command_res_urb = usb_alloc_urb(0, GFP_KERNEL); if (!command_res_urb) { retval = -ENOMEM; - goto error1; + goto err_free_out_urb; } /* this also allocates memory for our VUB300 mmc host device */ mmc = mmc_alloc_host(sizeof(*vub300), &udev->dev); if (!mmc) { retval = -ENOMEM; dev_err(&udev->dev, "not enough memory for the mmc_host\n"); - goto error4; + goto err_free_res_urb; } /* MMC core transfer sizes tunable parameters */ mmc->caps = 0; @@ -2336,10 +2336,11 @@ static int vub300_probe(struct usb_interface *interface, interface_to_InterfaceNumber(interface)); retval = mmc_add_host(mmc); if (retval) - goto error6; + goto err_delete_timer; return 0; -error6: + +err_delete_timer: timer_delete_sync(&vub300->inactivity_timer); err_free_host: mmc_free_host(mmc); @@ -2347,12 +2348,13 @@ err_free_host: * and hence also frees vub300 * which is contained at the end of struct mmc */ -error4: +err_free_res_urb: usb_free_urb(command_res_urb); -error1: +err_free_out_urb: usb_free_urb(command_out_urb); -error0: +err_put_udev: usb_put_dev(udev); + return retval; } -- cgit v1.2.3 From f924224650e6e8fc79609218c5cf67d5988d733a Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 27 Mar 2026 11:52:08 +0100 Subject: mmc: vub300: clean up module init Clean up module init by dropping redundant error messages (e.g. allocation and USB driver registration failure will already have been logged) and naming error labels after what they do. Signed-off-by: Johan Hovold Signed-off-by: Ulf Hansson --- drivers/mmc/host/vub300.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index 3057a69ff8c4..6c3cb2f1c9d3 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2429,37 +2429,36 @@ static int __init vub300_init(void) pr_info("VUB300 Driver rom wait states = %02X irqpoll timeout = %04X", firmware_rom_wait_states, 0x0FFFF & firmware_irqpoll_timeout); + cmndworkqueue = create_singlethread_workqueue("kvub300c"); - if (!cmndworkqueue) { - pr_err("not enough memory for the REQUEST workqueue"); - result = -ENOMEM; - goto out1; - } + if (!cmndworkqueue) + return -ENOMEM; + pollworkqueue = create_singlethread_workqueue("kvub300p"); if (!pollworkqueue) { - pr_err("not enough memory for the IRQPOLL workqueue"); result = -ENOMEM; - goto out2; + goto err_destroy_cmdwq; } + deadworkqueue = create_singlethread_workqueue("kvub300d"); if (!deadworkqueue) { - pr_err("not enough memory for the EXPIRED workqueue"); result = -ENOMEM; - goto out3; + goto err_destroy_pollwq; } + result = usb_register(&vub300_driver); - if (result) { - pr_err("usb_register failed. Error number %d", result); - goto out4; - } + if (result) + goto err_destroy_deadwq; + return 0; -out4: + +err_destroy_deadwq: destroy_workqueue(deadworkqueue); -out3: +err_destroy_pollwq: destroy_workqueue(pollworkqueue); -out2: +err_destroy_cmdwq: destroy_workqueue(cmndworkqueue); -out1: + return result; } -- cgit v1.2.3 From 5fa0e32ed4f4a8a62998d0c955311cbfe92919af Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Thu, 2 Apr 2026 07:31:55 -0500 Subject: mmc: core: Optimize size of struct mmc_queue_req ioc_count won't be more than MMC_IOC_MAX_CMDS (255), retries won't be more than MMC_NO_RETRIES (6), flags is newly introduced and uses only 1 bit. Therefore let's change them all to become u8. Signed-off-by: Bin Liu Signed-off-by: Ulf Hansson --- drivers/mmc/core/queue.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/core/queue.h b/drivers/mmc/core/queue.h index c254e6580afd..dd7211e3a6d5 100644 --- a/drivers/mmc/core/queue.h +++ b/drivers/mmc/core/queue.h @@ -69,9 +69,9 @@ struct mmc_queue_req { enum mmc_drv_op drv_op; int drv_op_result; void *drv_op_data; - unsigned int ioc_count; - int retries; - u32 flags; + u8 ioc_count; + u8 retries; + u8 flags; }; struct mmc_queue { -- cgit v1.2.3 From 873cc5560804f5270b1670f8bed4d55400343cf5 Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Tue, 7 Apr 2026 09:40:33 +0800 Subject: mmc: core: Simplify with scoped for each OF child loop Use scoped for-each loop when iterating over device nodes to simplify the code, but also to ensure the device node reference is automatically released when the loop scope ends. Signed-off-by: Hans Zhang <18255117159@163.com> Reviewed-by: Shawn Lin Signed-off-by: Ulf Hansson --- drivers/mmc/core/quirks.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index 6f727b4a60a5..940549d3b95d 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -225,14 +225,9 @@ static const struct mmc_fixup __maybe_unused sdio_card_init_methods[] = { static inline bool mmc_fixup_of_compatible_match(struct mmc_card *card, const char *compatible) { - struct device_node *np; - - for_each_child_of_node(mmc_dev(card->host)->of_node, np) { - if (of_device_is_compatible(np, compatible)) { - of_node_put(np); + for_each_child_of_node_scoped(mmc_dev(card->host)->of_node, np) + if (of_device_is_compatible(np, compatible)) return true; - } - } return false; } -- cgit v1.2.3 From 6546a49bbe656981d99a389195560999058c89c4 Mon Sep 17 00:00:00 2001 From: Shawn Lin Date: Wed, 8 Apr 2026 15:18:49 +0800 Subject: mmc: sdhci-of-dwcmshc: Disable clock before DLL configuration According to the ASIC design recommendations, the clock must be disabled before operating the DLL to prevent glitches that could affect the internal digital logic. In extreme cases, failing to do so may cause the controller to malfunction completely. Adds a step to disable the clock before DLL configuration and re-enables it at the end. Fixes: 08f3dff799d4 ("mmc: sdhci-of-dwcmshc: add rockchip platform support") Cc: stable@vger.kernel.org Signed-off-by: Shawn Lin Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-of-dwcmshc.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 6139516c6488..0b2158a7e409 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -783,12 +783,15 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock extra |= BIT(4); sdhci_writel(host, extra, reg); + /* Disable clock while config DLL */ + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + if (clock <= 52000000) { if (host->mmc->ios.timing == MMC_TIMING_MMC_HS200 || host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { dev_err(mmc_dev(host->mmc), "Can't reduce the clock below 52MHz in HS200/HS400 mode"); - return; + goto enable_clk; } /* @@ -808,7 +811,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock DLL_STRBIN_DELAY_NUM_SEL | DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); - return; + goto enable_clk; } /* Reset DLL */ @@ -835,7 +838,7 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock 500 * USEC_PER_MSEC); if (err) { dev_err(mmc_dev(host->mmc), "DLL lock timeout!\n"); - return; + goto enable_clk; } extra = 0x1 << 16 | /* tune clock stop en */ @@ -868,6 +871,16 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock DLL_STRBIN_TAPNUM_DEFAULT | DLL_STRBIN_TAPNUM_FROM_SW; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); + +enable_clk: + /* + * The sdclk frequency select bits in SDHCI_CLOCK_CONTROL are not functional + * on Rockchip's SDHCI implementation. Instead, the clock frequency is fully + * controlled via external clk provider by calling clk_set_rate(). Consequently, + * passing 0 to sdhci_enable_clk() only re-enables the already-configured clock, + * which matches the hardware's actual behavior. + */ + sdhci_enable_clk(host, 0); } static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) -- cgit v1.2.3 From 52957cdad30f8011da1f4ef1338ba0339ca4c158 Mon Sep 17 00:00:00 2001 From: Neeraj Soni Date: Fri, 10 Apr 2026 12:28:33 +0530 Subject: mmc: sdhci-msm: Fix the wrapped key handling Inline Crypto Engine (ICE) supports wrapped key generation. While registering crypto profile the supported key types are queried from ICE driver. So the explicit check for RAW key is not needed. Fixes: fd78e2b582a0 ("mmc: sdhci-msm: Add support for wrapped keys") Signed-off-by: Neeraj Soni Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index b4131b12df56..633462c0be5f 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1926,11 +1926,6 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, if (IS_ERR_OR_NULL(ice)) return PTR_ERR_OR_ZERO(ice); - if (qcom_ice_get_supported_key_type(ice) != BLK_CRYPTO_KEY_TYPE_RAW) { - dev_warn(dev, "Wrapped keys not supported. Disabling inline encryption support.\n"); - return 0; - } - msm_host->ice = ice; /* Initialize the blk_crypto_profile */ -- cgit v1.2.3