4、设备树配置:UFS控制器设备树节点、时钟与电源配置、PHY参数配置
设备树配置,说白了就是告诉内核:「你的UFS控制器长什么样?接在哪个时钟线上?电源怎么给?PHY怎么调?」。这一步要是配错了,驱动加载时直接卡死,连错误日志都来不及打印。我当年第一次移植海力士UFS时,就因为在设备树里少配了一个时钟,结果内核启动到一半就挂了,查了两天才找到原因。
4.1 UFS控制器设备树节点
先看一个完整的UFS控制器节点长什么样。嗯,这里要注意,不同SoC的写法略有差异,但核心结构是一样的。
/* 海力士UFS控制器设备树节点示例 */
ufs: ufs@15570000 {
compatible = "samsung,exynos-ufs", "jedec,ufs-2.0";
reg = <0x0 0x15570000 0x0 0x100>,
<0x0 0x15570100 0x0 0x100>,
<0x0 0x15571000 0x0 0x200>;
reg-names = "hci", "vendor", "unipro";
interrupts = <GIC_SPI 200 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clock CLK_UFS_MPHY>,
<&clock CLK_UFS_UNIPRO>,
<&clock CLK_UFS_TCON>;
clock-names = "mphy", "unipro", "tcon";
pinctrl-names = "default";
pinctrl-0 = <&ufs_rst_n &ufs_refclk_out>;
phys = <&ufs_phy>;
phy-names = "ufs-phy";
status = "okay";
};
这里我解释几个关键字段:
- compatible:驱动匹配的关键。我习惯把SoC厂商的compatible放在前面,然后是jedec标准。这样既保证兼容性,又方便后续扩展。
- reg和reg-names:UFS控制器内部有多个寄存器区域。HCI是主控接口,Vendor是厂商私有寄存器,Unipro是协议层寄存器。这三个区域必须配全,少一个驱动就报错。
- interrupts:UFS中断号。注意这里用的是GIC_SPI类型,具体中断号要看SoC手册。我遇到过中断号配错的情况,结果驱动一直轮询,性能直接掉一半。
重要提醒:reg-names的字符串必须和驱动里request_mem_region用的名字完全一致,大小写都不能错。我曾经因为把"hci"写成了"HCI",浪费了一整天。
4.2 时钟与电源配置
时钟配置是UFS设备树里最容易出问题的地方。UFS需要三个时钟:MPHY时钟、Unipro时钟和TCON时钟。这三个时钟的频率和相位必须严格匹配。
| 时钟名称 | 典型频率 | 作用 |
|---|---|---|
| MPHY时钟 | 26MHz | 物理层时钟,用于M-PHY模块 |
| Unipro时钟 | 100MHz | 协议层时钟,用于Unipro协议处理 |
| TCON时钟 | 200MHz | 传输层时钟,用于数据收发 |
电源配置方面,UFS设备通常需要两个电源域:
/* 电源配置示例 */
vcc_ufs: regulator-vcc-ufs {
compatible = "regulator-fixed";
regulator-name = "vcc_ufs";
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
gpio = <&gpa1 0 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
vccq_ufs: regulator-vccq-ufs {
compatible = "regulator-fixed";
regulator-name = "vccq_ufs";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
gpio = <&gpa1 1 GPIO_ACTIVE_HIGH>;
enable-active-high;
};
然后在UFS节点里引用:
ufs@15570000 {
...
vcc-supply = <&vcc_ufs>;
vccq-supply = <&vccq_ufs>;
vccq2-supply = <&vccq_ufs>;
};
个人经验:我建议在调试阶段把vccq2-supply也配成和vccq一样。虽然有些UFS设备不需要vccq2,但配上了不会出问题,不配反而可能因为电源域缺失导致初始化失败。
4.3 PHY参数配置
PHY参数配置是UFS设备树里最玄学的部分。说白了,就是告诉PHY芯片:「你的眼图要调成什么样?信号摆幅多大?预加重多少?」。这些参数直接决定了UFS链路的稳定性。
先看一个完整的PHY节点:
/* UFS PHY设备树节点 */
ufs_phy: ufs-phy@15571800 {
compatible = "samsung,exynos-ufs-phy";
reg = <0x0 0x15571800 0x0 0x100>;
reg-names = "phy";
clocks = <&clock CLK_UFS_MPHY>;
clock-names = "mphy";
samsung,pmu-syscon = <&pmu_system_controller>;
#phy-cells = <0>;
/* PHY参数配置 */
samsung,hs-gear = <4>; /* HS-G4 */
samsung,pwr-opt = <0>;
samsung,hs-series = <1>; /* 1: HS系列, 0: PWM系列 */
/* 眼图参数 */
samsung,eye-param {
tx-diff-amp = <0x28>; /* 差分摆幅 */
tx-pre-emphasis = <0x0A>; /* 预加重 */
tx-post-emphasis = <0x05>; /* 后加重 */
rx-eq-level = <0x03>; /* 接收均衡 */
};
status = "okay";
};
这里有几个关键参数需要特别关注:
- samsung,hs-gear:指定最高速率。HS-G4对应的是11.6Gbps。我建议刚开始调试时先用HS-G2,等链路稳定了再往上提。
- tx-diff-amp:差分信号摆幅。这个值太小信号传不远,太大又容易产生EMI。我一般从0x28开始调,然后根据眼图测试结果微调。
- tx-pre-emphasis:预加重。高频信号在传输过程中会有衰减,预加重就是提前把高频分量放大。这个值要根据PCB走线长度来调,没有固定公式。
避坑指南:我曾经遇到过一个问题——PHY参数配好后,UFS设备能识别,但跑压力测试时频繁掉盘。查了两天,最后发现是rx-eq-level设得太高,导致接收端过均衡。把值从0x05降到0x03后,问题就解决了。所以PHY参数一定要在实际工况下验证,不能只看识别结果。
4.4 知识体系总览
为了让你更直观地理解UFS设备树配置的整体结构,我画了一张图:
从这张图可以看出来,UFS设备树配置其实就三大块:时钟、电源、PHY参数。这三者缺一不可,而且相互关联。比如时钟频率不对,PHY的速率等级就上不去;电源电压不稳,眼图参数调得再好也没用。
我的调试习惯:拿到一块新板子,我会先把时钟和电源配好,确保UFS控制器能正常识别。然后再调PHY参数,从最低速率开始,逐步往上提。每提一档就跑一遍压力测试,确保链路稳定。这样一步步来,出问题了也知道是哪个环节的事。
好了,设备树配置这部分就讲到这里。记住一句话:设备树配得好,驱动调试少烦恼。下一节我们聊聊UFS驱动的初始化流程,到时候会用到今天配的这些参数。
公众号:蓝海资料掘金营,微信deep3321