在全志平台的I²C驱动(drv)模式下,如何正确配置I²C总线时钟频率?常见问题表现为:尽管在设备树中设置了`clock-frequency`属性(如400000表示400kHz),但实际测量的SCL频率偏低或未达到预期。该问题通常源于时钟源配置错误、分频系数计算不准确或驱动未正确解析设备树参数。此外,某些全志SoC(如A64、H6)的I²C控制器对时钟源依赖性强,若APB时钟频率被动态调整而未同步更新I²C寄存器,也会导致频率偏差。如何结合设备树配置与驱动代码,确保时钟频率精确生成?
1条回答 默认 最新
程昱森 2025-10-10 10:20关注一、I²C总线时钟配置基础:设备树与硬件接口
在全志平台(如A64、H6等SoC)中,I²C控制器通常挂载在APB(Advanced Peripheral Bus)总线上。其工作频率依赖于APB时钟源(
apb_clk)的输出,并通过分频机制生成SCL信号。标准Linux设备树(Device Tree)中通过以下属性配置I²C频率:
/ { i2c0: i2c@1c2ac00 { compatible = "allwinner,sun50i-a64-i2c"; reg = <0x01c2ac00 0x400>; interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>; clocks = <&ccu CLK_BUS_I2C0>, <&ccu CLK_APB1>; clock-names = "bus", "apb"; clock-frequency = <400000>; // 目标400kHz status = "okay"; }; };其中,
clock-frequency是用户期望的SCL频率,但实际是否生效取决于驱动是否正确解析该值并写入控制器寄存器。二、常见问题分析:为何设置未生效?
尽管设备树中设置了
clock-frequency,实测SCL频率偏低,原因可归纳为以下几类:- 时钟源未使能或频率错误:若
CLK_APB1未正确配置或动态调频后未更新,I²C控制器将基于错误的输入时钟计算分频系数。 - 驱动未读取设备树参数:部分旧版全志I²C驱动忽略
clock-frequency,使用默认值(如100kHz)初始化。 - 分频算法不准确:全志I²C控制器使用特定公式计算高/低电平周期,若算法实现有误会导致占空比失衡或频率偏低。
- 运行时APB时钟变更未同步:系统进入低功耗模式时APB频率降低,但I²C寄存器未重新配置,导致SCL变慢。
- 多时钟域处理不当:某些SoC(如H6)需同时管理
bus_clk和apb_clk,任一时钟异常均影响最终频率。 - 寄存器写保护或访问顺序错误:部分寄存器需按特定顺序写入或解锁才能生效。
- 上拉电阻不匹配:虽非软件问题,但过大的上拉电阻会延长上升时间,示波器测量显示“有效频率”下降。
- DTS编译或加载失败:设备树未正确烧录或被覆盖,导致配置未加载。
- 内核版本差异:不同主线或 BSP 内核对全志 I²C 驱动支持程度不同。
- 共享总线竞争:多个主设备或从设备响应延迟影响时序稳定性。
三、深入驱动层:全志I²C频率设置流程
以Linux内核中的
drivers/i2c/busses/i2c-sunxi.c为例,频率配置发生在sunxi_i2c_probe()函数中:static int sunxi_i2c_probe(struct platform_device *pdev) { struct sunxi_i2c *i2c; struct device_node *np = pdev->dev.of_node; u32 freq = 100000; // 默认100kHz of_property_read_u32(np, "clock-frequency", &freq); i2c->freq = min(freq, 400000U); // 获取时钟 i2c->clk_apb = devm_clk_get(&pdev->dev, "apb"); i2c->clk_bus = devm_clk_get(&pdev->dev, "bus"); clk_prepare_enable(i2c->clk_apb); clk_prepare_enable(i2c->clk_bus); // 计算分频系数 apb_freq = clk_get_rate(i2c->clk_apb); ret = sunxi_i2c_calc_timings(apb_freq, i2c->freq, &timings); if (ret) return ret; // 写入寄存器 sunxi_i2c_set_timings(i2c, &timings); }关键点在于:
clk_get_rate()必须返回当前真实的APB时钟频率,否则后续分频计算将出错。四、分频系数计算模型与校准方法
全志I²C控制器(如A64)使用如下公式计算分频参数:
参数 含义 公式 T_SCL_HSCL高电平周期 (CNT_H + 1) × 8 / APB_CLK T_SCL_LSCL低电平周期 (CNT_L + 1) × 8 / APB_CLK T_CYCLE总周期 T_SCL_H + T_SCL_L ≥ 1 / freq CNT_H高电平计数器 ≥ (APB_CLK / (8 × freq)) × 占空比 - 1 CNT_L低电平计数器 ≥ (APB_CLK / (8 × freq)) × (1 - 占空比) - 1 典型实现中采用50%~70%高电平占比,需确保上下限满足I²C规范(如400kHz模式下T_CYCLE ≥ 2.5μs)。
五、动态时钟适应性设计
当APB时钟因DVFS(动态电压频率调节)发生变化时,I²C频率会自动偏移。解决方案包括:
- 注册
clk_notifier监听APB时钟变化事件; - 在回调函数中重新计算并写入新的分频参数;
- 使用PM QoS锁定APB频率(适用于实时性要求高的场景);
- 在每次传输前检查当前时钟速率并动态调整寄存器。
示例代码片段:
static struct notifier_block apb_clk_nb = { .notifier_call = apb_clock_change_notify, }; static int apb_clock_change_notify(struct notifier_block *nb, unsigned long action, void *data) { if (action == POST_RATE_CHANGE) { recompute_i2c_timings(); update_i2c_registers(); } return NOTIFY_OK; }六、调试与验证流程图
为系统化排查I²C频率异常问题,建议遵循以下流程:
graph TD A[开始] --> B{设备树中设置clock-frequency?} B -- 否 --> C[添加clock-frequency属性] B -- 是 --> D[确认DTS已编译并加载] D --> E[检查驱动是否解析该属性] E --> F[读取APB时钟频率clk_get_rate()] F --> G[验证分频系数计算逻辑] G --> H[用示波器测量SCL波形] H --> I{频率是否符合预期?} I -- 否 --> J[检查上拉电阻/负载] I -- 是 --> K[完成] J --> L[调整分频算法或固定APB时钟] L --> G本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报- 时钟源未使能或频率错误:若