linux驱动开发学习笔记二十五:SPI驱动框架介绍

一、SPI 驱动框架简介

和I2C一样,SPI 也是很常用的串行通信协议,SPI 驱动框架和 I2C 很类似,都分为主机控制器驱动和设备驱动,主机控制器也就是 SOC的 SPI 控制器接口。

1、 SPI 主机驱动

SPI 主机驱动就是 SOC 的 SPI 控制器驱动,类似 I2C 驱动里面的适配器驱动。Linux 内核使用 spi_master 表示 SPI 主机驱动,spi_master 是个结构体,定义在 include/linux/spi/spi.h 文件中,内容如下(有缩减):

315 struct spi_master {
316 		struct device dev;
317
318 		struct list_head list;
			......
326 		s16 bus_num;
327
328 		/* chipselects will be integral to many controllers; some others
329 		* might use board-specific GPIOs.
330 		*/
331 		u16 num_chipselect;
332
333 		/* some SPI controllers pose alignment requirements on DMAable
334 		* buffers; let protocol drivers know about these requirements.
335 		*/
336 		u16 dma_alignment;
337
338 		/* spi_device.mode flags understood by this controller driver */
339 		u16 mode_bits;
340
341 		/* bitmask of supported bits_per_word for transfers */
342 		u32 bits_per_word_mask;
			......
347 		/* limits on transfer speed */
348 		u32 min_speed_hz;
349 		u32 max_speed_hz;
350
351 		/* other constraints relevant to this driver */
352 		u16 flags;
359 		/* lock and mutex for SPI bus locking */
360 		spinlock_t bus_lock_spinlock;
361 		struct mutex bus_lock_mutex;
362
363 		/* flag indicating that the SPI bus is locked for exclusive use */
364 		bool bus_lock_flag;
			......
372 		int (*setup)(struct spi_device *spi);
373
			......
393 		int (*transfer)(struct spi_device *spi,
394 		                struct spi_message *mesg);
			......
434 		int (*transfer_one_message)(struct spi_master *master,
435 		                            struct spi_message *mesg);
			......
462 };
			......

第 393 行,transfer 函数,和 i2c_algorithm 中的 master_xfer 函数一样,控制器数据传输函数。

第 434 行,transfer_one_message 函数,也用于 SPI 数据发送,用于发送一个 spi_message, SPI 的数据会打包成 spi_message,然后以队列方式发送出去。

也就是 SPI 主机端最终会通过 transfer 函数与 SPI 设备进行通信,因此对于 SPI 主机控制器的驱动编写者而言 transfer 函数是需要实现的,因为不同的 SOC 其 SPI 控制器不同,寄存器都不一样。和 I2C 适配器驱动一样,SPI 主机驱动一般都是 SOC 厂商去编写的,所以我们作为 SOC 的使用者,这一部分的驱动就不用操心了。

SPI 主机驱动的核心就是申请 spi_master,然后初始化 spi_master,最后向 Linux 内核注册spi_master。

  • spi_master 申请与释放

spi_alloc_master 函数用于申请 spi_master,函数原型如下:

struct spi_master *spi_alloc_master(struct device *dev, 
									unsigned size)

函数参数和返回值含义如下:
dev:设备,一般是 platform_device 中的 dev 成员变量。
size:私有数据大小,可以通过 spi_master_get_devdata 函数获取到这些私有数据。
返回值:申请到的 spi_master。

spi_master 的释放通过 spi_master_put 函数来完成,当我们删除一个 SPI 主机驱动的时候就需要释放掉前面申请的 spi_master,spi_master_put 函数原型如下:

void spi_master_put(struct spi_master *master)

函数参数和返回值含义如下:
master:要释放的 spi_master。
返回值:无。

  • spi_master 的注册与注销

当 spi_master 初始化完成以后就需要将其注册到 Linux 内核,spi_master 注册函数为spi_register_master,函数原型如下:

int spi_register_master(struct spi_master *master)

函数参数和返回值含义如下:
master:要注册的 spi_master。
返回值:0,成功;负值,失败。

有时候我们还会采用 spi_bitbang_start 这个 API 函数来完成spi_master 的注册,spi_bitbang_start 函数内部其实也是通过调用 spi_register_master 函数来完成 spi_master 的注册。

如果要注销 spi_master 的话可以使用 spi_unregister_master 函数,此函数原型为:

void spi_unregister_master(struct spi_master *master)

函数参数和返回值含义如下:
master:要注销的 spi_master。
返回值:无。

如果使用 spi_bitbang_start 注册 spi_master 的话就要使用 spi_bitbang_stop 来注销掉spi_master。

2、 SPI 设备驱动

spi 设备驱动也和 i2c 设备驱动也很类似,Linux 内核使用 spi_driver 结构体来表示 spi 设备驱动,我们在编写 SPI 设备驱动的时候需要实现 spi_driver。spi_driver 结构体定义在include/linux/spi/spi.h 文件中,结构体内容如下:

