From: Tianling Shen Date: Fri, 14 Nov 2025 10:37:40 +0000 (+0800) Subject: rockchip: backport eMMC CQE support X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;h=0ccb7c9100d32dde4c03a2a75f72236b8cf10222;p=openwrt%2Fstaging%2Fstintel.git rockchip: backport eMMC CQE support Backport eMMC Command Queuing support for RK3576/RK3588. As the RK3576 device-tree has been upstreamed with the 'supports-cqe;' property set by default, the kernel already tried to use CQE, which results in system hang during suspend. This fixes the issue. Signed-off-by: Tianling Shen Link: https://github.com/openwrt/openwrt/pull/20780 Signed-off-by: Robert Marko --- diff --git a/target/linux/rockchip/patches-6.12/011-v6.19-arm64-dts-rockchip-add-eMMC-CQE-support-for-rk3588.patch b/target/linux/rockchip/patches-6.12/011-v6.19-arm64-dts-rockchip-add-eMMC-CQE-support-for-rk3588.patch new file mode 100644 index 0000000000..f50f269b78 --- /dev/null +++ b/target/linux/rockchip/patches-6.12/011-v6.19-arm64-dts-rockchip-add-eMMC-CQE-support-for-rk3588.patch @@ -0,0 +1,25 @@ +From 9d856aa1c81930a5d8df0e29d6cb0faa3fa87206 Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Fri, 31 Oct 2025 16:58:24 +0100 +Subject: [PATCH] arm64: dts: rockchip: add eMMC CQE support for rk3588 + +The RK3588 eMMC controller supports CQE, so add the missing +DT flag. + +Signed-off-by: Sebastian Reichel +Link: https://patch.msgid.link/20251031-rockchip-emmc-cqe-support-v2-2-958171f5edad@collabora.com +Signed-off-by: Heiko Stuebner +--- + arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi ++++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi +@@ -1935,6 +1935,7 @@ + <&cru SRST_A_EMMC>, <&cru SRST_B_EMMC>, + <&cru SRST_T_EMMC>; + reset-names = "core", "bus", "axi", "block", "timer"; ++ supports-cqe; + status = "disabled"; + }; + diff --git a/target/linux/rockchip/patches-6.12/037-04-v6.19-mmc-sdhci-of-dwcmshc-Add-command-queue-support-for-rockch.patch b/target/linux/rockchip/patches-6.12/037-04-v6.19-mmc-sdhci-of-dwcmshc-Add-command-queue-support-for-rockch.patch new file mode 100644 index 0000000000..e7b1c466c1 --- /dev/null +++ b/target/linux/rockchip/patches-6.12/037-04-v6.19-mmc-sdhci-of-dwcmshc-Add-command-queue-support-for-rockch.patch @@ -0,0 +1,195 @@ +From fda1e0af7c28f96d4f33e57cf51565b0e9c14e63 Mon Sep 17 00:00:00 2001 +From: Sebastian Reichel +Date: Fri, 31 Oct 2025 16:58:23 +0100 +Subject: [PATCH] mmc: sdhci-of-dwcmshc: Add command queue support for rockchip + SOCs + +This adds CQE support for the Rockchip RK3588 and RK3576 platform. To +be functional, the eMMC device-tree node must have a 'supports-cqe;' +flag property. + +As the RK3576 device-tree has been upstreamed with the 'supports-cqe;' +property set by default, the kernel already tried to use CQE, which +results in system hang during suspend. This fixes the issue. + +Co-developed-by: Yifeng Zhao +Signed-off-by: Yifeng Zhao +Signed-off-by: Sebastian Reichel +Acked-by: Adrian Hunter +Signed-off-by: Ulf Hansson +--- + drivers/mmc/host/sdhci-of-dwcmshc.c | 93 ++++++++++++++++++++++++++++- + 1 file changed, 90 insertions(+), 3 deletions(-) + +--- a/drivers/mmc/host/sdhci-of-dwcmshc.c ++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c +@@ -24,6 +24,7 @@ + + #include "sdhci-pltfm.h" + #include "cqhci.h" ++#include "sdhci-cqhci.h" + + #define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16) + +@@ -82,6 +83,8 @@ + #define DWCMSHC_EMMC_DLL_TXCLK 0x808 + #define DWCMSHC_EMMC_DLL_STRBIN 0x80c + #define DECMSHC_EMMC_DLL_CMDOUT 0x810 ++#define DECMSHC_EMMC_MISC_CON 0x81C ++#define MISC_INTCLK_EN BIT(1) + #define DWCMSHC_EMMC_DLL_STATUS0 0x840 + #define DWCMSHC_EMMC_DLL_START BIT(0) + #define DWCMSHC_EMMC_DLL_LOCKED BIT(8) +@@ -234,6 +237,7 @@ struct dwcmshc_priv { + + struct dwcmshc_pltfm_data { + const struct sdhci_pltfm_data pdata; ++ const struct cqhci_host_ops *cqhci_host_ops; + int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); + void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv); + }; +@@ -603,6 +607,68 @@ static void dwcmshc_cqhci_dumpregs(struc + sdhci_dumpregs(mmc_priv(mmc)); + } + ++static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); ++ u32 reg; ++ ++ reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG); ++ reg |= CQHCI_ENABLE; ++ sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG); ++} ++ ++static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); ++ u32 reg; ++ ++ reg = sdhci_readl(host, SDHCI_PRESENT_STATE); ++ while (reg & SDHCI_DATA_AVAILABLE) { ++ sdhci_readl(host, SDHCI_BUFFER); ++ reg = sdhci_readl(host, SDHCI_PRESENT_STATE); ++ } ++ ++ sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); ++ ++ sdhci_cqe_enable(mmc); ++} ++ ++static void rk35xx_sdhci_cqe_disable(struct mmc_host *mmc, bool recovery) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); ++ unsigned long flags; ++ u32 ctrl; ++ ++ /* ++ * During CQE command transfers, command complete bit gets latched. ++ * So s/w should clear command complete interrupt status when CQE is ++ * either halted or disabled. Otherwise unexpected SDCHI legacy ++ * interrupt gets triggered when CQE is halted/disabled. ++ */ ++ spin_lock_irqsave(&host->lock, flags); ++ ctrl = sdhci_readl(host, SDHCI_INT_ENABLE); ++ ctrl |= SDHCI_INT_RESPONSE; ++ sdhci_writel(host, ctrl, SDHCI_INT_ENABLE); ++ sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS); ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ sdhci_cqe_disable(mmc, recovery); ++} ++ ++static void rk35xx_sdhci_cqe_post_disable(struct mmc_host *mmc) ++{ ++ struct sdhci_host *host = mmc_priv(mmc); ++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); ++ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); ++ u32 ctrl; ++ ++ ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG); ++ ctrl &= ~CQHCI_ENABLE; ++ sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG); ++} ++ + static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock) + { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +@@ -721,6 +787,10 @@ static void rk35xx_sdhci_reset(struct sd + 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; ++ u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON); ++ ++ if ((host->mmc->caps2 & MMC_CAP2_CQE) && (mask & SDHCI_RESET_ALL)) ++ cqhci_deactivate(host->mmc); + + if (mask & SDHCI_RESET_ALL && priv->reset) { + reset_control_assert(priv->reset); +@@ -729,6 +799,9 @@ static void rk35xx_sdhci_reset(struct sd + } + + sdhci_reset(host, mask); ++ ++ /* Enable INTERNAL CLOCK */ ++ sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON); + } + + static int dwcmshc_rk35xx_init(struct device *dev, struct sdhci_host *host, +@@ -1230,6 +1303,15 @@ static const struct dwcmshc_pltfm_data s + }; + #endif + ++static const struct cqhci_host_ops rk35xx_cqhci_ops = { ++ .pre_enable = rk35xx_sdhci_cqe_pre_enable, ++ .enable = rk35xx_sdhci_cqe_enable, ++ .disable = rk35xx_sdhci_cqe_disable, ++ .post_disable = rk35xx_sdhci_cqe_post_disable, ++ .dumpregs = dwcmshc_cqhci_dumpregs, ++ .set_tran_desc = dwcmshc_set_tran_desc, ++}; ++ + static const struct dwcmshc_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { + .pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, +@@ -1238,6 +1320,7 @@ static const struct dwcmshc_pltfm_data s + .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, + }; +@@ -1287,7 +1370,8 @@ static const struct cqhci_host_ops dwcms + .set_tran_desc = dwcmshc_set_tran_desc, + }; + +-static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev) ++static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev, ++ const struct dwcmshc_pltfm_data *pltfm_data) + { + struct cqhci_host *cq_host; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +@@ -1317,7 +1401,10 @@ static void dwcmshc_cqhci_init(struct sd + } + + cq_host->mmio = host->ioaddr + priv->vendor_specific_area2; +- cq_host->ops = &dwcmshc_cqhci_ops; ++ if (pltfm_data->cqhci_host_ops) ++ cq_host->ops = pltfm_data->cqhci_host_ops; ++ else ++ cq_host->ops = &dwcmshc_cqhci_ops; + + /* Enable using of 128-bit task descriptors */ + dma64 = host->flags & SDHCI_USE_64_BIT_DMA; +@@ -1486,7 +1573,7 @@ static int dwcmshc_probe(struct platform + priv->vendor_specific_area2 = + sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2); + +- dwcmshc_cqhci_init(host, pdev); ++ dwcmshc_cqhci_init(host, pdev, pltfm_data); + } + + if (pltfm_data->postinit)