[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