1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码 手册 视频下载地址:http://www.openedv.com/thread-318813-1-1.html 4)正点原子官方B站:https://space.bilibili.com/394620890 5)正点原子STM32MP157技术交流群:691905614
第四十四章Linux SPI总线框架
到目前为止,我们已经给大家介绍了Linux下的platform总线框架、I2C本章将向您介绍总线框架Linux下的SPI总线框架。与I2C总线一样,SPI它是物理总线,也是一种常用的串行通信协议;本章将学习如何学习Linux下编写SPI设备驱动总线接口。本章实验的最终目的是驱动STM32MP1开发板上的ICM-20608这个SPI可在应用程序中读取接口的六轴传感器ICM-原始传感器数据20608。
44.1 SPI & ICM-20608简介 44.1.1 SPI简介 我们解释了上一章I2C,I2C是串行通信的一种,只需要两根线就可以完成主机和从机之间的通信,但是I2C最高速度只有400KHz,如果要求访问速度比价高,I2C不合适。让我们从这一章中学习另一个和I2C同样广泛使用的串行通信:SPI,SPI全称是Serial Perripheral Interface,即串行外围设备接口。SPI是Motorola公司推出的同步串行接口技术是高速全双工同步通信总线,SPI与时钟频率相比I2C要高很多,最高可以工作几百MHz。SPI以主从方式工作,通常有一个主设备和一个或多个设备SPI需要四条线,但也可以使用三条线(单向传输)。本章将解释标准的四条线SPI,这四条线如下: ①、CS/SS,Slave Select/Chip Select,这是选择需要通信的于选择需要通信的设备。I2C主机通过发送从机设备地址来选择需要通信的从机设备,SPI主机不需要发送从机设备,直接降低相应的从机设备片选择信号。 ②、SCK,Serial Clock,串行时钟,和I2C的SCL一样,为SPI通信提供时钟。 ③、MOSI/SDO,Master Out Slave In/Serial Data Output,简称主出从入信号线,只能用于主机向从机发送数据,即主机输出和从机输入。 ④、MISO/SDI,Master In Slave Out/Serial Data Input,简称主进出信号线,该数据线只能由用户从机发送到主机,即主机输入,从机输出。 SPI通信由主机发起,需要提供通信时钟信号。主机通过SPI从设备的结构如图44所示.1.1.1所示:
图44.1.1.1 SPI设备连接图 SPI串行时钟极性有四种工作模式(CPOL)和相位(CPHA)获得四种工作模式: ①、CPOL=0.串行时钟的空闲状态为低电平。 ②、CPOL=1.串行时钟的空闲状态为高电平,此时可配置时钟相位(CPHA)选择具体的传输协议。 ③、CPHA=0.串行时钟的第一个跳变沿(上升或下降)采集数据。 ④、CPHA=1.串行时钟的第二个跳变沿(上升或下降)采集数据。 这四种工作模式如图44所示.1.1.2所示:
图44.1.1.2 SPI四种工作模式 和I2C一样,SPI也有时序图,以CPOL=0,CPHA=以这种工作模式为例,SPI全双工通信顺序如图44所示.1.1.3所示:
图44.1.1.3 SPI时序图 从图44.1.1.3可以看出,SPI时序图很简单,不像I2C也分为读时序和写时序,因为SPI全双工,读写时序可以一起完成。图44.1.1.3中,CS片选信号先降低,从设备中选择通信,然后通过MOSI和MISO这两条数据线收发数据,MOSI数据线发出0XD这个数据给设备,也通过设备MISO线返回0给主设备X66这个数据。这个就是SPI时序图。 关于SPI在这里,我们来看看。STM32MP1自带的SPI外设。 44.1.2 STM32MP1 SPI简介 STM32MP1自带的SPI全称为:Serial peripheral interface。SPI特性如下: ①、全双工同步串口接口。 ②、半双工模式。 ③、可配置的主/从模式。 ④、支持I2S协议。 ⑤、在达到 FIFO 当访问错误发生时,阈值、加时、操作完成和中断。 ⑥、数据长度允许16位、24位或32位。 ⑦、支持软件片选择和硬件片选择。 STM32MP1的SPI我们可以在主模式或从模式中工作,本章使用主模式,有6个芯片SPI,其中SPI1~3是支持I2S协议。在主模式下,可以选择硬件片选择和软件片选择。如果使用硬件片选择,那么每个都可以选择SPI只支持一个外设,软件片选择可以支持无数的外设,本章实验我们不使用硬件片选择信号,因为硬件片选择信号只能使用指定的片选择IO,如果选择软件片,可以使用任何软件片IO。 44.1.3 ICM-20608简介 ICM-20608是InvenSense生产的6轴MEMS传感器,包括3轴加速度和3轴陀螺仪。ICM-20608尺寸很小,只有3x3x0.75mm,采用16P的LGA封装。ICM-20608内部有一个512字节FIFO。陀螺仪的量程范围可以编程设置,可以选择±250,±500,±1000和±2000°/s,也可以编程设置加速度的范围,可以选择±2g,±4g,±4g,±8g和±16g。陀螺仪和加速度计均为16位ADC,并且支持I2C和SPI使用两种协议I2C如果接口的通信速度最高可达400KHz,使用SPI如果接口的通信速度最高可达8MHz。开发板上的ICM-20608通过SPI接口和STM32MP157连接在一起。ICM-20608特性如下: ①、陀螺仪支持X,Y与Z三轴输出,内部集成16位ADC,可设置测量范围:±250,±500,±1000和±2000°/s。 ②、加速度计支持X,Y与Z轴输出,内部集成16位ADC,可设置测量范围:±2g,±4g,±4g,±8g和±16g。 ③、可编程中断用户。 ④、内部包含512字节FIFO。 ⑤、它包含数字温度传感器。 ⑥、耐10000g的冲击。 ⑦、支持快速I2C,速度可达400KHz。 ⑧、支持SPI,速度可达8MHz。 ICM-20608年3轴方向如图44所示.1.3.1所示:
图44.1.3.1 ICM-20608年检测轴的方向和极性 ICM-图440608结构框图.1.3.2所示:
图44.1.3.2 ICM-20608框图 如果使用IIC接口的话,ICM-20608的AD0引脚决定I2C如果设备来自地址的最后一个,AD0为0的话ICM-20608设备地址0X68,如果AD0为1的话ICM-20608设备地址0X69。本章我们使用SPI与上一章一起使用接口AP3216C一样,ICM-20608年还通过读写寄存器配置和读取传感器数据SPI接口读写寄存器需要16个小时或更长时间(如果读写操作包括多个字节)。第一个字节包括要读写的寄存器地址。寄存器地址的最高位置是读写标志位。如果读取,寄存器地址的最高位置应为1,如果是写的话,寄存器地址的最高位应该是0,剩下的7个是实际的寄存器地址。寄存器地址的后面是阅读的数据。表44.1.3.1列出了本章实验用到的一些寄存器和位,关于ICM-20608的详细寄存器和位的介绍请参考ICM-20608的寄存器手册:
ICM-20608的介绍就到这里,关于ICM-20608的详细介绍请参考ICM-20608的数据手册和寄存器手册。 44.2 Linux下SPI驱动框架 SPI总线框架和I2C总线框架很类似,都采用了主机控制器驱动和设备驱动分离的思想;主机控制器也就是SoC的SPI控制器,例如STM32MP1的SPI控制器;而设备驱动对应的则是挂在SPI总线下的从机设备驱动程序。主机控制器针对具体的SOC平台,例如STM32MP1,对于同一个SOC平台来说,SPI控制器驱动程序是不用动的,不管外接的是什么SPI从机设备,对应的控制器驱动程序都一样,所以我们的重点就落在了种类繁多的SPI从机设备驱动开发了。SPI控制器驱动程序一般是不需要驱动开发工程师自己编写,SOC厂商会提供相应的主机驱动程序。 在Linux内核当中,与I2C总线框架一样,SPI总线框架(也可以叫做SPI子系统)也可以分为三个部分: SPI核心层:SPI核心层是Linux的SPI子系统的核心代码部分,提供了核心数据结构的定义、SPI控制器驱动和设备驱动的注册、注销、管理等API。其为硬件平台无关层,向下屏蔽了物理总线控制器的差异,定义了统一的访问策略和接口;其向上提供了统一的接口,以便SPI设备驱动通过总线控制器进行数据收发。在Linux系统中,SPI核心层的代码位于drivers/spi/spi.c。 SPI控制器驱动层:每种处理器平台都有自己的SPI控制器驱动程序,它的职责是为系统中的SPI总线实现相应的读写方法。例如STM32MP1就有六个SPI,那么就有六个SPI控制器,每个控制器都有一条特定的SPI总线的读写。SPI子系统使用struct spi_master数据结构体来描述SPI控制器。在内核源码drivers/spi目录下有很多以spi-xxxx.c命名的源文件,如图44.2.1:
图44.2.1 SPI控制器驱动源码 这些文件就是具体平台对应的SPI控制器驱动程序,使用SPI核心层提供的接口向SPI子系统注册SPI控制器。 SPI设备驱动层:SPI从设备对应的驱动程序,比如一些SPI接口的芯片器件对应的驱动程序。接下来我们详细的聊聊SPI子系统。 44.2.1 SPI主机驱动 SPI主机驱动就是SoC的SPI控制器驱动,类似I2C总线的适配器驱动。SPI子系统使用spi_master结构体来描述SPI控制器,其实spi_master是一个宏,这个宏定义在include/linux/spi/spi.h文件中,如下所示: #define spi_master spi_controller 所以由此可以知道,spi_master就是spi_controller结构体,该结构体定义在include/linux/spi/spi.h文件中,如下所示:
示例代码44.2.1 spi_controller结构体
424 struct spi_controller {
425 struct device dev; /* device 对象 */
426
427 struct list_head list;
......
435 s16 bus_num; /* SPI总线编号 */
......
440 u16 num_chipselect; /* 片选 */
441
442 /* some SPI controllers pose alignment requirements on DMAable 443 * buffers; let protocol drivers know about these requirements. 444 */
445 u16 dma_alignment;
446
447 /* spi_device.mode flags understood by this controller driver */
448 u32 mode_bits; /* 模式位 */
......
455 /* limits on transfer speed */
456 u32 min_speed_hz; /* SPI控制器支持的最小传输速率 */
457 u32 max_speed_hz; /* SPI控制器支持的最大传输速率 */
458
459 /* other constraints relevant to this driver */
460 u16 flags; /* 传输类型标志 */
......
468
469 /* flag indicating this is an SPI slave controller */
470 bool slave; /* 标志该控制器是否为SPI从设备存在 */
471
472 /* 473 * on some hardware transfer / message size may be constrained 474 * the limit may depend on device transfer settings 475 */
476 size_t (*max_transfer_size)(struct spi_device *spi);
477 size_t (*max_message_size)(struct spi_device *spi);
478
479 /* I/O mutex */
480 struct mutex io_mutex;
481
482 /* lock and mutex for SPI bus locking */
483 spinlock_t bus_lock_spinlock;
484 struct mutex bus_lock_mutex;
485
486 /* flag indicating that the SPI bus is locked for exclusive use */
487 bool bus_lock_flag;
......
495 int (*setup)(struct spi_device *spi);
......
527 int (*transfer)(struct spi_device *spi,
528 struct spi_message *mesg);
......
567 int (*prepare_transfer_hardware)(struct spi_controller *ctlr);
568 int (*transfer_one_message)(struct spi_controller *ctlr,
569 struct spi_message *mesg);
......
607 };
第495行,SPI控制器的setup函数,类似于初始化函数。
第527行,SPI控制器的transfer函数,和i2c_algorithm中的master_xfer函数一样,控制器数据传输函数。
第568行,transfer_one_message 函数,也用于 SPI 数据发送,用于发送一个 spi_message,
SPI 的数据会打包成 spi_message,然后以队列方式发送出去。 SPI 主机端最终会通过 transfer 函数与 SPI 设备进行通信,因此对于 SPI 主机控制器的驱动编写者而言 transfer 函数是需要实现的,因为不同的 SOC 其 SPI 控制器不同,寄存器都不一样。和 I2C 适配器驱动一样,SPI 主机驱动一般都是 SOC 厂商去编写的,所以我们作为 SOC 的使用者,这一部分的驱动就不用操心了,除非你是在 SOC 原厂工作,内容就是写 SPI 主机驱动。 SPI 主机驱动的核心就是申请 spi_master,然后初始化 spi_master,最后向 Linux 内核注册spi_master。 1、spi_master申请与释放 spi_alloc_master函数用于申请spi_master,函数原型如下: struct spi_controller *spi_alloc_master(struct device *host, unsigned int size) 函数参数和返回值含义如下: host:设备,一般是platform_device中的dev成员变量。 size:私有数据大小,可以通过spi_master_get_devdata函数获取到这些私有数据。 返回值:申请到的spi_controller,也就是spi_master。 spi_master的释放通过spi_master_put函数来完成,当我们删除一个SPI主机驱动的时候就需要释放掉前面申请的spi_master,spi_master_put本质上是个宏: #define spi_master_put(_ctlr) spi_controller_put(_ctlr) spi_master_put函数最终通过调用spi_controller_put函数来完成spi_master释放,原型如下: void spi_master_put(struct spi_controller *ctlr) 函数参数和返回值含义如下: ctlr:要释放的spi_master。 返回值:无。 2、spi_master的注册与注销 当spi_master初始化完成以后就需要将其注册到Linux内核,spi_master注册函数为spi_register_master,函数原型如下: int spi_register_master(struct spi_controller *ctlr) 函数参数和返回值含义如下: ctlr:要注册的spi_master。 返回值:0,成功;负值,失败。 如果要注销spi_master的话可以使用spi_unregister_master函数,此函数原型为: void spi_unregister_master(struct spi_controller *ctlr) 函数参数和返回值含义如下: ctlr:要注销的spi_master。 返回值:无。 44.2.2 SPI设备驱动 spi设备驱动和i2c设备驱动也很类似,Linux内核使用spi_driver结构体来表示spi设备驱动,我们在编写SPI设备驱动的时候需要实现spi_driver。spi_driver结构体定义在include/linux/spi/spi.h文件中,结构体内容如下:
示例代码44.2.2.1 spi_driver结构体
259 struct spi_driver {
260 const struct spi_device_id *id_table;
261 int (*probe)(struct spi_device *spi);
262 int (*remove)(struct spi_device *spi);
263 void (*shutdown)(struct spi_device *spi);
264 struct device_driver driver;
265 };
可以看出,spi_driver和i2c_driver、platform_driver基本一样,当SPI设备和驱动匹配成功以后probe函数就会执行。 同样的,spi_driver初始化完成以后需要向Linux内核注册,spi_driver注册函数为spi_register_driver,函数原型如下: int spi_register_driver(struct spi_driver *sdrv) 函数参数和返回值含义如下: sdrv:要注册的spi_driver。 返回值:0,注册成功;赋值,注册失败。 注销SPI设备驱动以后也需要注销掉前面注册的spi_driver,使用spi_unregister_driver函数完成spi_driver的注销,函数原型如下: void spi_unregister_driver(struct spi_driver *sdrv) 函数参数和返回值含义如下: sdrv:要注销的spi_driver。 返回值:无。 spi_driver注册示例程序如下:
示例代码44.2.2.2 spi_driver注册示例程序
1 /* probe函数 */
2 static int xxx_probe(struct spi_device *spi)
3 {
4 /* 具体函数内容 */
5 return 0;
6 }
7
8 /* remove函数 */
9 static int xxx_remove(struct spi_device *spi)
10 {
11 /* 具体函数内容 */
12 return 0;
13 }
14 /* 传统匹配方式ID列表 */
15 static const struct spi_device_id xxx_id[] = {
16 {
"xxx", 0},
17 {
}
18 };
19
20 /* 设备树匹配列表 */
21 static const struct of_device_id xxx_of_match[] = {
22 {
.compatible = "xxx" },
23 {
/* Sentinel */ }
24 };
25
26 /* SPI驱动结构体 */
27 static struct spi_driver xxx_driver = {
28 .probe = xxx_probe,
29 .remove = xxx_remove,
30 .driver = {
31 .owner = THIS_MODULE,
32 .name = "xxx",
33 .of_match_table = xxx_of_match,
34 },
35 .id_table = xxx_id,
36 };
37
38 /* 驱动入口函数 */
39 static int __init xxx_init(void)
40 {
41 return spi_register_driver(&xxx_driver);
42 }
43
44 /* 驱动出口函数 */
45 static void __exit xxx_exit(void)
46 {
47 spi_unregister_driver(&xxx_driver);
48 }
49
50 module_init(xxx_init);
51 module_exit(xxx_exit);
第1~36行,spi_driver结构体,需要SPI设备驱动人员编写,包括匹配表、probe函数等。和i2c_driver、platform_driver一样,就不详细讲解了。
第39~42行,在驱动入口函数中调用spi_register_driver来注册spi_driver。
第45~48行,在驱动出口函数中调用spi_unregister_driver来注销spi_driver。
44.2.3 SPI设备和驱动匹配过程 SPI设备和驱动的匹配过程是由SPI总线来完成的,这点和platform、I2C等驱动一样,SPI总线为spi_bus_type,定义在drivers/spi/spi.c文件中,内容如下:
示例代码44.2.3.1 spi_bus_type结构体
377 struct bus_type spi_bus_type = {
378 .name = "spi",
379 .dev_groups = spi_dev_groups,
380 .match = spi_match_device,
381 .uevent = spi_uevent,
382 };
可以看出,SPI设备和驱动的匹配函数为spi_match_device,函数内容如下:
示例代码44.2.3.2 spi_match_device函数
342 static int spi_match_device(struct device *dev,
struct device_driver *drv)
343 {
344 const struct spi_device *spi = to_spi_device(dev);
345 const struct spi_driver *sdrv = to_spi_driver(drv);
346
347 /* Check override first, and if set, only use the named driver */
348 if (spi->driver_override)
349 return strcmp(spi->driver_override, drv->name) == 0;
350
351 /* Attempt an OF style match */
352 if (of_driver_match_device(dev, drv))
353 return 1;
354
355 /* Then try ACPI */
356 if (acpi_driver_match_device(dev, drv))
357 return 1;
358
359 if (sdrv->id_table)
360 return !!spi_match_id(sdrv->id_table, spi);
361
362 return strcmp(spi->modalias, drv->name) == 0;
363 }
spi_match_device函数和i2c_match_device函数的对于设备和驱动的匹配过程基本一样。 第352行,of_driver_match_device函数用于完成设备树设备和驱动匹配。比较SPI设备节点的compatible属性和of_device_id中的compatible属性是否相等,如果相当的话就表示SPI设备和驱动匹配。 第356行,acpi_driver_match_device函数用于ACPI形式的匹配。 第360行,spi_match_id函数用于传统的、无设备树的SPI设备和驱动匹配过程。比较SPI设备名字和spi_device_id的name字段是否相等,相等的话就说明SPI设备和驱动匹配。 第362行,比较spi_device中modalias成员变量和device_driver中的name成员变量是否相等。 44.3 STM32MP1 SPI主机驱动分析 和I2C的适配器驱动一样,SPI主机驱动一般都由SOC厂商编写好了,打开stm32mp151.dtsi文件,找到如下所示内容:
示例代码44.3.1 stm32mp151.dtsi文件中的spi1节点内容
1 spi1: spi@44004000 {
2 #address-cells = <1>;
3 #size-cells = <0>;
4 compatible = "st,stm32h7-spi";
5 reg = <0x44004000 0x400>;
6 interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
7 clocks = <&rcc SPI1_K>;
8 resets = <&rcc SPI1_R>;
9 dmas = <&dmamux1 37 0x400 0x01>,
10 <&dmamux1 38 0x400 0x01>;
11 dma-names = "rx", "tx";
12 power-domains = <&pd_core>;
13 status = "disabled";
14 };
重点来看一下第4行的compatible属性值,compatible属性为“st,stm32h7-spi”,在Linux内核源码中搜素这两个属性值即可找到STM32MP1对应的SPI主机驱动。STM32MP1的SPI主机驱动文件为drivers/spi/spi-stm32.c,在此文件中找到如下内容:
示例代码44.3.2 stm32_spi_driver结构体
1859 static const struct of_device_id stm32_spi_of_match[] = {
1860 {
.compatible = "st,stm32h7-spi",
.data = (void *)&stm32h7_spi_cfg },
1861 {
.compatible = "st,stm32f4-spi",
.data = (void *)&stm32f4_spi_cfg },
1862 {
},
1863 };
......
2154 static struct platform_driver stm32_spi_driver = {
2155 .probe = stm32_spi_probe,
2156 .remove = stm32_spi_remove,
2157 .driver = {
2158 .name = DRIVER_NAME,
2159 .pm = &stm32_spi_pm_ops,
2160 .of_match_table = stm32_spi_of_match,
2161 },
2162 };
2163
2164 module_platform_driver(stm32_spi_driver);
第1860行,“st,stm32h7-spi”匹配项,因此可知STM32MP1主机驱动就是spi-stm32.c这个文件。
第2154~2164行,从这里可以知道,该主机驱动程序是基于platform总线框架编写,platform_driver结构体变量为stm32_spi_driver,当platform总线下设备和设备驱动匹配成功之后就会执行stm32_spi_probe函数,同样当驱动模块卸载的时候就会执行stm32_spi_remove函数。
接下来我们重点来看下stm32_spi_probe函数做了些什么,函数如下所示:
示例代码44.3.3 stm32_spi_probe函数
1866 static int stm32_spi_probe(struct platform_device *pdev)
1867 {
1868 struct spi_master *master;
1869 struct stm32_spi *spi;
1870 struct resource *res;
1871 struct reset_control *rst;
1872 int i, ret, num_cs, cs_gpio;
1873
1874 master = spi_alloc_master(&pdev->dev,
sizeof(struct stm32_spi));
1875 if (!master) {
1876 dev_err(&pdev->dev, "spi master allocation failed\n");
1877 return -ENOMEM;
1878 }
.....
1892 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1893 spi->base = devm_ioremap_resource(&pdev->dev, res);
1894 if (IS_ERR(spi->base)) {
1895 ret = PTR_ERR(spi->base);
1896 goto err_master_put;
1897 }
1898
1899 spi->phys_addr = (dma_addr_t)res->start;
1900
1901 spi->irq = platform_get_irq(pdev, 0);
1902 if (spi->irq <= 0) {
1903 ret = spi->irq;
1904 if (ret != -EPROBE_DEFER)
1905 dev_err(&pdev->dev, "failed to get irq: %d\n", ret);
1906 goto err_master_put;
1907 }
1908 ret = devm_request_threaded_irq(&pdev->dev, spi->irq,
1909 spi->cfg->irq_handler_event,
1910 spi->cfg->irq_handler_thread,
1911 IRQF_ONESHOT, pdev->name, master);
......
1963 master->dev.of_node = pdev->dev.of_node;
1964 master->auto_runtime_pm = true;
1965 master->bus_num = pdev->id;
1966 master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH |
1967 SPI_LSB_FIRST | SPI_3WIRE;
1968 master->bits_per_word_mask = spi->cfg->get_bpw_mask(spi);
1969 master->max_speed_hz = spi->clk_rate /
spi->cfg->baud_rate_div_min;
1970 master->min_speed_hz = spi->clk_rate /
spi->cfg->baud_rate_div_max;
1971 master->setup = stm32_spi_setup;
1972 master->prepare_message = stm32_spi_prepare_msg;
1973 master->transfer_one = stm32_spi_transfer_one;
1974 master->unprepare_message = stm32_spi_unprepare_msg;
......
2026 ret = spi_register_master(master);
......
2050 }
第1874行,通过调用spi_alloc_master函数为master指针申请内存,也就是实例化master。
第1901~1911行,获取中断号和注册中断函数。
第1963~1974行,对master变量进行初始化和赋值,从结果可以看到,master 结构体中并没有设置 transfer 和 transfer_one_message 这两个用于 SPI 数据传输的函数,而是使用了 transfer_one 作为 SPI 数据传输的函数,对应的函数为stm32_spi_transfer_one,也就是该函数是STM32MP1 SPI数