linux对spi、I2C、USB等子系统使用分离设计思想:主机驱动和外设驱动分离,主机只负责产生总线上的输出波形,外设通过标准接口让主机已适当的波形访问自己。
1. 主机驱动
2. 主机与外设的连接
3. 外设驱动
4. 板级逻辑
spi控制器驱动
spi_master描述一个spi主机控制器驱动
spi设备驱动
spi_driver描述一个spi外设驱动
spi_transfer
描述spi总线数据传输
spi_message
spi_message是对spi_transfer进行组织,将spi_message加入spi_message队列中
---------------------------------------------------------------------------------------------------
SPI驱动理解:
SPI子系统从上到下分为:spi设备驱动层,核心层和master驱动层。其中master驱动抽象出spi控制器的相关操作,而spi设备驱动层抽象出了用户空间API。
platform_device结构中描述了SPI控制器的相关资源,同时在板级信息中将会添加spi设备的相关信息。master驱动将以platform_driver形式体现出来,也就是说
在主控制器(master)和主控制器驱动将挂载到platform总线上。platform_driver的probe函数中将注册spi_master,同时将会获取在板级信息中添加的spi设备,将该
信息转换成spi_device,然后注册spi_device到spi总线上。spi_driver结构用于描述spi设备驱动,也将挂载到spi总线上。连同spi_driver一起注册的是字符设备,该
字符设备将提供5个API给用户空间。通过API,用户空间可以执行半双工读、半双工写和全双工读写。
SPI驱动框架可以分为SPI核心层、SPI控制器驱动层、SPI设备驱动层
SPI核心层(SPI总线层):
SPI核心层负责注册SPI总线,主要定义了总线类型和主控制器设备类
总线类型:
struct bus_type spi_bus_type = {
.name = "spi",
.dev_attrs = spi_dev_attrs,
.match = spi_match_device,
.uevent = spi_uevent,
.pm = &spi_pm,
};
SPI总线类型,通过bus_register()函数,将SPI总线注册进总线,成功注册后在sys/bus下即可找到spi节点
// spi总线注册过程
postcore_initcall(spi_init);
spi_init(void)
bus_register(&spi_bus_type); //注册spi总线
成功注册后在sys/bus下即可找到spi节点
spi_bus_type中的spi_match_device函数负责匹配spi总线上的device和driver
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;
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0;
}
控制器设备类:
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
};
主要定义了SPI总线主控制器的设备类,通过调用class_register()函数注册设备类,成功注册后,在/sys/class目录下即可找到spi_master文件节点。
// spi MASTER
postcore_initcall(spi_init);
spi_init(void)
bus_register(&spi_bus_type); //注册spi总线
class_register(&spi_master_class); //class_register()函数注册设备类,成功注册后,在/sys/class目录下即可找到spi_master文件节点。
SPI控制器驱动层
struct spi_master
问:struct spi_master谁会用到?
答:用到spi_master的代码基本都在内核drivers\spi目录下,drivers\spi目录下是各类spi主控制驱动实现
对driver\spi\spi_s3c24xx.c进行分析
定义s3c24xx_spi_driver:
static struct platform_driver s3c24xx_spi_driver = {
.remove = __exit_p(s3c24xx_spi_remove),
.driver = {
.name = "s3c2410-spi",
.owner = THIS_MODULE,
.pm = S3C24XX_SPI_PMOPS,
},
};
主控制器驱动注册到platform总线
s3c24xx_spi_init
platform_driver_probe(struct platform_driver *drv, int (*probe)(struct platform_device *))
platform_driver_register(struct platform_driver *drv)
SPI主控制机设备注册到platform总线
在arch\arm\plat-s3c24xx\devs.c中,定义了platform_device设备s3c_device_spi0
struct platform_device s3c_device_spi0 = {
.name = "s3c2410-spi",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_spi0_resource),
.resource = s3c_spi0_resource,
.dev = {
.dma_mask = &s3c_device_spi0_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
s3c_device_spi0加入到arch\arm\plat-s3c24xx\mach-nexcoder.c中的struct platform_device *nexcoder_devices[]数组中
static struct platform_device *nexcoder_devices[] __initdata = {
....
&s3c_device_spi0,
....
};
nexcoder_devices[]被同文件中的nexcoder_init调用,nexcoder_init再调用platform_add_devices加入到platform总线上。
SPI设备驱动层
在外设驱动实现中调用spi_register_driver(struct spi_driver *sdrv),向spi总线挂载spi_driver,该函数在drivers\spi\spi.c中实现
如:
static struct spi_driver mc33880_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.probe = mc33880_probe,
.remove = __devexit_p(mc33880_remove),
};
在mc33880_init中调用spi_register_driver(&mc33880_driver)将mc33880_driver注册到spi总线,向struct spi_driver的probe接口传入对应的mc33880_probe函数
调用过程如下:
mc33880_init(void)
spi_register_driver(&mc33880_driver);
driver_register(&sdrv->driver);
bus_add_driver(drv);
driver_attach(drv);
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
driver_match_device(drv, dev) // 调用spi总线spi_bus_type中的math函数
driver_probe_device(drv, dev);
really_probe(dev, drv)
dev->bus->probe(dev) 或者 drv->probe(dev);
SPI外设注册