mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-08-18 11:53:05 +08:00
[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:
@@ -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"
|
||||
|
||||
Executable
+662
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
|
||||
Executable
+16
@@ -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
|
||||
Executable
+18
@@ -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')
|
||||
Executable
+116
@@ -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);
|
||||
Executable
+783
File diff suppressed because it is too large
Load Diff
Executable
+312
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user