![](https://img.haomeiwen.com/i3482555/b9ffba52f8b7fa28.jpg)
Linux spi system
SPI是由Motorola提出的一种全双工同步串行通信接口,通信波特率可以高达5Mbps,但具体速度大小取决于SPI硬件,SPI接口具有全双工操作,操作简单,数据传输速率较高的优点,但也存在没有指定的流控制,没有应答机制确认是否接收到数据的缺点。
1.Linux下SPI的驱动架构
如下:
![](https://img.haomeiwen.com/i3482555/7e95bc4832d4fea1.jpg)
从图中可以观察到SPI系统的整个框架,发现跟I2C的框架很十分相似;
硬件和用户空间就不多说了,直接看内核空间部分。
内核空间主要分为三个模块,控制器驱动,设备器驱动,和连接两个驱动的SPI核心;
spi与i2c内核空间的对比
- master就相当于i2c中的apapter
- spi_device即i2c中的i2c_client
- spi core 即i2c core
不同于i2c的目录框架比较忙明确,i2c的控制器驱动在/drivers/i2c/busses/下面,设备器驱动在/drivers/i2c/chips/下面。而spi的控制器就直接放在spi/目录下,设备器驱动根据功能分开在各个地方,如spi flash的一般就在/drivers/mtd/device/下,siwtch的spi接口就放在/drivers/net/ethernet/下。
2.SPI master驱动
SPI控制器的驱动分为两部分,device和driver,使用platform总线进行连接。
platform_device
的注册一般在arch下面实现,使用platform_add_devices(arch_devices, ARRAY_SIZE(arch_devices));
进行添加,由于内核中有很多使用platform注册的总线,所以一般会统一使用一次platform_add_devices
函数进行添加,如下:
static struct platform_device *arch_devices[] __initdata = {
&arch_nor,
&arch_i2c,
&arch_wdt,
&arch_spi,
&arch_tdm_device,
&arch_pfe_device,
}
一个arch_devices
里面包含很多platform_device
,每个device各自实现后,再一块儿添加。
static struct platform_device comcerto_spi = {
.name = "comcerto_spi",
.id = 0,
.num_resources = ARRAY_SIZE(comcerto_spi_resource),
.resource = comcerto_spi_resource,
.dev = {
.platform_data = &ls_spi_pdata,
},
};
platform_driver
的实现在/driver/spi/中实现,使用platform_driver_register
和platform_driver_unregister
进行加载下载。
static struct platform_driver designware_spi_driver = {
.probe = designware_spi_probe,
.remove = __devexit_p(designware_spi_remove),
#if CONFIG_PM
.suspend = designware_spi_suspend,
.resume = designware_spi_resume,
#endif
.driver = {
.name = DESIGNWARE_SPI_NAME,
.owner = THIS_MODULE,
},
};
static int __init designware_spi_init(void)
{
return platform_driver_register(&designware_spi_driver);
}
static void __exit designware_spi_exit(void)
{
platform_driver_unregister(&designware_spi_driver);
}
module_init(designware_spi_init);
module_exit(designware_spi_exit);
designware_spi_probe
函数里面就是master的实现部分,里面会对spi进行很多处理,如中断/dma/等,最后会使用spi_register_master
函数进行注册spi_master,该函数就是spi的核心层了,位于/driver/spi/spi.c中,这一部分的内容还不知道如何描述,后面有思路时在更新。
3.SPI device驱动
SPI设备的驱动也是分为两部分,device和driver,使用spi总线进行连接。
spi_device
的注册一般在arch下面实现,使用spi_register_board_info(arch_spi_board_info, ARRAY_SIZE(arch_spi_board_info))
函数进行添加,函数里面的arch_spi_board_info
是
spi_board_info
结构体,需要我们对里面的参数进行配置,如下例子:
static struct spi_board_info comcerto_spi_board_info[] = {
{
/* FIXME: for chipselect-0 */
.modalias = "s25fl256s0",
.chip_select = 0,
.max_speed_hz = 4*1000*1000,
.bus_num = 1,
.irq = -1,
.mode = SPI_MODE_3,
.platform_data = &spi_pdata,
.controller_data = &spi_ctrl_data,
},
}
spi_driver
的实现也比较简单,一般就是通过spi_register_driver
来进行添加,spi_unregister_driver
进行卸载。
如下例子:
static struct spi_driver m25p80_driver = {
.driver = {
.name = "m25p80",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.id_table = m25p_ids,
.probe = m25p_probe,
.remove = __devexit_p(m25p_remove),
};
static int __init m25p80_init(void)
{
return spi_register_driver(&m25p80_driver);
}
static void __exit m25p80_exit(void)
{
spi_unregister_driver(&m25p80_driver);
}
module_init(m25p80_init);
module_exit(m25p80_exit);
当spi_driver.id_table
信息与spi_board_info
所指向的设备(或者设备树中的节点)匹配成功,则执行spi_driver.probe()
,对应probe函数里面实现的内容就要根据spi所使用的场景进行对于的实现了。
如spi flash一般是作为存储介质位于mtd下面的,所以probe函数里面就要使用mtd_device_parse_register
来实现mtd的添加;switch的spi接口是用来跟switch进行数据通信,probe函数里面就可以使用register_chrdev_region
来添加个字符设备进行操作。
调试驱动的时候最常用的方法就是使用printk来进行交互,进行定位、验证,但是要在哪边进行printk呢,个人觉得调试spi驱动有一个地方一定要进行printk,那就是位于spi.c
下的spi_match_id()
函数,如下:
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;
}
总结:分析调试I2C/SPI的驱动后,有以下几点感悟:
- 不管什么驱动,大致都可以分为两个动作响应,或者说两个probe接口,一个是控制器的probe(根据cpu的不同进行改变),一个是设备的probe(根据设备的不同进行改变)。
- 每一个probe接口有与之对于的device和driver,并且需要一种总线相连。
- 控制器的probe一般使用platform总线,对应
platform_device
和platform_driver
;设备的probe使用相关的总线,如i2c总线,则对应i2c_device
和i2c_driver
,i2c总线,则对应spi_device
和spi_driver
。 - probe函数的触发都是对应的
match_id
函数,如platform_match_id
、i2c_match_id
、spi_match_id
。 - platform的device的注册在arch下面,使用
platform_add_devices
添加,platform对应的driver,位于各目录下,如i2c位于i2c/busses/里面,使用i2c_register_adapter
来添加。 - 驱动如i2c的device的注册位于arch下,使用
i2c_register_board_info
或dts添加,对应i2c的driver位于i2c/chips/下面,使用i2c_add_driver
来添加。
Linux spi system的分析就到这边,有感悟时会持续会更新。
注:以上内容都是本人在学习过程积累的一些心得,难免会有参考到其他文章的一些知识,如有侵权,请及时通知我,我将及时删除或标注内容出处,如有错误之处也请指出,进行探讨学习。文章只是起一个引导作用,详细的数据解析内容还请查看Linux相关教程,感谢您的查阅。
网友评论