[dm][ufs] support Universal Flash Storage (UFS)

Support UFS over PCI, too.

Signed-off-by: GuEe-GUI <2991707448@qq.com>
This commit is contained in:
GuEe-GUI
2026-05-04 18:41:16 +08:00
committed by Rbb666
parent c1a43bc0de
commit ea13820c2c
8 changed files with 1912 additions and 0 deletions
+1
View File
@@ -30,6 +30,7 @@ rsource "ata/Kconfig"
rsource "nvme/Kconfig"
rsource "block/Kconfig"
rsource "scsi/Kconfig"
rsource "ufs/Kconfig"
rsource "firmware/Kconfig"
rsource "hwcache/Kconfig"
rsource "regulator/Kconfig"
File diff suppressed because it is too large Load Diff
+4
View File
@@ -135,6 +135,10 @@ extern "C" {
#include "drivers/thermal.h"
#endif /* RT_USING_THERMAL */
#ifdef RT_USING_UFS
#include "drivers/ufs.h"
#endif /* RT_USING_UFS */
#ifdef RT_USING_FIRMWARE
#ifdef RT_FIRMWARE_ARM_SCMI
#include "drivers/scmi.h"
+16
View File
@@ -0,0 +1,16 @@
menuconfig RT_USING_UFS
bool "Using Universal Flash Storage (UFS) device drivers"
depends on RT_USING_DM
depends on RT_USING_DMA
depends on RT_SCSI_SD
default n
config RT_UFS_PCI
bool "UFS support on PCI bus"
depends on RT_USING_UFS
depends on RT_USING_PCI
default n
if RT_USING_UFS
osource "$(SOC_DM_UFS_DIR)/Kconfig"
endif
+18
View File
@@ -0,0 +1,18 @@
from building import *
group = []
if not GetDepend(['RT_USING_UFS']):
Return('group')
cwd = GetCurrentDir()
CPPPATH = [cwd + '/../include']
src = ['ufs.c', 'ufs_pm.c']
if GetDepend(['RT_UFS_PCI']):
src += ['ufs-pci.c']
group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH)
Return('group')
+116
View File
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rtthread.h>
#include <rtdevice.h>
#define UFS_REG_BAR 0
struct pci_ufs_quirk
{
const struct rt_ufs_ops *ops;
};
struct pci_ufs_host
{
struct rt_ufs_host parent;
const struct pci_ufs_quirk *quirk;
};
static const struct rt_ufs_ops pci_ufs_std_ops =
{
};
static rt_err_t pci_ufs_probe(struct rt_pci_device *pdev)
{
rt_err_t err;
struct rt_ufs_host *ufs;
struct pci_ufs_host *pci_ufs = rt_calloc(1, sizeof(*pci_ufs));
const struct pci_ufs_quirk *quirk = pdev->id->data;
if (!pci_ufs)
{
return -RT_ENOMEM;
}
pci_ufs->quirk = quirk;
ufs = &pci_ufs->parent;
ufs->parent.dev = &pdev->parent;
ufs->regs = rt_pci_iomap(pdev, UFS_REG_BAR);
ufs->irq = pdev->irq;
if (!ufs->regs)
{
err = -RT_EIO;
goto _fail;
}
ufs->ops = quirk ? quirk->ops : &pci_ufs_std_ops;
rt_pci_irq_unmask(pdev);
rt_pci_set_master(pdev);
if ((err = rt_ufs_host_register(ufs)))
{
goto _fail;
}
pdev->parent.user_data = pci_ufs;
return RT_EOK;
_fail:
rt_free(pci_ufs);
return err;
}
static rt_err_t pci_ufs_remove(struct rt_pci_device *pdev)
{
struct rt_ufs_host *ufs;
struct pci_ufs_host *pci_ufs = pdev->parent.user_data;
ufs = &pci_ufs->parent;
rt_ufs_host_unregister(ufs);
/* INTx is shared, don't mask all */
rt_hw_interrupt_umask(pdev->irq);
rt_pci_irq_mask(pdev);
rt_pci_clear_master(pdev);
rt_iounmap(ufs->regs);
rt_free(pci_ufs);
return RT_EOK;
}
static rt_err_t pci_ufs_shutdown(struct rt_pci_device *pdev)
{
return pci_ufs_remove(pdev);
}
static const struct rt_pci_device_id pci_ufs_ids[] =
{
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_REDHAT, 0x0013), },
{ RT_PCI_DEVICE_ID(PCI_VENDOR_ID_SAMSUNG, 0xc00c), },
{ /* sentinel */ }
};
static struct rt_pci_driver pci_ufs_driver =
{
.name = "ufs-pci",
.ids = pci_ufs_ids,
.probe = pci_ufs_probe,
.remove = pci_ufs_remove,
.shutdown = pci_ufs_shutdown,
};
RT_PCI_DRIVER_EXPORT(pci_ufs_driver);
+783
View File
File diff suppressed because it is too large Load Diff
+312
View File
@@ -0,0 +1,312 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2023-02-25 GuEe-GUI the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/ufs.h>
#define DBG_TAG "rtdm.ufs.pm"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#define RT_UFS_DME_ATTR_SET_NOR 0
rt_inline rt_uint32_t ufs_uic_arg_attr_type(rt_uint8_t t)
{
return ((rt_uint32_t)(t) & 0xffu) << 16;
}
rt_err_t rt_ufs_dme_set(struct rt_ufs_host *ufs, rt_uint32_t attr_sel, rt_uint32_t value)
{
rt_err_t err;
rt_uint32_t arg2 = ufs_uic_arg_attr_type(RT_UFS_DME_ATTR_SET_NOR);
if ((err = rt_ufs_uic_cmd_send(ufs, RT_UFS_CMDOP_DME_SET, attr_sel, &arg2, value)))
{
return err;
}
if ((arg2 & RT_UFS_CMDRES_MASK) != RT_UFS_CMDRES_SUCCESS)
{
return -RT_ERROR;
}
return RT_EOK;
}
rt_err_t rt_ufs_dme_get(struct rt_ufs_host *ufs, rt_uint32_t attr_sel, rt_uint32_t *value)
{
rt_uint32_t arg2 = 0;
rt_err_t err;
if (!value)
{
return -RT_EINVAL;
}
if ((err = rt_ufs_uic_cmd_send(ufs, RT_UFS_CMDOP_DME_GET, attr_sel, &arg2, 0)))
{
return err;
}
if ((arg2 & RT_UFS_CMDRES_MASK) != RT_UFS_CMDRES_SUCCESS)
{
return -RT_ERROR;
}
*value = HWREG32(ufs->regs + RT_UFS_REG_UCMDARG3);
return RT_EOK;
}
rt_err_t rt_ufs_uic_pa_pwrmode(struct rt_ufs_host *ufs, rt_uint8_t mode)
{
return rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_PWRMODE), mode);
}
rt_err_t rt_ufs_pa_power_mode_set(struct rt_ufs_host *ufs, const struct rt_ufs_pa_layer_attr *attr, rt_bool_t force)
{
rt_err_t err;
rt_uint8_t mode;
if (!ufs || !attr || !ufs->regs)
{
return -RT_EINVAL;
}
if (!force && ufs->pwr_active_valid &&
attr->gear_rx == ufs->pwr_active.gear_rx &&
attr->gear_tx == ufs->pwr_active.gear_tx &&
attr->lane_rx == ufs->pwr_active.lane_rx &&
attr->lane_tx == ufs->pwr_active.lane_tx &&
attr->pwr_rx == ufs->pwr_active.pwr_rx &&
attr->pwr_tx == ufs->pwr_active.pwr_tx &&
attr->hs_rate == ufs->pwr_active.hs_rate)
{
return RT_EOK;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_RXGEAR), attr->gear_rx)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_ACTIVERXDATALANES), attr->lane_rx)))
{
return err;
}
if (attr->pwr_rx == RT_UFS_PA_FASTAUTO_MODE || attr->pwr_rx == RT_UFS_PA_FAST_MODE)
{
err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_RXTERMINATION), 1);
}
else
{
err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_RXTERMINATION), 0);
}
if (err)
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_TXGEAR), attr->gear_tx)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_ACTIVETXDATALANES), attr->lane_tx)))
{
return err;
}
if (attr->pwr_tx == RT_UFS_PA_FASTAUTO_MODE || attr->pwr_tx == RT_UFS_PA_FAST_MODE)
{
err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_TXTERMINATION), 1);
}
else
{
err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_TXTERMINATION), 0);
}
if (err)
{
return err;
}
if (attr->pwr_rx == RT_UFS_PA_FASTAUTO_MODE || attr->pwr_tx == RT_UFS_PA_FASTAUTO_MODE ||
attr->pwr_rx == RT_UFS_PA_FAST_MODE || attr->pwr_tx == RT_UFS_PA_FAST_MODE)
{
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_HSSERIES), attr->hs_rate)))
{
return err;
}
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_PWRMODEUSERDATA0), RT_UFS_DL_FC0_PROT_TO_DEFAULT)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_PWRMODEUSERDATA1), RT_UFS_DL_TC0_REPLAY_TO_DEFAULT)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_PWRMODEUSERDATA2), RT_UFS_DL_AFC0_REQ_TO_DEFAULT)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_PWRMODEUSERDATA3), RT_UFS_DL_FC1_PROT_TO_DEFAULT)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_PWRMODEUSERDATA4), RT_UFS_DL_TC1_REPLAY_TO_DEFAULT)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_PA_PWRMODEUSERDATA5), RT_UFS_DL_AFC1_REQ_TO_DEFAULT)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_DME_LOCAL_FC0_PROT_TO), RT_UFS_DL_FC0_PROT_TO_DEFAULT)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_DME_LOCAL_TC0_REPLAY_TO), RT_UFS_DL_TC0_REPLAY_TO_DEFAULT)))
{
return err;
}
if ((err = rt_ufs_dme_set(ufs, RT_UFS_UIC_ARG_MIB(RT_UFS_DME_LOCAL_AFC0_REQ_TO), RT_UFS_DL_AFC0_REQ_TO_DEFAULT)))
{
return err;
}
mode = (rt_uint8_t)((attr->pwr_rx << 4) | (attr->pwr_tx & 0xfu));
if ((err = rt_ufs_uic_pa_pwrmode(ufs, mode)))
{
LOG_E("%s: PA power mode change failed: %s", rt_dm_dev_get_name(ufs->parent.dev), rt_strerror(err));
return err;
}
ufs->pwr_active = *attr;
ufs->pwr_active_valid = 1;
return RT_EOK;
}
rt_err_t rt_ufs_hibern8_enter(struct rt_ufs_host *ufs)
{
rt_uint32_t arg2 = 0;
if (!ufs || !ufs->regs)
{
return -RT_EINVAL;
}
if (rt_ufs_uic_cmd_send(ufs, RT_UFS_CMDOP_DME_HIBERNATE_ENTER, 0, &arg2, 0))
{
return -RT_ERROR;
}
if ((arg2 & RT_UFS_CMDRES_MASK) != RT_UFS_CMDRES_SUCCESS)
{
return -RT_ERROR;
}
return RT_EOK;
}
rt_err_t rt_ufs_hibern8_exit(struct rt_ufs_host *ufs)
{
rt_uint32_t arg2 = 0;
if (!ufs || !ufs->regs)
{
return -RT_EINVAL;
}
if (rt_ufs_uic_cmd_send(ufs, RT_UFS_CMDOP_DME_HIBERNATE_EXIT, 0, &arg2, 0))
{
return -RT_ERROR;
}
if ((arg2 & RT_UFS_CMDRES_MASK) != RT_UFS_CMDRES_SUCCESS)
{
return -RT_ERROR;
}
return RT_EOK;
}
rt_err_t rt_ufs_auto_hibern8_set(struct rt_ufs_host *ufs, rt_uint32_t reg_val)
{
if (!ufs || !ufs->regs)
{
return -RT_EINVAL;
}
if (!(ufs->cap & RT_UFS_REG_CAP_AUTOH8))
{
return -RT_ENOSYS;
}
HWREG32(ufs->regs + RT_UFS_REG_AHIT) = reg_val;
ufs->ahit = reg_val;
return RT_EOK;
}
void rt_ufs_intr_aggr_configure(struct rt_ufs_host *ufs, rt_bool_t enable, rt_uint8_t cnt, rt_uint8_t timeout)
{
void *regs;
if (!ufs || !(regs = ufs->regs))
{
return;
}
if (!enable)
{
HWREG32(regs + RT_UFS_REG_UTRIACR) = 0;
return;
}
/* Same layout as Linux ufshcd_config_intr_aggr */
HWREG32(regs + RT_UFS_REG_UTRIACR) = RT_UFS_UTRIACR_ENABLE_PARAMS(cnt, timeout);
}
void rt_ufs_pm_post_linkup(struct rt_ufs_host *ufs)
{
rt_uint8_t cnt;
if (!ufs || !ufs->regs)
{
return;
}
/* Linux INT_AGGR_DEF_TO = 2 (~80µs), counter = nutrs - 1 */
cnt = (ufs->nutrs > 0) ? (rt_uint8_t)(ufs->nutrs - 1) : 0;
if (cnt > 31)
{
cnt = 31;
}
rt_ufs_intr_aggr_configure(ufs, RT_TRUE, cnt, 2);
if (ufs->cap & RT_UFS_REG_CAP_AUTOH8)
{
rt_uint32_t ahit = ufs->ahit ? ufs->ahit : RT_UFS_AHIT_DEFAULT;
if (rt_ufs_auto_hibern8_set(ufs, ahit))
{
LOG_W("%s: Auto-Hibernate8 (AHIT) not applied", rt_dm_dev_get_name(ufs->parent.dev));
}
else
{
LOG_D("%s: AHIT=%#08x (auto-Hibernate8)", rt_dm_dev_get_name(ufs->parent.dev), ahit);
}
}
}