You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux环境下SPI CLK无法通过初始化修改的排查要点咨询

SPI时钟设置不生效的排查要点

问题背景

在Linux环境下通过SPI接口访问外设,设备能正常通信,但修改SPI时钟频率后设置不生效。以下是相关初始化代码和设备树配置:

SPI初始化代码

int spidev_init(char *rfdev, int mode, int bits, int speed)
{   
    int file;
    int ret;
    
    file = open(rfdev, O_RDWR);
    if (file < 0) {
        printf("%s can't open device!!!\0", rfdev);
        return -1;
    }
    
    /*
     * spi mode
     */
    ret = ioctl(file, SPI_IOC_WR_MODE, &mode);
    if (ret == -1) {
        printf("%s can't set spi mode!!!", rfdev);
        return -2;
    }   
    else
        bit2equ(spi_mode, mode);
    
    ret = ioctl(file, SPI_IOC_RD_MODE, &mode);
    if (ret == -1) {
        printf("%s can't get spi mode!!!", rfdev);
        return -3;
    }
    
    /*
     * bits per word
     */
    ret = ioctl(file, SPI_IOC_WR_BITS_PER_WORD, &bits);
    if (ret == -1) {
        printf("%s can't set bits per word!!!", rfdev);
        return -4;
    } else
        bit2equ(spi_bits, bits); 
    
    ret = ioctl(file, SPI_IOC_RD_BITS_PER_WORD, &bits);
    if (ret == -1) {
        printf("%s can't get bits per word!!!", rfdev);
        return -5;
    }

    /*
     * max speed hz
     */
    ret = ioctl(file, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
    if (ret == -1) {
        printf("%s can't set max speed hz!!!", rfdev);
        return -6;
    } else
        bit2equ(spi_speed, speed);  
    
    ret = ioctl(file, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
    if (ret == -1) {
        printf("%s can't get max speed hz!!!", rfdev);
        return -7;
    }
    
    return file;
}

SPI设备树配置

&spi2 {
    pinctrl-names = "default";
    pinctrl-0 = <&spi2_pincfg>;
    pinctrl-1 = <&spi2_pincfg_sleep>;
    #address-cells = <1>;
    #size-cells = <0>;
    status = "okay";

    spidev@0 {
        compatible = "rohm,dh2228fv";
        reg = <0>;
        spi-max-frequency = <500000>;
//      spi-max-frequency = <1000000>;
//      spi-max-frequency = <25000000>;
    };
};

spi2: ssp@e401e000 {
    compatible = "zinus,zinus-spi";
    reg = <0xE401D000 0x40>;
    interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&cru CLK_SSP2_FUNC>, <&cru CLK_SSP2_APB_BUS>;
    clock-names = "spiclk", "apb_pclk";
    resets = <&cru RST_SSP2>;
    reset-names = "spi";
    dmas = <&sysdma ZN_DMA_SPI2_TX 3>, <&sysdma ZN_DMA_SPI2_RX 3>;
    dma-names = "tx", "rx";
    status = "disabled";
};

spi2 {
    spi2_pincfg:spi2_pincfg {
        zinus,pins = <ZN_GPIO_14 4 &config_pull_disable>,
                <ZN_GPIO_15 4 &config_pull_disable>,
                <ZN_GPIO_16 4 &config_pull_disable>,
                <ZN_GPIO_17 4 &config_pull_disable>;
    };

    spi2_pincfg_sleep:spi2-pincfg-sleep {
        zinus,pins = <ZN_GPIO_14 0 &config_pull_disable>,
                <ZN_GPIO_15 0 &config_pull_disable>,
                <ZN_GPIO_16 0 &config_pull_disable>,
                <ZN_GPIO_17 0 &config_pull_disable>;
    };
};

排查要点

  • 设备树频率上限限制:外设节点的spi-max-frequency是用户空间设置的硬上限,代码中设置的speed如果超过DTS里的500000,会被钳位到该值。需修改DTS中的spi-max-frequency为目标频率,重新编译烧录设备树。
  • SPI控制器时钟源配置:检查cru节点中CLK_SSP2_FUNC的分频、倍频参数,确保其能生成目标频率所需的基础时钟。比如目标25MHz时,需确认该时钟源头能提供足够高的频率进行分频。
  • SPI驱动时钟处理逻辑:查看zinus,zinus-spi驱动源码中处理SPI_IOC_WR_MAX_SPEED_HZ的逻辑,确认是否正确将用户设置的频率转换为硬件寄存器的分频值,是否存在硬编码限制。
  • 验证实际生效频率:在代码中SPI_IOC_RD_MAX_SPEED_HZ后添加打印,输出读取到的speed值,判断是设置未生效还是被驱动钳位。
  • 硬件分频兼容性:部分SPI控制器仅支持基础时钟的整数分频,无法设置任意频率。需确认目标频率是否在控制器支持的分频范围内。
  • 外设驱动冲突:外设节点compatible为rohm,dh2228fv,若内核加载了该专属驱动,可能会覆盖用户空间的时钟设置。可改为compatible = "spidev"使用通用驱动,或修改专属驱动的时钟配置。
  • 权限与设备节点检查:确保程序以root权限运行,避免ioctl权限不足导致设置失效;同时确认打开的rfdev是正确的SPI设备节点(如/dev/spidev2.0)。
  • 内核日志排查:执行dmesg | grep spi查看SPI相关的错误或警告日志,直接定位时钟设置失败的原因。

内容的提问来源于stack exchange,提问作者andy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:41:03