目录
SPI物理总线
信号线
spi时序
spi通信模式
常见spi设备
SPI驱动框架简介
spi主机驱动:spi_controller结构体
spi设备驱动:spi_device结构体、spi_driver结构体
spi总线注册:spi_init()
spi总线定义:spi_bus_type
spi控制器驱动
设备树节点
module_platform_driver()宏
SPI物理总线
信号线
SCK:时钟线,数据收发同步。
MOSI:数据线,主设备数据发送、从设备数据接收。
MISO:数据线,从设备数据发送、主设备数据接收。
NSS、CS:片选信号线。
支持一主多从,全双工通信,最大速率可达上百MHz。
spi时序
起始信号:NSS信号线由高变低。
停止信号:NSS信号线由低变高。
数据传输:在SCK的每个时钟周期MOSI和MISO同时传输一位数据,高/低位传输没有硬件规定。
传输单位:字节/半字
单位数量:不受限制
spi通信模式
总线空闲时SCK的时钟状态以及数据采样时刻。
时钟极性CPOL:指spi通讯设备处于空闲状态时,SCK信号线的电平信号
CPOL = 0时,SCK在空闲状态时为低电平
CPOL = 1时,SCK在空闲状态时为高电平
时钟相位CPHA:数据的采样时刻
CPHA = 0时,数据在SCK时钟线的“奇数边沿”被采样
CPHA = 1时,数据在SCK时钟线的“偶数边沿”被采样
SPI模式 | CPOL | CPHA | 空闲时SCK时钟 | 采样时刻 |
0 | 0 | 0 | 低电平 | 奇数边沿 |
1 | 0 | 1 | 低电平 | 偶数边沿 |
2 | 1 | 0 | 高电平 | 奇数边沿 |
3 | 1 | 1 | 高电平 | 偶数边沿 |
常见spi设备
EEPROM、FLASH、实时时钟、AD转换器等
SPI驱动框架简介
SPI核心层
提供SPI控制器驱动和设备驱动的注册方法、注销方法、SPI通信硬件无关接口
SPI主机驱动
主要包含SPI硬件体系结构中适配器(spi控制器)的控制,用于产生SPI读写时序
主要数据结构:spi_master(spi_controller)
SPI设备驱动
通过SPI主机驱动与CPU交换数据
主要数据结构:spi_device和spi_driver
spi主机驱动:spi_controller结构体
代码存放在内核/include/linux/spi/spi.h文件。
#define spi_master spi_controller
struct spi_controller {
struct device dev;
struct list_head list; // 连接全部spi_master链表节点
s16 bus_num; // spi控制器编号
u16 num_chipselect; // 片选信号的个数,用来和多个从设备进行通信
...
/* 当前正待处理的消息队列。抽象表示一条spi消息,要发送的内容填充此结构体 */
struct spi_message *cur_msg;
...
/* 初始化spi设备硬件 */
int (*setup)(struct spi_device *spi);
/* 传输spi消息,异步传输,此函数把消息加入spi控制器的消息队列,下面的queue */
int (*transfer)(struct spi_device *spi, struct spi_message *mesg);
/* spi通信结束后,释放 spi_master所需要的清理工作 */
void (*cleanup)(struct spi_device *spi);
struct kthread_worker kworker; // 内核流水线工人,由其来完成发送,由于spi速度快,数据量大,因此可采用异步传输方式发送数据
struct task_struct *kworker_task; // 进程/线程结构体,配合上面的kworker来工作
struct kthread_work pump_messages; // 具体的工作,即发送spi消息
struct list_head queue; // 所有等待传输的消息队列挂在该链表下
struct spi_message *cur_msg;
...
int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,struct spi_transfer *transfer);
int (*prepare_transfer_hardware)(struct spi_controller *ctlr);
// 发送一个spi消息,类似IIC适配器里的algo->master_xfer,产生spi通信时序
int (*transfer_one_message)(struct spi_controller *ctlr,struct spi_message *mesg);
void (*set_cs)(struct spi_device *spi, bool enable);
...
int *cs_gpios; // 数组,负责记录此spi设备上具体的片选信号线所对应的gpio
} // 一个结构体代表一个spi控制器,多个控制器通过其list_head链表节点串连起来
spi主机设备也被当成spi设备挂载在spi总线上。
相关API
int spi_register_master(struct spi_master *master):注册一个spi_controller。
int spi_unregister_master(struct spi_master *master):注销一个spi_controller。
spi设备驱动:spi_device结构体、spi_driver结构体
代码存放在内核/include/linux/spi/spi.h文件。
struct spi_device {
struct device dev; // 继承
struct spi_controller *controller; // 当前的spi设备属于哪一个spi主控制器,一个主控器下面可能有多个spi设备
struct spi_controller *master; /* compatibility layer */
u32 max_speed_hz; // 此spi最大的通信速率:Maximum clock rate to be used with this chip
u8 chip_select; // 片选
u8 bits_per_word; // 传输单位,8或16,由此成员来制定
u16 mode; // 重要,指定四大模式中的哪一个
// 下面的宏都是用来设置spi设备某方面的具体属性
#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 */
...
char modalias[SPI_NAME_SIZE]; // spi设备的名字,用于和spi_driver配对
...
} // 此结构体表示一个spi设备的具体属性。
struct spi_driver {
const struct spi_device_id *id_table; // 记录这此driver支持的设备列表,用来和spi_device配对
int (*probe)(struct spi_device *spi); // spi设备和spi驱动匹配后,回调该函数指针
int (*remove)(struct spi_device *spi); // Unbinds this driver from the spi device
void (*shutdown)(struct spi_device *spi);
// 继承,SPI device drivers should initialize the name and owner field of this structure.
struct device_driver driver;
};
相关API
int spi_register_driver(struct spi_driver *sdrv):注册一个spi驱动。
int spi_unregister_driver(struct spi_driver *sdrv):注销一个spi驱动。
spi总线注册:spi_init()
该函数存放于内核/drivers/spi/spi.c文件。在linux系统中上电自动运行。
// 开机自动运行
static int __init spi_init(void)
{
int status;
...
status = bus_register(&spi_bus_type); // 注册spi总线,完成后在/sys目录下新增目录 /sys/bus/spi
...
status = class_register(&spi_master_class); // 注册设备类,完成后在/sys目录下新增目录sys/class/spi_master
...
}
spi总线定义:spi_bus_type
struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device, // 设备和驱动匹配规则
.uevent = spi_uevent,
};
static int spi_match_device(struct device *dev, struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
return 1;
/* Then try ACPI */
if (acpi_driver_match_device(dev, drv))
return 1;
if (sdrv->id_table)
return !!spi_match_id(sdrv->id_table, spi);
return strcmp(spi->modalias, drv->name) == 0;
}
spi控制器驱动
设备树节点
4个spi设备树节点存放在内核/arch/arm/boot/dts/imx6ull.dtsi。设备树文件中的4个spi控制器节点对应芯片的4个spi控制器。例如:
ecspi3: ecspi@2010000 {
// 设置reg属性格式
#address-cells = <1>;
#size-cells = <0>;
// 对应驱动文件为 spi-imx.c
compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
// 起始地址 寄存器组长度
reg = <0x2010000 0x4000>;
interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_ECSPI3>,
<&clks IMX6UL_CLK_ECSPI3>;
clock-names = "ipg", "per";
dmas = <&sdma 7 7 1>, <&sdma 8 7 2>;
dma-names = "rx", "tx";
status = "disabled";
};
module_platform_driver()宏
该宏存放在内核/include/linux/platform_device.h文件。
// 参数2 3:注册,注销平台驱动
#define module_platform_driver(__platform_driver) \
module_driver(__platform_driver, platform_driver_register, platform_driver_unregister)
//该宏定义在内核/include/linux/device.h文件
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
//上诉等价于下方
static int __init spi_imx_driver_init(void)
{
return platform_driver_register(&(spi_imx_driver) , ##__VA_ARGS__);
}
module_init(spi_imx_driver_init);