ATF BL1 UFS初始化简单分析

本文详细介绍了在海思HiKey960平台上的ATFBL1UFS初始化过程,包括调用链路、hikey960_ufs_init函数、dw_ufs_init函数以及ufs_init中的具体步骤和功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以海思hikey960为例来介绍,简单介绍在ATF BL1阶段的初始化处理。

1 ATF的下载链接

https://github.com/ARM-software/arm-trusted-firmware

可以通过下面的命令来下载ATF的代码,或者通过打包下载的方式也可以。

git clone git@github.com:ARM-software/arm-trusted-firmware.git

2 ATF BL1 UFS 初始化简易流程图

在这里插入图片描述

3 ATF BL1 ufs初始化简单过程分析

3.1 调用过程

以以海思hikey960为例来介绍ATF BL1 ufs 初始化的调用关系

| -- bl1_main -------- bl1_main.c
	| - bl1_platform_setup -------- plat/hisilicon/hikey960/hikey960_bl1_setup.c
		| - hikey960_ufs_init -------- plat/hisilicon/hikey960/hikey960_bl1_setup.c
			| - hikey960_ufs_reset -------- plat/hisilicon/hikey960/hikey960_bl1_setup.c
			| - dw_ufs_init -------- drivers/synopsys/ufs/dw_ufs.c
				| - ufs_init  -------- drivers/synopsys/ufs/dw_ufs.c

3.2 hikey960_ufs_init

  • UFS_REG_BASE表示UFS 配置空间的基地址
  • HIKEY960_UFS_DESC_BASE表示ufs 描述符的基地址
  • HIKEY960_UFS_DESC_SIZE表示ufs描述符的大小
  • (ufs_params.flags & UFS_FLAGS_SKIPINIT) == 0当前ufs驱动的flag如果是skipinit则先去做复位操作,对于hikey960来说是不会参与该处理流程的。
  • dw_ufs_init(&ufs_params);dw ufs的初始化处理
static void hikey960_ufs_init(void)
{
        dw_ufs_params_t ufs_params;
        
        memset(&ufs_params, 0, sizeof(ufs_params));
        ufs_params.reg_base = UFS_REG_BASE;
        ufs_params.desc_base = HIKEY960_UFS_DESC_BASE;                                                                                                                                                                               
        ufs_params.desc_size = HIKEY960_UFS_DESC_SIZE;

        if ((ufs_params.flags & UFS_FLAGS_SKIPINIT) == 0)
                hikey960_ufs_reset();
        dw_ufs_init(&ufs_params);
}

3.3 dw_ufs_init

  • .phy_init ufs phy初始化的处理
  • .phy_set_pwr_mode ufs phy 电源模式设置
  • ufs_init(&dw_ufs_ops, &ufs_params); dw ufs以dw_ufs_ops作为初始化ops以及ufs_params配置参数去做对应的dw ufs初始化操作。
static const ufs_ops_t dw_ufs_ops = {
        .phy_init               = dwufs_phy_init,
        .phy_set_pwr_mode       = dwufs_phy_set_pwr_mode,
};

int dw_ufs_init(dw_ufs_params_t *params)
{       
        ufs_params_t ufs_params;

        memset(&ufs_params, 0, sizeof(ufs_params));
        ufs_params.reg_base = params->reg_base;
        ufs_params.desc_base = params->desc_base;
        ufs_params.desc_size = params->desc_size;
        ufs_params.flags = params->flags;                                                                                                                                                                                            
        ufs_init(&dw_ufs_ops, &ufs_params);
        return 0;
}

