SPI master-slave驱动框架分析
SPI主要分主控制器及SPI设备两端,两者之间靠spi.h这个公共接口来作为抽象层。
首先来分析SPI总线:【本篇着重分析注册及匹配流程,下篇将会详细分析SPI master驱动的时序及实现方式】
===================================================================================================
spi bus
===================================================================================================
总线类型:
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_type没有实现probe函数,对下面的drv.probe有重要影响。
总线注册:【对应与/sys/bus/目录】
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
status = bus_register(&spi_bus_type); //注册总线类型
status = class_register(&spi_master_class); //注册spi主控器类型
return 0;
}
spi主控器为一个device,为此类型
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_master_release,
};
整个spi master侧的设备及驱动都是依赖与平台驱动的,下面详细跟踪spi的整个注册流程:
以展讯平台的8810 spi master为例:
static struct platform_device sprd_spi_controller_device[] =
{
{
.name = "sprd_spi",
.id = 0,
.dev = {
.dma_mask = &spi_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.resource = spi_resources[0],
.num_resources = ARRAY_SIZE(spi_resources[0]),
},
{
.name = "sprd_spi",
.id = 1,
.dev = {
.dma_mask = &spi_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
},
.resource = spi_resources[1],
.num_resources = ARRAY_SIZE(spi_resources[1]),
},
};
在8810中一共有两个spi master,由id来区别,下面是对应的MEM/IRQ资源:
static struct resource spi_resources[][2] = {
{
{
.start = SPRD_SPI0_PHYS,
.end = SPRD_SPI0_PHYS + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
{
.start = IRQ_SPI0_INT,
.end = IRQ_SPI0_INT,
.flags = IORESOURCE_IRQ,
},
},
{
{
.start = SPRD_SPI1_PHYS,
.end = SPRD_SPI1_PHYS + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
{
.start = IRQ_SPI1_INT,
.end = IRQ_SPI1_INT,
.flags = IORESOURCE_IRQ,
},
},
};
首先需要注册平台设备:
platform_device_register(&sprd_spi_controller_device[0]);
platform_device_register(&sprd_spi_controller_device[1]);
这一步主要的工作有:
1. 初始化platform_device的dev结构
2. 设置dev的dma相关
3. 挂接设备所属总线到platform_bus_type
4. 解析资源RES
5. 挂接dev到驱动模型
然后需要注册平台驱动:【在master的驱动实现中】
static struct platform_driver sprd_spi_driver = {
.driver = {
.name = "sprd_spi",
.owner = THIS_MODULE,
},
.suspend = sprd_spi_suspend,
.resume = sprd_spi_resume,
.remove = __exit_p(sprd_spi_remove),
};
初始化:
static int __init sprd_spi_init(void)
{
return platform_driver_probe(&sprd_spi_driver, sprd_spi_probe);
}
这一步主要的工作有:
1. 填入驱动真正的probe,并注册平台驱动【platform_driver_register】
2. 挂上平台总线platform_bus_type,检测驱动名称是否重复,加入bus【bus_add_driver】
3. 下面的事情是设备驱动模型里面的任务了,为驱动建立节点,建立私有driver_private,初始化设备链等
probe的详细过程见上一篇【设备驱动模型】。
4. 调用probe函数,进行匹配
4.1 int __init sprd_spi_probe(struct platform_device *pdev),这里的参数来自于哪里呢?从上面的分析来看,很明显这个时候device/driver
都已经明确挂到了平台总线下,那么这里的平台设备为此驱动所属的bus下的所有设备。
4.2 在probe函数内部,可以区分也可以不区分具体的platform device, 这里分成两种模式:
a. 不同的spi master使用自己独立的master driver
b. 使用相同的spi master driver,但是需要生成两个master是必须的,并且由不同的bus_num来提供给device挂接
这里涉及到两个匹配的过程:
master device <-----> master driver
master driver <-----> spi slave
第一个过程可以由device的name + id来判断,并由id来转化成master的bus_num,第二个过程则直接由device的bus_num项与master匹配
4.3 生成具体的master后,需要挂接标准接口实现到spi master,如:setup/transfer/cleanup等
4.4 这里可以有两个数据位置值得注意,其一是master内部的private data,第二个是dev下的master
master = spi_alloc_master(&pdev->dev, sizeof *sprd_data);/spi_master_get_devdata(master);
platform_set_drvdata(pdev, master);/platform_get_drvdata(pdev);
4.5 还有就是一些资源的解析到private data中,设置dma 参数,中断及相关的变量/队列等
4.6 最重要的一个步骤,注册master到整个SPI系统中【注,不是bus!,master作为一个抽象,代表了spi的master端的所有功能及控制】
ret = spi_register_master(master);
a.初始化一些标准接口,如用于传输spi_message的消息队列
b.加入到全局链尾,spi_master_list
c.匹配已经存在的board_list链,并进行匹配,spi_match_master_to_boardinfo,这里主要进行两步操作:第一是匹配bus_num,第二步是
进行实际的slave与master的匹配工作,
struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *chip)【分析在slave后面】
注:这里的触发spi_match_master_to_boardinfo流程的源头可能有两个:
1. 注册master:spi_register_master
2. 注册spi board:spi_register_board_info
系统为了SPI slave及master的匹配,声明了两个静态链表
static LIST_HEAD(board_list); 宿主为board_info->{list + spi_board_info}
static LIST_HEAD(spi_master_list); 宿主为spi_master【已包含list_head】
一个用于存储spi_board_info【slave端会详细描述】,一个用于存储master链。
slave端的device声明:
static struct spi_board_info openhone_spi_devices_wifi[] = {
{
.modalias = "spi_slot0", // "spidev" --> spidev_spi
.chip_select = SPRD_3RDPARTY_SPI_WIFI_CS,
//.max_speed_hz = 24 * 1000 * 1000, wch
.max_speed_hz = 8 * 1000 * 1000,
.mode = SPI_CPOL | SPI_CPHA,
},
};
slave端的注册流程:
1.标准做法
spi_register_board_info
2.展讯做法
gps_spi_dev = sprd_spi_wifi_device_register(1, NULL);
struct spi_device *sprd_spi_wifi_device_register(int master_bus_num, struct spi_board_info *chip)
{
return sprd_spi_device_register(master_bus_num, chip, SPRD_3RDPARTY_SPI_WIFI_CS);
}
struct spi_device *sprd_spi_device_register(int master_bus_num,struct spi_board_info *chip,int type)
展讯的实现流程较为混乱,梳理后的方式如下:【没有采用标准的做法】
一共需要两个参数:1. 与master相对应的bus_num; 2. 与spi_board_info spi slave device相对应的CS【每个设备需要一个独立的CS】
找到这两个设备后:
使用1获取到master结构体,使用2获取到spi_board_info结构体,直接调用 spi_new_device
标准的做法为:
只需要注册spi_register_board_info即可,相关的匹配流程交给kernel去匹配。【boardinfo中含有bus_num/cs等,不需要单独拿出来自己匹配】
至此:master与slave端都同时汇聚到了一起:spi_new_device,下面是这个函数的详细描述,
在spi_new_device中并没有判断是否已经生成spi proxy的代码,那么是否会同一个master/slave生成了两次proxy?
展讯的方法中,spi_board_info并没有注册到board_list中,故master不会匹配到,只有等到slave主动调用spi_new_device的时候才会成功匹配,
标准的方法中,总是后加入链的进行匹配的工作。
spi_new_device
这个函数调用的条件是:master与slave bus_num一致,进行两者之间的匹配
具体的工作如下:
1.生成一个父节点为master->dev的spi_device对象,并且master指向匹配的master结构【注意:这里将设备的bus指向 &spi_bus_type,后续对设备驱动有大的作用!,
而master指针则会负责后续的传输责任转发】
2.获取spi_board_info上的所有信息到spi_device对象中去
3.添加此spi_device设备到系统中去:spi_add_device
检查片选小于master支持的片选总数
设置名称为master->dev.name + cs_num
确认没有使用总线上其它设备已占用的cs号(从名称判断)【不管什么的master上配对的device都隶属于这条系统仅有的spi总线】
【这里有个问题:不同master上的cs号的范围不能重复?一般cs号即为gpio号,不会重复】
启动master接口,setup
添加device->dev到sys系统中去
以上都是在平台总线上的master device与driver的配对,master与slave的配对,这些对于一般的spi设备驱动开发人员都是透明的,下面来说
spi driver与spi device的配对过程:
static struct spi_driver gpsspidev_spi = {
.driver = {
.name = "spi_slot0",
.owner = THIS_MODULE,
},
.probe = gpsspidev_probe,
.remove = __devexit_p(mxdspidev_remove),
};
注册到系统中去:
major= spi_register_driver(&gpsspidev_spi);
主要完成如下工作:
首先设置其bus指向spi_bus_type,然后调用driver_register【加入到bus的driver链表中】
一般在加入device或者driver的时候都会调用:driver_attach/device_attach
Probe的详细过程见【设备驱动模型】
在匹配过程中__driver_attach,首先会调用spi bus type的match函数【最高匹配否认优先级】
其次是bus的probe【spi未实现,则调用driver本身的probe函数】,最后执行设备与驱动的绑定
probe成功后需要对驱动本生的一些成员进行初始化,代表我probe到设备后就要开始准备工作了。
SPI框架的match实现:
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;
}
先匹配driver的id table中的每一项中的名字是否与device匹配
其次匹配driver的name与device是否相配
一般的SPI slave驱动,直接名称匹配即可,不需要自己进行probe
在分析的过程中,想到这么几个问题:
总线类型与实际总线【master】的关系?
系统中不区分master的不同,而只有一条类型为spi_bus_type的spi总线,所有注册上来的设备(spi_device)及设备驱动(spi_driver)都隶属于
这条总线,这样可以对整个spi体系下的所有slave设备及驱动进行管理。
spi device是什么时候挂到spi_bus_type设备链中去的?
spi device指向总线spi_bus_type是在spi device初始化的时候做的
反向挂是在:初始化完成之后,device_add函数中通过 bus_add_device(dev);添加到spi总线中去的
如何访问spi设备以及访问过程中如何分发到master?
spi slave driver会自行封装spi message,并请求同步到slave,具体由slave挂接的master实现具体的分发功能。
这部分内容放在下一篇【SPI master驱动的时序及实现方式】详细讲解