Linux SPI体系结构图
本文对驱动框架不做详细分析(可参见孟浩依然的博文《linux内核SPI总线驱动分析》),只阐述SPI驱动各环节衔接过程,因为我发现在缺乏必要的驱动理论基础的前提下,对模块分而治之就像瞎子摸象,缺少整体感和流畅性。
Linux中SPI子系统的初始化是从drivers/spi/spi.c文件中的spi_init函数开始的,看看它的定义:
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type);
if (status < 0)
goto err1;
status = class_register(&spi_master_class);
if (status < 0)
goto err2;
return 0;
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}
初始化过程主要为总线分配内存资源、完成总线注册,这里涉及到SPI总线结构体,定义如下:
struct bus_type spi_bus_type = {
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device,
.uevent = spi_uevent,
.suspend = spi_suspend,
.resume = spi_resume,
};
其中包含了最重要的match函数,该函数作用是匹配总线设备与驱动,在spi_driver注册时,会被用于扫描spi总线上的设备,详见match定义
。
补充知识:
Device Naming and Driver Binding
platform_device.dev.bus_id是一个设备在总线上的名字,它包含两部分:
* platform_device.name 设备名字,用来进行driver的匹配
* platform_device.id 设备实例的标号,如果是-1,表示同样名字的设备只有一个
举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1 如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。
driver的绑定是通过driver core自动完成的,完成driver和device的匹配后以后会自动执行probe()函数,如果函数执行成功,则driver和device就绑定在一起了,drvier和device匹配的方法有3种:
* 当一个设备注册的时候,会在总线上寻找匹配的driver,platform device一般在系统启动很早的时候就注册了。
* 当一个驱动注册[platform_driver_register()]的时候,会遍历所有总线上的设备来寻找匹配,在启动的过程驱动的注册一般比较晚,或者在模块载入的时候
* 当一个驱动注册[platform_driver_probe()]的时候, 功能上和使用platform_driver_register()是一样的,唯一的区别是它不能被以后其他的device probe了,也就是说这个driver只能和一个device绑定。
spi_device 和spi_master
platform总线有platform_device和platform_driver,对于SPI总线,也有对应的spi_device和spi_driver。
struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
#define SPI_NO_CS 0x40 /* 1 dev/bus, no chipselect */
#define SPI_READY 0x80 /* slave pulls low to pause */
u8 bits_per_word;
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
/*
* likely need more hooks for more protocol options affecting how
* the controller talks to each chip, like:
* - memory packing (12 bit samples into low bits, others zeroed)
* - priority
* - drop chipselect after each word
* - chipselect delays
* - ...
*/
};
spi_device通常在BSP的板文件中定义,它的板信息用spi_board_info结构体描述,该结构体记录SPI外设使用的主机控制器序号、片选序号、数据比特率、SPI传输模式(即使)等。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;
};
在系统开机时,在机器初始化函数init_machine()中将通过spi_register_board_info()将spi_board_info注册到链表board_list上,这一点与启动时通过platform_add_devices()函数统一注册platform_device(归纳为一个数组)过程非常相似。但是必须要强调的是,此时并未创建spi_device或者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);
};
另外,一般来说设备是不能被热插拔的,所以可以将probe()函数放在spi控制器驱动init段里面来节省driver运行时候的内存开销:
static int __init s3c64xx_spi_init(void)
{
return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe);
}
在s3c6410中,spi控制器驱动在设备与驱动匹配成功后才创建并注册的,详见s3c64xx_spi_probe函数定义。
spi_driver在驱动模块加载时进行初始化并注册,注册时会扫描总线上的设备,通过总线match函数进行设备与驱动匹配,若成功,则调用spi_driver的probe函数,通过platform_get_resource等函数获取spi_board_info中定义的资源信息,创建并注册spi_master,spi_master注册过程会调用scan_boardinfo扫描board_list,通过总线号(bus_num)找到挂接在它上面的spi设备,创建并注册一个spi_device。
SPI外设驱动
spi外设驱动遍布于内核的drivers、sounds各个子目录之下,spi只是一种总线,spi_driver的作用只是将spi外设挂接在该总线上,因此在spi_driver的probe函数中,将注册本身所属设备驱动的类型。