您的位置:首页 > 运维架构 > Linux

一、概览linux spi驱动子系统

2015-06-17 00:34 591 查看

1、概要

Linux 中的spi驱动主要是由spi子系统来管理的,其核心代码位于kernel/drivers/spi/spi.c中。具体的spi控制器驱动也在kernel/drivers/spi/目录中。目前spi子系统只支持spi主机模式,还不支持spi从机模式。

spi通常是由spiI控制器、spi总线和连接在spi总线上的设备构成:这里的总线只是指物理的spi连线,并不是指Linux设备模型中逻辑上的总线概念。可以把spi控制器和spi总线看成是一体的,spi总线就是spi控制器加上和spi设备的连接线。spi设备包含很多种,它可以是一个spi接口的nor flash,例如ST的M25P80。它也可以是一个spi网卡,例如ENC28J60。

2、spi控制器

Linux的spi子系统对spi控制器的描述使用的是struct spi_master这个数据结构,所以在内核中,一个spi_master结构就代表了一个spi控制器,或者说代表一个spi主机。

它的主要定义如下:

[code]struct spi_master {
     struct device   dev;
     struct list_head list;
     s16         bus_num;        //总线(或控制器)编号,总线和设备匹配时用到
     u16         num_chipselect; //片选数量,决定该控制器下面挂接多少个SPI设备
     u16         dma_alignment;
     u16         mode_bits;
     u16         flags;
     spinlock_t      bus_lock_spinlock;
     struct mutex        bus_lock_mutex;
     bool            bus_lock_flag;
     int   (*setup)(struct spi_device *spi);   //这个需要我们自己具体实现,
                                               //主要设置SPI控制器和工作方式
     int   (*transfer)(struct spi_device *spi,
            struct spi_message *mesg);         //这个不同的控制器要具体实现,
                                               //传输数据最后都要调用这个函数
     void   (*cleanup)(struct spi_device *spi); //注销时使用
};


在Linux-2.6以后引入设备模型概念,所有的设备和驱动都需要依附于总线。附于Spi总线的设备驱动对应的总线类型为spi_bus_type,在内核的drivers/spi/spi.c中定义。

[code]struct bus_type spi_bus_type = {
      .name       = "spi",
      .dev_attrs  = spi_dev_attrs,
      .match      = spi_match_device,
      .uevent     = spi_uevent,
      .pm     = &spi_pm,
};
EXPORT_SYMBOL_GPL(spi_bus_type);


后续会有文章专门讲述如何构建一个struct spi_master对象,并将之注册到spi子系统中去。

3、spi设备

Linux spi子系统对spi总线上的设备用struct spi_device结构来描述,运行的内核中,通常一个struct spi_device对象对应一个spi设备:

[code]struct spi_device {
      struct device       dev; 
      struct spi_master   *master;      //挂在哪个主控器下
      u32         max_speed_hz;         //设备支持的最大速度
      u8          chip_select;          //设备的片选号
      u8          mode;                 //SPI的模式,参见SPI的四种模式
      u8          bits_per_word;        //每个字的位数
      int         irq;                  //中断号
      void        *controller_state;
      void        *controller_data;
      char        modalias[SPI_NAME_SIZE]; //设备名
 };


struct spi_device主要用来描述连接在spi总线上的一个spi设备的一些电气信息的。

在2.6.xx版本的内核中可能会经常看到平台代码中用 struct spi_board_info来描述spi设备的电气信息,该结构体包含外设的片选号、总线号、模式以及传输速率等信息。struct spi_board_info原型如下:

[code]struct spi_board_info {
    /* the device name and module name are coupled, like platform_bus;
     * "modalias" is normally the driver name.
     *
     * platform_data goes to spi_device.dev.platform_data,
     * controller_data goes to spi_device.controller_data,
     * irq is copied too
     */
    char        modalias[SPI_NAME_SIZE];
    const void  *platform_data;
    void        *controller_data;
    int     irq;

    /* slower signaling on noisy or low voltage boards */
    u32     max_speed_hz;

    /* bus_num is board specific and matches the bus_num of some
     * spi_master that will probably be registered later.
     *
     * chip_select reflects how this chip is wired to that master;
     * it's less than num_chipselect.
     */
    u16     bus_num;
    u16     chip_select;
    /* mode becomes spi_device.mode, and is essential for chips
     * where the default of SPI_CS_HIGH = 0 is wrong.
     */
    u8      mode;
    /* ... may need additional spi_device chip config data here.
     * avoid stuff protocol drivers can set; but include stuff
     * needed to behave without being bound to a driver:
     *  - quirks like clock rate mattering when not selected
     */
};


在板级初始化代码中,2.6.xx版本的内核中通常会用一个 struct spi_board_info数组来描述系统中的spi设备信息,然后调用spi_register_board_info()将这些设备信息注册到系统中。当然这些struct spi_board_info最终也会转化为struct spi_device结构。关于spi_device是如何构建和注册的,请关注后面的博文。