3.3 ufs_init

  • nutrs = (mmio_read_32(ufs_params.reg_base + CAP) & CAP_NUTRS_MASK) + 1;读取CAP寄存器去获取UTP传输请求槽数量Number of UTP Transfer Request Slots (NUTRS)。当前在BL1阶段采用的是Legacy Single Doorbell mode
    Number of UTP Transfer Request Slots (NUTRS): For Legacy Single Doorbell mode, this indicates the number of slots provided by the UTP Transfer Request List. A minimum of 1 and maximum of 32 slots may be supported.
    UTP传输请求槽数 (NUTRS): 对于传统单门铃模式,它表示UTP 传输请求列表提供的槽数量。可支持最少 1 个、最多 32 个插槽。
    For MCQ mode, this field specifies how many active transfer tasks the Host HW controller is capable of managing in parallel. The minimum of 1 and maximum of 256 slots may be supported.
    对于 MCQ 模式,该字段指定主机硬件控制器能够并行管理的活动传输任务数量。最少可支持 1 个插槽,最多可支持 256 个插槽。
    在这里插入图片描述
  • ufshc_reset(ufs_params.reg_base); ufs 复位操作
  • ops->phy_init(&ufs_params); ufs phy初始化,这个会调用在dw_ufs_init注册的ops的phy_init接口,即dwufs_phy_init
  • ufshc_link_startup(ufs_params.reg_base);执行linkstartup
  • ufs_get_device_info(&card);获取当前UFS设备的设备描述符
  • ops->phy_set_pwr_mode(&ufs_params);设置当前ufs的工作电源模式,会调用到dw_ufs_init注册的opsphy_set_pwr_mode接口,即dwufs_phy_set_pwr_mode函数
int ufs_init(const ufs_ops_t *ops, ufs_params_t *params)
{
        int result;
        unsigned int data;
        uic_cmd_t cmd;
        struct ufs_dev_desc card = {0};

        assert((params != NULL) &&
               (params->reg_base != 0) &&
               (params->desc_base != 0) &&
               (params->desc_size >= UFS_DESC_SIZE));

        memcpy(&ufs_params, params, sizeof(ufs_params_t));

        /* 0 means 1 slot */
        nutrs = (mmio_read_32(ufs_params.reg_base + CAP) & CAP_NUTRS_MASK) + 1;
        if (nutrs > (ufs_params.desc_size / UFS_DESC_SIZE)) {
                nutrs = ufs_params.desc_size / UFS_DESC_SIZE;
        }


        if (ufs_params.flags & UFS_FLAGS_SKIPINIT) {
                mmio_write_32(ufs_params.reg_base + UTRLBA,
                              ufs_params.desc_base & UINT32_MAX);
                mmio_write_32(ufs_params.reg_base + UTRLBAU,
                              (ufs_params.desc_base >> 32) & UINT32_MAX);

                result = ufshc_dme_get(0x1571, 0, &data);
                assert(result == 0);
                result = ufshc_dme_get(0x41, 0, &data);
                assert(result == 0);
                if (data == 1) {
                        /* prepare to exit hibernate mode */
                        memset(&cmd, 0, sizeof(uic_cmd_t));
                        cmd.op = DME_HIBERNATE_EXIT;                                                                                                                                                                                                                                                                                                                                                                                                                      
                        result = ufshc_send_uic_cmd(ufs_params.reg_base,
                                                    &cmd);
                        assert(result == 0);
                        data = mmio_read_32(ufs_params.reg_base + UCMDARG2);
                        assert(data == 0);
                        do {
                                data = mmio_read_32(ufs_params.reg_base + IS);
                        } while ((data & UFS_INT_UHXS) == 0);
                        mmio_write_32(ufs_params.reg_base + IS, UFS_INT_UHXS);
                        data = mmio_read_32(ufs_params.reg_base + HCS);
                        assert((data & HCS_UPMCRS_MASK) == HCS_PWR_LOCAL);
                }
                result = ufshc_dme_get(0x1568, 0, &data);
                assert(result == 0);
                assert((data > 0) && (data <= 3));
        } else {
                assert((ops != NULL) && (ops->phy_init != NULL) &&
                       (ops->phy_set_pwr_mode != NULL));

                result = ufshc_reset(ufs_params.reg_base);
                assert(result == 0);
                ops->phy_init(&ufs_params);
                result = ufshc_link_startup(ufs_params.reg_base);
                assert(result == 0);

                /* enable all interrupts */
                data = UFS_INT_UCCS | UFS_INT_UHES | UFS_INT_UHXS | UFS_INT_UPMS;
                data |= UFS_INT_UTRCS | UFS_INT_ERR;
                mmio_write_32(ufs_params.reg_base + IE, data);

                ufs_enum();

                ufs_get_device_info(&card);
                if (card.wmanufacturerid == UFS_VENDOR_SKHYNIX) {
                        ufs_params.flags |= UFS_FLAGS_VENDOR_SKHYNIX;
                }

                ops->phy_set_pwr_mode(&ufs_params);
        }

        (void)result;
        return 0;
}

