简介

  本文主要讲解linux的spi子系统。从宏观到微观的角度,循序渐进解剖spi子系统,为开发spi相关驱动打好基础;也可通过此文理解linux相关总线子系统(如I2C、USB)的实现思想和本质,避免看内核源码时,处于管中窥豹的状态。
  编写完此文后,作者会继续分享出具体spi外设和主机驱动相关的实操技术出来。

软硬件环境

  • linux kernel version:2.6.35.7
  • SOC:S5PV210 (ARMv7指令集,Cortex-A8架构)
  • gcc version: 4.4.1 (Sourcery G++ Lite 2009q3-67)

第一节 面向对象分析spi总线

  关于spi(Serial Peripheral Interface—串行外设接口)总线的电气硬件特性,相关介绍的文章琳琅满目,本文不再赘述。
  spi总线应用的一般硬件抽象模型如下图1左所示。SOC(system-on-chip)集成多个spi控制器,每个控制器连入一条spi总线,其上坐着多个spi设备;SOC可作主机或从机,需注意linux的spi子系统只实现了主机。怎么实现soc与外部的spi设备通讯? 一般情况你会这样想,先实现操作spi控制器本身的驱动(操作特殊寄存器):spi控制器的初始化(规定spi总线的波形特征),控制与其它spi设备的数据传输;然后用前述驱动包装好的spi读写方法,实现不同外部spi设备的驱动:控制与spi设备通讯的内容。这是所谓面向过程的思考。
  现以面向对象思考上述问题。总结出,上述过程中有数个主要对象:spi主机控制器spi外部设备板级硬件逻辑通讯消息;如图1右所示。
soc和外设抽象图
                      图1 soc和外设抽象图

  另外,linux kernel总线相关子系统践行主机驱动和外设驱动分离设计思想,以及用设备驱动表示硬件外设的属性和行为,所以接下来以上述思想分析内核的spi体系结构。

第二节 Linux spi 体系结构

  Linux 的spi体系结构分为4个软件模块。如图2所示为spi子系统的体系结构,描绘了各个软件模块的关系。
  (1)SPI controller driver(主机端驱动)
  SPI总线控制器驱动根据SOC的datasheet,由CPU core操作一般集成在SOC内的SPI控制器。
  SPI主机端驱动主要包含SPI主机数据结构spi_master和控制SPI主机产生通信信号的函数(transfer method)。经由SPI总线驱动的代码,我们可以控制SPI控制器以主机模式产生spi总线的SCLK、MOSI、CS波形,以及读取MISO的数据。
  (2)SPI-core(spi核心)
  SPI核心提供了SPI总线驱动和设备驱动的注册和注销方法,SPI通信方法(读和写)与具体的主机控制器无关的代码以及匹配设备、驱动的探测等,实现于drivers/spi/spi.c
linux spi体系结构
               图2 linux spi体系结构

  (3)protocol driver(设备协议驱动)
  SPI设备驱动,内核doc称它为protocol driver,是对硬件体系中外部设备端的实现,通过SPI主机控制器与CPU交换数据。
  SPI设备协议驱动主要包含数据结构spi_driver, 通过调用SPI-core提供的标准SPI通信API,实现具体成员函数和所寄生的设备类驱动。
  (4)spi board info(板级逻辑)
  spi板级逻辑描述了主机和外设的互联,它相当于第二次工业革命中接线员的角色,如图3。
接线员
         图3 板级逻辑是接线员

  假设板子上有多个SPI控制器和多个SPI外设,那究竟谁接在谁上面?这个互联关系,既不是主机的责任,也不是外设端的责任,而是属于板级硬件逻辑规格的责任。
  spi板级逻辑主要包含数据结构spi_board_info,其成员主要描述了spi设备的通信规格、硬件配置,将其注册进SPI核心后,衍生为spi_device
  在Linux kernel 2.6中,可通过sysfs文件系统看到所有的SPI设备,存于/sys/bus/spi/路径下,以总线号和外部设备号形式列出,如:

[root@zxd210 /]# tree /sys/bus/spi/
/sys/bus/spi/
|-- devices
|   |-- spi0.0 -> ../../../devices/platform/spi-s5p.0/spi0.0
|   `-- spi1.0 -> ../../../devices/platform/spi-s5p.1/spi1.0
|-- drivers
|   `-- mpu6500_spi
|       |-- bind
|       |-- module -> ../../../../module/mpu6500_spidrv
|       |-- spi1.0 -> ../../../../devices/platform/spi-s5p.1/spi1.0
|       |-- uevent
|       `-- unbind
|-- drivers_autoprobe
|-- drivers_probe
`-- uevent

第三节 关键数据结构

  上节主要介绍了Linux 的SPI体系结构及其主要研究对象,这节从宏观到微观讲解这些对象宏观协作套路代码清单
  数据结构spi_masterspi_driverspi_board_infospi_device定义于include/linux/spi/spi.h文件中,如下代码清单所示。

struct spi_master {
    struct device   dev;
    s16         bus_num;
    u16         num_chipselect;
    u16         dma_alignment;
    u16         mode_bits;
    u16         flags;
    int         (*setup)(struct spi_device *spi);
    int         (*transfer)(struct spi_device *spi, struct spi_message *mesg);
    void        (*cleanup)(struct spi_device *spi);
};

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;
};

struct spi_board_info {
    char        modalias[SPI_NAME_SIZE];
    const void  *platform_data;
    void        *controller_data;
    int         irq;
    u32         max_speed_hz;
    u16         bus_num;
    u16         chip_select;
    u8          mode;
};

struct spi_device {
    struct device       dev;
    struct spi_master   *master;
    u32             max_speed_hz;
    u8              chip_select;
    u8              mode;
    u8              bits_per_word;
    int             irq;
    void            *controller_state;
    void            *controller_data;
    char            modalias[SPI_NAME_SIZE];
};

  上述数据结构在spi子系统上怎么协作的呢?如图4所示为关键数据结构间宏观协作关系。

关键数据结构间宏观协作
                      图4 关键数据结构间宏观协作

  图4中标出来的序号为宏观执行顺序:

①通常在arch/arm/mach-<SOC>/mach-<BOARD>.c里进行,将板级信息挂到spi核心的board_list链表,形成”羊肉串”;
②主机驱动注册spi_master时在board_list链表中匹配所有对应bus_num的板级信息;
③用流程②匹配到的板级信息构造spi_device
④设备驱动中注册spi_driver时,通过成员name匹配对应的spi_device,再执行probe
⑤外设端驱动通过调用spi核心层的标准API传输通信内容时,会传递到对应spi总线主机驱动中,让它操作底层硬件产生⑥波形。

  假设SPI总线主机控制器xxx 上有两个使用相同外设端驱动程序的yyy SPI设备,在打开SPI总线的设备节点后,相关数据结构之间的逻辑组织关系如图5所示。
spi驱动的各种数据结构的关系
                      图5 spi驱动的各种数据结构的关系

参考文献

  [1] 宋宝华. Linux设备驱动开发详解[M].北京:机械工业出版社,2015
  [2] David Brownell,Russell King,Dmitry Pervushin等. spi-summary[EB/OL].Kernel.org git repositories,2007
  [3] 韦东山.SPI模块配套视频[M/CD].韦东山老师个人店,2018

  由于作者水平有限,难免出现纰漏,需要您的宝贵修改意见,后续作者会持续更新此文以完善它。

Logo

开源、云原生的融合云平台

更多推荐