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);
};
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;
- };
名字
进行匹配绑定。
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_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_board_info结构体通过spi_register_board_info函数注册到链表board_list上。
2.4枚举过程
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_writespi_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