Linux系统SPI驱动总结(一)

SPI是“Serial Peripheral Interface”的缩写,串行外设接口,是一种四线制的同步串行通信接口,用来连接MCU、传感器、存储设备,SPI设备分为主设备和从设备两种,用于通信和控制的四根线分别是:CS(片选信号)、SCLK(时钟信号)、MISO(主设备的数据输入、从设备的数据输出脚)、MOSI(主设备的数据输出、从设备的数据输入脚)。
一、硬件结构
通常,负责发出时钟信号的设备为主设备,另一方为从设备。下图是一个SPI系统的硬件连接示意图:

主设备对应SoC芯片中的SPI控制器,通常,一个SoC中可能存在多个SPI控制器,如上图所示,SoC芯片中有3个SPI控制器。每个SPI控制器可以连接多个SPI从设备,每个从设备有各自独立的CS引脚。每个从设备共享另外三个信号引脚:SCLK、MISO、MOSI。任何时刻,只有一个CS引脚处于有效状态,与该有效CS引脚连接的设备此时可以与主设备(SPI控制器)通信,其它的从设备处于等待状态,并且它们的3个引脚必须处于高阻状态。
二、工作时序
按照时钟信号和数据信号之间的相位关系,SPI有四种工作时序模式:
用CPOL表示时钟信号的初始电平的状态,CPOL为0时表示时钟信号初始状态为低电平,为1时表示时钟信号初始状态为高电平。用CPHA表示在那个时钟沿采样数据,CPHA为0时表示在首个时钟变化沿采样数据,而CPHA为1时表示要在第二个时钟变化沿来采样数据。内核用CPOL和CPHA的组合来表示当前SPI需要的工作模式:
1、CPOL=0,CPHA=0  模式为0
2、CPOL=0,CPHA=1  模式为1
3、 CPOL=1,CPHA=0  模式为2

4、CPOL=1,CPHA=1  模式为3

三、确定驱动文件
SPI作为Linux的一个小子系统,驱动程序位于/drivers/spi/*目录,首先,可以通过Makefile和Kconfig来确定需要分析的源文件。
1、Makefile
#
# Makefile for kernel SPI drivers.
#
# small core, mostly translating board-specific
# config declarations into driver model code
obj-$(CONFIG_SPI_MASTER)                += spi.o
obj-$(CONFIG_SPI_SPIDEV)                += spidev.o

# SPI master controller drivers (bus)
obj-$(CONFIG_SPI_BITBANG)               += spi-bitbang.o

obj-$(CONFIG_SPI_IMX)                   += spi-imx.o

2、对应的Kconfig配置内核

+---------------------------------------------------------------------------------------------+ | 
  | |           --- SPI support                                                                   | | 
  | |           [ ]   Debug support for SPI drivers                                               | | 
  | |                 *** SPI Master Controller Drivers ***                                       | | 
  | |           <M>   Altera SPI Controller                                                       | | 
  | |           {M}   Utilities for Bitbanging SPI masters                                        | | 
  | |           <M>   Parallel port adapter for AVR Butterfly (DEVELOPMENT)                       | | 
  | |           <M>   GPIO-based bitbanging SPI Master                                            | | 
  | |           <M>   Parallel port adapter for LM70 eval board (DEVELOPMENT)                     | | 
  | |           <M>   OpenCores tiny SPI                                                          | | 
  | |           <M>   PXA2xx SSP SPI master         
3、编译生成的目标文件如下:

通过以上分析知道,SPI驱动由三部分组成,分别是core、master controller driver以及SPI protocol drivers。

四、数据结构分析
SPI驱动涉及的数据结构主要位于include/linux/spi/spi.h。
1、数据结构spi_master

spi_master代表一个主机控制器,一般不需要自己编写spi控制器驱动,但了解这个结构体还是必要的。

struct spi_master {
        struct device   dev;        //设备模型使用
        struct list_head list;
        /* other than negative (== assign one dynamically), bus_num is fully
         * board-specific.  usually that simplifies to being SOC-specific.
         * example:  one SOC has three SPI controllers, numbered 0..2,
         * and one board's schematics might show it using SPI-2.  software
         * would normally use bus_num=2 for that controller.
         */
        s16                     bus_num;       // 总线(或控制器)编号
        /* chipselects will be integral to many controllers; some others
         * might use board-specific GPIOs.
         */
        u16                     num_chipselect;    // 片选数量,决定该控制器下面挂接多少个SPI设备,从设备的片选号不能大于这个数量
        /* some SPI controllers pose alignment requirements on DMAable
         * buffers; let protocol drivers know about these requirements.
         */
        u16                     dma_alignment;
        /* spi_device.mode flags understood by this controller driver */
        u16                     mode_bits;    // master支持的设备模式
        /* bitmask of supported bits_per_word for transfers */
        u32                     bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))
        /* limits on transfer speed */
        u32                     min_speed_hz;
        u32                     max_speed_hz;
        /* other constraints relevant to this driver */
        u16                     flags;
