一、概览linux spi驱动子系统

来源:互联网 发布:淘宝可以申请几个账号 编辑:程序博客网 时间:2024/06/05 03:02

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主机。
它的主要定义如下:

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中定义。

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设备:

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原型如下:

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结构体来描述,结构体中定义对应的操作函数指针,用来管理依附于总线上的相关设备:

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,数据结构定义如下:

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,定义如下:

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数据结构中引入下面数据结构:

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控制器驱动代码中常常可以看到如下定义。

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,定义如下:

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驱动中的使用,后面也会有博文进行介绍。

未完待续………

0 0
原创粉丝点击