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

Linux系统SPI驱动总结(一)相关推荐

  1. Linux系统SPI驱动总结

    平台:rk3288 系统:Android7.1 kernel4.4.143 linux spi 驱动分为三部分: SPI外设驱动:我们写.oled,spi flash 等 linux spi核心层:d ...

  2. 转载:Linux kernel SPI驱动解释

    From: http://www.cnblogs.com/liugf05/archive/2012/12/03/2800457.html 下面有两个大的模块: 一个是SPI总线驱动的分析        ...

  3. linux中spi驱动框架

    原 linux中spi驱动框架 2016年09月14日 15:57:06 andylauren 阅读数:403 <span class="tags-box artic-tag-box& ...

  4. linux系统网络驱动简介

    网络设备驱动简介 网络设备驱动是linux内核中三大类设备驱动之一,它用来完成高层网络协议的底层数据传输及设备控制. 网络设备与其他两种设备的区别: 网络接口不存在于linux的文件系统中,及/dev ...

  5. linux系统LCD驱动(三):mtk lcd驱动lcm的加载以及初始化

    上一篇博文(linux系统LCD驱动(二):mtk lcd驱动fb_info初始化)https://blog.csdn.net/Ian22l/article/details/105929192 提到m ...

  6. Linux下spi驱动分析与测试【详细流程】

    驱动是基于ARM的pl022的SSP控制器,其支持三种通信格式:SPI.SSI以及Microwrite,llinux5.4内核下,SSP控制器驱动的路径为/drivers/spi/spi-pl022. ...

  7. Linux下SPI驱动详解

    更多嵌入式原创文章,请关注公众号:一口Linux 1. SPI总线 1.1. SPI总线概述 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口. ...

  8. Linux下SPI驱动详解(干货)

    关注.星标公众号,直达精彩内容 本文由嵌入式大牛:蒙工投稿! 1. SPI总线 1.1. SPI总线概述 SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外 ...

  9. linux系统无线驱动在哪下载,在linux上怎么安装无线网卡驱动?

    在linux上怎么安装无线网卡驱动? 在linux上安装无线网卡驱动的方法: (1)先确定无线网卡型号,因驱动安装和型号是密切相关的,不同的型号,安装和下载驱动有所不同,但原理是一样的.图例为无线网卡 ...

最新文章

  1. hive 修改分桶数 分桶表_Hive中的分桶
  2. 小胖机器人能刷碗吗_小胖机器人好不好?透过真相看本质
  3. 原相机水印怎么改字_抖音/自媒体做影视二次剪辑,如何下载高清无水印视频?...
  4. PHP类的静态(static)方法和静态(static)变量使用介绍
  5. 期货与期权(part5)--期货市场机制
  6. 对抗思想与强化学习的碰撞-SeqGAN模型原理和代码解析
  7. java打包----“Artifacts”
  8. Java url转MultipartFile inputStream转File file转multipartFile
  9. 求职必备素材:个人简历Word模板
  10. 三维模型重建(1):关于三维模型重建的一些简介
  11. 阿里云国际版控制台使用海外云服务器教程详解
  12. 【CyberSecurityLearning 12】数据链路层 及 交换机工作原理与配置
  13. 团队用过最好的bug管理软件-delbug管理
  14. edgexfoundry docker 容器化部署 ubuntu16.4 跑起来 go0.6.0 版
  15. 了不起的 Webpack HMR 学习指南(含源码分析)
  16. EditText设置输入的类型,比如说限制只能输入字母和数字
  17. 青藤 #10115 栈练习1
  18. 会声会影如何新建html项目,会声会影如何使用即时项目模板
  19. RocketMQTemplate 注入失败
  20. 关于如何获得虚拟机还是真机信息

热门文章

  1. 顶级销售经理的七个特质
  2. Docker中安装宝塔
  3. Evita项目-1-项目介绍
  4. 零基础学前端开发之CSS3深入选择器
  5. HTTP/2和HTTP/3特性介绍
  6. android nfs文件管理器,NFS 文件
  7. Django DEBUG 模式
  8. redis 数据删除策略
  9. docker 查看redis 版本
  10. java drawimage 效果,Java Graphics.drawImage()如何工作以及ImageObserver的作用是什么