圆山中庸 2025-10-10 10:20 采纳率: 98.6%
浏览 1
已采纳

全志IIC drv模式下如何正确配置时钟频率?

在全志平台的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频率偏低,原因可归纳为以下几类:

    1. 时钟源未使能或频率错误:若CLK_APB1未正确配置或动态调频后未更新,I²C控制器将基于错误的输入时钟计算分频系数。
    2. 驱动未读取设备树参数:部分旧版全志I²C驱动忽略clock-frequency,使用默认值(如100kHz)初始化。
    3. 分频算法不准确:全志I²C控制器使用特定公式计算高/低电平周期,若算法实现有误会导致占空比失衡或频率偏低。
    4. 运行时APB时钟变更未同步:系统进入低功耗模式时APB频率降低,但I²C寄存器未重新配置,导致SCL变慢。
    5. 多时钟域处理不当:某些SoC(如H6)需同时管理bus_clkapb_clk,任一时钟异常均影响最终频率。
    6. 寄存器写保护或访问顺序错误:部分寄存器需按特定顺序写入或解锁才能生效。
    7. 上拉电阻不匹配:虽非软件问题,但过大的上拉电阻会延长上升时间,示波器测量显示“有效频率”下降。
    8. DTS编译或加载失败:设备树未正确烧录或被覆盖,导致配置未加载。
    9. 内核版本差异:不同主线或 BSP 内核对全志 I²C 驱动支持程度不同。
    10. 共享总线竞争:多个主设备或从设备响应延迟影响时序稳定性。

    三、深入驱动层:全志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
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 10月23日
  • 创建了问题 10月10日