1、概要
Linux 中的spi驱动主要是由spi子系统来管理的,其核心代码位于kernel/drivers/spi/spi.c中。具体的spi控制器驱动也在kernel/drivers/spi/目录中。目前spi子系统只支持spi主机模式,还不支持spi从机模式。
spi通常是由spiI控制器、spi总线和连接在spi总线上的设备构成:这里的总线只是指物理的spi连线,并不是指Linux设备模型中逻辑上的总线概念。可以把spi控制器和spi总线看成是一体的,spi总线就是spi控制器加上和spi设备的连接线。spi设备包含很多种,它可以是一个spi接口的nor flash,例如ST的M25P80。它也可以是一个spi网卡,例如ENC28J60。
2、spi控制器
Linux的spi子系统对spi控制器的描述使用的是struct spi_master这个数据结构,所以在内核中,一个spi_master结构就代表了一个spi控制器,或者说代表一个spi主机。
它的主要定义如下
struct spi_master {
struct device dev;
struct list_head list;
s16 bus_num; //总线(或控制器)编号,总线和设备匹配时用到
u16 num_chipselect; //片选数量,决定该控制器下面挂接多少个SPI设备
u16 dma_alignment;
u16 mode_bits;
u16 flags;
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
bool bus_lock_flag;
int (*setup)(struct spi_device *spi); //这个需要我们自己具体实现,
//主要设置SPI控制器和工作方式
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg); //这个不同的控制器要具体实现,
//传输数据最后都要调用这个函数
void (*cleanup)(struct spi_device *spi); //注销时使用
};
在Linux-2.6以后引入设备模型概念,所有的设备和驱动都需要依附于总线。附于Spi总线的设备驱动对应的总线类型为spi_bus_type,在内核的drivers/spi/spi.c中定义。
struct bus_type spi_bus_type = {
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device,
.uevent = spi_uevent,
.pm = &spi_pm,
};
EXPORT_SYMBOL_GPL(spi_bus_type);
后续会有文章专门讲述如何构建一个struct spi_master对象,并将之注册到spi子系统中去。
3、spi设备
Linux spi子系统对spi总线上的设备用struct spi_device结构来描述,运行的内核中,通常一个struct spi_device对象对应一个spi设备:
struct spi_device {
struct device dev;
struct spi_master *master; //挂在哪个主控器下
u32 max_speed_hz; //设备支持的最大速度
u8 chip_select; //设备的片选号
u8 mode; //SPI的模式,参见SPI的四种模式
u8 bits_per_word; //每个字的位数
int irq; //中断号
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE]; //设备名
};
struct spi_device主要用来描述连接在spi总线上的一个spi设备的一些电气信息的。
在2.6.xx版本的内核中可能会经常看到平台代码中用 struct spi_board_info来描述spi设备的电气信息,该结构体包含外设的片选号、总线号、模式以及传输速率等信息。struct spi_board_info原型如下:
struct spi_board_info {
/* the device name and module name are coupled, like platform_bus;
* "modalias" is normally the driver name.
*
* platform_data goes to spi_device.dev.platform_data,
* controller_data goes to spi_device.controller_data,
* irq is copied too
*/
char modalias[SPI_NAME_SIZE];
const void *platform_data;
void *controller_data;
int irq;
/* slower signaling on noisy or low voltage boards */
u32 max_speed_hz;
/* bus_num is board specific and matches the bus_num of some
* spi_master that will probably be registered later.
*
* chip_select reflects how this chip is wired to that master;
* it's less than num_chipselect.
*/
u16 bus_num;
u16 chip_select;
/* mode becomes spi_device.mode, and is essential for chips
* where the default of SPI_CS_HIGH = 0 is wrong.
*/
u8 mode;
/* ... may need additional spi_device chip config data here.
* avoid stuff protocol drivers can set; but include stuff
* needed to behave without being bound to a driver:
* - quirks like clock rate mattering when not selected
*/
};
在板级初始化代码中,2.6.xx版本的内核中通常会用一个 struct spi_board_info数组来描述系统中的spi设备信息,然后调用spi_register_board_info()将这些设备信息注册到系统中。当然这些struct spi_board_info最终也会转化为struct spi_device结构。关于spi_device是如何构建和注册的,请关注后面的博文。
4、spi设备驱动
spi设备的驱动都会有一个struc spi_driver结构体来描述,结构体中定义对应的操作函数指针,用来管理依附于总线上的相关设备
struct spi_driver {
const struct spi_device_id *id_table;
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;
};
通常对于从事Linux驱动工作人员来说,spi设备的驱动主要就是实现这个结构体中的各个接口,并将之注册到spi子系统中去。后面也会有文章专门讲解如何实现struct spi_driver。
5、数据传输相关数据结构
第一个与数据传输相关的数据结构为struct spi_transfer,数据结构定义如下:
struct spi_transfer {
/* it's ok if tx_buf == rx_buf (right?)
* for MicroWire, one buffer must be null
* buffers must work with dma_*map_single() calls, unless
* spi_message.is_dma_mapped reports a pre-existing mapping
*/
const void *tx_buf; //发送数据的缓冲区
void *rx_buf; //接收数据的缓冲区
unsigned len; //需要交换数据的长度
dma_addr_t tx_dma;
dma_addr_t rx_dma;
unsigned cs_change:1; //片选信号
u8 bits_per_word;
u16 delay_usecs;
u32 speed_hz;
struct list_head transfer_list;
};
一个struct spi_transfer对象代表了一次单段的spi数据传输。struct spi_transfer结构体中记录了本段传输需要交换的数据和长度,传输的速度,传输时片选信号的变化情况。
另外一个与数据交换相关的数据结构为struct 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 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;
};
一个struct spi_message代表对一个设备进行一个多段spi数据传输。每一段传输其实就是使用上面提到的struct spi_trasnfer对象完成的。struct spi_message主要记录了这次传输针对的设备。上面提到的struct spi_transfer对象会被链接到struct spi_message对象中。
6、其他数据结构
对于3.0内核以前,ARM平台通常的做法是在板级代码中注册struct platform_device来描述各种控制器,例如spi控制器,i2c控制器等等。然后再向系统中注册struct platform_driver来管理和驱动对应的平台设备。spi控制器驱动也是同样的做法。
3.0以后的内核引入Device Tree来描述ARM平台及板级设备。这样一来我们就不需要在板级代码手动的构建并注册描述spi的platform_device数据了。这一切可以由Device Tree来描述,并且又Device Tree相关代码来解析Device Tree对应spi的相关描述信息,并自动创建并注册表示spi控制器的platform_device数据。与之对应的platform_driver和以前差不多,只不过支持Device Tree后,platform_driver中可以通过Device Tree获取spi控制器的相关信息。引入Device Tree之后platform_driver数据结构中引入下面数据结构:
struct of_device_id
{
char name[32];
char type[32];
char compatible[128];
const void *data;
};
引入Device Tree后platform_driver优先使用struct of_device_id对象中的compatible成员和platform_device中dev.of_node中的compatible成员匹配的。在一些spi控制器驱动代码中常常可以看到如下定义。
static struct of_device_id xxx_spi_of_match[] = {
{ .compatible = "vendor,chip-spi", },
{}
};
MODULE_DEVICE_TABLE(of, xxx_spi_of_match);
另外一个有spi设备和spi驱动相关的数据结构struct of_spi_device_id,定义如下:
struct spi_device_id {
char name[SPI_NAME_SIZE];
kernel_ulong_t driver_data /* Data private to the driver */
__attribute__((aligned(sizeof(kernel_ulong_t))));
};