- ti-j721e: Correct error detection
- Fix wrong bitfield usage for Data Direction in Transfer Request
- Add support for sending UFS attribute requests
- Add bRefClkFreq attribute setting
- Add ufshcd_dme_enable() and ufshcd_dme_reset()
- unipro: Add PA_SCRAMBLING property
- Cleanups:
 - Keep Makefile and Kconfig list sorted
 - Fold ufs-uclass into ufs and rename to ufs-uclass
 - amd-versal2: Fix indent
 - Call ufs_scsi_bind() from uclass .post_bind
 - renesas: Update Kconfig entry help text
- New plaforms:
 - Rockchip UFS
 - Mediatek UFS
 - Renesas R-Car X5H UFS
This commit is contained in:
Tom Rini
2025-10-29 07:41:43 -06:00
19 changed files with 3603 additions and 2172 deletions

View File

@@ -410,6 +410,7 @@ ARM MEDIATEK
M: Ryder Lee <ryder.lee@mediatek.com>
M: Weijie Gao <weijie.gao@mediatek.com>
M: Chunfeng Yun <chunfeng.yun@mediatek.com>
M: Igor Belwon <igor.belwon@mentallysanemainliners.org>
R: GSS_MTK_Uboot_upstream <GSS_MTK_Uboot_upstream@mediatek.com>
S: Maintained
F: arch/arm/mach-mediatek/
@@ -435,6 +436,7 @@ F: drivers/spi/mtk_snfi_spi.c
F: drivers/spi/mtk_spim.c
F: drivers/spi/mtk_snor.c
F: drivers/timer/mtk_timer.c
F: drivers/ufs/ufs-mediatek*
F: drivers/usb/host/xhci-mtk.c
F: drivers/usb/mtu3/
F: drivers/watchdog/mtk_wdt.c
@@ -575,6 +577,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

View File

@@ -15,6 +15,50 @@ config CADENCE_UFS
This selects the platform driver for the Cadence UFS host
controller present on present TI's J721e devices.
config UFS_MEDIATEK
tristate "MediaTek UFS Host Controller Driver"
depends on UFS && ARCH_MEDIATEK
select PHY_MTK_UFS
help
This selects the MediaTek specific additions to UFSHCD platform driver.
UFS host on Mediatek 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 MediaTek chipset.
If unsure, say N.
config QCOM_UFS
bool "Qualcomm Host Controller driver for UFS"
depends on UFS && ARCH_SNAPDRAGON
help
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
This selects the glue layer driver for Cadence controller
present on TI's J721E devices.
config UFS_AMD_VERSAL2
bool "AMD Versal Gen 2 UFS controller platform driver"
depends on UFS && ZYNQMP_FIRMWARE
help
This selects the AMD specific additions to UFSHCD platform driver.
UFS host on AMD needs some vendor specific configuration before accessing
the hardware.
config UFS_PCI
bool "PCI bus based UFS Controller support"
depends on PCI && UFS
@@ -26,34 +70,22 @@ config UFS_PCI
If unsure, say N.
config QCOM_UFS
bool "Qualcomm Host Controller driver for UFS"
depends on UFS && ARCH_SNAPDRAGON
help
This selects the platform driver for the UFS host
controller present on Qualcomm Snapdragon SoCs.
config TI_J721E_UFS
bool "Glue Layer driver for UFS on TI J721E devices"
help
This selects the glue layer driver for Cadence controller
present on TI's J721E devices.
config UFS_RENESAS
bool "Renesas specific hooks to UFS controller platform driver"
bool "Renesas R-Car S4 UFS Controller support"
depends on UFS
select BOUNCE_BUFFER
help
This selects the Renesas specific additions to UFSHCD platform driver.
UFS host on Renesas needs some vendor specific configuration before
accessing the hardware.
This selects the Renesas S4 specific additions to UFSHCD
platform driver. UFS host on Renesas needs some vendor
specific configuration before accessing the hardware.
config UFS_AMD_VERSAL2
bool "AMD Versal Gen 2 UFS controller platform driver"
depends on UFS && ZYNQMP_FIRMWARE
config UFS_RENESAS_GEN5
bool "Renesas R-Car X5H UFS Controller support"
depends on UFS
select BOUNCE_BUFFER
help
This selects the AMD specific additions to UFSHCD platform driver.
UFS host on AMD needs some vendor specific configuration before accessing
the hardware.
This selects the Renesas X5H specific additions to UFSHCD
platform driver. UFS host on Renesas needs some vendor
specific configuration before accessing the hardware.
endmenu

View File

@@ -3,10 +3,13 @@
# Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
#
obj-$(CONFIG_UFS) += ufs.o ufs-uclass.o
obj-$(CONFIG_UFS) += ufs-uclass.o
obj-$(CONFIG_CADENCE_UFS) += cdns-platform.o
obj-$(CONFIG_UFS_MEDIATEK) += ufs-mediatek.o
obj-$(CONFIG_QCOM_UFS) += ufs-qcom.o
obj-$(CONFIG_TI_J721E_UFS) += ti-j721e-ufs.o
obj-$(CONFIG_UFS_AMD_VERSAL2) += ufs-amd-versal2.o ufshcd-dwc.o
obj-$(CONFIG_UFS_PCI) += ufs-pci.o
obj-$(CONFIG_UFS_RENESAS) += ufs-renesas.o
obj-$(CONFIG_UFS_AMD_VERSAL2) += ufs-amd-versal2.o ufshcd-dwc.o
obj-$(CONFIG_UFS_RENESAS_GEN5) += ufs-renesas-rcar-gen5.o
obj-$(CONFIG_ROCKCHIP_UFS) += ufs-rockchip.o

