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: 8+ 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-22 11:04 ` Minda Chen
2026-04-22 16:07 ` Conor Dooley
2026-04-23 1:23 ` 回复: " Minda Chen
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.