1、SPI概述
SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口,SPI
是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线。通信方式为主
从式,通常是1个主设备、1个或多个从设备。SDI(数据输入),SDO(数据输出),SCLK(时钟),
CS(片选)。
2、Linux SPI 驱动总体架构
SPI的驱动架构可以分为如下层次:SPI核心层、SPI控制器驱动层、SPI设备驱动层。
2.1、SPI核心层
SPI核心层是Linux 内核SPI的核心部分,提供了核心数据结构的定义、SPI控制器驱动和
设备驱动的注册和注销接口。向下屏蔽
了物理总线
的差异,向上提供了统一的接口,以便设备
驱动通过总线控制器进行数据读写。
2.2、SPI控制器驱动层
SPI控制器驱动层,每种平台都有属于自己的控制器驱动。它的职责是为系统中每条SPI
总线提
供具体的读写方法。在物理上,
每个SPI控制器可以连接若干个SPI从设备。
系统开机时,控制器驱动首先被加载。一个控制器驱动用于一条特定的SPI总线的读写,
一个控制器驱动可以用
struct spi_master
来描述。
数据结构struct spi_master
struct spi_master { struct device dev; s16 bus_num; u16 num_chipselect; int (*setup) (struct spi_device *spi); int (*transfer) (struct spi_device *spi, struct spi_message *mesg); void (*cleanup)(struct spi_device *spi); };
bus_num表示控制器对应的总线号。
num_chipselect表示控制器可以支持多少个SPI设备。
setup函数用于设置控制器相关的工作时钟、传输模式等。
transfer函数是实现SPI总线读写方法的函数,实现数据的双向传输。
cleanup函数为控制器驱动注销时调用,针对驱动初始化时申请的一些内核资源进行清理恢
复工作。
2.3、SPI设备驱动层
SPI设备驱动层为用户接口层,其为用户提供了通过SPI总线访问具体设备的接口。
SPI设备驱动层可以用两个数据结构来描述,struct spi_driver 和 struct spi_device
struct spi_driver { int (*probe)(struct spi_device *spi); int (*remove)(struct spi_device *spi); void (*shutdown)(struct spi_device *spi); int (*suspend)(struct spi_device *spi, pm_message_t mesg); int (*resume)(struct spi_device *spi); struct device_driver driver; };
driver是为device服务的,spi_driver注册时会扫描spi bus上的设备,设备和驱动通过
名字
进行匹配绑定。
probe函数用于驱动和设备匹配时被调用。SPI通信是通过消息队列,而不像i2c通过
与从
设备对话方式来实现
数据交互。
struct spi_device { struct device dev; struct spi_master *master; u32 max_speed_hz; u8 chip_select; u8 mode; u8 bits_per_word; int irq; void *controller_state; void *controller_data; char modalias[32]; };
通常spi_device对应SPI bus上某个特定的spi设备。并且spi_device封装了一个
spi_master结
构体。spi_device包含了该spi
设备特定的私有属性,比如它的最大频率,
片选,输入输出模
式等等。
spi_device的板信息用spi_board_info结构体来描述:
struct spi_board_info {
char
modalias[SPI_NAME_SIZE];
const void* platform_data;
void* controller_data;
int irq;
u32 max_speed_hz;
u16 bus_num;
u16 chip_select;
u8
mode;
};
这个结构体记录了SPI外设使用的主机控制器序号、片选信号、数据比特率、SPI传输方式等
spi_board_info结构体通过spi_register_board_info函数注册到链表board_list上。
2.4枚举过程
drivers/spi/spi.c: spi_register_board_info /* 对于每一个spi_master,调用spi_match_master_to_boardinfo */ list_for_each_entry(master, &spi_master_list, list) spi_match_master_to_boardinfo /* board_info里含有bus_num, 如果某个spi_master的bus_num跟它一样 * 则创建一个新的spi_device */ if (master->bus_num == bi->busnum) spi_new_device spi_alloc_device /* 记录bi信息, 比如片选,MODE,MAX HZ */ spi_add_device /* 根据名字找到spi_driver, 调用它的probe函数 */ spi_setup(spi);
device_add /* 会绑定到一个spi_driver */
2.5数据收发过程
spi_write spi_message_init(&m); 初始化一个spi_message /* 一个不可打断的SPI传输过程: cs=0,传数据,cs=1 */ /* 一个spi_message由多个spi_transfer组成 */ spi_message_add_tail(&t, &m); /* spi_transfe是SPI上传输的单方向1个或多个字节 */ spi_sync(spi, &m); /* 启动传输并等待完成 */
2.6 spi_driver如何调用spi_controller
spi_sync __spi_sync(spi, message, 0); spi_async_locked __spi_async master->transfer(spi, message); wait_for_completion