#define SPI_MASTER_HALF_DUPLEX  BIT(0)          /* can't do full duplex */
#define SPI_MASTER_NO_RX        BIT(1)          /* can't do buffer read */
#define SPI_MASTER_NO_TX        BIT(2)          /* can't do buffer write */
#define SPI_MASTER_MUST_RX      BIT(3)          /* requires rx */
#define SPI_MASTER_MUST_TX      BIT(4)          /* requires tx */
        /* lock and mutex for SPI bus locking */
        spinlock_t              bus_lock_spinlock;
        struct mutex            bus_lock_mutex;
        /* flag indicating that the SPI bus is locked for exclusive use */
        bool                    bus_lock_flag;
        /* Setup mode and clock, etc (spi driver may call many times).
         *
         * IMPORTANT:  this may be called when transfers to another
         * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
         * which could break those transfers.
         */
        int                     (*setup)(struct spi_device *spi); // 根据SPI设备更新硬件配置。设置模式、时钟等,这个需要自己具体实现,主要设置SPI控制器和工作方式
        /* bidirectional bulk transfers
         *
         * + The transfer() method may not sleep; its main role is
         *   just to add the message to the queue.
         * + For now there's no remove-from-queue operation, or
         *   any other request management
         * + To a given spi_device, message queueing is pure fifo
         *
         * + The master's main job is to process its message queue,
         *   selecting a chip then transferring data
         * + If there are multiple spi_device children, the i/o queue
         *   arbitration algorithm is unspecified (round robin, fifo,
         *   priority, reservations, preemption, etc)
         *
         * + Chipselect stays active during the entire message
         *   (unless modified by spi_transfer.cs_change != 0).
         * + The message transfers use clock and SPI mode parameters
         *   previously established by setup() for this device
         */
        int                     (*transfer)(struct spi_device *spi,
                                                struct spi_message *mesg);// 添加消息到队列的方法。这个函数不可以睡眠。主要是安排发生的传送并且调用注册的回调函数complete()。这个不同的控制器要具体实现,传输数据最后都要调用这个函数
        /* called on release() to free memory provided by spi_master */
        void                    (*cleanup)(struct spi_device *spi);// 在spidev_release函数中被调用。
        /*
         * Used to enable core support for DMA handling, if can_dma()
         * exists and returns true then the transfer will be mapped
         * prior to transfer_one() being called.  The driver should
         * not modify or store xfer and dma_tx and dma_rx must be set
         * while the device is prepared.
         */
        bool                    (*can_dma)(struct spi_master *master,
                                           struct spi_device *spi,
                                           struct spi_transfer *xfer);
        /*
         * These hooks are for drivers that want to use the generic
         * master transfer queueing mechanism. If these are used, the
         * transfer() function above must NOT be specified by the driver.
         * Over time we expect SPI drivers to be phased over to this API.
         */
        bool                            queued;
        struct kthread_worker           kworker;
        struct task_struct              *kworker_task;
        struct kthread_work             pump_messages;
        spinlock_t                      queue_lock;
        struct list_head                queue;
        struct spi_message              *cur_msg;
        bool                            busy;
        bool                            running;
        bool                            rt;
        bool                            auto_runtime_pm;
        bool                            cur_msg_prepared;
        bool                            cur_msg_mapped;
        struct completion               xfer_completion;
        size_t                          max_dma_len;
        int (*prepare_transfer_hardware)(struct spi_master *master);
        int (*transfer_one_message)(struct spi_master *master,
                                    struct spi_message *mesg);
        int (*unprepare_transfer_hardware)(struct spi_master *master);
        int (*prepare_message)(struct spi_master *master,
                               struct spi_message *message);
        int (*unprepare_message)(struct spi_master *master,
                                 struct spi_message *message);
        /*
         * These hooks are for drivers that use a generic implementation
         * of transfer_one_message() provied by the core.
         */
        void (*set_cs)(struct spi_device *spi, bool enable);
        int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
                            struct spi_transfer *transfer);
        /* gpio chip select */
        int                     *cs_gpios;
        /* DMA channels for use with core dmaengine helpers */
        struct dma_chan         *dma_tx;
        struct dma_chan         *dma_rx;
        /* dummy data for full duplex devices */
        void                    *dummy_rx;
        void                    *dummy_tx;
};

