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驱动的时序及实现方式】详细讲解


posted @ 2013-04-28 20:38  天地不仁  阅读(4734)  评论(0编辑  收藏  举报