<think>好的,我现在需要分析UFS初始化过程中反复进退H8状态的原因。首先,我需要了解H8状态是什么。根据参考内容中的引用,H8是UFS设备的一种低功耗状态。在初始化过程中,设备可能需要多次进入和退出这个状态,可能是为了进行不同的配置或检查。 参考引用[1]提到ATF中的UFS驱动初始化流程,特别是物理层(M-PHY)的处理,这可能涉及到电源管理。另外,引用[4]指出主机设置fDeviceInit后轮询直到设备完成初始化,这可能与状态切换有关。H8状态的反复进出可能是在初始化不同阶段需要调整电源状态,以节省功耗或确保硬件正确配置。 另外,引用[3]中的内核初始化流程显示,在探测和添加逻辑单元时可能会进行多次操作,每次操作可能需要设备处于不同的电源状态。例如,某些配置步骤可能需要设备处于活跃状态,完成后又进入低功耗状态,从而导致反复切换。 可能的另一个原因是错误处理。如果在初始化过程中遇到错误,设备可能退回H8状态,然后重新尝试初始化步骤,导致多次状态切换。此外,协议规范可能要求在特定步骤前后进入低功耗模式,以确保信号稳定或电路状态正确。 需要进一步确认UFS协议中关于H8状态的具体规定,以及初始化流程中各步骤如何触发状态转换。结合引用[2]提到的初始化过程,可能涉及多个阶段的配置,每个阶段完成后设备进入H8等待下一步指令,主机确认后再唤醒设备继续初始化,从而导致反复进退H8。</think>UFS初始化过程中反复进退H8状态的原因分析如下: ### 1. **H8状态的定义与作用** H8是UFS协议定义的低功耗模式,设备在此状态下可显著降低功耗[^4]。在初始化过程中,设备需在不同阶段切换电源状态以完成配置: - **硬件初始化**:M-PHY层需通过H8状态进行电气参数校准和信号完整性检查,此时需短暂退出H8以激活物理层[^1]。 - **协议层协商**:主机与设备需通过DME(设备管理实体)命令交互(如`DME_GET/SET`),执行这些命令时需设备处于活跃状态,完成后立即返回H8以节省功耗[^1][^4]。 ### 2. **初始化阶段的多步骤触发** - **引导配置阶段**:主机需多次下发初始化指令(如设置`fDeviceInit`),每次操作后设备需短暂唤醒以响应,随后返回H8等待下一指令[^2][^4]。 - **错误恢复机制**:若某步骤失败(如寄存器写入超时),主机会触发设备复位并重新进入H8,随后重试初始化流程[^3]。 ### 3. **协议规范与功耗优化** - **状态切换合规性**:JESD220标准要求设备在非活动时段进入H8,主机需按协议规范严格管理状态切换时序。 - **动态功耗管理**:为平衡初始化速度与功耗,设备在等待主机命令时自动进入H8,减少静态电流损耗。 ### 4. **典型案例分析** 例如在`ufshcd_async_scan`函数中,内核需依次执行探测HBA、添加逻辑单元等操作。每次探测前需唤醒设备,完成后立即切回H8,导致多次状态切换。 ```c // 示例:内核初始化流程中的状态切换(简化) static void ufshcd_async_scan(void *data) { ufshcd_probe_hba(hba, true); // 唤醒设备进行探测 ufshcd_add_lus(hba); // 再次唤醒添加逻辑单元 // 每次操作后触发H8进入 } ```
评论 75
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值