180 struct spi_driver {
181 	const struct spi_device_id *id_table;
182 	int (*probe)(struct spi_device *spi);
183 	int (*remove)(struct spi_device *spi);
184 	void (*shutdown)(struct spi_device *spi);
185 	struct device_driver driver;
186 };

可以看出,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 注册示例程序如下:

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。

3、SPI 设备和驱动匹配过程

SPI 设备和驱动的匹配过程是由 SPI 总线来完成的,这点和 platform、I2C 等驱动一样,SPI总线为 spi_bus_type,定义在 drivers/spi/spi.c 文件中,内容如下:

131 struct bus_type spi_bus_type = {
132 		.name = "spi",
133 		.dev_groups = spi_dev_groups,
134 		.match = spi_match_device,
135 		.uevent = spi_uevent,
136 };

可以看出,SPI 设备和驱动的匹配函数为 spi_match_device,函数内容如下:

99 static int spi_match_device(struct device *dev, struct device_driver *drv)
100 {
101 		const struct spi_device *spi = to_spi_device(dev);
102 		const struct spi_driver *sdrv = to_spi_driver(drv);
103
104 		/* Attempt an OF style match */
105 		if (of_driver_match_device(dev, drv))
106 			return 1;
107
108 		/* Then try ACPI */
109 		if (acpi_driver_match_device(dev, drv))
110 			return 1;
111
112 		if (sdrv->id_table)
113 			return !!spi_match_id(sdrv->id_table, spi);
114
115 		return strcmp(spi->modalias, drv->name) == 0;
116 }

spi_match_device 函数和 i2c_match_device 函数的对于设备和驱动的匹配过程基本一样。

第 105 行,of_driver_match_device 函数用于完成设备树设备和驱动匹配。比较 SPI 设备节点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 SPI 设备和驱动匹配。

第 109 行,acpi_driver_match_device 函数用于 ACPI 形式的匹配。

第 113 行,spi_match_id 函数用于传统的、无设备树的 SPI 设备和驱动匹配过程。比较 SPI设备名字和 spi_device_id 的 name 字段是否相等,相等的话就说明 SPI 设备和驱动匹配。

第 115 行,比较 spi_device 中 modalias 成员变量和 device_driver 中的 name 成员变量是否相等。

二、SPI 设备驱动编写流程

1、SPI 设备信息描述

采用设备树的情况下,SPI 设备信息描述就通过创建相应的设备子节点来完成,如下所示:

308 &ecspi1 {
309 	fsl,spi-num-chipselects = <1>;
310 	cs-gpios = <&gpio4 9 0>;
311 	pinctrl-names = "default";
312 	pinctrl-0 = <&pinctrl_ecspi1>;
313 	status = "okay";
314
315 	flash: m25p80@0 {
316 		#address-cells = <1>;
317 		#size-cells = <1>;
318 		compatible = "st,m25p32";
319 		spi-max-frequency = <20000000>;
320 		reg = <0>;
321 	};
322 };

第 309 行,设置“fsl,spi-num-chipselects”属性为 1,表示只有一个设备。

第 310 行,设置“cs-gpios”属性,也就是片选信号为 GPIO4_IO09。 第 311 行,设置“pinctrl-names”属性,也就是 SPI 设备所使用的 IO 名字。

第 312 行,设置“pinctrl-0”属性,也就是所使用的 IO 对应的 pinctrl 节点。

第 313 行,将 ecspi1 节点的“status”属性改为“okay”。

第 315~320 行,ecspi1 下的 m25p80 设备信息,每一个 SPI 设备都采用一个子节点来描述其设备信息。第 315 行的“m25p80@0”后面的“0”表示 m25p80 的接到了 ECSPI 的通道 0上。这个要根据自己的具体硬件来设置。

第 318 行,SPI 设备的 compatible 属性值,用于匹配设备驱动。

第 319 行,“spi-max-frequency”属性设置 SPI 控制器的最高频率,这个要根据所使用的
SPI 设备来设置,比如在这里将 SPI 控制器最高频率设置为 20MHz。

第 320 行,reg 属性设置 m25p80 这个设备所使用的 ECSPI 通道,和“m25p80@0”后面的“0”一样。

2、SPI 设备数据收发处理流程

当我们向 Linux 内核注册成功 spi_driver 以后就可以使用 SPI 核心层提供的 API 函数来对设备进行读写操作了。首先是 spi_transfer 结构体,此结构体用于描述 SPI 传输信息,结构体内容如下:

