对技术执着
分类: 嵌入式
2015-03-14 15:55:37
原文地址:Linux SPI框架(上) 作者:enzo26
水平有限,描述不当之处还请指出,转载请注明出处http://blog.csdn.net/vanbreaker/article/details/7733476
Linux的SPI子系统采用主机驱动和外设驱动分离的思想,首先主机SPI控制器是一种平台设备,因此它以platform的方式注册进内核,外设的信息是以boardinfo形式静态定义的,在创建spi_master时,会根据外设的bus_num和主机的bus_num是否相等,来选择是否将该外设挂接在该SPI主控制器下。先看SPI子系统中几个关键的数据结构:
struct spi_master用来描述一个SPI主控制器
struct spi_master { struct device dev; s16 bus_num; /*总线编号*/ u16 num_chipselect;/*支持的外设数量*/ u16 dma_alignment; int (*transfer)(struct spi_device *spi, struct spi_message *mesg);/*用于将消息添加到队列*/ void (*cleanup)(struct spi_device *spi); };
struct spi_device用来描述一个SPI从设备
struct spi_device { struct device dev; struct spi_master *master; /*从设备所属的SPI主控器*/ u32 max_speed_hz; /*最大传输频率*/ u8 chip_select; /*片选号,用于区别其他从设备*/ u8 mode; /*传输模式*/ /*各个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 */ u8 bits_per_word; /*每个字的比特数*/ int irq; /*所使用的中断*/ void *controller_state; void *controller_data; char modalias[32]; /*设备名,在和从设备驱动匹配时会用到*/ };
struct spi_driver用来描述一个SPI从设备的驱动,它的形式和struct platform_driver是一致的
struct spi_driver { 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; };
SPI子系统初始化的第一步就是将SPI总线注册进内核,并且在/sys下创建一个spi_master的类,以后注册的从设备都将挂接在该总线下
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);//注册SPI总线 if (status < 0) goto err1; status = class_register(&spi_master_class);//注册spi_master类 if (status < 0) goto err2; return 0; err2: bus_unregister(&spi_bus_type); err1: kfree(buf); buf = NULL; err0: return status; }
我们来看spi_bus_type的定义
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, };
来看挂接在SPI总线下的从设备和从设备驱动是如何匹配的,也就是spi_match_device函数
static const struct spi_device_id *spi_match_id(const struct spi_device_id *id,
const struct spi_device *sdev)
{
while (id->name[0]) {
if (!strcmp(sdev->modalias, id->name))
return id;
id++;
}
return NULL;
}
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; //如果驱动中定义id_table,查看driver结构体id_table->name与 平台设备结构体dev->modalias 是否相同 if (sdrv->id_table) return !!spi_match_id(sdrv->id_table, spi); //如果驱动中没有定义id_table,则对比drv->name 与 dev->modalias是否相同 return strcmp(spi->modalias, drv->name) == 0; }
这里可以看到是将struct device_driver中的name字段与struct spi_device中的modalias字段进行匹配
这里已经完成了SPI子系统初始化的第一步,也就是注册SPI总线,这一步是和平台无关的,第二步是和平台相关的初始化,下一节再做介绍。