You need to enable JavaScript to run this app. Linux驱动开发:如何获取SPI主机句柄及跨主机访问SPI从设备
优惠活动
大模型
产品
解决方案
定价
更多

Linux驱动开发:如何获取SPI主机句柄及跨主机访问SPI从设备

跨SPI主控制器访问设备的实现方法

与现有I2C访问逻辑等效的SPI接口对应关系

你当前用i2c_get_adapter按总线号拿适配器、组织消息做传输、用完释放的逻辑,在SPI子系统中有一一对应的设计,但存在一个核心差异:

  • I2C子系统的每个传输消息自带从地址,因此拿到适配器句柄后即可直接构造消息发往任意合法从地址
  • SPI子系统的从设备通信参数(片选极性、通信模式、最大速率)是绑定在预注册的spi_device结构体上的,所有传输必须关联到具体的spi_device实例,不允许绕过设备模型直接通过master句柄操作总线发任意数据,这是为了避免多驱动共享SPI总线时的并发冲突

对应到接口层面的一一匹配关系:

  • 与i2c_get_adapter(<bus-ID>)等效:使用spi_bus_get(<spi-bus-ID>, THIS_MODULE)可以获取指定总线号对应的SPI主控制器句柄,用完需调用spi_master_put()释放
  • 与i2c_transfer()等效:使用spi_sync()(同步阻塞)或spi_async()(异步非阻塞)发起SPI传输,核心是提前初始化好关联对应spi_device的spi_message结构体

同驱动跨主控制器管理两个SPI设备的实现路径

你当前掌握的通过struct spi_driver、struct spi_device_id匹配单个SPI设备的方法,存在天然的适配局限:这类驱动是直接挂载在SPI总线上的,每个SPI从设备匹配到驱动时会单独触发一次probe流程,每个probe上下文仅能拿到当前匹配的单个spi_device实例,无法天然关联其他SPI主控制器上的同属本驱动管理的设备。

针对这个场景有两种成熟的实现方案:

方案1:符合Linux设备模型的推荐实现

不要硬编码总线ID、手动查找控制器,而是将当前驱动从挂载在SPI总线的spi_driver,改为通用的平台设备驱动:

  1. 在设备树中新增一个属于你这个驱动的平台设备节点
  2. 在该节点中通过phandle关联需要管理的两个跨主控制器SPI设备、以及那个I2C设备,比如定义自定义属性:
    my_device@0 {
        compatible = "vendor,my-spi-i2c-combo-driver";
        spi-periph-1 = <&spi0 0>; /* 主控制器spi0上的片选0设备 */
        spi-periph-2 = <&spi1 1>; /* 主控制器spi1上的片选1设备 */
        i2c-periph = <&i2c1 0x48>; /* i2c1总线上地址0x48的设备 */
    };
    
  3. 驱动probe阶段通过设备树解析接口,直接从节点中取出对应的spi_device、i2c_client句柄即可使用,全程不需要手动按总线号查找控制器,也不会因为总线枚举顺序变化导致驱动失效。

方案2:与你现有I2C写法逻辑一致的手动访问实现

如果确实需要手动按总线号查找设备,可参考如下核心代码逻辑:

// 1. 等价于 i2c_get_adapter(<bus-ID>): 获取两个主控制器的句柄
struct spi_master *master0 = spi_bus_get(<spi0总线号>, THIS_MODULE);
struct spi_master *master1 = spi_bus_get(<spi1总线号>, THIS_MODULE);
if (IS_ERR(master0) || IS_ERR(master1)) {
    // 错误处理
}

// 2. 等价于 <define 'msg'> 步骤:定位对应主控制器上需要访问的spi从设备
struct spi_device *spi0_dev = of_find_spi_device_by_node(<spi0上对应从设备的设备树节点>);
struct spi_device *spi1_dev = of_find_spi_device_by_node(<spi1上对应从设备的设备树节点>);
if (!spi0_dev || !spi1_dev) {
    // 错误处理
}

// 3. 等价于 i2c_transfer(): 构造传输消息并发起访问
char tx_buf[2] = {0xAB, 0x00};
char rx_buf[2] = {0};
struct spi_transfer xfer = {
    .tx_buf = tx_buf,
    .rx_buf = rx_buf,
    .len = sizeof(tx_buf),
    .speed_hz = 1000000, // 可临时覆盖设备默认配置速率
};
struct spi_message spi_msg;
spi_message_init(&spi_msg);
spi_message_add_tail(&xfer, &spi_msg);
// 对spi0上的设备发起传输
spi_sync(spi0_dev, &spi_msg);
// 切换到spi1上的设备,重复构造消息发起传输即可
spi_sync(spi1_dev, &spi_msg);

// 4. 等价于 i2c_put_adapter(): 用完释放所有引用
spi_put_device(spi0_dev);
spi_put_device(spi1_dev);
spi_master_put(master0);
spi_master_put(master1);

注意事项

不推荐在驱动中硬编码SPI总线号查找设备。实际系统中SPI主控制器的总线号会根据设备树枚举顺序、驱动加载顺序动态变化,硬编码总线号很容易在系统版本升级、硬件改版后出现设备查找失败的问题,优先使用设备树phandle关联设备的方式实现解耦。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.01 01:27:05