603 struct spi_transfer {
604 	/* it's ok if tx_buf == rx_buf (right?)
605 	* for MicroWire, one buffer must be null
606 	* buffers must work with dma_*map_single() calls, unless
607 	* spi_message.is_dma_mapped reports a pre-existing mapping
608 	*/
609 	const void *tx_buf;
610 	void *rx_buf;
611 	unsigned len;
612
613 	dma_addr_t tx_dma;
614 	dma_addr_t rx_dma;
615 	struct sg_table tx_sg;
616 	struct sg_table rx_sg;
617
618 	unsigned cs_change:1;
619 	unsigned tx_nbits:3;
620 	unsigned rx_nbits:3;
621 	#define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */
622 	#define SPI_NBITS_DUAL 0x02 /* 2bits transfer */
623 	#define SPI_NBITS_QUAD 0x04 /* 4bits transfer */
624 	u8 bits_per_word;
625 	u16 delay_usecs;
626 	u32 speed_hz;
627
628 	struct list_head transfer_list;
629 };

第 609 行,tx_buf 保存着要发送的数据。

第 610 行,rx_buf 用于保存接收到的数据。

第 611 行,len 是要进行传输的数据长度,SPI 是全双工通信,因此在一次通信中发送和接收的字节数都是一样的,所以 spi_transfer 中也就没有发送长度和接收长度之分。

spi_transfer 需要组织成 spi_message,spi_message 也是一个结构体,内容如下:

660 struct spi_message {
661 	struct list_head transfers;
662
663 	struct spi_device *spi;
664
665 	unsigned is_dma_mapped:1;
		......
678 	/* completion is reported through a callback */
679 	void (*complete)(void *context);
680 	void *context;
681 	unsigned frame_length;
682 	unsigned actual_length;
683 	int status;
684
685 	/* for optional use by whatever driver currently owns the
686 	* spi_message ... between calls to spi_async and then later
687 	* complete(), that's the spi_master controller driver.
688 	*/
689 	struct list_head queue;
690 	void *state;
691 };
  • 在使用spi_message之前需要对其进行初始化,spi_message初始化函数为spi_message_init,函数原型如下:
void spi_message_init(struct spi_message *m)

函数参数和返回值含义如下:
m:要初始化的 spi_message。
返回值:无。

  • spi_message 初始化完成以后需要将 spi_transfer 添加到spi_message 队列中,这里我们要用到 spi_message_add_tail 函数,此函数原型如下:
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)

函数参数和返回值含义如下:
t:要添加到队列中的 spi_transfer。
m:spi_transfer 要加入的 spi_message。
返回值:无。

  • spi_message 准备好以后既可以进行数据传输了,数据传输分为同步传输和异步传输,同步传输会阻塞的等待 SPI 数据传输完成,同步传输函数为 spi_sync,函数原型如下:
int spi_sync(struct spi_device *spi, struct spi_message *message)

函数参数和返回值含义如下:
spi:要进行数据传输的 spi_device。
message:要传输的 spi_message。
返回值:无。

  • 异步传输不会阻塞的等到 SPI 数据传输完成,异步传输需要设置 spi_message 中的 complete成员变量,complete 是一个回调函数,当 SPI 异步传输完成以后此函数就会被调用。SPI 异步传输函数为 spi_async,函数原型如下:
int spi_async(struct spi_device *spi, struct spi_message *message)

函数参数和返回值含义如下:
spi:要进行数据传输的 spi_device。
message:要传输的 spi_message。
返回值:无。

综上所述,SPI 数据传输步骤如下:
①、申请并初始化 spi_transfer,设置 spi_transfer 的 tx_buf 成员变量,tx_buf 为要发送的数据。然后设置 rx_buf 成员变量,rx_buf 保存着接收到的数据。最后设置 len 成员变量,也就是要进行数据通信的长度。
②、使用 spi_message_init 函数初始化 spi_message。
③、使用spi_message_add_tail函数将前面设置好的spi_transfer添加到spi_message队列中。
④、使用 spi_sync 函数完成 SPI 数据同步传输。

通过 SPI 进行 n 个字节的数据发送和接收的示例代码如下所示:

/* SPI 多字节发送 */
static int spi_send(struct spi_device *spi, u8 *buf, int len) {
 		int ret;
 		struct spi_message m;
 
 		struct spi_transfer t = {
 			.tx_buf = buf,
 			.len = len,
 		};
 		spi_message_init(&m); /* 初始化 spi_message */
 		spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
 		ret = spi_sync(spi, &m); /* 同步传输 */
 		return ret; }
/* SPI 多字节接收 */
static int spi_receive(struct spi_device *spi, u8 *buf, int len) {
 		int ret;
 		struct spi_message m;
 
 		struct spi_transfer t = {
 			.rx_buf = buf,
 			.len = len,
 		};
 		spi_message_init(&m); /* 初始化 spi_message */
 		spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
 		ret = spi_sync(spi, &m); /* 同步传输 */
 		return ret; }

猜你喜欢

转载自blog.csdn.net/qq_39507748/article/details/106093379