mirror of
https://git.openwrt.org/openwrt/openwrt.git
synced 2026-03-02 09:43:46 +01:00
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@immortalwrt.org> Link: https://github.com/openwrt/openwrt/pull/20780 Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
parent
5d85657f6d
commit
0ccb7c9100
2 changed files with 220 additions and 0 deletions
|
|
@ -0,0 +1,25 @@
|
|||
From 9d856aa1c81930a5d8df0e29d6cb0faa3fa87206 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Reichel <sebastian.reichel@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@collabora.com>
|
||||
Link: https://patch.msgid.link/20251031-rockchip-emmc-cqe-support-v2-2-958171f5edad@collabora.com
|
||||
Signed-off-by: Heiko Stuebner <heiko@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";
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
From fda1e0af7c28f96d4f33e57cf51565b0e9c14e63 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Reichel <sebastian.reichel@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@rock-chips.com>
|
||||
Signed-off-by: Yifeng Zhao <yifeng.zhao@rock-chips.com>
|
||||
Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com>
|
||||
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
|
||||
Signed-off-by: Ulf Hansson <ulf.hansson@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)
|
||||
Loading…
Add table
Reference in a new issue