4、spi设备驱动

spi设备的驱动都会有一个struc spi_driver结构体来描述,结构体中定义对应的操作函数指针,用来管理依附于总线上的相关设备:

[code]struct spi_driver {
    const struct spi_device_id *id_table;
    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;
};


通常对于从事Linux驱动工作人员来说,spi设备的驱动主要就是实现这个结构体中的各个接口,并将之注册到spi子系统中去。后面也会有文章专门讲解如何实现struct spi_driver。

5、数据传输相关数据结构

第一个与数据传输相关的数据结构为struct spi_transfer,数据结构定义如下:

[code]struct spi_transfer {    
    /* it's ok if tx_buf == rx_buf (right?)
     * for MicroWire, one buffer must be null
     * buffers must work with dma_*map_single() calls, unless
     *   spi_message.is_dma_mapped reports a pre-existing mapping
     */
    const void  *tx_buf;  //发送数据的缓冲区
    void        *rx_buf;  //接收数据的缓冲区
    unsigned    len;      //需要交换数据的长度

    dma_addr_t  tx_dma;  
    dma_addr_t  rx_dma;  

    unsigned    cs_change:1;  //片选信号
    u8      bits_per_word;    
    u16     delay_usecs; 
    u32     speed_hz;    

    struct list_head transfer_list;
};


一个struct spi_transfer对象代表了一次单段的spi数据传输。struct spi_transfer结构体中记录了本段传输需要交换的数据和长度,传输的速度,传输时片选信号的变化情况。

另外一个与数据交换相关的数据结构为struct spi_message,定义如下:

[code]struct spi_message {
    struct list_head transfers;

    struct spi_device   *spi;     /* 传输数据到这个设备 */

    unsigned        is_dma_mapped:1;

    /* REVISIT:  we might want a flag affecting the behavior of the
     * last transfer ... allowing things like "read 16 bit length L"
     * immediately followed by "read L bytes".  Basically imposing
     * a specific message scheduling algorithm.
     *
     * Some controller drivers (message-at-a-time queue processing)
     * could provide that as their default scheduling algorithm.  But
     * others (with multi-message pipelines) could need a flag to
     * tell them about such special cases.
     */

    /* completion is reported through a callback */
    void            (*complete)(void *context);
    void            *context;
    unsigned        actual_length;
    int         status;

    /* for optional use by whatever driver currently owns the
     * spi_message ...  between calls to spi_async and then later
     * complete(), that's the spi_master controller driver.
     */
    struct list_head    queue;
    void            *state;
};


一个struct spi_message代表对一个设备进行一个多段spi数据传输。每一段传输其实就是使用上面提到的struct spi_trasnfer对象完成的。struct spi_message主要记录了这次传输针对的设备。上面提到的struct spi_transfer对象会被链接到struct spi_message对象中。

6、其他数据结构

对于3.0内核以前,ARM平台通常的做法是在板级代码中注册struct platform_device来描述各种控制器,例如spi控制器,i2c控制器等等。然后再向系统中注册struct platform_driver来管理和驱动对应的平台设备。spi控制器驱动也是同样的做法。

3.0以后的内核引入Device Tree来描述ARM平台及板级设备。这样一来我们就不需要在板级代码手动的构建并注册描述spi的platform_device数据了。这一切可以由Device Tree来描述,并且又Device Tree相关代码来解析Device Tree对应spi的相关描述信息,并自动创建并注册表示spi控制器的platform_device数据。与之对应的platform_driver和以前差不多,只不过支持Device Tree后,platform_driver中可以通过Device Tree获取spi控制器的相关信息。引入Device Tree之后platform_driver数据结构中引入下面数据结构:

[code]struct of_device_id
{
         char    name[32];
         char    type[32];
         char    compatible[128];
         const void *data;
};


引入Device Tree后platform_driver优先使用struct of_device_id对象中的compatible成员和platform_device中dev.of_node中的compatible成员匹配的。在一些spi控制器驱动代码中常常可以看到如下定义。

[code]static struct of_device_id xxx_spi_of_match[] = {
         { .compatible = "vendor,chip-spi", },
         {}
};
MODULE_DEVICE_TABLE(of, xxx_spi_of_match);


另外一个有spi设备和spi驱动相关的数据结构struct of_spi_device_id,定义如下:

[code]struct spi_device_id {                                                                                                                                                    
    char name[SPI_NAME_SIZE]; 
    kernel_ulong_t driver_data  /* Data private to the driver */
            __attribute__((aligned(sizeof(kernel_ulong_t))));
};


这个数据结构主要是用来匹配spi设备和驱动使用的。

关于device tree在spi驱动中的使用,后面也会有博文进行介绍。

未完待续………

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: