Files
u-boot/drivers/ufs/ufs-rockchip.c
Alexey Charkov c7299ff33e ufs: rockchip: Make use of controller resets
Assert Rockchip UFS controller resets during initialization and HCE
enable, as it is done by the Linux driver.

This is required to make some UFS chips, such as Foresee FEUDNN064G-C2G0,
work properly.

Note that the resets were already requested in the probe function, just
not used.

Signed-off-by: Alexey Charkov <alchark@flipper.net>
Link: https://patch.msgid.link/20260316-rockchip-ufs-reset-v1-1-808eb017459a@flipper.net
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
2026-04-22 10:03:54 +02:00

233 lines
7.1 KiB
C

// SPDX-License-Identifier: GPL-2.0+
/*
* Rockchip UFS Host Controller driver
*
* Copyright (C) 2025 Rockchip Electronics Co.Ltd.
*/
#include <asm/gpio.h>
#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 "unipro.h"
#include "ufs-rockchip.h"
static void ufs_rockchip_controller_reset(struct ufs_rockchip_host *host)
{
reset_assert_bulk(&host->rsts);
udelay(1);
reset_deassert_bulk(&host->rsts);
}
static int ufs_rockchip_hce_enable_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
int err = 0;
if (status != POST_CHANGE) {
ufs_rockchip_controller_reset(dev_get_priv(hba->dev));
return 0;
}
ufshcd_dme_reset(hba);
ufshcd_dme_enable(hba);
err = ufshcd_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;
}
ufs_rockchip_controller_reset(host);
err = gpio_request_by_name(dev, "reset-gpios", 0, &host->device_reset,
GPIOD_IS_OUT | GPIOD_ACTIVE_LOW);
if (err) {
dev_err(dev, "Cannot get reset GPIO\n");
return err;
}
host->hba = hba;
return 0;
}
static int ufs_rockchip_device_reset(struct ufs_hba *hba)
{
struct ufs_rockchip_host *host = dev_get_priv(hba->dev);
dm_gpio_set_value(&host->device_reset, true);
udelay(20);
dm_gpio_set_value(&host->device_reset, false);
udelay(20);
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,
.device_reset = ufs_rockchip_device_reset,
};
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),
};