目录
2.4 struct spi_transfer和struct spi_message
3.2 数据准备函数:spi_message_init和spi_message_add_tail
SPI核心层
上次简单介绍了下Linux SPI子系统的系统结构,主要有3部分组成,分别是SPI核心、SPI总线驱动(控制器驱动)以及SPI设备驱动。
SPI核心层代码位于drivers/spi/spi.c,头文件位于include/linux/spi/spi.h,SPI核心提供了SPI总线驱动和设备驱动的注册、注销方法,并提供一些需要控制器驱动实现的回调函数。
本文主要介绍SPI核心层,包括重要的数据结构和API。
1. SPI子系统初始化
/* drivers/spi/spi.c */
/* 1 */
struct bus_type spi_bus_type = {
.name = "spi",
.dev_groups = spi_dev_groups,
.match = spi_match_device,
.uevent = spi_uevent,
};
/* 2 */
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;
}
/* 3 */
static struct class spi_master_class = {
.name = "spi_master",
.owner = THIS_MODULE,
.dev_release = spi_controller_release,
.dev_groups = spi_master_groups,
};
1. struct bus_type是总线的类型,spi_bus_type即类型为SPI的总线,其中的match成员函数spi_match_device的作用是将SPI设备和设备驱动进行匹配。
2. 从四个方面进行匹配,分别从驱动中的of_match_table的compatible和acpi_match_table的compatible与dts中的compatible进行比较,看是否一致、如支持id数组,则查找与id数组中匹配的SPI设备、比较驱动和设备的名字是否一致。
3. struct class是设备的类,spi_master_class即SPI控制器设备的类。
/* 4 */
static int __init spi_init(void)
{
int status;
buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
if (!buf) {
status = -ENOMEM;
goto err0;
}
status = bus_register(&spi_bus_type);
if (status < 0)
goto err1;
status = class_register(&spi_master_class);
if (status < 0)
goto err2;
if (IS_ENABLED(CONFIG_SPI_SLAVE)) {
status = class_register(&spi_slave_class);
if (status < 0)
goto err3;
}
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
if (IS_ENABLED(CONFIG_ACPI))
WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));
return 0;
err3:
class_unregister(&spi_master_class);
err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}
4. spi_init是SPI子系统的初始化函数,起作用的注册了spi bus和spi master class。
2. 重要的数据结构
2.1 struct spi_controller
#define spi_master spi_controller
struct spi_controller {
/* SPI设备的设备结构体 */
struct device dev;
/* 链表头 */
struct list_head list;
/* SPI总线序号 */
s16 bus_num;
/* 片选信号的数量 */
u16 num_chipselect;
/* SPI控制器DMA缓冲区对齐定义 */
u16 dma_alignment;
/* 工作模式位 */
u16 mode_bits;
/* 传输支持的 bits_per_word 的位掩码 */
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_CONTROLLER_HALF_DUPLEX BIT(0) /* can't do full duplex */
#define SPI_CONTROLLER_NO_RX BIT(1) /* can't do buffer read */
#define SPI_CONTROLLER_NO_TX BIT(2) /* can't do buffer write */
#define SPI_CONTROLLER_MUST_RX BIT(3) /* requires rx */
#define SPI_CONTROLLER_MUST_TX BIT(4) /* requires tx */
#define SPI_MASTER_GPIO_SS BIT(5) /* GPIO CS must select slave */
/* 指示这是非devres托管控制器的标志 */
bool devm_allocated;
/* 指示这是一个 SPI 从控制器的标志 */
bool slave;
/* 在某些硬件传输/消息大小可能受到限制,该限制取决于设备传输设置 */
size_t (*max_transfer_size)(struct spi_device *spi);
size_t (*max_message_size)(struct spi_device *spi);
/* I/O互斥锁 */
struct mutex io_mutex;
/* 用于SPI总线锁定的lock和mutex */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
/* 指示 SPI 总线被锁定为独占使用的标志 */
bool bus_lock_flag;
/* 设置模式和时钟等(spi驱动可能调用多次) */
int (*setup)(struct spi_device *spi);
/* 双向批量传输 */
int (*transfer)(struct spi_device *spi,
struct spi_message *mesg);
/* 调用 release()以释放spi_controller提供的内存 */
void (*cleanup)(struct spi_device *spi);
/* 用于启用对DMA处理的核心支持,如果 can_dma()存在并返回true,则传输将在调用
transfer_one()之前进行映射 */
bool (*can_dma)(struct spi_controller *ctlr,
struct spi_device *spi,
struct spi_transfer *xfer);
/* 下面的成员适用于想要使用通用控制器传输队列机制的驱动程序 */
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 idling;
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_controller *ctlr);
int (*transfer_one_message)(struct spi_controller *ctlr,
struct spi_message *mesg);
int (*unprepare_transfer_hardware)(struct spi_controller *ctlr);
int (*prepare_message)(struct spi_controller *ctlr,
struct spi_message *message);
int (*unprepare_message)(struct spi_controller *ctlr,
struct spi_message *message);
int (*slave_abort)(struct spi_controller *ctlr);
int (*spi_flash_read)(struct spi_device *spi,
struct spi_flash_read_message *msg);
bool (*spi_flash_can_dma)(struct spi_device *spi,
struct spi_flash_read_message *msg);
bool (*flash_read_supported)(struct spi_device *spi);
/* 下面的成员适用于使用核心提供的transfer_one_message()的通用实现的驱动程序 */
void (*set_cs)(struct spi_device *spi, bool enable);
int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,
struct spi_transfer *transfer);
void (*handle_err)(struct spi_controller *ctlr,
struct spi_message *message);
/* 为类似SPI内存的操作优化了处理程序 */
const struct spi_controller_mem_ops *mem_ops;
const struct spi_controller_mem_caps *mem_caps;
/* gpio片选 */
int *cs_gpios;
/* 统计数据 */
struct spi_statistics statistics;
/* DMA通道 */
struct dma_chan *dma_tx;
struct dma_chan *dma_rx;
/* 全双工设备的虚拟数据 */
void *dummy_rx;
void *dummy_tx;
int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned int cs);
};
struct spi_controller是SPI控制器驱动的核心数据结构,spi_controller代表一个SPI控制器,在编写控制器驱动时需要对其中的一些参数和回调函数进行填充或实现。
2.2 struct spi_driver
struct spi_driver {
/* 支持的SPI device的设备表 */
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);
struct device_driver driver;
};
spi_driver代表一个SPI设备驱动,实现id_table或者of_match_table等,使得匹配成功即可;匹配成功后调用probe函数,驱动卸载时调用remove函数。
2.3 struct spi_device
struct spi_device {
struct device dev;
struct spi_controller *controller;
struct spi_controller *master; /* compatibility layer */
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 */
#define SPI_CS_WORD 0x1000 /* toggle cs after each word */
#define SPI_TX_OCTAL 0x2000 /* transmit with 8 wires */
#define SPI_RX_OCTAL 0x4000 /* receive with 8 wires */
#define SPI_3WIRE_HIZ 0x8000 /* high impedance turnaround */
int irq;
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
int cs_gpio; /* chip select gpio */
/* the statistics */
struct spi_statistics statistics;
/*
* 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_device就不多介绍了,现在一般都是用dts来描述,由系统自动生成spi_device。
2.4 struct spi_transfer和struct spi_message
struct spi_transfer {
const void *tx_buf;
void *rx_buf;
unsigned int len;
dma_addr_t tx_dma;
dma_addr_t rx_dma;
struct sg_table tx_sg;
struct sg_table rx_sg;
unsigned dummy_data:1;
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,用来连接的双向链接节点*/
struct list_head transfer_list;
};
struct spi_message {
/*spi_transfer链表队列 */
struct list_head transfers;
/*传输的目的设备*/
struct spi_device *spi;
/*如果为真,此次调用提供dma和cpu虚拟地址 */
unsigned is_dma_mapped:1;
/* 通过回调告知完成 */
void (*complete)(void *context);
void *context;
unsigned int frame_length;
unsigned int actual_length;
int status;
struct list_head queue;
void *state;
/* list of spi_res reources when the spi message is processed */
struct list_head resources;
};
spi_message用于执行数据传输的原子序列,每个数据传输由spi_transfer表示。该序列是“原子的”,因为在该序列完成之前没有其他spi_message可以使用该SPI总线。在某些系统上,许多这样的序列可以作为单个编程的DMA传输执行。在所有系统上,这些消息都是排队的,并且可能在与其他设备进行事务处理后完成。发送到给定spi_device的消息始终按FIFO顺序执行。
3. 重要的API
3.1 spi_register_controller
#define spi_register_master(_ctlr) spi_register_controller(_ctlr)
int spi_register_controller(struct spi_controller *ctlr)
作用是将spi控制器驱动注册到内核。
3.2 数据准备函数:spi_message_init和spi_message_add_tail
/* include/linux/spi/spi.h */
static inline void spi_message_init_no_memset(struct spi_message *m)
{
INIT_LIST_HEAD(&m->transfers);
INIT_LIST_HEAD(&m->resources);
}
static inline void spi_message_init(struct spi_message *m)
{
memset(m, 0, sizeof(*m));
spi_message_init_no_memset(m);
}
static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
list_add_tail(&t->transfer_list, &m->transfers);
}
spi_message_init函数作用是初始化spi_message,将message清零,初始化transfers链表头。
spi_message_add_tail函数作用是将spi_transfer尾部加入到spi_message链表中。
3.3 数据传输函数:spi_sync和spi_async
SPI数据传输分为同步方式和异步方式,同步方式即发起数据传输后,必须等待传输完成后才能返回,期间不能做其他事情;异步方式即发起数据传输后,不用管传输是否完成,直接返回,期间可以做其他事情。
/* 调用关系 */
spi_sync(struct spi_device *spi, struct spi_message *message)
|-> __spi_sync(struct spi_device *spi, struct spi_message *message)
static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
struct spi_controller *ctlr = spi->controller;
unsigned long flags;
status = __spi_validate(spi, message);
if (status != 0)
return status;
message->complete = spi_complete;
message->context = &done;
message->spi = spi;
SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_sync);
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);
/* If we're not using the legacy transfer method then we will
* try to transfer in the calling context so special case.
* This code would be less tricky if we could remove the
* support for driver implemented message queues.
*/
if (ctlr->transfer == spi_queued_transfer) {
spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);
trace_spi_message_submit(message);
status = __spi_queued_transfer(spi, message, false);
spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);
} else {
status = spi_async_locked(spi, message);
}
if (status == 0) {
/* Push out the messages in the calling context if we
* can.
*/
if (ctlr->transfer == spi_queued_transfer) {
SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics,
spi_sync_immediate);
SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
spi_sync_immediate);
__spi_pump_messages(ctlr, false);
}
wait_for_completion(&done);
status = message->status;
}
message->context = NULL;
return status;
}
spi_sync:首先对spi_message中的complete回调函数进行初始化;之后调用__spi_queued_transfer函数,这里第三个参数传的是false,所以只将message尾部加入到ctlr链表中后返回;随后调用__spi_pump_messages,将spi_message发出去;最后调用wait_for_completion函数,等待完成的信号量,当接收到信号量时说明传输完成并返回。
/* 调用关系 */
spi_async(struct spi_device *spi, struct spi_message *message)
|-> __spi_async(struct spi_device *spi, struct spi_message *message)
|-> ctlr->transfer(spi, message)
static int __spi_queued_transfer(struct spi_device *spi,
struct spi_message *msg,
bool need_pump)
{
struct spi_controller *ctlr = spi->controller;
unsigned long flags;
spin_lock_irqsave(&ctlr->queue_lock, flags);
if (!ctlr->running) {
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
return -ESHUTDOWN;
}
msg->actual_length = 0;
msg->status = -EINPROGRESS;
list_add_tail(&msg->queue, &ctlr->queue);
if (!ctlr->busy && need_pump)
kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);
spin_unlock_irqrestore(&ctlr->queue_lock, flags);
return 0;
}
spi_async:如果控制器驱动没有实现transfer函数,内核会自动将transfer函数初始化为spi_queued_transfer函数,此函数中,将message尾部加入到ctlr链表中,随后唤醒工作线程,把具体的工作交付给worker后返回,不等待传输完成。