Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions components/drivers/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
662 changes: 662 additions & 0 deletions components/drivers/include/drivers/ufs.h

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions components/drivers/include/rtdevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,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"
Expand Down
16 changes: 16 additions & 0 deletions components/drivers/ufs/Kconfig
Original file line number Diff line number Diff line change
@@ -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 changes: 18 additions & 0 deletions components/drivers/ufs/SConscript
Original file line number Diff line number Diff line change
@@ -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 changes: 116 additions & 0 deletions components/drivers/ufs/ufs-pci.c
Original file line number Diff line number Diff line change
@@ -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;
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug/缺陷]: pci_ufs_probe() chooses ufs->ops as quirk ? quirk->ops : &pci_ufs_std_ops, which can set ops to NULL when quirk exists but quirk->ops is not provided.

English: Please match the pattern used by AHCI/NVMe PCI drivers: ufs->ops = (quirk && quirk->ops) ? quirk->ops : &pci_ufs_std_ops; to avoid rt_ufs_host_register() failing/crashing.
中文:pci_ufs_probe() 使用 quirk ? quirk->ops : &pci_ufs_std_ops 选择 ops,当 quirk 存在但 quirk->ops 未设置时会把 ops 置为 NULL,导致后续注册失败/潜在崩溃。建议参考 AHCI/NVMe PCI 驱动的写法:(quirk && quirk->ops) ? quirk->ops : &pci_ufs_std_ops

Suggested change
ufs->ops = quirk ? quirk->ops : &pci_ufs_std_ops;
ufs->ops = (quirk && quirk->ops) ? quirk->ops : &pci_ufs_std_ops;

Copilot uses AI. Check for mistakes.

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:
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug/缺陷]: pci_ufs_probe() failure path leaks resources.

English: If rt_ufs_host_register() fails after rt_pci_iomap()/rt_pci_set_master()/rt_pci_irq_unmask(), the _fail label only frees pci_ufs and does not: mask IRQ, clear bus master, or iounmap regs. Please add cleanup similar to components/drivers/ata/ahci-pci.c (disable/mask IRQ, rt_pci_clear_master, rt_iounmap).
中文:pci_ufs_probe() 在 rt_pci_iomap()/rt_pci_set_master()/rt_pci_irq_unmask() 之后若 rt_ufs_host_register() 失败,_fail 仅 rt_free(pci_ufs),会泄露/遗留资源:未 mask IRQ、未 clear master、未 iounmap regs。建议参考 components/drivers/ata/ahci-pci.c 的清理流程(mask/disable IRQ、rt_pci_clear_master、rt_iounmap)。

Suggested change
_fail:
_fail:
if (ufs->regs)
{
rt_pci_irq_mask(pdev);
rt_pci_clear_master(pdev);
rt_iounmap(ufs->regs);
}

Copilot uses AI. Check for mistakes.
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);
Loading
Loading