ufs: rockchip: Add initial support
authorShawn Lin <shawn.lin@rock-chips.com>
Mon, 20 Oct 2025 08:16:22 +0000 (16:16 +0800)
committerNeil Armstrong <neil.armstrong@linaro.org>
Tue, 28 Oct 2025 16:20:17 +0000 (17:20 +0100)
This patch adds initial support for UFS controller on Rockchip
platforms.

Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
Link: https://patch.msgid.link/1760948182-128561-2-git-send-email-shawn.lin@rock-chips.com
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
MAINTAINERS
drivers/ufs/Kconfig
drivers/ufs/Makefile
drivers/ufs/ufs-rockchip.c [new file with mode: 0644]
drivers/ufs/ufs-rockchip.h [new file with mode: 0644]

index 6719036..ee4ab37 100644 (file)
@@ -575,6 +575,7 @@ F:  drivers/mmc/rockchip_dw_mmc.c
 F:     drivers/pinctrl/rockchip/
 F:     drivers/ram/rockchip/
 F:     drivers/sysreset/sysreset_rockchip.c
+F:     drivers/ufs/*rockchip*
 F:     drivers/video/rockchip/
 F:     tools/rkcommon.c
 F:     tools/rkcommon.h
index b08ca08..3d36785 100644 (file)
@@ -33,6 +33,15 @@ config QCOM_UFS
          This selects the platform driver for the UFS host
          controller present on Qualcomm Snapdragon SoCs.
 
+config ROCKCHIP_UFS
+       bool "Rockchip specific hooks to UFS controller platform driver"
+       depends on UFS
+       help
+         This selects the Rockchip specific additions to UFSHCD platform driver.
+
+         Select this if you have UFS controller on Rockchip chipset.
+         If unsure, say N.
+
 config TI_J721E_UFS
        bool "Glue Layer driver for UFS on TI J721E devices"
        help
index 2a378e4..41e910c 100644 (file)
@@ -9,4 +9,5 @@ obj-$(CONFIG_QCOM_UFS) += ufs-qcom.o
 obj-$(CONFIG_TI_J721E_UFS) += ti-j721e-ufs.o
 obj-$(CONFIG_UFS_PCI) += ufs-pci.o
 obj-$(CONFIG_UFS_RENESAS) += ufs-renesas.o
+obj-$(CONFIG_ROCKCHIP_UFS) += ufs-rockchip.o
 obj-$(CONFIG_UFS_AMD_VERSAL2) += ufs-amd-versal2.o ufshcd-dwc.o
diff --git a/drivers/ufs/ufs-rockchip.c b/drivers/ufs/ufs-rockchip.c
new file mode 100644 (file)
index 0000000..aa924e5
--- /dev/null
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Rockchip UFS Host Controller driver
+ *
+ * Copyright (C) 2025 Rockchip Electronics Co.Ltd.
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <reset.h>
+#include <ufs.h>
+
+#include "ufs.h"
+#include "unipro.h"
+#include "ufs-rockchip.h"
+
+static int ufs_rockchip_hce_enable_notify(struct ufs_hba *hba,
+                                         enum ufs_notify_change_status status)
+{
+       int err = 0;
+
+       if (status != POST_CHANGE)
+               return 0;
+
+       ufshcd_dme_reset(hba);
+       ufshcd_dme_enable(hba);
+
+       if (hba->ops->phy_initialization) {
+               err = hba->ops->phy_initialization(hba);
+               if (err)
+                       dev_err(hba->dev,
+                               "Phy init failed (%d)\n", err);
+       }
+
+       return err;
+}
+
+static void ufs_rockchip_rk3576_phy_parameter_init(struct ufs_hba *hba)
+{
+       struct ufs_rockchip_host *host = dev_get_priv(hba->dev);
+
+       ufs_sys_writel(host->mphy_base, 0x80, CMN_REG23);
+       ufs_sys_writel(host->mphy_base, 0xB5, TRSV0_REG14);
+       ufs_sys_writel(host->mphy_base, 0xB5, TRSV1_REG14);
+       ufs_sys_writel(host->mphy_base, 0x03, TRSV0_REG15);
+       ufs_sys_writel(host->mphy_base, 0x03, TRSV1_REG15);
+       ufs_sys_writel(host->mphy_base, 0x38, TRSV0_REG08);
+       ufs_sys_writel(host->mphy_base, 0x38, TRSV1_REG08);
+       ufs_sys_writel(host->mphy_base, 0x50, TRSV0_REG29);
+       ufs_sys_writel(host->mphy_base, 0x50, TRSV1_REG29);
+       ufs_sys_writel(host->mphy_base, 0x80, TRSV0_REG2E);
+       ufs_sys_writel(host->mphy_base, 0x80, TRSV1_REG2E);
+       ufs_sys_writel(host->mphy_base, 0x18, TRSV0_REG3C);
+       ufs_sys_writel(host->mphy_base, 0x18, TRSV1_REG3C);
+       ufs_sys_writel(host->mphy_base, 0x03, TRSV0_REG16);
+       ufs_sys_writel(host->mphy_base, 0x03, TRSV1_REG16);
+       ufs_sys_writel(host->mphy_base, 0x20, TRSV0_REG17);
+       ufs_sys_writel(host->mphy_base, 0x20, TRSV1_REG17);
+       ufs_sys_writel(host->mphy_base, 0xC0, TRSV0_REG18);
+       ufs_sys_writel(host->mphy_base, 0xC0, TRSV1_REG18);
+       ufs_sys_writel(host->mphy_base, 0x03, CMN_REG25);
+       ufs_sys_writel(host->mphy_base, 0x03, TRSV0_REG3D);
+       ufs_sys_writel(host->mphy_base, 0x03, TRSV1_REG3D);
+       ufs_sys_writel(host->mphy_base, 0xC0, CMN_REG23);
+       udelay(1);
+       ufs_sys_writel(host->mphy_base, 0x00, CMN_REG23);
+       udelay(200);
+}
+
+static int ufs_rockchip_rk3576_phy_init(struct ufs_hba *hba)
+{
+       ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(PA_LOCAL_TX_LCC_ENABLE, 0x0), 0x0);
+       /* enable the mphy DME_SET cfg */
+       ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MPHY_CFG, 0x0), MPHY_CFG_ENABLE);
+       for (int i = 0; i < 2; i++) {
+               /* Configuration M-TX */
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD, SEL_TX_LANE0 + i), 0x06);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD_EN, SEL_TX_LANE0 + i), 0x02);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_VALUE, SEL_TX_LANE0 + i), 0x44);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE1, SEL_TX_LANE0 + i), 0xe6);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE2, SEL_TX_LANE0 + i), 0x07);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_TASE_VALUE, SEL_TX_LANE0 + i), 0x93);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_BASE_NVALUE, SEL_TX_LANE0 + i), 0xc9);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_POWER_SAVING_CTRL, SEL_TX_LANE0 + i), 0x00);
+               /* Configuration M-RX */
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD, SEL_RX_LANE0 + i), 0x06);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD_EN, SEL_RX_LANE0 + i), 0x00);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE, SEL_RX_LANE0 + i), 0x58);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_PVALUE1, SEL_RX_LANE0 + i), 0x8c);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_PVALUE2, SEL_RX_LANE0 + i), 0x02);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_OPTION, SEL_RX_LANE0 + i), 0xf6);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_POWER_SAVING_CTRL, SEL_RX_LANE0 + i), 0x69);
+               ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_SAVE_DET_CTRL, SEL_RX_LANE0 + i), 0x18);
+       }
+
+       /* disable the mphy DME_SET cfg */
+       ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MPHY_CFG, 0x0), MPHY_CFG_DISABLE);
+
+       ufs_rockchip_rk3576_phy_parameter_init(hba);
+
+       /* start link up */
+       ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MIB_T_DBG_CPORT_TX_ENDIAN, 0), 0x0);
+       ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(MIB_T_DBG_CPORT_RX_ENDIAN, 0), 0x0);
+       ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(N_DEVICEID, 0), 0x0);
+       ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(N_DEVICEID_VALID, 0), 0x1);
+       ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(T_PEERDEVICEID, 0), 0x1);
+       ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, 0), 0x1);
+
+       return 0;
+}
+
+static int ufs_rockchip_common_init(struct ufs_hba *hba)
+{
+       struct udevice *dev = hba->dev;
+       struct ufs_rockchip_host *host = dev_get_priv(dev);
+       struct resource res;
+       int err;
+
+       /* system control register for hci */
+       err = dev_read_resource_byname(dev, "hci_grf", &res);
+       if (err) {
+               dev_err(dev, "cannot ioremap for hci system control register\n");
+               return err;
+       }
+       host->ufs_sys_ctrl = (void *)(res.start);
+
+       /* system control register for mphy */
+       err = dev_read_resource_byname(dev, "mphy_grf", &res);
+       if (err) {
+               dev_err(dev, "cannot ioremap for mphy system control register\n");
+               return err;
+       }
+       host->ufs_phy_ctrl = (void *)(res.start);
+
+       /* mphy base register */
+       err = dev_read_resource_byname(dev, "mphy", &res);
+       if (err) {
+               dev_err(dev, "cannot ioremap for mphy base register\n");
+               return err;
+       }
+       host->mphy_base = (void *)(res.start);
+
+       host->phy_config_mode = dev_read_u32_default(dev, "ufs-phy-config-mode", 0);
+
+       err = reset_get_bulk(dev, &host->rsts);
+       if (err) {
+               dev_err(dev, "Can't get reset: %d\n", err);
+               return err;
+       }
+
+       host->hba = hba;
+
+       return 0;
+}
+
+static int ufs_rockchip_rk3576_init(struct ufs_hba *hba)
+{
+       int ret = 0;
+
+       ret = ufs_rockchip_common_init(hba);
+       if (ret) {
+               dev_err(hba->dev, "%s: ufs common init fail\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct ufs_hba_ops ufs_hba_rk3576_vops = {
+       .init = ufs_rockchip_rk3576_init,
+       .phy_initialization = ufs_rockchip_rk3576_phy_init,
+       .hce_enable_notify = ufs_rockchip_hce_enable_notify,
+};
+
+static const struct udevice_id ufs_rockchip_of_match[] = {
+       { .compatible = "rockchip,rk3576-ufshc", .data = (ulong)&ufs_hba_rk3576_vops},
+       {},
+};
+
+static int ufs_rockchip_probe(struct udevice *dev)
+{
+       struct ufs_hba_ops *ops = (struct ufs_hba_ops *)dev_get_driver_data(dev);
+       int err;
+
+       err = ufshcd_probe(dev, ops);
+       if (err)
+               dev_err(dev, "ufshcd_pltfrm_init() failed %d\n", err);
+
+       return err;
+}
+
+static int ufs_rockchip_bind(struct udevice *dev)
+{
+       struct udevice *scsi_dev;
+
+       return ufs_scsi_bind(dev, &scsi_dev);
+}
+
+U_BOOT_DRIVER(rockchip_ufs) = {
+       .name           = "ufshcd-rockchip",
+       .id             = UCLASS_UFS,
+       .of_match       = ufs_rockchip_of_match,
+       .probe          = ufs_rockchip_probe,
+       .bind           = ufs_rockchip_bind,
+       .priv_auto      = sizeof(struct ufs_rockchip_host),
+};
diff --git a/drivers/ufs/ufs-rockchip.h b/drivers/ufs/ufs-rockchip.h
new file mode 100644 (file)
index 0000000..3dcb80f
--- /dev/null
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip UFS Host Controller driver
+ *
+ * Copyright (C) 2025 Rockchip Electronics Co.Ltd.
+ */
+
+#ifndef _UFS_ROCKCHIP_H_
+#define _UFS_ROCKCHIP_H_
+
+#define UFS_MAX_CLKS 3
+
+#define SEL_TX_LANE0                   0x0
+#define SEL_TX_LANE1                   0x1
+#define SEL_TX_LANE2                   0x2
+#define SEL_TX_LANE3                   0x3
+#define SEL_RX_LANE0                   0x4
+#define SEL_RX_LANE1                   0x5
+#define SEL_RX_LANE2                   0x6
+#define SEL_RX_LANE3                   0x7
+
+#define VND_TX_CLK_PRD                  0xAA
+#define VND_TX_CLK_PRD_EN               0xA9
+#define VND_TX_LINERESET_PVALUE2        0xAB
+#define VND_TX_LINERESET_PVALUE1        0xAC
+#define VND_TX_LINERESET_VALUE          0xAD
+#define VND_TX_BASE_NVALUE              0x93
+#define VND_TX_TASE_VALUE               0x94
+#define VND_TX_POWER_SAVING_CTRL        0x7F
+#define VND_RX_CLK_PRD                  0x12
+#define VND_RX_CLK_PRD_EN               0x11
+#define VND_RX_LINERESET_PVALUE2        0x1B
+#define VND_RX_LINERESET_PVALUE1        0x1C
+#define VND_RX_LINERESET_VALUE          0x1D
+#define VND_RX_LINERESET_OPTION         0x25
+#define VND_RX_POWER_SAVING_CTRL        0x2F
+#define VND_RX_SAVE_DET_CTRL            0x1E
+
+#define CMN_REG23                       0x8C
+#define CMN_REG25                       0x94
+#define TRSV0_REG08                     0xE0
+#define TRSV1_REG08                     0x220
+#define TRSV0_REG14                     0x110
+#define TRSV1_REG14                     0x250
+#define TRSV0_REG15                     0x134
+#define TRSV1_REG15                     0x274
+#define TRSV0_REG16                     0x128
+#define TRSV1_REG16                     0x268
+#define TRSV0_REG17                     0x12C
+#define TRSV1_REG17                     0x26c
+#define TRSV0_REG18                     0x120
+#define TRSV1_REG18                     0x260
+#define TRSV0_REG29                     0x164
+#define TRSV1_REG29                     0x2A4
+#define TRSV0_REG2E                     0x178
+#define TRSV1_REG2E                     0x2B8
+#define TRSV0_REG3C                     0x1B0
+#define TRSV1_REG3C                     0x2F0
+#define TRSV0_REG3D                     0x1B4
+#define TRSV1_REG3D                     0x2F4
+
+#define MPHY_CFG                        0x200
+#define MPHY_CFG_ENABLE                 0x40
+#define MPHY_CFG_DISABLE                0x0
+
+#define MIB_T_DBG_CPORT_TX_ENDIAN      0xc022
+#define MIB_T_DBG_CPORT_RX_ENDIAN      0xc023
+
+struct ufs_rockchip_host {
+       struct ufs_hba *hba;
+       void __iomem *ufs_phy_ctrl;
+       void __iomem *ufs_sys_ctrl;
+       void __iomem *mphy_base;
+       struct reset_ctl_bulk rsts;
+       struct clk ref_out_clk;
+       uint64_t caps;
+       uint32_t phy_config_mode;
+};
+
+#define ufs_sys_writel(base, val, reg) \
+       writel((val), (base) + (reg))
+#define ufs_sys_readl(base, reg) readl((base) + (reg))
+#define ufs_sys_set_bits(base, mask, reg)      \
+       ufs_sys_writel((base), ((mask) | (ufs_sys_readl((base), (reg)))), (reg))
+#define ufs_sys_ctrl_clr_bits(base, mask, reg) \
+       ufs_sys_writel((base), ((~(mask)) & (ufs_sys_readl((base), (reg)))), (reg))
+
+#endif /* _UFS_ROCKCHIP_H_ */