2、数据结构spi_device
spi_device代表一个外围spi设备,由主控制器驱动注册完成后扫描BSP中注册设备产生的设备链表并向spi_bus注册产生。在Linux内核中,每个spi_device代表一个物理的spi设备。

struct spi_device {
        struct device           dev;// 设备模型使用
        struct spi_master       *master;// 设备使用的master结构,挂接在哪个主控制器下
        u32                     max_speed_hz;// 通信时钟最大频率
        u8                      chip_select;// 片选号,每个master支持多个spi_device
        u8                      bits_per_word;// 每个字长的比特数,默认是8
        u16                     mode;// 设备支持的模式,如片选是高还是低
#define SPI_CPHA        0x01                    /* clock phase */
#define SPI_CPOL        0x02                    /* clock polarity */
#define SPI_MODE_0      (0|0)                   /* (original MicroWire) */
#define SPI_MODE_1      (0|SPI_CPHA)
#define SPI_MODE_2      (SPI_CPOL|0)
#define SPI_MODE_3      (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH     0x04                    /* chipselect active high? */
#define SPI_LSB_FIRST   0x08                    /* per-word bits-on-wire */
#define SPI_3WIRE       0x10                    /* SI/SO signals shared */
#define SPI_LOOP        0x20                    /* loopback mode */
#define SPI_NO_CS       0x40                    /* 1 dev/bus, no chipselect */
#define SPI_READY       0x80                    /* slave pulls low to pause */
#define SPI_TX_DUAL     0x100                   /* transmit with 2 wires */
#define SPI_TX_QUAD     0x200                   /* transmit with 4 wires */
#define SPI_RX_DUAL     0x400                   /* receive with 2 wires */
#define SPI_RX_QUAD     0x800                   /* receive with 4 wires */
        int                     irq;// 中断号
        void                    *controller_state;// 控制寄存器状态
        void                    *controller_data;
        char                    modalias[SPI_NAME_SIZE];
        int                     cs_gpio;        /* chip select gpio */
        /*
         * likely need more hooks for more protocol options affecting how
         * the controller talks to each chip, like:
         *  - memory packing (12 bit samples into low bits, others zeroed)
         *  - priority
         *  - drop chipselect after each word
         *  - chipselect delays
         *  - ...
         */
};

由于一个SPI总线上可以有多个SPI设备,需要片选号来区分它们,SPI控制器根据片选号来选择不同的片选线,从而实现每次只同一个设备通信。
spi_device的mode成员有两个比特位含义很重要。SPI_CPHA选择对数据线采样的时机,0选择每个时钟周期的第一个沿跳变时采样数据,1选择第二个时钟沿采样数据;SPI_CPOL选择每个时钟周期开始的极性,0表示时钟以低电平开始,1选择高电平开始。这两个比特位有四种组合,对应SPI_MODE_0~SPI_MODE_3。

另一个比较重要的成员是 bits_per_word。这个成员指定每次读写的字长,单位是比特。注意:如果这个成员为0,默认使用8作为字长。
3、数据结构spi_driver

spi_driver代表一个SPI协议驱动,也就是外设驱动。

struct spi_driver {
        const struct spi_device_id *id_table; // 支持的spi_device设备表
        int                     (*probe)(struct spi_device *spi);
        int                     (*remove)(struct spi_device *spi);// 解除spi_device和spi_driver的绑定,释放probe申请的资源
        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;// 设备模型使用
};
对于从事驱动开发的工程师来说,spi设备的驱动主要实现这个结构体的各个接口,将之注册到SPI子系统中去。

4、数据结构spi_transfer
spi_transfer代表一个读写缓冲对,包含接收缓冲区以及发送缓冲区,其实,spi_transfer的发送是通过构建spi_message实现,通过将spi_transfer中的链表transfer_list链接到spi_message中的transfers,再以spi_message形式向底层发送数据。每个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;
        struct sg_table tx_sg;
        struct sg_table rx_sg;
        unsigned        cs_change:1;
        unsigned        tx_nbits:3;
        unsigned        rx_nbits:3;
#define SPI_NBITS_SINGLE        0x01 /* 1bit transfer */
#define SPI_NBITS_DUAL          0x02 /* 2bits transfer */
#define SPI_NBITS_QUAD          0x04 /* 4bits transfer */
        u8              bits_per_word;
        u16             delay_usecs;
        u32             speed_hz;
        struct list_head transfer_list;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值