From: Minda Chen <minda.chen@starfivetech.com>
To: Alim Akhtar <alim.akhtar@samsung.com>,
Avri Altman <avri.altman@wdc.com>,
Bart Van Assche <bvanassche@acm.org>,
Sai Krishna Potthuri <sai.krishna.potthuri@amd.com>,
Ajay Neeli <ajay.neeli@amd.com>,
"James E . J . Bottomley" <James.Bottomley@HansenPartnership.com>,
"Martin K . Petersen" <martin.petersen@oracle.com>,
Pedro Sousa <pedrom.sousa@synopsys.com>,
Arnd Bergmann <arnd@arndb.de>,
AngeloGioacchino Del Regno
<angelogioacchino.delregno@collabora.com>,
Conor Dooley <conor@kernel.org>, Rob Herring <robh+dt@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
linux-scsi@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org,
Minda Chen <minda.chen@starfivetech.com>
Subject: [PATCH v1 3/3] scsi: ufs: starfive: Add UFS support for StarFive JHB100 SoC
Date: Tue, 21 Apr 2026 17:12:15 +0800 [thread overview]
Message-ID: <20260421091215.120632-4-minda.chen@starfivetech.com> (raw)
In-Reply-To: <20260421091215.120632-1-minda.chen@starfivetech.com>
Add support for the UFS host controller on JHB100 SoC, built on
the Synopsys DWC UFS controller and using UFSHCD platform driver.
This controller requires specific configurations like
M-PHY/RMMI/UniPro
Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
---
MAINTAINERS | 1 +
drivers/ufs/host/Kconfig | 13 ++
drivers/ufs/host/Makefile | 1 +
drivers/ufs/host/ufs-starfive.c | 279 ++++++++++++++++++++++++++++++++
drivers/ufs/host/ufshcd-dwc.h | 17 ++
5 files changed, 311 insertions(+)
create mode 100644 drivers/ufs/host/ufs-starfive.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 3792c51da63c..658f65c78482 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27194,6 +27194,7 @@ UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER STARFIVE
M: Minda Chen <minda.cheb@starfivetech.com>
S: Maintained
F: Documentation/devicetree/bindings/ufs/starfive,ufs.yaml
+F: drivers/ufs/host/ufs-starfive.c
UNIWILL LAPTOP DRIVER
M: Armin Wolf <W_Armin@gmx.de>
diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig
index 964ae70e7390..b742f7a2b0b6 100644
--- a/drivers/ufs/host/Kconfig
+++ b/drivers/ufs/host/Kconfig
@@ -168,3 +168,16 @@ config SCSI_UFS_AMD_VERSAL2
Select this if you have UFS controller on AMD Versal Gen 2 SoC.
If unsure, say N.
+
+config SCSI_UFS_STARFIVE
+ tristate "Starfive UFS controller platform driver"
+ depends on OF && SCSI_UFSHCD_PLATFORM
+ depends on ARCH_STARFIVE || COMPILE_TEST
+ help
+ This selects the StarFive specific additions to UFSHCD platform driver.
+ UFS host on StarFive needs some vendor specific configuration before
+ accessing the hardware which includes PHY configuration and vendor
+ specific registers.
+
+ Select this if you have UFS controller on StarFive chipset.
+ If unsure, say N.
diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile
index 65d8bb23ab7b..adfee2ae3b48 100644
--- a/drivers/ufs/host/Makefile
+++ b/drivers/ufs/host/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_SCSI_UFS_ROCKCHIP) += ufs-rockchip.o
obj-$(CONFIG_SCSI_UFS_SPRD) += ufs-sprd.o
obj-$(CONFIG_SCSI_UFS_TI_J721E) += ti-j721e-ufs.o
obj-$(CONFIG_SCSI_UFS_AMD_VERSAL2) += ufs-amd-versal2.o ufshcd-dwc.o
+obj-$(CONFIG_SCSI_UFS_STARFIVE) += ufs-starfive.o ufshcd-dwc.o
diff --git a/drivers/ufs/host/ufs-starfive.c b/drivers/ufs/host/ufs-starfive.c
new file mode 100644
index 000000000000..cdd5f9264cdb
--- /dev/null
+++ b/drivers/ufs/host/ufs-starfive.c
@@ -0,0 +1,279 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Starfive UFS host platform driver
+ *
+ * Copyright (C) 2026 Starfive, Inc.
+ *
+ * Authors: Minda Chen <minda.chen@starfivetech.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <ufs/unipro.h>
+
+#include "ufshcd-pltfrm.h"
+#include "ufshcd-dwc.h"
+#include "ufshci-dwc.h"
+
+struct ufs_starfive_host {
+ struct ufs_hba *hba;
+ struct regmap *syscon;
+ struct reset_control *core_reset;
+ struct reset_control *phy_reset;
+ struct clk *ufs_clk;
+};
+
+#define SRAM_STATUS 0x38
+#define SRAM_EXT_LD_DONE BIT(1)
+#define SRAM_INIT_DONE BIT(2)
+#define UFS_REFCLK 0x3c
+#define REFCLK_OEN BIT(8)
+#define RESET_I BIT(9)
+#define RESET_OEN BIT(10)
+
+#define MPHY_POLL_INTERVAL_US 100
+#define MPHY_POLL_TIMEOUT_US 10000
+
+static int ufs_starfive_phy_config(struct ufs_hba *hba, struct ufs_starfive_host *host)
+{
+ static struct ufs_dwc_phy_pair_data phy_data[] = {
+ { MPLL_SKIPCAL_COARSE_TUNE, 0},
+ { RX_AFE_ATT_IDAC(0), 0x8a},
+ { RX_AFE_ATT_IDAC(1), 0xc2},
+ { RX_AFE_CTLE_IDAC(0), 0x8e},
+ { RX_AFE_CTLE_IDAC(1), 0x8b},
+ { FAST_FLAGS(0), 0x0004 },
+ { FAST_FLAGS(1), 0x0004 },
+ { RX_ADAPT_DFE(0), 0xa00},
+ { RX_ADAPT_DFE(1), 0xa00},
+ };
+ struct ufs_dwc_phy_pair_data *data;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(phy_data); i++) {
+ data = &phy_data[i];
+ ret = ufs_dwc_phy_reg_write(hba, data->addr, data->value);
+ if (ret)
+ return ret;
+ }
+
+ ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_MPHYDISABLE), 0);
+ if (ret)
+ return ret;
+
+ ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_MPHYCFGUPDT), 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int ufs_starfive_phy_init(struct ufs_hba *hba)
+{
+ struct ufs_starfive_host *host = ufshcd_get_variant(hba);
+ static struct ufshcd_dme_attr_val rmmi_config[] = {
+ { UIC_ARG_MIB(CBRATESEL), 0x1,
+ DME_LOCAL },
+ { UIC_ARG_MIB(CBREFCLKCTRL2), CBREFREFCLK_GATE_OVR_EN,
+ DME_LOCAL },
+ { UIC_ARG_MIB_SEL(RXSQCONTROL, SELIND_LN0_RX), 0x01,
+ DME_LOCAL },
+ { UIC_ARG_MIB_SEL(RXRHOLDCTRLOPT, SELIND_LN0_RX), 0x02,
+ DME_LOCAL },
+ { UIC_ARG_MIB_SEL(RXSQCONTROL, SELIND_LN1_RX), 0x01,
+ DME_LOCAL },
+ { UIC_ARG_MIB_SEL(RXRHOLDCTRLOPT, SELIND_LN1_RX), 0x02,
+ DME_LOCAL },
+ { UIC_ARG_MIB(EXT_COARSE_TUNE_RATEA), 0x25,
+ DME_LOCAL },
+ { UIC_ARG_MIB(EXT_COARSE_TUNE_RATEB), 0x51,
+ DME_LOCAL },
+ { UIC_ARG_MIB(CBCRCTRL), 0x01, DME_LOCAL },
+ { UIC_ARG_MIB(VS_MPHYCFGUPDT), 0x1,
+ DME_LOCAL },
+ };
+ int ret, val;
+
+ ret = ufshcd_dwc_dme_set_attrs(hba, rmmi_config,
+ ARRAY_SIZE(rmmi_config));
+ if (ret) {
+ dev_err(hba->dev, "set rmmi config failed\n");
+ return ret;
+ }
+
+ ret = reset_control_deassert(host->phy_reset);
+ if (ret) {
+ dev_err(hba->dev, "Failed to reset phy\n");
+ return ret;
+ }
+
+ ret = regmap_read_poll_timeout(host->syscon,
+ SRAM_STATUS, val,
+ (val & SRAM_INIT_DONE),
+ MPHY_POLL_INTERVAL_US,
+ MPHY_POLL_TIMEOUT_US);
+ if (ret) {
+ dev_err(hba->dev, "wait sram init done timeout\n");
+ return ret;
+ }
+
+ regmap_update_bits(host->syscon, SRAM_STATUS,
+ SRAM_EXT_LD_DONE, SRAM_EXT_LD_DONE);
+
+ ret = ufs_starfive_phy_config(hba, host);
+ if (ret) {
+ dev_err(hba->dev, "configure phy failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ufs_starfive_init(struct ufs_hba *hba)
+{
+ struct ufs_starfive_host *host;
+ struct device *dev = hba->dev;
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = container_of(dev, struct platform_device, dev);
+ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+ if (!host)
+ return dev_err_probe(dev, -ENOMEM,
+ "no memory for starfive ufs host\n");
+
+ host->syscon = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "starfive,syscon");
+
+ if (IS_ERR(host->syscon))
+ return dev_err_probe(dev, PTR_ERR(host->syscon), "getting the regmap failed\n");
+
+ host->core_reset = devm_reset_control_get_exclusive(hba->dev, "main");
+ if (IS_ERR(host->core_reset))
+ return dev_err_probe(dev, PTR_ERR(host->core_reset),
+ "Failed to get core clock resets");
+
+ host->phy_reset = devm_reset_control_get_exclusive(hba->dev, "phy");
+ if (IS_ERR(host->phy_reset))
+ return dev_err_probe(dev, PTR_ERR(host->phy_reset),
+ "Failed to get phy clk reset\n");
+
+ host->ufs_clk = devm_clk_get_enabled(&pdev->dev, "ufs");
+ if (IS_ERR(host->ufs_clk))
+ return dev_err_probe(dev, PTR_ERR(host->ufs_clk),
+ "Failed to get ufs clock\n");
+
+ regmap_update_bits(host->syscon, UFS_REFCLK,
+ REFCLK_OEN | RESET_OEN, 0);
+ usleep_range(2, 3);
+ regmap_update_bits(host->syscon, UFS_REFCLK, RESET_I, RESET_I);
+
+ ret = reset_control_deassert(host->core_reset);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to reset core clock");
+
+ host->hba = hba;
+ ufshcd_set_variant(hba, host);
+ hba->caps |= UFSHCD_CAP_WB_EN;
+
+ return 0;
+}
+
+static int ufs_starfive_link_startup_notify(struct ufs_hba *hba,
+ enum ufs_notify_change_status status)
+{
+ int ret;
+
+ if (status == PRE_CHANGE) {
+ ret = ufshcd_vops_phy_initialization(hba);
+ if (ret) {
+ dev_err(hba->dev, "Phy setup failed (%d)\n", ret);
+ return ret;
+ }
+ } else { /* POST_CHANGE */
+ return ufshcd_dwc_link_startup_notify(hba, status);
+ }
+
+ return 0;
+}
+
+static int ufs_starfive_hce_enable_notify(struct ufs_hba *hba,
+ enum ufs_notify_change_status status)
+{
+ u32 val;
+
+ if (status != POST_CHANGE)
+ return 0;
+
+ /* Disable Gating clock. Auto hibernation quirk */
+ val = ufshcd_readl(hba, REG_BUSTHRTL);
+ val &= ~(LP_AH8_POWER_GATING_EN
+ | LP_POWER_GATING_EN
+ | CLK_GATING_EN);
+ ufshcd_writel(hba, val, REG_BUSTHRTL);
+
+ return 0;
+}
+
+static struct ufs_hba_variant_ops ufs_hba_vops = {
+ .name = "ufs_starfive_platform",
+ .init = ufs_starfive_init,
+ .link_startup_notify = ufs_starfive_link_startup_notify,
+ .phy_initialization = ufs_starfive_phy_init,
+ .hce_enable_notify = ufs_starfive_hce_enable_notify,
+};
+
+static int ufs_starfive_probe(struct platform_device *pdev)
+{
+ int err;
+
+ /* Perform generic probe */
+ err = ufshcd_pltfrm_init(pdev, &ufs_hba_vops);
+ if (err)
+ dev_err(&pdev->dev, "ufshcd_pltfrm_init() failed %d\n", err);
+
+ return err;
+}
+
+static void ufs_starfive_remove(struct platform_device *pdev)
+{
+ struct ufs_hba *hba = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&(pdev)->dev);
+ ufshcd_remove(hba);
+}
+
+static const struct dev_pm_ops ufs_starfive_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume)
+ SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL)
+};
+
+static const struct of_device_id ufs_starfive_pltfm_match[] = {
+ { .compatible = "starfive,jhb100-ufs", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ufs_starfive_pltfm_match);
+
+static struct platform_driver ufs_starfive_driver = {
+ .probe = ufs_starfive_probe,
+ .remove = ufs_starfive_remove,
+ .driver = {
+ .name = "ufs-starfive",
+ .pm = &ufs_starfive_pm_ops,
+ .of_match_table = of_match_ptr(ufs_starfive_pltfm_match),
+ },
+};
+
+module_platform_driver(ufs_starfive_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ufs-starfive");
+MODULE_DESCRIPTION("Starfive UFS host platform glue driver");
diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
index 8091f186a9b3..ab8728f92b22 100644
--- a/drivers/ufs/host/ufshcd-dwc.h
+++ b/drivers/ufs/host/ufshcd-dwc.h
@@ -12,7 +12,15 @@
#include <ufs/ufshcd.h>
+/* ufshcd vendor specific register */
+#define REG_BUSTHRTL 0xc0
+#define LP_AH8_POWER_GATING_EN BIT(17)
+#define LP_POWER_GATING_EN BIT(16)
+#define CLK_GATING_EN BIT(12)
+
/* RMMI Attributes */
+#define RXSQCONTROL 0x8009
+#define RXRHOLDCTRLOPT 0x8013
#define CBREFCLKCTRL2 0x8132
#define CBCRCTRL 0x811F
#define CBC10DIRECTCONF2 0x810E
@@ -24,6 +32,8 @@
#define CBCREGRDLSB 0x811A
#define CBCREGRDMSB 0x811B
#define CBCREGRDWRSEL 0x811C
+#define EXT_COARSE_TUNE_RATEA 0x814D
+#define EXT_COARSE_TUNE_RATEB 0x814E
#define CBREFREFCLK_GATE_OVR_EN BIT(7)
@@ -32,9 +42,11 @@
#define MRX_FSM_STATE 0xC1
/* M-PHY registers */
+#define MPLL_SKIPCAL_COARSE_TUNE 0x28
#define RX_OVRD_IN_1(n) (0x3006 + ((n) * 0x100))
#define RX_PCS_OUT(n) (0x300F + ((n) * 0x100))
#define FAST_FLAGS(n) (0x401C + ((n) * 0x100))
+#define RX_ADAPT_DFE(n) (0x401E + ((n) * 0x100))
#define RX_AFE_ATT_IDAC(n) (0x4000 + ((n) * 0x100))
#define RX_AFE_CTLE_IDAC(n) (0x4001 + ((n) * 0x100))
#define FW_CALIB_CCFG(n) (0x404D + ((n) * 0x100))
@@ -64,6 +76,11 @@ struct ufshcd_dme_attr_val {
u8 peer;
};
+struct ufs_dwc_phy_pair_data {
+ u32 addr;
+ u32 value;
+};
+
int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status);
int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba,
--
2.17.1
prev parent reply other threads:[~2026-04-21 9:12 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-21 9:12 [PATCH v1 0/3] Add StarFive JHB100 soc UFS platform driver Minda Chen
2026-04-21 9:12 ` [PATCH v1 1/3] scsi: ufs: dt-bindings: starfive: Add UFS Host Controller for JHB100 soc Minda Chen
2026-04-21 17:03 ` Conor Dooley
2026-04-21 9:12 ` [PATCH v1 2/3] scsi: ufs: dwc: Rename amd-versal2 read/write PHY API and move to dwc common file Minda Chen
2026-04-21 9:12 ` Minda Chen [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=20260421091215.120632-4-minda.chen@starfivetech.com \
--to=minda.chen@starfivetech.com \
--cc=James.Bottomley@HansenPartnership.com \
--cc=ajay.neeli@amd.com \
--cc=alim.akhtar@samsung.com \
--cc=angelogioacchino.delregno@collabora.com \
--cc=arnd@arndb.de \
--cc=avri.altman@wdc.com \
--cc=bvanassche@acm.org \
--cc=conor@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-scsi@vger.kernel.org \
--cc=martin.petersen@oracle.com \
--cc=pedrom.sousa@synopsys.com \
--cc=robh+dt@kernel.org \
--cc=sai.krishna.potthuri@amd.com \
/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