* [PATCH v3 0/2] Improve eMMC performance for the StarFive JHB100 SoC
@ 2026-11-09 8:18 Changhuang Liang
2026-11-09 8:18 ` [PATCH v3 1/2] mmc: cqhci: Add support for setting the Send Status Command Idle Timer Changhuang Liang
2026-11-09 8:18 ` [PATCH v3 2/2] mmc: sdhci-of-dwcmshc: Set CQHCI command idle time for StarFive JHB100 Changhuang Liang
0 siblings, 2 replies; 3+ messages in thread
From: Changhuang Liang @ 2026-11-09 8:18 UTC (permalink / raw)
To: Ritesh Harjani, Ulf Hansson, Adrian Hunter, Shawn Lin
Cc: linux-mmc, linux-kernel, Changhuang Liang
This series implements programmable support for CIT, allowing host drivers to
set the Send Status Command Idle Timer (CIT) in CQHCI_SSC1, either in
nanoseconds or directly in internal timer clock cycles.
Different platforms can improve MMC performance by configuring their respective
CIT. This series has been tested on the JHB100 SoC.
before:
dd if=/dev/mmcblk0 of=/dev/null bs=1M count=800
838860800 bytes (839 MB, 800 MiB) copied, 9.38511 s, 89.4 MB/s
dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=800
838860800 bytes (839 MB, 800 MiB) copied, 12.2127 s, 68.7 MB/s
after:
dd if=/dev/mmcblk0 of=/dev/null bs=1M count=800
838860800 bytes (839 MB, 800 MiB) copied, 3.19472 s, 263 MB/s
dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=800
838860800 bytes (839 MB, 800 MiB) copied, 10.7587 s, 78.0 MB/s
Changes since v2:
Patch 1:
- cqhci_set_cmd_idle_time() is removed, along with its EXPORT_SYMBOL.
- A new internal helper, cqhci_init_cit(), computes the CIT during
cqhci_init(), replacing cqhci_itcf_init().
- Replace itcf_hz with a driver-provided cit_ns field.
Patch 2:
- simply assigns cq_host->cit_ns = pltfm_data->cqhci_cit_ns before cqhci_init().
- dropping the post-init cqhci_set_cmd_idle_time() call and its warning path.
v2: https://lore.kernel.org/all/20261104112657.110536-1-changhuang.liang@starfivetech.com/
v1: https://lore.kernel.org/all/20261014121222.31385-1-changhuang.liang@starfivetech.com/
Changhuang Liang (2):
mmc: cqhci: Add support for setting the Send Status Command Idle Timer
mmc: sdhci-of-dwcmshc: Set CQHCI command idle time for StarFive JHB100
drivers/mmc/host/cqhci-core.c | 45 +++++++++++++++++++++++++++++
drivers/mmc/host/cqhci.h | 13 +++++++++
drivers/mmc/host/sdhci-of-dwcmshc.c | 7 +++++
3 files changed, 65 insertions(+)
--
2.25.1
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v3 1/2] mmc: cqhci: Add support for setting the Send Status Command Idle Timer
2026-11-09 8:18 [PATCH v3 0/2] Improve eMMC performance for the StarFive JHB100 SoC Changhuang Liang
@ 2026-11-09 8:18 ` Changhuang Liang
2026-11-09 8:18 ` [PATCH v3 2/2] mmc: sdhci-of-dwcmshc: Set CQHCI command idle time for StarFive JHB100 Changhuang Liang
1 sibling, 0 replies; 3+ messages in thread
From: Changhuang Liang @ 2026-11-09 8:18 UTC (permalink / raw)
To: Ritesh Harjani, Ulf Hansson, Adrian Hunter, Shawn Lin
Cc: linux-mmc, linux-kernel, Changhuang Liang
Add cit_ns and cit members to struct cqhci_host so that host drivers can
set the Send Status Command Idle Timer (CIT) in CQHCI_SSC1, either in
nanoseconds or directly in internal timer clock cycles.
The CIT defines how long the bus has to be idle, in internal timer clock
cycles, before the CQE issues CMD13 to poll the Queue Status Register.
Its reset value is controller specific and can be far from optimal, so
host drivers may need to tune it for their platform.
If cit_ns is non-zero, cqhci_init() converts it to cycles of the internal
timer clock frequency (ITCF) derived from the ITCFMUL and ITCFVAL fields
of the CQHCI capabilities register. The result is rounded up so that the
programmed idle time is never shorter than requested. Expressing the
time in nanoseconds lets platforms with different ITCF share the same
setting. If the ITCF is not reported or the result does not fit the
16-bit CIT field, a warning is printed and cit is left unchanged. If
both are set, cit_ns takes precedence over cit.
If cit is non-zero, program it into CQHCI_SSC1 every time the CQE is
enabled in __cqhci_enable().
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
drivers/mmc/host/cqhci-core.c | 45 +++++++++++++++++++++++++++++++++++
drivers/mmc/host/cqhci.h | 13 ++++++++++
2 files changed, 58 insertions(+)
diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c
index a466cd88b043..58182561e14e 100644
--- a/drivers/mmc/host/cqhci-core.c
+++ b/drivers/mmc/host/cqhci-core.c
@@ -279,6 +279,14 @@ static void __cqhci_enable(struct cqhci_host *cq_host)
cqhci_writel(cq_host, upper_32_bits(cq_host->desc_dma_base),
CQHCI_TDLBAU);
+ if (cq_host->cit) {
+ u32 ssc1 = cqhci_readl(cq_host, CQHCI_SSC1);
+
+ ssc1 &= ~CQHCI_SSC1_CIT_MASK;
+ ssc1 |= FIELD_PREP(CQHCI_SSC1_CIT_MASK, cq_host->cit);
+ cqhci_writel(cq_host, ssc1, CQHCI_SSC1);
+ }
+
cqhci_writel(cq_host, cq_host->rca, CQHCI_SSC2);
cqhci_set_irqs(cq_host, 0);
@@ -1163,6 +1171,41 @@ struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev)
}
EXPORT_SYMBOL(cqhci_pltfm_init);
+static void cqhci_init_cit(struct cqhci_host *cq_host)
+{
+ u32 cap, itcfmul, itcfval;
+ u64 cit;
+
+ if (!cq_host->cit_ns)
+ return;
+
+ cap = cqhci_readl(cq_host, CQHCI_CAP);
+ itcfmul = CQHCI_ITCFMUL(cap);
+ itcfval = CQHCI_ITCFVAL(cap);
+
+ /* ITCFMUL: 0 = 1 kHz, 1 = 10 kHz, ..., 4 = 10 MHz, others reserved */
+ if (itcfmul > 4 || !itcfval) {
+ pr_warn("%s: cqhci: internal timer clock frequency not available (CAP 0x%08x), CIT not set\n",
+ mmc_hostname(cq_host->mmc), cap);
+ return;
+ }
+
+ /*
+ * ITCF in kHz is int_pow(10, itcfmul) * itcfval, so kHz * ns / 10^6
+ * gives the number of cycles. Round up so that the idle time is never
+ * shorter than requested.
+ */
+ cit = DIV_ROUND_UP_ULL(int_pow(10, itcfmul) * itcfval * cq_host->cit_ns,
+ USEC_PER_SEC);
+ if (cit > FIELD_MAX(CQHCI_SSC1_CIT_MASK)) {
+ pr_warn("%s: cqhci: CIT %u ns out of range, CIT not set\n",
+ mmc_hostname(cq_host->mmc), cq_host->cit_ns);
+ return;
+ }
+
+ cq_host->cit = cit;
+}
+
static unsigned int cqhci_ver_major(struct cqhci_host *cq_host)
{
return CQHCI_VER_MAJOR(cqhci_readl(cq_host, CQHCI_VER));
@@ -1207,6 +1250,8 @@ int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc,
goto out_err;
}
+ cqhci_init_cit(cq_host);
+
spin_lock_init(&cq_host->lock);
init_completion(&cq_host->halt_comp);
diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h
index 8fbbc48c3f85..7381a2104449 100644
--- a/drivers/mmc/host/cqhci.h
+++ b/drivers/mmc/host/cqhci.h
@@ -26,6 +26,8 @@
#define CQHCI_CAP_CS 0x10000000 /* Crypto Support */
#define CQHCI_CAP_ITCFMUL GENMASK(15, 12)
#define CQHCI_ITCFMUL(x) FIELD_GET(CQHCI_CAP_ITCFMUL, (x))
+#define CQHCI_CAP_ITCFVAL GENMASK(9, 0)
+#define CQHCI_ITCFVAL(x) FIELD_GET(CQHCI_CAP_ITCFVAL, (x))
/* configuration */
#define CQHCI_CFG 0x08
@@ -234,6 +236,17 @@ struct cqhci_host {
int num_slots;
int qcnt;
+ /*
+ * Send Status Command Idle Timer (CIT), set by host drivers before
+ * calling cqhci_init(), either in nanoseconds via @cit_ns or directly
+ * in internal timer clock cycles via @cit. A non-zero @cit_ns takes
+ * precedence and is converted to @cit by cqhci_init(). If the
+ * conversion fails, @cit is left unchanged. A zero @cit keeps the
+ * reset default.
+ */
+ u32 cit_ns;
+ u16 cit;
+
u32 dcmd_slot;
u32 caps;
#define CQHCI_TASK_DESC_SZ_128 0x1
--
2.25.1
^ permalink raw reply [flat|nested] 3+ messages in thread
* [PATCH v3 2/2] mmc: sdhci-of-dwcmshc: Set CQHCI command idle time for StarFive JHB100
2026-11-09 8:18 [PATCH v3 0/2] Improve eMMC performance for the StarFive JHB100 SoC Changhuang Liang
2026-11-09 8:18 ` [PATCH v3 1/2] mmc: cqhci: Add support for setting the Send Status Command Idle Timer Changhuang Liang
@ 2026-11-09 8:18 ` Changhuang Liang
1 sibling, 0 replies; 3+ messages in thread
From: Changhuang Liang @ 2026-11-09 8:18 UTC (permalink / raw)
To: Ritesh Harjani, Ulf Hansson, Adrian Hunter, Shawn Lin
Cc: linux-mmc, linux-kernel, Changhuang Liang
Add a cqhci_cit_ns member to struct dwcmshc_pltfm_data so that platforms
can set the CQHCI Send Status Command Idle Timer (CIT). When it is set,
dwcmshc_cqhci_init() passes it to the CQHCI core via cq_host->cit_ns
before cqhci_init(), and the CQHCI core programs it every time the CQE
is enabled.
Use it to set the CIT to 320 ns on StarFive JHB100 to improve eMMC CQE
performance. Before/after dd results:
before:
dd if=/dev/mmcblk0 of=/dev/null bs=1M count=800
838860800 bytes (839 MB, 800 MiB) copied, 9.38511 s, 89.4 MB/s
dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=800
838860800 bytes (839 MB, 800 MiB) copied, 12.2127 s, 68.7 MB/s
after:
dd if=/dev/mmcblk0 of=/dev/null bs=1M count=800
838860800 bytes (839 MB, 800 MiB) copied, 3.19472 s, 263 MB/s
dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=800
838860800 bytes (839 MB, 800 MiB) copied, 10.7587 s, 78.0 MB/s
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
drivers/mmc/host/sdhci-of-dwcmshc.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 249055f14162..2848dc39be82 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -283,6 +283,9 @@
#define PHY_DELAY_CODE_SD 0x55
#define PHY_DELAY_CODE_SDIO 0x29
+/* CIT_NS for the JHB100 SoC */
+#define JHB100_CQHCI_CIT_NS 320
+
struct rk35xx_priv {
struct reset_control *reset;
u8 txclk_tapnum;
@@ -322,6 +325,7 @@ struct dwcmshc_priv {
struct dwcmshc_pltfm_data {
const struct sdhci_pltfm_data pdata;
const struct cqhci_host_ops *cqhci_host_ops;
+ u32 cqhci_cit_ns; /* CQHCI Send Status Command Idle Timer */
int (*init)(struct device *dev, struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
void (*deinit)(struct dwcmshc_priv *dwc_priv);
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
@@ -2412,6 +2416,7 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_jhb100_pdata = {
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
},
+ .cqhci_cit_ns = JHB100_CQHCI_CIT_NS,
.init = dwcmshc_starfive_jhb100_init,
.deinit = dwcmshc_starfive_jhb100_deinit,
};
@@ -2459,6 +2464,8 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *
else
cq_host->ops = &dwcmshc_cqhci_ops;
+ cq_host->cit_ns = pltfm_data->cqhci_cit_ns;
+
/* Enable using of 128-bit task descriptors */
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
if (dma64) {
--
2.25.1
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-09 8:18 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-09 8:18 [PATCH v3 0/2] Improve eMMC performance for the StarFive JHB100 SoC Changhuang Liang
2026-11-09 8:18 ` [PATCH v3 1/2] mmc: cqhci: Add support for setting the Send Status Command Idle Timer Changhuang Liang
2026-11-09 8:18 ` [PATCH v3 2/2] mmc: sdhci-of-dwcmshc: Set CQHCI command idle time for StarFive JHB100 Changhuang Liang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®