View File

@@ -101,13 +101,6 @@ static int cdns_ufs_pltfm_probe(struct udevice *dev)
return err;
}
static int cdns_ufs_pltfm_bind(struct udevice *dev)
{
struct udevice *scsi_dev;
return ufs_scsi_bind(dev, &scsi_dev);
}
static const struct udevice_id cdns_ufs_pltfm_ids[] = {
{
.compatible = "cdns,ufshc-m31-16nm",
@@ -120,5 +113,4 @@ U_BOOT_DRIVER(cdns_ufs_pltfm) = {
.id = UCLASS_UFS,
.of_match = cdns_ufs_pltfm_ids,
.probe = cdns_ufs_pltfm_probe,
.bind = cdns_ufs_pltfm_bind,
};

View File

@@ -17,7 +17,7 @@
static int ti_j721e_ufs_probe(struct udevice *dev)
{
void __iomem *base;
unsigned int clock;
unsigned long clock;
struct clk clk;
u32 reg = 0;
int ret;
@@ -29,9 +29,9 @@ static int ti_j721e_ufs_probe(struct udevice *dev)
}
clock = clk_get_rate(&clk);
if (IS_ERR_VALUE(clock)) {
if ((long)clock <= 0) {
dev_err(dev, "failed to get rate\n");
return ret;
return clock ? clock : -EIO;
}
base = dev_remap_addr_index(dev, 0);

View File

@@ -330,7 +330,7 @@ static int ufs_versal2_init(struct ufs_hba *hba)
return PTR_ERR(priv->rstphy);
}
ret = zynqmp_pm_ufs_cal_reg(&cal);
ret = zynqmp_pm_ufs_cal_reg(&cal);
if (ret)
return ret;
@@ -552,13 +552,6 @@ static int ufs_versal2_probe(struct udevice *dev)
return ret;
}
static int ufs_versal2_bind(struct udevice *dev)
{
struct udevice *scsi_dev;
return ufs_scsi_bind(dev, &scsi_dev);
}
static const struct udevice_id ufs_versal2_ids[] = {
{
.compatible = "amd,versal2-ufs",
@@ -567,9 +560,8 @@ static const struct udevice_id ufs_versal2_ids[] = {
};
U_BOOT_DRIVER(ufs_versal2_pltfm) = {
.name = "ufs-versal2-pltfm",
.id = UCLASS_UFS,
.of_match = ufs_versal2_ids,
.probe = ufs_versal2_probe,
.bind = ufs_versal2_bind,
.name = "ufs-versal2-pltfm",
.id = UCLASS_UFS,
.of_match = ufs_versal2_ids,
.probe = ufs_versal2_probe,
};

View File

@@ -0,0 +1,56 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 MediaTek Inc.
* Copyright (c) 2025, Igor Belwon <igor.belwon@mentallysanemainliners.org>
*
* Slimmed down header from Linux: drivers/ufs/host/ufs-mediatek-sip.h
*/
#ifndef _UFS_MEDIATEK_SIP_H
#define _UFS_MEDIATEK_SIP_H
#include <linux/arm-smccc.h>
/*
* SiP (Slicon Partner) commands
*/
#define MTK_SIP_UFS_CONTROL ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, \
ARM_SMCCC_SMC_64, \
ARM_SMCCC_OWNER_SIP, 0x276)
#define UFS_MTK_SIP_DEVICE_RESET BIT(1)
#define UFS_MTK_SIP_REF_CLK_NOTIFICATION BIT(3)
/*
* SMC call wrapper function
*/
struct ufs_mtk_smc_arg {
unsigned long cmd;
struct arm_smccc_res *res;
unsigned long v1;
unsigned long v2;
unsigned long v3;
unsigned long v4;
unsigned long v5;
unsigned long v6;
unsigned long v7;
};
static inline void _ufs_mtk_smc(struct ufs_mtk_smc_arg s)
{
arm_smccc_smc(MTK_SIP_UFS_CONTROL,
s.cmd,
s.v1, s.v2, s.v3, s.v4, s.v5, s.v6, s.res);
}
#define ufs_mtk_smc(...) \
_ufs_mtk_smc((struct ufs_mtk_smc_arg) {__VA_ARGS__})
/* SIP interface */
#define ufs_mtk_ref_clk_notify(on, stage, res) \
ufs_mtk_smc(UFS_MTK_SIP_REF_CLK_NOTIFICATION, &(res), on, stage)
#define ufs_mtk_device_reset_ctrl(high, res) \
ufs_mtk_smc(UFS_MTK_SIP_DEVICE_RESET, &(res), high)
#endif /* !_UFS_MEDIATEK_SIP_H */

395
drivers/ufs/ufs-mediatek.c Normal file
View File

@@ -0,0 +1,395 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2025, Igor Belwon <igor.belwon@mentallysanemainliners.org>
*
* Loosely based on Linux driver: drivers/ufs/host/ufs-mediatek.c
*/
#include <asm/io.h>
#include <clk.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <generic-phy.h>
#include <ufs.h>
#include <asm/gpio.h>
#include <reset.h>
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/err.h>
#include "ufs.h"
#include "ufs-mediatek.h"
#include "ufs-mediatek-sip.h"
static void ufs_mtk_advertise_quirks(struct ufs_hba *hba)
{
hba->quirks |= UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL |
UFSHCD_QUIRK_MCQ_BROKEN_INTR |
UFSHCD_QUIRK_BROKEN_LSDBS_CAP;
}
static int ufs_mtk_hce_enable_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
struct ufs_mtk_host *host = dev_get_priv(hba->dev);
if (status == PRE_CHANGE) {
if (host->caps & UFS_MTK_CAP_DISABLE_AH8) {
ufshcd_writel(hba, 0,
REG_AUTO_HIBERNATE_IDLE_TIMER);
hba->capabilities &= ~MASK_AUTO_HIBERN8_SUPPORT;
}
/*
* Turn on CLK_CG early to bypass abnormal ERR_CHK signal
* to prevent host hang issue
*/
ufshcd_writel(hba,
ufshcd_readl(hba, REG_UFS_XOUFS_CTRL) | 0x80,
REG_UFS_XOUFS_CTRL);
/* DDR_EN setting */
if (host->ip_ver >= IP_VER_MT6989) {
ufshcd_rmwl(hba, UFS_MASK(0x7FFF, 8),
0x453000, REG_UFS_MMIO_OPT_CTRL_0);
}
}
return 0;
}
static int ufs_mtk_unipro_set_lpm(struct ufs_hba *hba, bool lpm)
{
int ret;
struct ufs_mtk_host *host = dev_get_priv(hba->dev);
ret = ufshcd_dme_set(hba,
UIC_ARG_MIB_SEL(VS_UNIPROPOWERDOWNCONTROL, 0),
lpm ? 1 : 0);
if (!ret || !lpm) {
/*
* Forcibly set as non-LPM mode if UIC commands is failed
* to use default hba_enable_delay_us value for re-enabling
* the host.
*/
host->unipro_lpm = lpm;
}
return ret;
}
static int ufs_mtk_pre_link(struct ufs_hba *hba)
{
int ret;
u32 tmp;
ret = ufs_mtk_unipro_set_lpm(hba, false);
if (ret)
return ret;
/*
* Setting PA_Local_TX_LCC_Enable to 0 before link startup
* to make sure that both host and device TX LCC are disabled
* once link startup is completed.
*/
ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0);
if (ret)
return ret;
/* disable deep stall */
ret = ufshcd_dme_get(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
if (ret)
return ret;
tmp &= ~(1 << 6);
ret = ufshcd_dme_set(hba, UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
if (ret)
return ret;
ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_SCRAMBLING), tmp);
return ret;
}
static void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable)
{
u32 tmp;
if (enable) {
ufshcd_dme_get(hba,
UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
tmp = tmp |
(1 << RX_SYMBOL_CLK_GATE_EN) |
(1 << SYS_CLK_GATE_EN) |
(1 << TX_CLK_GATE_EN);
ufshcd_dme_set(hba,
UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
ufshcd_dme_get(hba,
UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp);
tmp = tmp & ~(1 << TX_SYMBOL_CLK_REQ_FORCE);
ufshcd_dme_set(hba,
UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp);
} else {
ufshcd_dme_get(hba,
UIC_ARG_MIB(VS_SAVEPOWERCONTROL), &tmp);
tmp = tmp & ~((1 << RX_SYMBOL_CLK_GATE_EN) |
(1 << SYS_CLK_GATE_EN) |
(1 << TX_CLK_GATE_EN));
ufshcd_dme_set(hba,
UIC_ARG_MIB(VS_SAVEPOWERCONTROL), tmp);
ufshcd_dme_get(hba,
UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), &tmp);
tmp = tmp | (1 << TX_SYMBOL_CLK_REQ_FORCE);
ufshcd_dme_set(hba,
UIC_ARG_MIB(VS_DEBUGCLOCKENABLE), tmp);
}
}
static void ufs_mtk_post_link(struct ufs_hba *hba)
{
/* enable unipro clock gating feature */
ufs_mtk_cfg_unipro_cg(hba, true);
}
static int ufs_mtk_link_startup_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
int ret = 0;
switch (status) {
case PRE_CHANGE:
ret = ufs_mtk_pre_link(hba);
break;
case POST_CHANGE:
ufs_mtk_post_link(hba);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int ufs_mtk_bind_mphy(struct ufs_hba *hba)
{
struct ufs_mtk_host *host = dev_get_priv(hba->dev);
int err = 0;
err = generic_phy_get_by_index(hba->dev, 0, host->mphy);
if (IS_ERR(host->mphy)) {
err = PTR_ERR(host->mphy);
if (err != -ENODEV) {
dev_info(hba->dev, "%s: Could NOT get a valid PHY %d\n", __func__,
err);
}
}
if (err)
host->mphy = NULL;
return err;
}
static void ufs_mtk_init_reset_control(struct ufs_hba *hba,
struct reset_ctl **rc,
char *str)
{
*rc = devm_reset_control_get(hba->dev, str);
if (IS_ERR(*rc)) {
dev_info(hba->dev, "Failed to get reset control %s: %ld\n",
str, PTR_ERR(*rc));
*rc = NULL;
}
}
static void ufs_mtk_init_reset(struct ufs_hba *hba)
{
struct ufs_mtk_host *host = dev_get_priv(hba->dev);
ufs_mtk_init_reset_control(hba, &host->hci_reset,
"hci_rst");
ufs_mtk_init_reset_control(hba, &host->unipro_reset,
"unipro_rst");
ufs_mtk_init_reset_control(hba, &host->crypto_reset,
"crypto_rst");
}
static void ufs_mtk_get_hw_ip_version(struct ufs_hba *hba)
{
struct ufs_mtk_host *host = dev_get_priv(hba->dev);
u32 hw_ip_ver;
hw_ip_ver = ufshcd_readl(hba, REG_UFS_MTK_IP_VER);
if (((hw_ip_ver & (0xFF << 24)) == (0x1 << 24)) ||
((hw_ip_ver & (0xFF << 24)) == 0)) {
hw_ip_ver &= ~(0xFF << 24);
hw_ip_ver |= (0x1 << 28);
}
host->ip_ver = hw_ip_ver;
dev_info(hba->dev, "MediaTek UFS IP Version: 0x%x\n", hw_ip_ver);
}
static int ufs_mtk_setup_ref_clk(struct ufs_hba *hba, bool on)
{
struct ufs_mtk_host *host = dev_get_priv(hba->dev);
struct arm_smccc_res res;
int timeout, time_checked = 0;
u32 value;
if (host->ref_clk_enabled == on)
return 0;
ufs_mtk_ref_clk_notify(on, PRE_CHANGE, res);
if (on) {
ufshcd_writel(hba, REFCLK_REQUEST, REG_UFS_REFCLK_CTRL);
} else {
udelay(10);
ufshcd_writel(hba, REFCLK_RELEASE, REG_UFS_REFCLK_CTRL);
}
/* Wait for ack */
timeout = REFCLK_REQ_TIMEOUT_US;
do {
value = ufshcd_readl(hba, REG_UFS_REFCLK_CTRL);
/* Wait until ack bit equals to req bit */
if (((value & REFCLK_ACK) >> 1) == (value & REFCLK_REQUEST))
goto out;
udelay(200);
time_checked += 200;
} while (time_checked != timeout);
dev_err(hba->dev, "missing ack of refclk req, reg: 0x%x\n", value);
/*
* If clock on timeout, assume clock is off, notify tfa do clock
* off setting.(keep DIFN disable, release resource)
* If clock off timeout, assume clock will off finally,
* set ref_clk_enabled directly.(keep DIFN disable, keep resource)
*/
if (on)
ufs_mtk_ref_clk_notify(false, POST_CHANGE, res);
else
host->ref_clk_enabled = false;
return -ETIMEDOUT;
out:
host->ref_clk_enabled = on;
if (on)
udelay(10);
ufs_mtk_ref_clk_notify(on, POST_CHANGE, res);
return 0;
}
/**
* ufs_mtk_init - bind phy with controller
* @hba: host controller instance
*
* Powers up PHY enabling clocks and regulators.
*
* Returns -ENODEV if binding fails, returns negative error
* on phy power up failure and returns zero on success.
*/
static int ufs_mtk_init(struct ufs_hba *hba)
{
struct ufs_mtk_host *priv = dev_get_priv(hba->dev);
int err;
priv->hba = hba;
err = ufs_mtk_bind_mphy(hba);
if (err)
return -ENODEV;
ufs_mtk_advertise_quirks(hba);
ufs_mtk_init_reset(hba);
// TODO: Clocking
err = generic_phy_power_on(priv->mphy);
if (err) {
dev_err(hba->dev, "%s: phy init failed, err = %d\n",
__func__, err);
return err;
}
ufs_mtk_setup_ref_clk(hba, true);
ufs_mtk_get_hw_ip_version(hba);
return 0;
}
static int ufs_mtk_device_reset(struct ufs_hba *hba)
{
struct arm_smccc_res res;
ufs_mtk_device_reset_ctrl(0, res);
/*
* The reset signal is active low. UFS devices shall detect
* more than or equal to 1us of positive or negative RST_n
* pulse width.
*
* To be on safe side, keep the reset low for at least 10us.
*/
udelay(13);
ufs_mtk_device_reset_ctrl(1, res);
/* Some devices may need time to respond to rst_n */
mdelay(13);
dev_dbg(hba->dev, "device reset done\n");
return 0;
}
static struct ufs_hba_ops ufs_mtk_hba_ops = {
.init = ufs_mtk_init,
.hce_enable_notify = ufs_mtk_hce_enable_notify,
.link_startup_notify = ufs_mtk_link_startup_notify,
.device_reset = ufs_mtk_device_reset,
};
static int ufs_mtk_probe(struct udevice *dev)
{
int ret;
ret = ufshcd_probe(dev, &ufs_mtk_hba_ops);
if (ret) {
dev_err(dev, "ufshcd_probe() failed, ret:%d\n", ret);
return ret;
}
return 0;
}
static const struct udevice_id ufs_mtk_ids[] = {
{ .compatible = "mediatek,mt6878-ufshci" },
{},
};
U_BOOT_DRIVER(mediatek_ufshci) = {
.name = "mediatek-ufshci",
.id = UCLASS_UFS,
.of_match = ufs_mtk_ids,
.probe = ufs_mtk_probe,
.priv_auto = sizeof(struct ufs_mtk_host),
};

210
drivers/ufs/ufs-mediatek.h Normal file
View File

@@ -0,0 +1,210 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
* Copyright (c) 2025, Igor Belwon <igor.belwon@mentallysanemainliners.org>
*
* Slimmed down header from Linux: drivers/ufs/host/ufs-mediatek.h
*/
#ifndef _UFS_MEDIATEK_H
#define _UFS_MEDIATEK_H
#include <clk.h>
#include <linux/bitops.h>
/*
* MCQ define and struct
*/
#define UFSHCD_MAX_Q_NR 8
#define MTK_MCQ_INVALID_IRQ 0xFFFF
/* REG_UFS_MMIO_OPT_CTRL_0 160h */
#define EHS_EN BIT(0)
#define PFM_IMPV BIT(1)
#define MCQ_MULTI_INTR_EN BIT(2)
#define MCQ_CMB_INTR_EN BIT(3)
#define MCQ_AH8 BIT(4)
#define MCQ_INTR_EN_MSK (MCQ_MULTI_INTR_EN | MCQ_CMB_INTR_EN)
/*
* Vendor specific UFSHCI Registers
*/
#define REG_UFS_XOUFS_CTRL 0x140
#define REG_UFS_REFCLK_CTRL 0x144
#define REG_UFS_MMIO_OPT_CTRL_0 0x160
#define REG_UFS_EXTREG 0x2100
#define REG_UFS_MPHYCTRL 0x2200
#define REG_UFS_MTK_IP_VER 0x2240
#define REG_UFS_REJECT_MON 0x22AC
#define REG_UFS_DEBUG_SEL 0x22C0
#define REG_UFS_PROBE 0x22C8
#define REG_UFS_DEBUG_SEL_B0 0x22D0
#define REG_UFS_DEBUG_SEL_B1 0x22D4
#define REG_UFS_DEBUG_SEL_B2 0x22D8
#define REG_UFS_DEBUG_SEL_B3 0x22DC
#define REG_UFS_MTK_SQD 0x2800
#define REG_UFS_MTK_SQIS 0x2814
#define REG_UFS_MTK_CQD 0x281C
#define REG_UFS_MTK_CQIS 0x2824
#define REG_UFS_MCQ_STRIDE 0x30
/*
* Ref-clk control
*
* Values for register REG_UFS_REFCLK_CTRL
*/
#define REFCLK_RELEASE 0x0
#define REFCLK_REQUEST BIT(0)
#define REFCLK_ACK BIT(1)
#define REFCLK_REQ_TIMEOUT_US 3000
#define REFCLK_DEFAULT_WAIT_US 32
/*
* Other attributes
*/
#define VS_DEBUGCLOCKENABLE 0xD0A1
#define VS_SAVEPOWERCONTROL 0xD0A6
#define VS_UNIPROPOWERDOWNCONTROL 0xD0A8
/*
* Vendor specific link state
*/
enum {
VS_LINK_DISABLED = 0,
VS_LINK_DOWN = 1,
VS_LINK_UP = 2,
VS_LINK_HIBERN8 = 3,
VS_LINK_LOST = 4,
VS_LINK_CFG = 5,
};
/*
* Vendor specific host controller state
*/
enum {
VS_HCE_RESET = 0,
VS_HCE_BASE = 1,
VS_HCE_OOCPR_WAIT = 2,
VS_HCE_DME_RESET = 3,
VS_HCE_MIDDLE = 4,
VS_HCE_DME_ENABLE = 5,
VS_HCE_DEFAULTS = 6,
VS_HIB_IDLEEN = 7,
VS_HIB_ENTER = 8,
VS_HIB_ENTER_CONF = 9,
VS_HIB_MIDDLE = 10,
VS_HIB_WAITTIMER = 11,
VS_HIB_EXIT_CONF = 12,
VS_HIB_EXIT = 13,
};
/*
* VS_DEBUGCLOCKENABLE
*/
enum {
TX_SYMBOL_CLK_REQ_FORCE = 5,
};
/*
* VS_SAVEPOWERCONTROL
*/
enum {
RX_SYMBOL_CLK_GATE_EN = 0,
SYS_CLK_GATE_EN = 2,
TX_CLK_GATE_EN = 3,
};
/*
* Host capability
*/
enum ufs_mtk_host_caps {
UFS_MTK_CAP_BOOST_CRYPT_ENGINE = 1 << 0,
UFS_MTK_CAP_VA09_PWR_CTRL = 1 << 1,
UFS_MTK_CAP_DISABLE_AH8 = 1 << 2,
UFS_MTK_CAP_BROKEN_VCC = 1 << 3,
/*
* Override UFS_MTK_CAP_BROKEN_VCC's behavior to
* allow vccqx upstream to enter LPM
*/
UFS_MTK_CAP_ALLOW_VCCQX_LPM = 1 << 5,
UFS_MTK_CAP_PMC_VIA_FASTAUTO = 1 << 6,
UFS_MTK_CAP_TX_SKEW_FIX = 1 << 7,
UFS_MTK_CAP_DISABLE_MCQ = 1 << 8,
/* Control MTCMOS with RTFF */
UFS_MTK_CAP_RTFF_MTCMOS = 1 << 9,
UFS_MTK_CAP_MCQ_BROKEN_RTC = 1 << 10,
};
struct ufs_mtk_hw_ver {
u8 step;
u8 minor;
u8 major;
};
struct ufs_mtk_mcq_intr_info {
struct ufs_hba *hba;
u32 irq;
u8 qid;
};
struct ufs_mtk_host {
struct phy *mphy;
struct reset_ctl *unipro_reset;
struct reset_ctl *crypto_reset;
struct reset_ctl *hci_reset;
struct ufs_hba *hba;
struct ufs_mtk_crypt_cfg *crypt;
struct clk_bulk clks;
struct ufs_mtk_hw_ver hw_ver;
enum ufs_mtk_host_caps caps;
bool mphy_powered_on;
bool unipro_lpm;
bool ref_clk_enabled;
bool is_clks_enabled;
u16 ref_clk_ungating_wait_us;
u16 ref_clk_gating_wait_us;
u32 ip_ver;
bool legacy_ip_ver;
bool mcq_set_intr;
bool is_mcq_intr_enabled;
int mcq_nr_intr;
struct ufs_mtk_mcq_intr_info mcq_intr_info[UFSHCD_MAX_Q_NR];
};
/* MTK delay of autosuspend: 500 ms */
#define MTK_RPM_AUTOSUSPEND_DELAY_MS 500
/* MTK RTT support number */
#define MTK_MAX_NUM_RTT 2
/* UFSHCI MTK ip version value */
enum {
/* UFSHCI 3.1 */
IP_VER_MT6983 = 0x10360000,
IP_VER_MT6878 = 0x10420200,
/* UFSHCI 4.0 */
IP_VER_MT6897 = 0x10440000,
IP_VER_MT6989 = 0x10450000,
IP_VER_MT6899 = 0x10450100,
IP_VER_MT6991_A0 = 0x10460000,
IP_VER_MT6991_B0 = 0x10470000,
IP_VER_MT6993 = 0x10480000,
IP_VER_NONE = 0xFFFFFFFF
};
enum ip_ver_legacy {
IP_LEGACY_VER_MT6781 = 0x10380000,
IP_LEGACY_VER_MT6879 = 0x10360000,
IP_LEGACY_VER_MT6893 = 0x20160706
};
#endif /* !_UFS_MEDIATEK_H */

View File

@@ -11,13 +11,6 @@
#include <dm/device_compat.h>
#include "ufs.h"
static int ufs_pci_bind(struct udevice *dev)
{
struct udevice *scsi_dev;
return ufs_scsi_bind(dev, &scsi_dev);
}
static int ufs_pci_probe(struct udevice *dev)
{
int err;
@@ -32,7 +25,6 @@ static int ufs_pci_probe(struct udevice *dev)
U_BOOT_DRIVER(ufs_pci) = {
.name = "ufs_pci",
.id = UCLASS_UFS,
.bind = ufs_pci_bind,
.probe = ufs_pci_probe,
};

View File

@@ -648,13 +648,6 @@ static int ufs_qcom_probe(struct udevice *dev)
return 0;
}
static int ufs_qcom_bind(struct udevice *dev)
{
struct udevice *scsi_dev;
return ufs_scsi_bind(dev, &scsi_dev);
}
static const struct udevice_id ufs_qcom_ids[] = {
{ .compatible = "qcom,ufshc" },
{},
@@ -665,6 +658,5 @@ U_BOOT_DRIVER(qcom_ufshcd) = {
.id = UCLASS_UFS,
.of_match = ufs_qcom_ids,
.probe = ufs_qcom_probe,
.bind = ufs_qcom_bind,
.priv_auto = sizeof(struct ufs_qcom_priv),
};

View File

@@ -0,0 +1,268 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Renesas UFS host controller driver
*
* Copyright (C) 2025 Renesas Electronics Corporation
*/
#include <clk.h>
#include <dm.h>
#include <ufs.h>
#include <asm/io.h>
#include <dm/device_compat.h>
#include <linux/iopoll.h>
#include <reset.h>
#include "ufs.h"
#define UFS_RENESAS_TIMEOUT_US 100000
struct ufs_renesas_priv {
struct clk_bulk clks;
struct reset_ctl_bulk resets;
fdt_addr_t phy_base;
/* The hardware needs initialization once */
bool initialized;
};
static void ufs_dme_command(struct ufs_hba *hba, u32 cmd,
u32 arg1, u32 arg2, u32 arg3)
{
ufshcd_writel(hba, arg1, REG_UIC_COMMAND_ARG_1);
ufshcd_writel(hba, arg2, REG_UIC_COMMAND_ARG_2);
ufshcd_writel(hba, arg3, REG_UIC_COMMAND_ARG_3);
ufshcd_writel(hba, cmd, REG_UIC_COMMAND);
}
static int ufs_renesas_pre_init(struct ufs_hba *hba)
{
struct ufs_renesas_priv *priv = dev_get_priv(hba->dev);
u32 val32;
u16 val16;
int ret;
writew(0x0001, priv->phy_base + 0x20000);
writew(0x005c, priv->phy_base + 0x20212);
writew(0x005c, priv->phy_base + 0x20214);
writew(0x005c, priv->phy_base + 0x20216);
writew(0x005c, priv->phy_base + 0x20218);
writew(0x036a, priv->phy_base + 0x201d0);
writew(0x0102, priv->phy_base + 0x201d2);
writew(0x001f, priv->phy_base + 0x20082);
writew(0x000b, priv->phy_base + 0x20084);
writew(0x0126, priv->phy_base + 0x201d2);
writew(0x01dc, priv->phy_base + 0x20214);
writew(0x01dc, priv->phy_base + 0x20218);
writew(0x0000, priv->phy_base + 0x201cc);
writew(0x0200, priv->phy_base + 0x201ce);
writew(0x0000, priv->phy_base + 0x20212);
writew(0x0000, priv->phy_base + 0x20216);
ret = readw_poll_timeout(priv->phy_base + 0x201ec, val16,
!(val16 & BIT(12)), UFS_RENESAS_TIMEOUT_US);
if (ret)
return ret;
ret = readw_poll_timeout(priv->phy_base + 0x201e4, val16,
!(val16 & BIT(12)), UFS_RENESAS_TIMEOUT_US);
if (ret)
return ret;
ret = readw_poll_timeout(priv->phy_base + 0x201f0, val16,
!(val16 & BIT(12)), UFS_RENESAS_TIMEOUT_US);
if (ret)
return ret;
writew(0x0000, priv->phy_base + 0x20000);
ufshcd_writel(hba, BIT(0), REG_CONTROLLER_ENABLE);
ret = read_poll_timeout(ufshcd_readl, val32, (val32 & BIT(0)),
1, UFS_RENESAS_TIMEOUT_US,
hba, REG_CONTROLLER_ENABLE);
if (ret)
return ret;
ret = read_poll_timeout(ufshcd_readl, val32, (val32 & BIT(3)),
1, UFS_RENESAS_TIMEOUT_US,
hba, REG_CONTROLLER_STATUS);
if (ret)
return ret;
/* Skip IE because we cannot handle interrupts here */
ufs_dme_command(hba, 0x00000002, 0x81010000, 0x00000000, 0x00000005);
ufs_dme_command(hba, 0x00000002, 0x81150000, 0x00000000, 0x00000001);
ufs_dme_command(hba, 0x00000002, 0x81180000, 0x00000000, 0x00000001);
ufs_dme_command(hba, 0x00000002, 0x80090000, 0x00000000, 0x00000000);
ufs_dme_command(hba, 0x00000002, 0x800a0000, 0x00000000, 0x000000c8);
ufs_dme_command(hba, 0x00000002, 0x80090001, 0x00000000, 0x00000000);
ufs_dme_command(hba, 0x00000002, 0x800a0001, 0x00000000, 0x000000c8);
ufs_dme_command(hba, 0x00000002, 0x800a0004, 0x00000000, 0x00000000);
ufs_dme_command(hba, 0x00000002, 0x800b0004, 0x00000000, 0x00000064);
ufs_dme_command(hba, 0x00000002, 0x800a0005, 0x00000000, 0x00000000);
ufs_dme_command(hba, 0x00000002, 0x800b0005, 0x00000000, 0x00000064);
ufs_dme_command(hba, 0x00000002, 0xd0850000, 0x00000000, 0x00000001);
writew(0x0001, priv->phy_base + 0x20000);
clrbits_le16(priv->phy_base + 0x20022, BIT(0));
ret = readw_poll_timeout(priv->phy_base + (0x00198 << 1), val16,
(val16 & BIT(0)), UFS_RENESAS_TIMEOUT_US);
if (ret)
return ret;
writew(0x0368, priv->phy_base + 0x201d0);
ret = readw_poll_timeout(priv->phy_base + 0x201e4, val16,
!(val16 & BIT(11)), UFS_RENESAS_TIMEOUT_US);
if (ret)
return ret;
ret = readw_poll_timeout(priv->phy_base + 0x201e8, val16,
!(val16 & BIT(11)), UFS_RENESAS_TIMEOUT_US);
if (ret)
return ret;
ret = readw_poll_timeout(priv->phy_base + 0x201ec, val16,
!(val16 & BIT(11)), UFS_RENESAS_TIMEOUT_US);
if (ret)
return ret;
ret = readw_poll_timeout(priv->phy_base + 0x201f0, val16,
!(val16 & BIT(11)), UFS_RENESAS_TIMEOUT_US);
if (ret)
return ret;
priv->initialized = true;
return 0;
}
static int ufs_renesas_init(struct ufs_hba *hba)
{
hba->quirks |= UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS | UFSHCD_QUIRK_HIBERN_FASTAUTO |
UFSHCD_QUIRK_BROKEN_LCC;
return 0;
}
static int ufs_renesas_hce_enable_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
struct ufs_renesas_priv *priv = dev_get_priv(hba->dev);
int ret;
if (priv->initialized)
return 0;
if (status == PRE_CHANGE) {
ret = ufs_renesas_pre_init(hba);
if (ret)
return ret;
}
priv->initialized = true;
return 0;
}
static int ufs_renesas_link_startup_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
if (status == PRE_CHANGE)
return ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0);
return 0;
}
static int ufs_renesas_get_max_pwr_mode(struct ufs_hba *hba,
struct ufs_pwr_mode_info *max_pwr_info)
{
max_pwr_info->info.gear_rx = UFS_HS_G5;
max_pwr_info->info.gear_tx = UFS_HS_G5;
max_pwr_info->info.pwr_tx = FASTAUTO_MODE;
max_pwr_info->info.pwr_rx = FASTAUTO_MODE;
max_pwr_info->info.hs_rate = PA_HS_MODE_A;
max_pwr_info->info.lane_rx = 1;
max_pwr_info->info.lane_tx = 1;
dev_info(hba->dev, "Max HS Gear: %d\n", max_pwr_info->info.gear_rx);
return 0;
}
static struct ufs_hba_ops ufs_renesas_vops = {
.init = ufs_renesas_init,
.hce_enable_notify = ufs_renesas_hce_enable_notify,
.link_startup_notify = ufs_renesas_link_startup_notify,
.get_max_pwr_mode = ufs_renesas_get_max_pwr_mode,
};
static int ufs_renesas_pltfm_probe(struct udevice *dev)
{
struct ufs_renesas_priv *priv = dev_get_priv(dev);
int ret;
priv->phy_base = dev_read_addr_name(dev, "phy");
if (priv->phy_base == FDT_ADDR_T_NONE)
return -EINVAL;
ret = reset_get_bulk(dev, &priv->resets);
if (ret < 0)
return ret;
ret = clk_get_bulk(dev, &priv->clks);
if (ret < 0)
goto err_clk_get;
ret = clk_enable_bulk(&priv->clks);
if (ret)
goto err_clk_enable;
reset_assert_bulk(&priv->resets);
reset_deassert_bulk(&priv->resets);
ret = ufshcd_probe(dev, &ufs_renesas_vops);
if (ret) {
dev_err(dev, "ufshcd_probe() failed %d\n", ret);
goto err_ufshcd_probe;
}
return 0;
err_ufshcd_probe:
reset_assert_bulk(&priv->resets);
clk_disable_bulk(&priv->clks);
err_clk_enable:
clk_release_bulk(&priv->clks);
err_clk_get:
reset_release_bulk(&priv->resets);
return ret;
}
static int ufs_renesas_pltfm_remove(struct udevice *dev)
{
struct ufs_renesas_priv *priv = dev_get_priv(dev);
reset_release_bulk(&priv->resets);
clk_disable_bulk(&priv->clks);
clk_release_bulk(&priv->clks);
return 0;
}
static const struct udevice_id ufs_renesas_pltfm_ids[] = {
{ .compatible = "renesas,r8a78000-ufs" },
{ /* sentinel */ }
};
U_BOOT_DRIVER(ufs_renesas) = {
.name = "ufs-renesas-gen5",
.id = UCLASS_UFS,
.of_match = ufs_renesas_pltfm_ids,
.probe = ufs_renesas_pltfm_probe,
.remove = ufs_renesas_pltfm_remove,
.priv_auto = sizeof(struct ufs_renesas_priv),
};

