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,改为通用的平台设备驱动:
- 在设备树中新增一个属于你这个驱动的平台设备节点
- 在该节点中通过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的设备 */ }; - 驱动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
相关产品推荐
相关产品推荐

