Linux SPI设备分析1(基于Linux6.6)---SPI设备框架介绍
一、概述
1.1、SPI 驱动框架概述
SPI 驱动的主要任务是管理 SPI 总线与 SPI 设备之间的通信。SPI 设备通常通过 SPI 总线连接到主设备,而 SPI 驱动则负责处理数据传输、配置和设备管理。Linux 中的 SPI 驱动分为两部分:
- SPI 总线驱动(SPI bus driver):负责管理 SPI 总线的硬件操作。
- SPI 设备驱动(SPI device driver):与特定 SPI 设备进行交互,执行数据的读写等操作。
1.2、SPI 设备驱动框架的组成部分
一个完整的 SPI 设备驱动通常由以下几个部分组成:
1.SPI 设备的描述结构(spi_device)
在 Linux 内核中,SPI 设备通过 spi_device 结构表示。该结构包含了与特定 SPI 设备相关的所有信息,如设备名称、SPI 总线、设备 ID、片选等。
struct spi_device {
struct device dev; // 设备的基础结构体
struct spi_controller *controller; // 关联的 SPI 控制器
unsigned int max_speed_hz; // 最大传输速度
unsigned int chip_select; // 片选编号
unsigned int mode; // SPI 模式
u16 irq; // 中断号
const char *modalias; // 设备别名
// 其他 SPI 设备相关参数
};
2.SPI 控制器(spi_controller)
spi_controller 结构代表了 SPI 总线控制器。SPI 控制器驱动负责管理总线的具体硬件操作,如配置时钟、传输数据等。
struct spi_controller {
struct device dev;
int (*setup)(struct spi_device *spi); // 设置 SPI 设备
int (*transfer)(struct spi_device *spi, struct spi_transfer *xfer); // SPI 传输接口
// 其他 SPI 总线控制器相关信息
};
3.SPI 传输(spi_transfer)
spi_transfer 结构用于表示一个 SPI 传输操作,描述了数据缓冲区、传输长度、传输方式等信息。
struct spi_transfer {
unsigned long tx_buf; // 发送数据缓冲区
unsigned long rx_buf; // 接收数据缓冲区
unsigned len; // 传输数据长度
unsigned speed_hz; // 传输速度
unsigned cs_change; // 是否改变片选
unsigned delay_usecs; // 延时
// 其他传输相关参数
};
4.SPI 设备驱动(spi_driver)
spi_driver 结构用于注册和管理 SPI 设备驱动。它定义了如何绑定 SPI 设备、初始化设备、处理传输请求等。
struct spi_driver {
struct device_driver driver;
int (*probe)(struct spi_device *spi); // 设备探测函数
int (*remove)(struct spi_device *spi); // 设备移除函数
int (*suspend)(struct spi_device *spi); // 设备挂起函数
int (*resume)(struct spi_device *spi); // 设备恢复函数
const struct spi_device_id *id_table; // 设备 ID 表
};
1.3、SPI 驱动的核心操作流程
SPI 驱动的操作流程大致分为以下几个步骤:
1.设备探测(Probe)
当 SPI 总线上的设备插入时,SPI 驱动会通过设备的 probe 函数来初始化设备。probe 函数负责设置设备的基本参数,如最大速度、SPI 模式、传输缓冲区等。
static int spi_example_probe(struct spi_device *spi)
{
pr_info("Probing SPI device\n");
// 初始化设备,设置 SPI 设备的参数
return 0;
}
2.设备移除(Remove)
当设备从 SPI 总线上移除时,SPI 驱动会调用 remove 函数来清理资源并释放设备。
static int spi_example_remove(struct spi_device *spi)
{
pr_info("Removing SPI device\n");
// 清理设备资源
return 0;
}
3.设备传输(Transfer)
SPI 设备驱动通过 spi_transfer 来发送或接收数据。spi_transfer 结构体描述了数据传输的相关信息,传输通常通过 spi_sync 或 spi_async 来执行。
spi_sync:同步传输,即调用后会等待传输完成。spi_async:异步传输,即启动传输后不会等待,传输完成后通过回调函数通知。
static int spi_example_transfer(struct spi_device *spi)
{
struct spi_transfer transfer = {
.tx_buf = tx_data,
.rx_buf = rx_data,
.len = sizeof(tx_data),
.speed_hz = 1000000,
};
// 执行同步传输
return spi_sync(spi, &transfer);
}
4.SPI 驱动注册(Registration)
在 init 函数中,SPI 驱动通过 spi_register_driver 函数将驱动注册到内核。这样内核会根据 SPI 设备的 ID 自动匹配合适的驱动。
static struct spi_driver example_spi_driver = {
.driver = {
.name = "example_spi_driver",
},
.probe = spi_example_probe,
.remove = spi_example_remove,
.id_table = example_spi_id_table,
};
module_spi_driver(example_spi_driver);
1.4、SPI 驱动开发中的注意事项
- SPI 模式:SPI 有 4 种模式,具体取决于时钟极性(CPOL)和时钟相位(CPHA)。需要确保驱动中正确配置 SPI 模式,以确保与设备正确通信。
- 传输时序:SPI 协议是同步的,时序要求严格。确保在传输时保持时钟和数据的同步。
- 性能优化:SPI 总线的速度较高时,可能会出现数据传输的瓶颈。可以通过增加 DMA 支持来提高传输效率。
- 多设备支持:SPI 总线上可能有多个设备,通过不同的片选线来区分。在设备驱动中要正确配置片选线和传输时序。
1.5、cpu-spi控制器-spi设备关联说明
spi驱动模型是如何抽象:
- 针对spi控制器与spi控制器驱动抽象出spi master类型,其中spi_master可以理解为spi控制器设备,同时该类中包含了spi通信实现产生接口spi_transfer(此处没有为spi 控制器抽象一个驱动类,而是将spi控制器产生通信时序的驱动,抽象为spi_transfer方法);
- 针对spi设备抽象了spi_dev、spi_driver类;
- 针对spi总线,实例化一个bus_type类型的对象。
spi总线与i2c类型,它们的控制器抽象类都提供了总线通信的方法(在i2c_adapter中抽象了algo方法,而在spi_master抽象了transfer接口)。
二、spi_master、spi dev、spi bus、spi driver的关联
2.1、基于设备-总线-驱动模型的关联
spi模型、i2c模型均是基于LINUX设备-总线-驱动模型的,主要描述下spi_driver、spi_device、spi_master、spi_bus这四个类型中device类型、device_driver类型的成员变量之间的关联。
如下图所示:
- 在spi总线上只有spi device、spi driver是注册到spi总线上的,而spi master是没有注册到spi总线上的,这点是与i2c驱动模型的区别(在i2c驱动模型中spi master也是注册到i2c总线上的,只是不进行与i2c driver的绑定);
- Spi device与spi driver借助设备-总线-驱动模型的数据结构与接口,完成绑定与解绑操作;
- Spi device与spi master之间借助设备模型的数据结构,完成父子节点的关联(如下图中橙色箭头所指)
- Spi device与spi master之间借助spi的数据结构,完成了device与master绑定(如下图中绿色箭头所指)

2.2、spi通用字符设备创建相关
通过spi master提供的方法与具体的cs引脚设置,即可完成针对某一个spi dev的通信。因此spi 驱动模块也开发了通用字符设备,但与i2c不同的是,创建的spi通用字符设备需要同时包含spi master与spi device这两个变量。
在i2c模型中,也存在i2c adapter设备对应的字符设备,在i2c设备驱动模型中,主要是在i2cadper 注册到i2c总线时,通过触发BUS_NOTIFY_ADD_DEVICE、BUS_NOTIFY_DEL_DEVICE通知,在这两个通知注册的处理接口中,借助device_add接口完成字符设备的添加;
而在spi驱动模型中,因没有将spi master注册到spi总线上,因此无法使用i2c模块那一套方式创建字符设备,且spi字符设备必须与spi device进行绑定(主要是获取对应的cs引脚),这是与i2c通用字符设备不同的地方。而且spi模块注册了名为“spidev”spi driver,该spi driver就是为了创建spi 通用字符设备而存在的。
4607

被折叠的 条评论
为什么被折叠?



