From: Changhuang Liang <changhuang.liang@starfivetech.com>
To: Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>, Ulf Hansson <ulfh@kernel.org>,
Adrian Hunter <adrian.hunter@intel.com>,
Inochi Amaoto <inochiama@gmail.com>,
Chen Wang <chen.wang@linux.dev>,
Jisheng Zhang <jszhang@kernel.org>
Cc: linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org,
devicetree@vger.kernel.org, sophgo@lists.linux.dev,
Philipp Zabel <p.zabel@pengutronix.de>,
Changhuang Liang <changhuang.liang@starfivetech.com>
Subject: [PATCH v2 2/2] mmc: sdhci-of-dwcmshc: Add support for StarFive JHB100
Date: Wed, 30 Sep 2026 03:32:58 -0700 [thread overview]
Message-ID: <20260930103258.13375-3-changhuang.liang@starfivetech.com> (raw)
In-Reply-To: <20260930103258.13375-1-changhuang.liang@starfivetech.com>
Add support for the DWCMSHC controller found on the StarFive JHB100
SoC.
The JHB100 CMSHC controller only drives 32 DMA address bits. The higher
DMA address bits [35:32] are supplied by the PER1 SYSCON, so the DMA
mask is set to 36 bits to describe this hardware capability. Fix these
bits to 4, which limits the DMA range to [0x400000000 - 0x4ffffffff].
The dma-ranges of the parent bus are expected to restrict DMA to this
window, so that any address outside of it fails to map instead of
being passed to the hardware.
The cclk_tx clock must be disabled before sdhci_set_clock() to
eliminate glitches, and re-enabled afterwards unless the card clock is
turned off, so that cclk_tx stays gated during suspend. The 15us delay
after disabling cclk_tx is a JHB100 specific requirement from the SoC
simulation. cclk_tx is enabled during probe and disabled in the
platform deinit callback, which is invoked from dwcmshc_remove().
Additionally, clear the PHY_DLLDL_CNFG_R register and re-initialize
the PHY when setting UHS signaling, and force 1.8V fixed I/O when the
DT specifies a DDR/HS200/HS400 1.8V mode.
Signed-off-by: Changhuang Liang <changhuang.liang@starfivetech.com>
---
drivers/mmc/host/sdhci-of-dwcmshc.c | 169 ++++++++++++++++++++++++++++
1 file changed, 169 insertions(+)
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 1baa2379c9c3..249055f14162 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -48,6 +48,9 @@
#define DWCMSHC_ENHANCED_STROBE BIT(8)
#define DWCMSHC_EMMC_ATCTRL 0x40
#define DWCMSHC_AT_STAT 0x44
+/* StarFive JHB100 SoC MSHCCS register */
+#define DWCMSHC_ARWADDR GENMASK(3, 0)
+#define DWCMSHC_ARWADDR_DEFAULT 4
/* Tuning and auto-tuning fields in AT_CTRL_R control register */
#define AT_CTRL_AT_EN BIT(0) /* autotuning is enabled */
#define AT_CTRL_CI_SEL BIT(1) /* interval to drive center phase select */
@@ -184,6 +187,7 @@
#define PHY_SDCLKDL_DC_INITIAL 0x40 /* initial delay code */
#define PHY_SDCLKDL_DC_DEFAULT 0x32 /* default delay code */
#define PHY_SDCLKDL_DC_HS400 0x18 /* delay code for HS400 mode */
+#define PHY_SDCLKDL_DC_JHB100 0x10 /* delay code for StarFive JHB100 */
#define PHY_SMPLDL_CNFG_R (DWC_MSHC_PTR_PHY_R + 0x20)
#define PHY_SMPLDL_CNFG_EXTDLY_EN BIT(0)
@@ -294,6 +298,11 @@ struct k230_priv {
struct regmap *hi_sys_regmap;
};
+struct jhb100_priv {
+ struct clk *cclk_tx;
+ bool cclk_tx_on;
+};
+
#define DWCMSHC_MAX_OTHER_CLKS 3
struct dwcmshc_priv {
@@ -314,6 +323,7 @@ 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 (*deinit)(struct dwcmshc_priv *dwc_priv);
void (*postinit)(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv);
};
@@ -2257,6 +2267,155 @@ static const struct dwcmshc_pltfm_data sdhci_dwcmshc_hpe_gsc_pdata = {
.init = dwcmshc_hpe_gsc_init,
};
+static int sdhci_jhb100_set_dma_mask(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ struct device *dev = mmc_dev(mmc);
+ int ret;
+
+ /*
+ * The StarFive JHB100's CMSHC controller only drives 32 DMA address
+ * bits, the higher DMA address bits [35:32] are supplied by the
+ * ARWADDR field in the PER1 SYSCON. The 36-bit mask describes this
+ * hardware capability. As ARWADDR is fixed to 4, the controller can
+ * only reach [0x400000000 - 0x4ffffffff]. The dma-ranges of the
+ * parent bus must restrict DMA to this window, so that any address
+ * outside of it fails to map and is never passed to the hardware.
+ */
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
+ if (ret)
+ dev_err(dev, "Failed to set 36-bit DMA mask.\n");
+
+ return ret;
+}
+
+static void sdhci_jhb100_set_clock(struct sdhci_host *host, unsigned int clock)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
+ struct jhb100_priv *priv = dwc_priv->priv;
+
+ /*
+ * The cclk_tx clock is disabled before sdhci_set_clock(), and then a
+ * 15us delay is required, This delay is determined by SoC simulation
+ * and is a special requirement on JHB100 SoC, mainly used to eliminate
+ * glitches caused by frequency switching.
+ */
+ if (priv->cclk_tx_on) {
+ clk_disable_unprepare(priv->cclk_tx);
+ priv->cclk_tx_on = false;
+ fsleep(15);
+ }
+
+ sdhci_set_clock(host, clock);
+
+ /* keep cclk_tx gated while the card clock is off, e.g. during suspend */
+ if (!clock)
+ return;
+
+ if (clk_prepare_enable(priv->cclk_tx))
+ dev_err(mmc_dev(host->mmc), "failed to re-enable cclk_tx\n");
+ else
+ priv->cclk_tx_on = true;
+}
+
+static void sdhci_jhb100_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int timing)
+{
+ dwcmshc_set_uhs_signaling(host, timing);
+ sdhci_writeb(host, 0, PHY_DLLDL_CNFG_R);
+ th1520_sdhci_set_phy(host);
+}
+
+static const struct sdhci_ops starfive_jhb100_ops = {
+ .set_dma_mask = sdhci_jhb100_set_dma_mask,
+ .set_clock = sdhci_jhb100_set_clock,
+ .set_uhs_signaling = sdhci_jhb100_set_uhs_signaling,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = dwcmshc_reset,
+ .get_max_clock = dwcmshc_get_max_clock,
+ .adma_write_desc = dwcmshc_adma_write_desc,
+ .irq = dwcmshc_cqe_irq_handler,
+ .voltage_switch = dwcmshc_phy_init,
+ .platform_execute_tuning = th1520_execute_tuning,
+};
+
+static int dwcmshc_starfive_jhb100_init(struct device *dev, struct sdhci_host *host,
+ struct dwcmshc_priv *dwc_priv)
+{
+ struct reset_control *reset;
+ struct jhb100_priv *priv;
+ unsigned int reg_offset;
+ struct regmap *syscon;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(struct jhb100_priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ syscon = syscon_regmap_lookup_by_phandle_args(dev->of_node,
+ "starfive,jhb100-per1-syscon",
+ 1, ®_offset);
+ if (IS_ERR(syscon))
+ return PTR_ERR(syscon);
+
+ /* configure the higher DMA address bits [35:32] */
+ ret = regmap_update_bits(syscon, reg_offset, DWCMSHC_ARWADDR,
+ DWCMSHC_ARWADDR_DEFAULT);
+ if (ret)
+ return ret;
+
+ reset = devm_reset_control_get_exclusive_deasserted(mmc_dev(host->mmc), NULL);
+ if (IS_ERR(reset))
+ return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(reset),
+ "Failed to get&deassert reset control\n");
+
+ priv->cclk_tx = devm_clk_get(dev, "cclk_tx");
+ if (IS_ERR(priv->cclk_tx))
+ return dev_err_probe(dev, PTR_ERR(priv->cclk_tx),
+ "Failed to get cclk_tx clock\n");
+
+ ret = clk_prepare_enable(priv->cclk_tx);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable cclk_tx clock\n");
+
+ priv->cclk_tx_on = true;
+
+ if (host->mmc->caps & MMC_CAP_1_8V_DDR ||
+ host->mmc->caps2 & (MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR))
+ dwc_priv->flags |= FLAG_IO_FIXED_1V8;
+ else
+ dwc_priv->flags &= ~FLAG_IO_FIXED_1V8;
+
+ dwc_priv->delay_line = PHY_SDCLKDL_DC_JHB100;
+ dwc_priv->priv = priv;
+
+ if (dwc_priv->flags & FLAG_IO_FIXED_1V8) {
+ host->flags &= ~SDHCI_SIGNALING_330;
+ host->flags |= SDHCI_SIGNALING_180;
+ }
+
+ return 0;
+}
+
+static void dwcmshc_starfive_jhb100_deinit(struct dwcmshc_priv *dwc_priv)
+{
+ struct jhb100_priv *priv = dwc_priv->priv;
+
+ if (priv->cclk_tx_on)
+ clk_disable_unprepare(priv->cclk_tx);
+}
+
+static const struct dwcmshc_pltfm_data sdhci_dwcmshc_jhb100_pdata = {
+ .pdata = {
+ .ops = &starfive_jhb100_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
+ },
+ .init = dwcmshc_starfive_jhb100_init,
+ .deinit = dwcmshc_starfive_jhb100_deinit,
+};
+
static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.enable = dwcmshc_sdhci_cqe_enable,
.disable = sdhci_cqe_disable,
@@ -2377,6 +2536,10 @@ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
.compatible = "hpe,gsc-dwcmshc",
.data = &sdhci_dwcmshc_hpe_gsc_pdata,
},
+ {
+ .compatible = "starfive,jhb100-dwcmshc",
+ .data = &sdhci_dwcmshc_jhb100_pdata,
+ },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_dwcmshc_dt_ids);
@@ -2503,6 +2666,8 @@ static int dwcmshc_probe(struct platform_device *pdev)
err_rpm:
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
+ if (pltfm_data->deinit)
+ pltfm_data->deinit(priv);
err_bus_clk:
clk_disable_unprepare(priv->bus_clk);
err_clk:
@@ -2527,6 +2692,7 @@ static void dwcmshc_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ const struct dwcmshc_pltfm_data *pltfm_data = priv->dwcmshc_pdata;
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -2534,6 +2700,9 @@ static void dwcmshc_remove(struct platform_device *pdev)
sdhci_remove_host(host, 0);
+ if (pltfm_data->deinit)
+ pltfm_data->deinit(priv);
+
dwcmshc_disable_card_clk(host);
clk_disable_unprepare(pltfm_host->clk);
--
2.25.1
prev parent reply other threads:[~2026-09-30 10:33 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-30 10:32 [PATCH v2 0/2] Add StarFive JHB100 DWCMSHC controller support Changhuang Liang
2026-09-30 10:32 ` [PATCH v2 1/2] dt-bindings: mmc: snps,dwcmshc-sdhci: Add starfive,jhb100-dwcmshc Changhuang Liang
2026-09-30 10:32 ` Changhuang Liang [this message]
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=20260930103258.13375-3-changhuang.liang@starfivetech.com \
--to=changhuang.liang@starfivetech.com \
--cc=adrian.hunter@intel.com \
--cc=chen.wang@linux.dev \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=inochiama@gmail.com \
--cc=jszhang@kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mmc@vger.kernel.org \
--cc=p.zabel@pengutronix.de \
--cc=robh@kernel.org \
--cc=sophgo@lists.linux.dev \
--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®