[openwrt/openwrt] rockchip: backport eMMC CQE support
LEDE Commits
lede-commits at lists.infradead.org
Tue Nov 18 08:26:48 PST 2025
robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/0ccb7c9100d32dde4c03a2a75f72236b8cf10222
commit 0ccb7c9100d32dde4c03a2a75f72236b8cf10222
Author: Tianling Shen <cnsztl at immortalwrt.org>
AuthorDate: Fri Nov 14 18:37:40 2025 +0800
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 <cnsztl at immortalwrt.org>
Link: https://github.com/openwrt/openwrt/pull/20780
Signed-off-by: Robert Marko <robimarko at gmail.com>
---
...-rockchip-add-eMMC-CQE-support-for-rk3588.patch | 25 +++
...mshc-Add-command-queue-support-for-rockch.patch | 195 +++++++++++++++++++++
2 files changed, 220 insertions(+)
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 <sebastian.reichel at collabora.com>
+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 <sebastian.reichel at collabora.com>
+Link: https://patch.msgid.link/20251031-rockchip-emmc-cqe-support-v2-2-958171f5edad@collabora.com
+Signed-off-by: Heiko Stuebner <heiko at sntech.de>
+---
+ 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 <sebastian.reichel at collabora.com>
+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 <yifeng.zhao at rock-chips.com>
+Signed-off-by: Yifeng Zhao <yifeng.zhao at rock-chips.com>
+Signed-off-by: Sebastian Reichel <sebastian.reichel at collabora.com>
+Acked-by: Adrian Hunter <adrian.hunter at intel.com>
+Signed-off-by: Ulf Hansson <ulf.hansson at linaro.org>
+---
+ 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)
More information about the lede-commits
mailing list