View File

@@ -351,13 +351,6 @@ static struct ufs_hba_ops ufs_renesas_vops = {
.hce_enable_notify = ufs_renesas_hce_enable_notify,
};
static int ufs_renesas_pltfm_bind(struct udevice *dev)
{
struct udevice *scsi_dev;
return ufs_scsi_bind(dev, &scsi_dev);
}
static int ufs_renesas_pltfm_probe(struct udevice *dev)
{
struct ufs_renesas_priv *priv = dev_get_priv(dev);
@@ -405,7 +398,6 @@ U_BOOT_DRIVER(ufs_renesas) = {
.name = "ufs-renesas",
.id = UCLASS_UFS,
.of_match = ufs_renesas_pltfm_ids,
.bind = ufs_renesas_pltfm_bind,
.probe = ufs_renesas_pltfm_probe,
.remove = ufs_renesas_pltfm_remove,
.priv_auto = sizeof(struct ufs_renesas_priv),

203
drivers/ufs/ufs-rockchip.c Normal file
View File

@@ -0,0 +1,203 @@
// 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;
}
U_BOOT_DRIVER(rockchip_ufs) = {
.name = "ufshcd-rockchip",
.id = UCLASS_UFS,
.of_match = ufs_rockchip_of_match,
.probe = ufs_rockchip_probe,
.priv_auto = sizeof(struct ufs_rockchip_host),
};

View File

@@ -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_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -77,6 +77,9 @@ enum {
/* UTP Transfer Request Command Offset */
#define UPIU_COMMAND_TYPE_OFFSET 28
/* UTP Transfer Request Data Direction Offset */
#define UPIU_DATA_DIRECTION_OFFSET 25
/* Offset of the response code in the UPIU header */
#define UPIU_RSP_CODE_OFFSET 8
@@ -172,6 +175,15 @@ enum query_opcode {
UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8,
};
/* bRefClkFreq attribute values */
enum ufs_ref_clk_freq {
REF_CLK_FREQ_19_2_MHZ = 0,
REF_CLK_FREQ_26_MHZ = 1,
REF_CLK_FREQ_38_4_MHZ = 2,
REF_CLK_FREQ_52_MHZ = 3,
REF_CLK_FREQ_INVAL = -1,
};
/* Query response result code */
enum {
QUERY_RESULT_SUCCESS = 0x00,
@@ -435,6 +447,8 @@ int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
u8 attr_set, u32 mib_val, u8 peer);
int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
u32 *mib_val, u8 peer);
int ufshcd_dme_enable(struct ufs_hba *hba);
int ufshcd_dme_reset(struct ufs_hba *hba);
static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel,
u32 mib_val)

View File

@@ -125,6 +125,7 @@
#define PA_RXPWRSTATUS 0x1582
#define PA_RXGEAR 0x1583
#define PA_RXTERMINATION 0x1584
#define PA_SCRAMBLING 0x1585
#define PA_MAXRXPWMGEAR 0x1586
#define PA_MAXRXHSGEAR 0x1587
#define PA_PACPREQTIMEOUT 0x1590