本次的spi专题,主要参考:这位大佬的博客
感觉大佬们的无私奉献,而我写博客的初衷除了记录自己的学习经历之外,同时也分享给大家,分享知识。
系列文章:
Linux spi驱动框架分析(一)
Linux spi驱动框架分析(二)
Linux spi驱动框架分析(三)
Linux spi驱动框架分析(四)
Linux spi驱动框架
Linux下spi的驱动架构如下图:
从图中可以观察到spi系统的整个框架,发现跟i2c的框架很十分相似;spi驱动框架主要分为三个部分,spi控制器驱动,spi设备驱动,和spi核心。
内核把spi控制器抽象为struct spi_master结构体,设备驱动抽象为spi_driver,spi设备抽象spi_device结构体。
spi与i2c内核空间的类比:
(1)spi_master就相当于i2c中的i2c_adapter
(2)spi_driver即i2c_driver
(3)spi_device即i2c中的i2c_client
(4)spi core 即i2c core
主要相关结构体的定义
spi_driver,描述一个spi驱动,定义如下:
struct spi_driver {
//id匹配表
const struct spi_device_id *id_table;
//与设备匹配时,会调用probe函数
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
struct device_driver driver;
};
spi_device ,描述一个spi设备:
struct spi_device {
struct device dev;
//所属spi_master
struct spi_master *master;
//该设备支持的最大时钟频率
u32 max_speed_hz;
//片选索引
u8 chip_select;
//传输位宽
u8 bits_per_word;
//设备的工作模式
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;
//设备的name
char modalias[SPI_NAME_SIZE];
//片选对应的gpio
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
};
spi_transfer,描述一个spi的读/写缓存区:
struct spi_transfer {
//发送buf
const void *tx_buf;
//接收buf
void *rx_buf;
//数据长度
unsigned len;
//DMA
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;
//节点,用于链接到spi_message的transfers链表上
struct list_head transfer_list;
};
tx_buf和rx_buf提供了非dma模式下的数据缓冲区地址,tx_dma和rx_dma则给出了dma模式下的缓冲区地址。
对于spi_master来说,一次数据传输的单位由spi_message结构体描述,多个spi_transfer会链接到spi_message的transfers链表,可以认为spi_message就是一次SPI数据交换的原子操作。spi_master接收了一个spi_message,其中的spi_transfer应该按顺序被发送,并且不能被其它spi_message打断。
struct spi_message {
//链表头,链接该spi_message的所有spi_transfer
struct list_head transfers;
struct spi_device *spi;
unsigned is_dma_mapped:1;
//该message下的所有spi_transfer都被传输完成时complete会被调用
void (*complete)(void *context);
void *context;
unsigned frame_length;
unsigned actual_length;
int status;
//通过该节点,把spi_message加入到消息队列上
struct list_head queue;
void *state;
/* list of spi_res reources when the spi message is processed */
struct list_head resources;
};
spi_master,描述一个spi控制器,定义如下:
struct spi_master {
struct device dev;
//系统上的spi_master,会链在一个全局链表上
struct list_head list;
//总线号
s16 bus_num;
//片选信号的个数
u16 num_chipselect;
......
/* spi_device.mode flags understood by this controller driver */
u16 mode_bits;
/* 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))
//支持的最小时钟频率
u32 min_speed_hz;
//支持的最大时钟频率
u32 max_speed_hz;
/* 用于设定某些限制条件的标志位 */
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 */
......
//用于设置模式、工作时钟等
int (*setup)(struct spi_device *spi);
/* 如果想采用消息队列机制,注册spi_matser时该成员要设置为NULL
之后会设置为spi_queued_transfer,该函数一般把spi_message加入到消息队列上
*/
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
//spi_master被释放时,该函数会调用
void (*cleanup)(struct spi_device *spi);
//如果spi_master支持DMA,则设置该成员
bool (*can_dma)(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *xfer);
//如果spi_master提供消息队列机制则设置该成员
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;
......
//单个spi_message传输函数
int (*transfer_one_message)(struct spi_master *master,
struct spi_message *mesg);
/* 这两个回调函数用于在发起一个数据传送过程前和后,给控制器驱动一个
机会,申请或释放某些必要的硬件资源,例如DMA资源和内存资源等等
*/
int (*prepare_transfer_hardware)(struct spi_master *master);
int (*unprepare_transfer_hardware)(struct spi_master *master);
/* 这两个回调函数也是用于在发起一个数据传送过程前和后,给控制器驱动
一个机会,对message进行必要的预处理或后处理,如设定控制器的正确工作时钟、字长和工作模式等 */
int (*prepare_message)(struct spi_master *master,
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
......
//片选函数
void (*set_cs)(struct spi_device *spi, bool enable);
//用于传输单个spi_transfer
int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
struct spi_transfer *transfer);
void (*handle_err)(struct spi_master *master,
struct spi_message *message);
/* 片选信号所用到的gpio引脚 */
int *cs_gpios;
/* statistics */
struct spi_statistics statistics;
......
};