Linux设备树中MCP2518FD的时钟配置:从0.00MHz到40.00MHz

Linux设备树中MCP2518FD的时钟配置:从0.00MHz到40.00MHz

[!TIP] 午夜码农的咏叹调 啊,键盘上的勇士,夜深人静战bug, 代码如顽敌,纠缠至天明将近。 眼皮如铅重,咖啡已成空杯, 仅剩三时安眠,闹钟已张开巨口。 然此时灵光乍现,博客须发于世! 笑看睡眠债堆积,技术火炬永不灭。 程序猿啊,程序猿,人生如代码,debug永无止境!

问题背景

在使用MCP2518FD CAN控制器时,通过dmesg查看驱动初始化日志,发现了一个值得关注的细节:

[    3.207754] mcp251xfd spi0.0 can0: MCP2518FD rev15.15 (-RX_INT -PLL -MAB_NO_WARN +CRC_REG +CRC_RX +CRC_TX +ECC -HD o:0.00MHz c:40.00MHz m:0.60MHz rs:0.60MHz es:0.59MHz rf:0.60MHz ef:0.59MHz) successfully initialized.

注意其中的 o:0.00MHz,这表示驱动检测到的外部晶振(Oscillator)频率为0,而实际硬件使用的是40MHz晶振。这种不匹配可能会导致时钟相关的潜在问题。

时钟参数解读

日志中的时钟相关参数含义如下:

参数 全称 含义
o: Oscillator 外部晶振频率,驱动识别到的基准时钟源
c: CAN Clock CAN控制器内核实际运行时钟
m: Max SPI SPI总线最大通信频率
rs: Requested Speed 设备树中请求的SPI速率
es: Effective Speed SPI控制器实际输出的有效速率

o:0.00MHz 时,说明驱动没有正确识别外部晶振频率。虽然 c:40.00MHz 显示CAN时钟为40MHz,但这可能是驱动使用的默认值或推测值,而非基于正确的晶振配置计算得出。

为什么会出现o:0.00MHz?

原始设备树配置中使用了 clock-frequency 属性:

&spi0 {
    status = "okay";
    // ... 其他配置 ...
    spican0: spican@0 {
        compatible = "microchip,mcp2518fd";
        reg = <0>;
        spi-max-frequency = <100000>;
        interrupts-extended = <&porta 10 IRQ_TYPE_LEVEL_LOW 0>;
        clock-frequency = <40000000>;
    };
};

正确的解决方案

Linux设备树推荐使用 fixed-clock 节点来描述固定频率的时钟源:

// 在根设备树节点 /{ } 中添加
can_osc: can-osc {
    compatible = "fixed-clock";
    #clock-cells = <0>;
    clock-frequency = <40000000>;
};

&spi0 {
    status = "okay";
    clocks = <&scmi_clk GATE_LSP0_SSI_M_WCLK_EN>,
             <&scmi_clk GATE_LSP0_SSI_M_PCLK_EN>;
    clock-names = "wclk", "pclk";
    resets = <&scmi_reset RST_LSP0_SSI_M_WCLK_SW>;
    reset-names = "spi";
    pinctrl-names = "default";
    pinctrl-0 = <&spi0_pinctrl>;
    
    spican0: spican@0 {
        compatible = "microchip,mcp2518fd";
        reg = <0>;
        spi-max-frequency = <100000>;
        interrupts-extended = <&porta 10 IRQ_TYPE_LEVEL_LOW 0>;
        clocks = <&can_osc>;  /* 显式引用时钟节点 */
    };
};

关键改进点

  1. 创建独立的时钟节点can_osc 作为一个标准的 fixed-clock 提供者
  2. 显式引用时钟:通过 clocks = <&can_osc> 建立明确的时钟消费关系
  3. 符合设备树规范:这种方式更容易被[[时钟框架]]([[CCF]])正确处理

修改后的效果

重新编译设备树并启动后,dmesg日志应显示:

[    3.207754] mcp251xfd spi0.0 can0: MCP2518FD rev15.15 (...  o:40.00MHz c:40.00MHz ...) successfully initialized.

此时 o: 参数正确显示为 40.00MHz,表明驱动已正确识别外部晶振频率。

为什么这很重要?

虽然在某些场景下,即使 o:0.00MHz 系统也可能表面上正常工作,但正确配置时钟源具有以下重要意义:

  1. 波特率计算准确性:CAN控制器需要基于准确的晶振频率计算位时间参数(Bit Timing)
  2. 时钟同步:确保发送和接收逻辑使用相同的时间基准
  3. 硬件功能完整性:某些高级功能(如时间戳)依赖准确的时钟源
  4. 诊断信息可靠性:准确的时钟信息有助于排查其他问题

总结

在配置MCP2518FD等需要外部晶振的SPI CAN控制器时,应该:

  • ✅ 使用 fixed-clock 节点定义时钟源
  • ✅ 通过 clocks 属性显式引用时钟
  • ✅ 验证dmesg中 o: 参数显示正确频率
  • ❌ 避免直接使用 clock-frequency 属性
  • ❌ 避免注释掉关键配置而不替换

这种规范的配置方式不仅能解决当前问题,还能为后续的功能扩展和问题排查打下良好基础。