mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Changhuang Liang <changhuang.liang@starfivetech.com>
To: Asutosh Das <quic_asutoshd@quicinc.com>,
	Ritesh Harjani <ritesh.list@gmail.com>,
	Ulf Hansson <ulfh@kernel.org>,
	Adrian Hunter <adrian.hunter@intel.com>
Cc: linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org,
	Changhuang Liang <changhuang.liang@starfivetech.com>
Subject: [PATCH v1 1/2] mmc: cqhci: Add support for setting command idle time in nanoseconds
Date: Wed, 14 Oct 2026 05:12:21 -0700	[thread overview]
Message-ID: <20261014121222.31385-2-changhuang.liang@starfivetech.com> (raw)
In-Reply-To: <20261014121222.31385-1-changhuang.liang@starfivetech.com>

Add a helper that derives the internal timer clock frequency (ITCF) of
the CQE from the ITCFMUL and ITCFVAL fields of the CQHCI capabilities
register, and export cqhci_set_cmd_idle_time() to let host drivers
program the Send Status Command Idle Timer (CIT) in CQHCI_SSC1 using a
value in nanoseconds.

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. Doing the
conversion in the core avoids every host driver re-implementing the
ITCFMUL/ITCFVAL decoding.

The requested time is rounded up to whole clock cycles so that the
programmed idle time is never shorter than requested. The helper returns
-EOPNOTSUPP when the controller does not report a usable ITCF and
-ERANGE when the result does not fit the 16-bit CIT field, so callers
can report the failure instead of silently running with the reset
default.

Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
 drivers/mmc/host/cqhci-core.c | 60 +++++++++++++++++++++++++++++++++++
 drivers/mmc/host/cqhci.h      |  6 ++++
 2 files changed, 66 insertions(+)

diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c
index a466cd88b043..aff8d6619ea9 100644
--- a/drivers/mmc/host/cqhci-core.c
+++ b/drivers/mmc/host/cqhci-core.c
@@ -12,6 +12,7 @@
 #include <linux/scatterlist.h>
 #include <linux/platform_device.h>
 #include <linux/ktime.h>
+#include <linux/units.h>
 
 #include <linux/mmc/mmc.h>
 #include <linux/mmc/host.h>
@@ -1163,6 +1164,63 @@ struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev)
 }
 EXPORT_SYMBOL(cqhci_pltfm_init);
 
+static u64 cqhci_itcfmul_to_hz(u32 itcfmul)
+{
+	/* CQHCI_CAP.ITCFMUL: 0 = 1 kHz, 1 = 10 kHz, ..., 4 = 10 MHz, others reserved */
+	if (itcfmul > 4)
+		return 0;
+
+	return HZ_PER_KHZ * int_pow(10, itcfmul);
+}
+
+static void cqhci_itcf_init(struct cqhci_host *cq_host)
+{
+	u32 cap = cqhci_readl(cq_host, CQHCI_CAP);
+	u64 itcf;
+
+	itcf = cqhci_itcfmul_to_hz(CQHCI_ITCFMUL(cap)) * CQHCI_ITCFVAL(cap);
+	if (!itcf || itcf > U32_MAX) {
+		pr_debug("%s: cqhci: internal timer clock frequency not available (CAP 0x%08x)\n",
+			 mmc_hostname(cq_host->mmc), cap);
+		return;
+	}
+
+	cq_host->itcf_hz = itcf;
+}
+
+/**
+ * cqhci_set_cmd_idle_time - program the Send Status Command Idle Timer (CIT)
+ * @cq_host: CQHCI host
+ * @ns: idle time in nanoseconds
+ *
+ * Set the number of internal timer clock cycles the CQE waits on an idle bus
+ * before issuing CMD13 to poll the Queue Status Register. The cycle count is
+ * rounded up so that the resulting idle time is never shorter than @ns.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if the controller does not report its
+ * internal timer clock frequency, or -ERANGE if @ns does not fit the CIT field.
+ */
+int cqhci_set_cmd_idle_time(struct cqhci_host *cq_host, u32 ns)
+{
+	u32 ssc1;
+	u64 cit;
+
+	if (!cq_host->itcf_hz)
+		return -EOPNOTSUPP;
+
+	cit = DIV_ROUND_UP_ULL((u64)cq_host->itcf_hz * ns, NSEC_PER_SEC);
+	if (!cit || cit > FIELD_MAX(CQHCI_SSC1_CIT_MASK))
+		return -ERANGE;
+
+	ssc1 = cqhci_readl(cq_host, CQHCI_SSC1);
+	ssc1 &= ~CQHCI_SSC1_CIT_MASK;
+	ssc1 |= FIELD_PREP(CQHCI_SSC1_CIT_MASK, cit);
+	cqhci_writel(cq_host, ssc1, CQHCI_SSC1);
+
+	return 0;
+}
+EXPORT_SYMBOL(cqhci_set_cmd_idle_time);
+
 static unsigned int cqhci_ver_major(struct cqhci_host *cq_host)
 {
 	return CQHCI_VER_MAJOR(cqhci_readl(cq_host, CQHCI_VER));
@@ -1207,6 +1265,8 @@ int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc,
 		goto out_err;
 	}
 
+	cqhci_itcf_init(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..07eb753f9586 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,9 @@ struct cqhci_host {
 	int num_slots;
 	int qcnt;
 
+	/* Internal Timer Clock Frequency (Hz), 0 if not reported by CQHCI_CAP */
+	u32 itcf_hz;
+
 	u32 dcmd_slot;
 	u32 caps;
 #define CQHCI_TASK_DESC_SZ_128		0x1
@@ -318,6 +323,7 @@ struct platform_device;
 irqreturn_t cqhci_irq(struct mmc_host *mmc, int cmd_error, int data_error);
 int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, bool dma64);
 struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev);
+int cqhci_set_cmd_idle_time(struct cqhci_host *cq_host, u32 ns);
 int cqhci_deactivate(struct mmc_host *mmc);
 void cqhci_set_tran_desc(u8 *desc, dma_addr_t addr, int len, bool end, bool dma64);
 static inline int cqhci_suspend(struct mmc_host *mmc)
-- 
2.25.1


  reply	other threads:[~2026-10-14 12:12 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-14 12:12 [PATCH v1 0/2] Improve eMMC performance for the StarFive JHB100 SoC Changhuang Liang
2026-10-14 12:12 ` Changhuang Liang [this message]
2026-10-14 12:12 ` [PATCH v1 2/2] mmc: sdhci-of-dwcmshc: Set CQHCI command idle time for StarFive JHB100 Changhuang Liang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261014121222.31385-2-changhuang.liang@starfivetech.com \
    --to=changhuang.liang@starfivetech.com \
    --cc=adrian.hunter@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=quic_asutoshd@quicinc.com \
    --cc=ritesh.list@gmail.com \
    --cc=ulfh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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®