SPI主机驱动
SPI 主机驱动就是 SOC 的 SPI 控制器驱动,类似 I2C 驱动里面的适配器驱动。 Linux 内核使用 spi_master 表示 SPI 主机驱动, spi_master 是个结构体,定义在 include/linux/spi/spi.h 文件中,内容如下:
struct spi_master { struct device dev; // 设备 struct list_head list; // 链表 /* other than negative (== assign one dynamically), bus_num is fully * board-specific. usually that simplifies to being SOC-specific. * example: one SOC has three SPI controllers, numbered 0..2, * and one board's schematics might show it using SPI-2. software * would normally use bus_num=2 for that controller. */ s16 bus_num; // 总线编号 /* chipselects will be integral to many controllers; some others * might use board-specific GPIOs. */ u16 num_chipselect; // 片选数量 /* some SPI controllers pose alignment requirements on DMAable * buffers; let protocol drivers know about these requirements. */ u16 dma_alignment; // DMA对齐要求 /* spi_device.mode flags understood by this controller driver */ u16 mode_bits; // SPI模式 /* bitmask of supported bits_per_word for transfers */ u32 bits_per_word_mask; // 支持的字长掩码 #define SPI_BPW_MASK(bits) BIT((bits) - 1) // 位掩码 #define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1)) // 位掩码 #define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1)) // 位掩码 /* limits on transfer speed */ // 传输速度限制 u32 min_speed_hz; // 最小传输速度 u32 max_speed_hz; // 最大传输速度 /* other constraints relevant to this driver */ u16 flags; #define SPI_MASTER_HALF_DUPLEX BIT(0) /* can't do full duplex */ #define SPI_MASTER_NO_RX BIT(1) /* can't do buffer read */ #define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */ #define SPI_MASTER_MUST_RX BIT(3) /* requires rx */ #define SPI_MASTER_MUST_TX BIT(4) /* requires tx */ /* lock and mutex for SPI bus locking */ spinlock_t bus_lock_spinlock; struct mutex bus_lock_mutex; /* flag indicating that the SPI bus is locked for exclusive use */ bool bus_lock_flag; /* Setup mode and clock, etc (spi driver may call many times). * * IMPORTANT: this may be called when transfers to another * device are active. DO NOT UPDATE SHARED REGISTERS in ways * which could break those transfers. */ int (*setup)(struct spi_device *spi); /* bidirectional bulk transfers * * + The transfer() method may not sleep; its main role is * just to add the message to the queue. * + For now there's no remove-from-queue operation, or * any other request management * + To a given spi_device, message queueing is pure fifo * * + The master's main job is to process its message queue, * selecting a chip then transferring data * + If there are multiple spi_device children, the i/o queue * arbitration algorithm is unspecified (round robin, fifo, * priority, reservations, preemption, etc) * * + Chipselect stays active during the entire message * (unless modified by spi_transfer.cs_change != 0). * + The message transfers use clock and SPI mode parameters * previously established by setup() for this device */ int (*transfer)(struct spi_device *spi, struct spi_message *mesg); /* called on release() to free memory provided by spi_master */ void (*cleanup)(struct spi_device *spi); /* * Used to enable core support for DMA handling, if can_dma() * exists and returns true then the transfer will be mapped * prior to transfer_one() being called. The driver should * not modify or store xfer and dma_tx and dma_rx must be set * while the device is prepared. */ bool (*can_dma)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer); /* * These hooks are for drivers that want to use the generic * master transfer queueing mechanism. If these are used, the * transfer() function above must NOT be specified by the driver. * Over time we expect SPI drivers to be phased over to this API. */ bool queued; // 是否排队 struct kthread_worker kworker; // 内核线程工作者 struct task_struct *kworker_task; // 内核线程任务 struct kthread_work pump_messages; // 工作队列 spinlock_t queue_lock; // 自旋锁 struct list_head queue; // 消息队列 struct spi_message *cur_msg; // 当前消息 bool idling; // 是否空闲 bool busy; // 是否忙 bool running; // 是否运行 bool rt; // 是否实时 bool auto_runtime_pm; // 是否自动运行时电源管理 bool cur_msg_prepared; // 当前消息是否准备好 bool cur_msg_mapped; // 当前消息是否映射 struct completion xfer_completion; // 完成 size_t max_dma_len; // 最大DMA长度 /* 准备传输硬件 */ int (*prepare_transfer_hardware)(struct spi_master *master); /* 传输单个消息 */ int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); /* 取消准备传输硬件 */ int (*unprepare_transfer_hardware)(struct spi_master *master); /* 准备消息 */ int (*prepare_message)(struct spi_master *master, struct spi_message *message); /* 取消准备消息 */ int (*unprepare_message)(struct spi_master *master, struct spi_message *message); /* * 这些钩子是为使用核心提供的transfer_one_message()的通用实现的驱动程序而设计的。 */ /* 设置芯片选择 */ void (*set_cs)(struct spi_device *spi, bool enable); /* 传输单个spi_transfer */ int (*transfer_one)(struct spi_master *master, struct spi_device *spi, struct spi_transfer *transfer); /* 处理错误 */ void (*handle_err)(struct spi_master *master, struct spi_message *message); /* gpio chip select */ int *cs_gpios; // GPIO片选 /* DMA channels for use with core dmaengine helpers */ struct dma_chan *dma_tx; // DMA发送通道 struct dma_chan *dma_rx; // DMA接收通道 /* dummy data for full duplex devices */ void *dummy_rx; // 全双工设备的虚拟接收缓冲区 void *dummy_tx; // 全双工设备的虚拟发送缓冲区 };
- transfer 函数,和 i2c_algorithm 中的 master_xfer 函数一样,控制器数据传输函数。
- 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_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。
返回值:无。
2、spi_master 的注册与注销
当 spi_master 初始化完成以后就需要将其注册到 Linux 内核,spi_master 注册函数为spi_register_master,函数原型如下:
int spi_register_master(struct spi_master *master)
函数参数和返回值含义如下:
master:要注册的 spi_master。
返回值:0,成功;负值,失败。
I.MX6U 的 SPI 主机驱动会采用 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。
SPI设备驱动
spi 设备驱动也和 i2c 设备驱动也很类似, Linux 内核使用 spi_driver 结构体来表示 spi 设备驱动,我们在编写 SPI 设备驱动的时候需要实现 spi_driver。 spi_driver 结构体定义在include/linux/spi/spi.h 文件中,结构体内容如下:
struct spi_driver { // SPI设备ID表 const struct spi_device_id *id_table; // 探测SPI设备 int (*probe)(struct spi_device *spi); // 移除SPI设备 int (*remove)(struct spi_device *spi); // 关闭SPI设备 void (*shutdown)(struct spi_device *spi); // 设备驱动 struct dev ice_driver driver; };
可以看出,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 注册示例程序如下:
/* probe 函数 */ static int xxx_probe(struct spi_device *spi) { /* 具体函数内容 */ return 0; } /* remove 函数 */ static int xxx_remove(struct spi_device *spi) { /* 具体函数内容 */ return 0; } /* 传统匹配方式 ID 列表 */ static const struct spi_device_id xxx_id[] = { {"xxx", 0}, {} }; /* 设备树匹配列表 */ static const struct of_device_id xxx_of_match[] = { { .compatible = "xxx" }, { /* Sentinel */ } }; /* SPI 驱动结构体 */ static struct spi_driver xxx_driver = { .probe = xxx_probe, .remove = xxx_remove, .driver = { .owner = THIS_MODULE, .name = "xxx", .of_match_table = xxx_of_match, }, .id_table = xxx_id, }; /* 驱动入口函数 */ static int init xxx_init(void) { return spi_register_driver(&xxx_driver); } /* 驱动出口函数 */ static void exit xxx_exit(void) { spi_unregister_driver(&xxx_driver); } module_init(xxx_init); 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。
SPI 设备和驱动匹配过程
SPI 设备和驱动的匹配过程是由 SPI 总线来完成的,这点和 platform、 I2C 等驱动一样, SPI总线为 spi_bus_type,定义在 drivers/spi/spi.c 文件中,内容如下:
struct bus_type spi_bus_type = { .name = "spi", .dev_groups = spi_dev_groups, .match = spi_match_device, .uevent = spi_uevent, };
可以看出, SPI 设备和驱动的匹配函数为 spi_match_device,函数内容如下:
static int spi_match_device(struct device *dev, struct device_driver *drv) { const struct spi_device *spi = to_spi_device(dev); const struct spi_driver *sdrv = to_spi_driver(drv); /* Attempt an OF style match */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI */ if (acpi_driver_match_device(dev, drv)) return 1; if (sdrv->id_table) return !!spi_match_id(sdrv->id_table, spi); return strcmp(spi->modalias, drv->name) == 0; }
- spi_match_device 函数和 i2c_match_device 函数的对于设备和驱动的匹配过程基本一样。
- of_driver_match_device 函数用于完成设备树设备和驱动匹配。比较 SPI 设备节点的 compatible 属性和 of_device_id 中的compatible 属性是否相等,如果相当的话就表示 SPI 设备和驱动匹配。
- acpi_driver_match_device 函数用于 ACPI 形式的匹配。
- spi_match_id 函数用于传统的、无设备树的 SPI 设备和驱动匹配过程。比较SPI设备名字和
- spi_device_id 的 name 字段是否相等,相等的话就说明 SPI 设备和驱动匹配。
比较 spi_device 中 modalias 成员变量和 device_driver 中的 name 成员变量是否相等。
I.MX6U SPI 主机驱动分析
和 I2C 的适配器驱动一样, SPI 主机驱动一般都由 SOC 厂商编写好了,打开 imx6ull.dtsi文件,找到如下所示内容:
ecspi1: ecspi@02008000 { compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi"; reg = <0x02008000 0x4000>; interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>; dmas = <&sdma 3 7 1>, <&sdma 4 7 2>; dma-names = "rx", "tx"; clocks = <&clks IMX6SLL_CLK_ECSPI1>, <&clks IMX6SLL_CLK_ECSPI1>; clock-names = "ipg", "per"; status = "disabled"; }; ecspi2: ecspi@0200c000 { compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi"; reg = <0x0200c000 0x4000>; interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>; dmas = <&sdma 5 7 1>, <&sdma 6 7 2>; dma-names = "rx", "tx"; clocks = <&clks IMX6SLL_CLK_ECSPI2>, <&clks IMX6SLL_CLK_ECSPI2>; clock-names = "ipg", "per"; status = "disabled"; }; ecspi3: ecspi@02010000 { compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi"; reg = <0x02010000 0x4000>; interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>; dmas = <&sdma 7 7 1>, <&sdma 8 7 2>; dma-names = "rx", "tx"; clocks = <&clks IMX6SLL_CLK_ECSPI3>, <&clks IMX6SLL_CLK_ECSPI3>; clock-names = "ipg", "per"; status = "disabled"; }; ecspi4: ecspi@02014000 { compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi"; reg = <0x02014000 0x4000>; interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>; dmas = <&sdma 9 7 1>, <&sdma 10 7 2>; dma-names = "rx", "tx"; clocks = <&clks IMX6SLL_CLK_ECSPI4>, <&clks IMX6SLL_CLK_ECSPI4>; clock-names = "ipg", "per"; status = "disabled"; };
重点来看一下第 4 行的 compatible 属性值, compatible 属性有两个值“fsl,imx6ul-ecspi”和“fsl,imx51-ecspi”,在 Linux 内核源码中搜素这两个属性值即可找到 I.MX6U 对应的 ECSPI(SPI)主机驱动。 I.MX6U 的 ECSPI 主机驱动文件为 drivers/spi/spi-imx.c,在此文件中找到如下内容:
static struct platform_device_id spi_imx_devtype[] = { { .name = "imx1-cspi", .driver_data = (kernel_ulong_t) &imx1_cspi_devtype_data, }, { .name = "imx21-cspi", .driver_data = (kernel_ulong_t) &imx21_cspi_devtype_data, }, { .name = "imx27-cspi", .driver_data = (kernel_ulong_t) &imx27_cspi_devtype_data, }, { .name = "imx31-cspi", .driver_data = (kernel_ulong_t) &imx31_cspi_devtype_data, }, { .name = "imx35-cspi", .driver_data = (kernel_ulong_t) &imx35_cspi_devtype_data, }, { .name = "imx51-ecspi", .driver_data = (kernel_ulong_t) &imx51_ecspi_devtype_data, }, { .name = "imx6ul-ecspi", .driver_data = (kernel_ulong_t) &imx6ul_ecspi_devtype_data, }, { /* sentinel */ } };
static const struct of_device_id spi_imx_dt_ids[] = { { .compatible = "fsl,imx1-cspi", .data = &imx1_cspi_devtype_data, }, { .compatible = "fsl,imx21-cspi", .data = &imx21_cspi_devtype_data, }, { .compatible = "fsl,imx27-cspi", .data = &imx27_cspi_devtype_data, }, { .compatible = "fsl,imx31-cspi", .data = &imx31_cspi_devtype_data, }, { .compatible = "fsl,imx35-cspi", .data = &imx35_cspi_devtype_data, }, { .compatible = "fsl,imx51-ecspi", .data = &imx51_ecspi_devtype_data, }, { .compatible = "fsl,imx6ul-ecspi", .data = &imx6ul_ecspi_devtype_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, spi_imx_dt_ids);
static struct platform_driver spi_imx_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = spi_imx_dt_ids, .pm = IMX_SPI_PM, }, .id_table = spi_imx_devtype, .probe = spi_imx_probe, .remove = spi_imx_remove, }; module_platform_driver(spi_imx_driver);
- spi_imx_devtype 为 SPI 无设备树匹配表。
- spi_imx_dt_ids 为 SPI 设备树匹配表。
- “fsl,imx6ul-ecspi”匹配项,因此可知 I.MX6U 的 ECSPI 驱动就是 spi-imx.c 这个文件。
- platform_driver 驱动框架,和 I2C 的适配器驱动一样,SPI 主机驱动器采用了 platfom 驱动框架。当设备和驱动匹配成功以后 spi_imx_probe 函数就会执行。
spi_imx_probe 函数会从设备树中读取相应的节点属性值,申请并初始化 spi_master,最后调用
spi_bitbang_start 函数(spi_bitbang_start 会调用 spi_register_master 函数)向 Linux 内核注册spi_master。
对于 I.MX6U 来讲,SPI 主机的最终数据收发函数为spi_imx_transfer,此函数通过如下层层调用最终实现SPI 数据发送:
spi_imx_transfer-> spi_imx_pio_transfer-> spi_imx_push-> spi_imx->tx
spi_imx 是个 spi_imx_data 类型的机构指针变量,其中 tx 和 rx 这两个成员变量分别为 SPI数据发送和接收函数。I.MX6U SPI 主机驱动会维护一个 spi_imx_data 类型的变量 spi_imx,并且使用 spi_imx_setupxfer 函数来设置 spi_imx 的 tx 和 rx 函数。根据要发送的数据数据位宽的不同,分别有 8 位、16 位和 32 位的发送函数,如下所示:
spi_imx_buf_tx_u8 spi_imx_buf_tx_u16 spi_imx_buf_tx_u32
同理,也有 8 位、16 位和 32 位的数据接收函数,如下所示:
spi_imx_buf_rx_u8 spi_imx_buf_rx_u16 spi_imx_buf_rx_u32
我们就以 spi_imx_buf_tx_u8 这个函数为例,看看,一个自己的数据发送是怎么完成的,在spi-imx.c 文件中找到如下所示内容:
#define MXC_SPI_BUF_TX(type) \ static void spi_imx_buf_tx_##type(struct spi_imx_data *spi_imx) \ { \ type val = 0; \ \ if (spi_imx->tx_buf) { \ val = *(type *)spi_imx->tx_buf; \ spi_imx->tx_buf += sizeof(type); \ } \ \ spi_imx->count -= sizeof(type); \ \ writel(val, spi_imx->base + MXC_CSPITXDATA); \ } MXC_SPI_BUF_RX(u8) MXC_SPI_BUF_TX(u8) MXC_SPI_BUF_RX(u16) MXC_SPI_BUF_TX(u16) MXC_SPI_BUF_RX(u32) MXC_SPI_BUF_TX(u32)
spi_imx_buf_tx_u8 函数是通过 MXC_SPI_BUF_TX 宏来实现的。第 164 行就是将要发送的数据值写入到 ECSPI 的 TXDATA 寄存器里面去,这和我们 SPI 裸机实验的方法一样。将第 168 行的 MXC_SPI_BUF_TX(u8)展开就是 spi_imx_buf_tx_u8 函数。其他的 tx 和 rx 函数都是这样实现的,这里就不做介绍了。基本套路和 I2C 的适配器驱动程序类似。
SPI 设备驱动编写流程
SPI 设备信息描述
1、 IO 的 pinctrl 子节点创建与修改
首先肯定是根据所使用的 IO 来创建或修改 pinctrl 子节点,这个没什么好说的,唯独要注意的就是检查相应的 IO 有没有被其他的设备所使用,如果有的话需要将其删除掉!
2、 SPI 设备节点的创建与修改
采用设备树的情况下, SPI 设备信息描述就通过创建相应的设备子节点来完成,我们可以打开 imx6qdl-sabresd.dtsi 这个设备树头文件,在此文件里面找到如下所示内容:
&ecspi1 { fsl,spi-num-chipselects = <1>; cs-gpios = <&gpio4 9 0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ecspi1>; status = "okay"; flash: m25p80@0 { #address-cells = <1>; #size-cells = <1>; compatible = "st,m25p32"; spi-max-frequency = <20000000>; reg = <0>; }; };
I.MX6Q 的一款板子上的一个SPI 设备节点,在这个板子的 ECSPI 接
口上接了一个m25p80,这是一个 SPI 接口的设备。
- 设置“fsl,spi-num-chipselects”属性为 1,表示只有一个设备。
- 设置“cs-gpios”属性,也就是片选信号为 GPIO4_IO09。
- 设置“pinctrl-names”属性,也就是 SPI 设备所使用的 IO 名字。
- 设置“pinctrl-0”属性,也就是所使用的 IO 对应的 pinctrl 节点。
- 将 ecspi1 节点的“status”属性改为“okay”。
- ecspi1 下的 m25p80 设备信息,每一个 SPI 设备都采用一个子节点来描述其设备信息。第 315 行的“m25p80@0”后面的“0”表示 m25p80 的接到了ECSPI 的通道 0上。这个要根据自己的具体硬件来设置。
- SPI 设备的 compatible 属性值,用于匹配设备驱动。
- “spi-max-frequency”属性设置 SPI 控制器的最高频率,这个要根据所使用的SPI 设备来设置,比如在这里将 SPI 控制器最高频率设置为 20MHz。
reg 属性设置 m25p80 这个设备所使用的ECSPI 通道,和“m25p80@0”后面的“0”一样。
我们一会在编写 ICM20608 的设备树节点信息的时候就参考示例代码 中的内容即可。
SPI 设备数据收发处理流程
SPI 设备驱动的核心是 spi_driver,这个我们已经在 62.1.2 小节讲过了。当我们向 Linux 内核注册成功 spi_driver 以后就可以使用 SPI 核心层提供的 API 函数来对设备进行读写操作了。首先是 spi_transfer 结构体,此结构体用于描述 SPI 传输信息,结构体内容如下:
/** * struct spi_transfer - 一个读写缓冲区对 * @tx_buf: 待写入的数据(dma-safe memory),或者为NULL * @rx_buf: 待读取的数据(dma-safe memory),或者为NULL * @tx_dma: tx_buf的DMA地址,如果@spi_message.is_dma_mapped * @rx_dma: rx_buf的DMA地址,如果@spi_message.is_dma_mapped * @tx_nbits: 写入使用的位数。如果为0,则使用默认值(SPI_NBITS_SINGLE)。 * @rx_nbits: 读取使用的位数。如果为0,则使用默认值(SPI_NBITS_SINGLE)。 * @len: rx和tx缓冲区的大小(以字节为单位) * @speed_hz: 选择与设备默认速度不同的速度进行此传输。如果为0,则使用默认值(来自@spi_device)。 * @bits_per_word: 选择与设备默认的bits_per_word不同的bits_per_word进行此传输。如果为0,则使用默认值(来自@spi_device)。 * @cs_change: 在此传输完成后影响chipselect * @delay_usecs: 在此传输之后延迟的微秒数,然后(可选)更改chipselect状态,然后开始下一个传输或完成此@spi_message。 * @transfer_list: 通过@spi_message.transfers对传输进行排序 * @tx_sg: 用于传输的Scatterlist,目前不适用于客户端使用 * @rx_sg: 用于接收的Scatterlist,目前不适用于客户端使用 * * SPI传输始终写入与读取相同数量的字节。 * 协议驱动程序应始终提供@rx_buf和/或@tx_buf。 * 在某些情况下,它们可能还希望为正在传输的数据提供DMA地址;当底层驱动程序使用dma时,这可能会减少开销。 * * 如果传输缓冲区为null,则填充的数据将被忽略。如果tx_buf和rx_buf相同,则可以使用单个缓冲区进行全双工传输。 在这种情况下,传输长度必须是偶数,并且将交替写入和读取数据。 SPI传输的字节顺序为big-endian,除了SPI_LSB_FIRST。因此,例如,当bits_per_word为16时,缓冲区的长度为2N字节(@len = 2N),并以CPU字节顺序保存N个16位字。 当SPI传输的字大小不是8位的2的幂时,这些内存字包括额外的位。内存中的字始终由协议驱动程序视为右对齐,因此未定义的(rx)或未使用的(tx)位始终是最高有效位。 所有SPI传输都从相关的chipselect处开始。通常,它保持选定状态,直到消息中的最后一个传输之后。驱动程序可以使用cs_change来影响chipselect信号。 (i)如果传输不是消息中的最后一个,则使用此标志使chipselect在消息中间短暂地变为非活动状态。以这种方式切换chipselect可能需要终止芯片命令,从而使单个spi_message执行所有芯片事务。 (ii)当传输是消息中的最后一个时,芯片可能保持选定状态,直到下一个传输。在没有阻止消息发送到其他设备的情况下,这只是性能提示;启动发送到另一个设备的消息会取消选择此设备。但在其他情况下,这可以用于确保正确性。某些设备需要从一系列spi_message提交构建协议事务,在这些事务中,一个消息的内容由先前消息的结果确定,并且整个事务在chipselect变为非活动状态时结束。 当SPI可以以1x、2x或4x传输时,它可以通过@tx_nbits和@rx_nbits从设备获取此传输信息。在双向传输中,这两个应该都设置。用户可以使用SPI_NBITS_SINGLE(1x)SPI_NBITS_DUAL(2x)和SPI_NBITS_QUAD(4x)设置传输模式以支持这三种传输。 提交spi_message(及其spi_transfers)到较低层的代码负责管理其内存。对您没有明确设置的每个字段进行零初始化,以隔离未来的API更新。在提交消息及其传输后,请忽略它们,直到完成回调。 */ struct spi_transfer { const void *tx_buf; /* 待写入的数据(dma-safe memory),或者为NULL */ void *rx_buf; /* 待读取的数据(dma-safe memory),或者为NULL */ unsigned len; /* rx和tx缓冲区的大小(以字节为单位) */ dma_addr_t tx_dma; /* tx_buf的DMA地址,如果@spi_message.is_dma_mapped */ dma_addr_t rx_dma; /* rx_buf的DMA地址,如果@spi_message.is_dma_mapped */ struct sg_table tx_sg; /* 用于传输的Scatterlist,目前不适用于客户端使用 */ struct sg_table rx_sg; /* 用于接收的Scatterlist,目前不适用于客户端使用 */ unsigned cs_change:1; /* 在此传输完成后影响chipselect */ unsigned tx_nbits:3; /* 写入使用的位数。如果为0,则使用默认值(SPI_NBITS_SINGLE)。 */ unsigned rx_nbits:3; /* 读取使用的位数。如果为0,则使用默认值(SPI_NBITS_SINGLE)。 */ #define SPI_NBITS_SINGLE 0x01 /* 1bit transfer */ #define SPI_NBITS_DUAL 0x02 /* 2bits transfer */ #define SPI_NBITS_QUAD 0x04 /* 4bits transfer */ u8 bits_per_word; /* 选择与设备默认的bits_per_word不同的bits_per_word进行此传输。如果为0,则使用默认值(来自@spi_device)。 */ u16 delay_usecs; /* 在此传输之后延迟的微秒数,然后(可选)更改chipselect状态,然后开始下一个传输或完成此@spi_message。 */ u32 speed_hz; /* 选择与设备默认速度不同的速度进行此传输。如果为0,则使用默认值(来自@spi_device)。 */ struct list_head transfer_list; /* 通过@spi_message.transfers对传输进行排序 */ };
- tx_buf 保存着要发送的数据。
- rx_buf 用于保存接收到的数据。
- len 是要进行传输的数据长度, SPI 是全双工通信,因此在一次通信中发送和接收的字节数都是一样的,所以 spi_transfer 中也就没有发送长度和接收长度之分。
spi_transfer 需要组织成 spi_message, spi_message 也是一个结构体,内容如下:
struct spi_message { struct list_head transfers; struct spi_device *spi; unsigned is_dma_mapped:1; /* REVISIT: we might want a flag affecting the behavior of the * last transfer ... allowing things like "read 16 bit length L" * immediately followed by "read L bytes". Basically imposing * a specific message scheduling algorithm. * * Some controller drivers (message-at-a-time queue processing) * could provide that as their default scheduling algorithm. But * others (with multi-message pipelines) could need a flag to * tell them about such special cases. */ /* completion is reported through a callback */ void (*complete)(void *context); void *context; unsigned frame_length; unsigned actual_length; int status; /* for optional use by whatever driver currently owns the * spi_message ... between calls to spi_async and then later * complete(), that's the spi_master controller driver. */ struct list_head queue; void *state; };
在使用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_sync 函数。综上所述,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_message_init(&m); /* 初始化 spi_message */ spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */ ret = spi_sync(spi, &m); /* 同步传输 */ return ret; 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_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 多字节接收 */ 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, }; }