Basic Implementation of SPI Subsystem in Embedded Linux System

1. SPI driver source file directory

Linux common spi driver

kernel-4.14/drivers/spi/spi.c  Linux 提供的通用接口封装层驱动
kernel-4.14/drivers/spi/spidev.c  linux 提供的 SPI 通用设备驱动程序
kernel-4.14/include/linux/spi/spi.h  linux 提供的包含 SPI 的主要数据结构和函数

spi controller driver, provided by IC manufacturers, different manufacturers have different names

kernel-4.14/drivers/spi/spi-mt65xx.c  MTK SPI 控制器驱动
kernel-4.14/drivers/spi/spi-mt65xx-dev.c
kernel-4.14/include/linux/platform_data/spi-mt65xx.h

dts

kernel-4.14/arch/arm/boot/dts/...
kernel-4.14/arch/arm64/boot/dts/...

The above files correspond to the following SPI driver software architecture:

SPI controller driver

The SPI controller does not need to care about the specific functions of the device. It is only responsible for sending the data prepared by the upper-layer protocol driver to the SPI device according to the timing requirements of the SPI bus, and at the same time returning the data received from the device to the upper-layer protocol driver. Therefore, the kernel Separate the driver of the SPI controller.

The SPI controller driver is responsible for controlling specific controller hardware, such as DMA and interrupt operations, etc., because multiple upper-layer protocol drivers may request data transfer operations through the controller, so the SPI controller driver is also responsible for these requests. Carry out queue management to ensure the principle of first in first out.

SPI general interface encapsulation layer

In order to simplify the programming work of the SPI driver, and to reduce the degree of coupling between the [protocol driver] and the [controller driver], the kernel encapsulates some common operations of the controller driver and the protocol driver into a standard interface, plus some General logic processing operations constitute the SPI general interface encapsulation layer.

The advantage of this is that for the controller driver, it is only necessary to implement the standard interface callback API and register it in the general interface layer, without directly interacting with the protocol layer driver. For the protocol layer driver, the device and driver registration can be completed only through the API provided by the general interface layer, and the data transmission can be completed through the API provided by the general interface layer, without paying attention to the implementation details of the SPI controller driver.

SPI protocol driver

The specific function of the SPI device is completed by the SPI protocol driver, and the SPI protocol driver understands the function of the device and the protocol format of the communication data. Downward, the protocol driver exchanges data with the controller through the general interface layer, and upward, the protocol driver usually interacts with other subsystems of the kernel according to the specific functions of the device.

For example, interact with the MTD layer to implement an SPI-interfaced storage device as a file system, interact with the TTY subsystem to implement an SPI device as a TTY device, and interact with the network subsystem to implement an SPI device as a network device. If it is a proprietary SPI device, we can also implement our own proprietary protocol driver according to the protocol requirements of the device.

SPI Generic Device Driver

Considering the variability of devices connected to the SPI controller, the kernel is not equipped with a corresponding protocol driver. In this case, the kernel has prepared a general SPI device driver for us, which provides the user space with In order to control the control interface controlled by SPI, the specific protocol control and data transmission work is handed over to the user space according to the specific device. In this way, only the synchronous method can be used to communicate with the SPI device, so it is usually used in some Simple SPI device with low data volume.

2. SPI general interface layer

  1. The SPI general interface layer connects the specific SPI device protocol driver with the SPI controller driver.

  2. Responsible for the initialization of the SPI system and the Linux device model.

  3. Provide a series of standard interface APIs and data structures for protocol drivers and controller drivers.

  4. Data abstraction for SPI devices, SPI protocol drivers, and SPI controllers

  5. Data structures defined to facilitate data transfer

kernel-4.14/drivers/spi/spi.c

static int __init spi_init(void)
{
 int status;

 buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
 if (!buf) {
  status = -ENOMEM;
  goto err0;
 }
        
  // 创建 /sys/bus/spi 节点
 status = bus_register(&spi_bus_type);
 if (status < 0)
  goto err1;

  //创建 /sys/class/spi_master 节点
 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;
 }
        ......
}

The SPI bus is created here, the /sys/bus/spi node and the /sys/class/spi_master node are created.

Important data structures:

spi_device
spi_driver
spi_board_info
spi_controller/spi_master
spi_transfer
spi_message

Important APIs

spi_message_init
spi_message_add_tail
spi_sync
spi_async
spi_write
spi_read

Next, analyze the structure and API in detail, and only explain the key parts. For a complete analysis, please refer to the official document

https://www.kernel.org/doc/html/v4.14//driver-api/spi.html

Only when you are familiar with what is stored in each structure can you really understand the SPI module.

spi_master/spi_controller: Describes a spi master device

struct spi_master {
  //Linux 驱动模型中的设备
 struct device dev;

  //此 spi_master 设备在全局 spi_master 链表中的节点
 struct list_head list;

  //此 spi_master 编号
 s16   bus_num;

  //此 spi_master 支持的片选信号数量
 u16   num_chipselect;

  //dma 地址对齐
 u16   dma_alignment;

  //此 spi_master 支持传输的 mode
 u16   mode_bits;
 u32   bits_per_word_mask;
 /* limits on transfer speed */
 u32   min_speed_hz;
 u32   max_speed_hz;

 /* other constraints relevant to this driver */
 u16   flags;

 /* lock and mutex for SPI bus locking */
 spinlock_t  bus_lock_spinlock;//总线自旋锁
 struct mutex  bus_lock_mutex;//总线互斥锁

  //总线是否处于 lock 状态
 bool   bus_lock_flag;

  //准备传输,设置传输的参数
 int   (*setup)(struct spi_device *spi);

  //传输数据
 int   (*transfer)(struct spi_device *spi,
     struct spi_message *mesg);
  // 设备 release 时的清除工作
 void   (*cleanup)(struct spi_device *spi);

 bool   (*can_dma)(struct spi_master *master,
        struct spi_device *spi,
        struct spi_transfer *xfer);

 bool   queued;//是否采用系统的序列化传输
 struct kthread_worker kworker;//序列化传输时的线程 worker
 struct task_struct *kworker_task;//序列化传输的线程
 struct kthread_work pump_messages;//序列化传输时的处理函数
 spinlock_t  queue_lock;//序列化传输时的queue_lock
 struct list_head queue;//序列化传输时的 msg 队列头
 struct spi_message *cur_msg;//序列化传输时当前的 msg
 bool   idling;
 bool   busy;//序列化传输时线程是否处于busy状态
 bool   running;//序列化传输时线程是否在运行
 bool   rt;//是否实时传输
  ......

 int (*prepare_transfer_hardware)(struct spi_master *master);

  //一个 msg 的传输实现
 int (*transfer_one_message)(struct spi_master *master,
        struct spi_message *mesg);
  ......

 /* gpio chip select */
 int   *cs_gpios;
  ......
};

spi_device: Describes a spi slave device

struct spi_device {
  //Linux驱动模型中的设备
 struct device  dev;
 struct spi_master *master;//设备所连接的 spi 主机设备
 u32   max_speed_hz;//该设备最大传输速率
 u8   chip_select;//CS片选信号编号
 u8   bits_per_word;//每次传输长度
 u16   mode;//传输模式
 ......
 int   irq;//软件中断号
 void   *controller_state;//控制器状态
 void   *controller_data;//控制参数
 char   modalias[SPI_NAME_SIZE];//设备名称
  //CS 片选信号对应的 GPIO number
 int   cs_gpio;  /* chip select gpio */

 /* the statistics */
 struct spi_statistics statistics;
};

spi_driver: describes a spi device driver

struct spi_driver {
  //此driver所支持的 spi 设备 list
 const struct spi_device_id *id_table;
 int   (*probe)(struct spi_device *spi);
 int   (*remove)(struct spi_device *spi);
  //系统 shutdown 时的回调函数
 void   (*shutdown)(struct spi_device *spi);
 struct device_driver driver;
};

spi_board_info: Describes a spi slave device board-level information, used when there is no device tree

struct spi_board_info {
  //设备名称
 char  modalias[SPI_NAME_SIZE];
 const void *platform_data;//设备的平台数据
 void  *controller_data;//设备的控制器数据
 int  irq;//设备的中断号
 u32  max_speed_hz;//设备支持的最大速率
 u16  bus_num;//设备连接的 spi 总线编号
 u16  chip_select;//设备连接的 CS 信号编号
 u16  mode;//设备使用的传输 mode
};

spi_transfer: Describe the specific data transmitted by spi

struct spi_transfer {

 const void *tx_buf;//spi_transfer 的发送 buf
 void  *rx_buf;//spi_transfer 的接收 buf
 unsigned len;//spi_transfer 发送和接收的长度

 dma_addr_t tx_dma;//tx_buf 对应的 dma 地址
 dma_addr_t rx_dma;//rx_buf 对应的 dma 地址
 struct sg_table tx_sg;
 struct sg_table rx_sg;

  //spi_transfer传输完成后是否要改变 CS 片选信号
 unsigned cs_change:1;
 unsigned tx_nbits:3;
 unsigned rx_nbits:3;
  ......
 u8  bits_per_word;//spi_transfer 中一个 word 占的bits
 u16  delay_usecs;//两个 spi_transfer 直接的等待延迟
 u32  speed_hz;//spi_transfer 的传输速率

 struct list_head transfer_list;//spi_transfer挂载到的 message 节点
};

spi_message: information describing a spi transmission

struct spi_message {
  //挂载在此 msg 上的 transfer 链表头
 struct list_head transfers;
  //此 msg 需要通信的 spi 从机设备
 struct spi_device *spi;
  //所使用的地址是否是 dma 地址
 unsigned  is_dma_mapped:1;

 //msg 发送完成后的处理函数
 void   (*complete)(void *context);
 void   *context;//complete函数的参数
 unsigned  frame_length;
 unsigned  actual_length;//此 msg 实际成功发送的字节数
 int   status;//此 msg 的发送状态,0:成功,负数,失败

 struct list_head queue;//此 msg 在所有 msg 中的链表节点
 void   *state;//此 msg 的私有数据
};

queuing

There are two ways of SPI data transmission: synchronous and asynchronous.

Synchronous mode: The initiator of the data transmission must wait for the end of the transmission, and cannot do other things during the period. To explain it in code, after calling the transmission function, the function will not return until the data transmission is completed.

Asynchronous mode: The initiator of the data transmission does not need to wait for the end of the transmission. Other things can be done during the data transmission. To explain it in code, after calling the function of the transmission, the function will return immediately without waiting for the completion of the data transmission. We only need to set A callback function that will be called after the transfer is complete to notify the initiator that the data transfer has completed.

The synchronization method is simple and easy to use, and it is very suitable for those single transfers of small amounts of data. However, for transmissions with a large amount of data and a large number of times, the asynchronous method is more suitable.

For the SPI controller, the following two situations must be considered in order to support the asynchronous mode:

  1. For the initiator of the same data transmission, since the asynchronous method returns without waiting for the completion of the data transmission, after returning, the initiator can immediately initiate another message, while the previous message has not been processed yet.

  2. For another different initiator, it is also possible to initiate a message transmission request at the same time.

Queuing is just to solve the above problems. The so-called queuing refers to putting the messages waiting to be transmitted into a waiting queue and initiating a transmission operation. In fact, it is to put the corresponding messages into a waiting queue in order. The system will continuously check whether there are messages waiting to be transmitted in the queue, and if so, it will continuously schedule the data transmission kernel thread, and take out the messages in the queue one by one for processing until the queue becomes empty. The SPI general interface layer implements the basic framework of queuing for us.

spi_message is an atomic operation of SPI data exchange and cannot be interrupted.

3. SPI controller driver layer

The SPI controller driver layer is responsible for the lowest data transmission and reception, mainly has the following functions:

  1. Apply for necessary hardware resources, such as interrupts, DMA channels, DMA memory buffers, etc.

  2. Configure the working mode and parameters of the SPI controller so that it can exchange data correctly with the corresponding device

  3. Provide an interface to the general interface layer, so that the upper protocol driver can access the controller driver through the general interface layer

  4. Cooperate with the general interface layer to complete the queuing and processing of the data message queue until the message queue becomes empty

The SPI host driver is the SPI controller driver of the SOC. The Linux kernel uses spi_master/spi_controller to represent the SPI host driver, and spi_master is a structure defined in the include/linux/spi/spi.h file.

The core of the SPI host driver is to apply for spi_master, then initialize spi_master, and finally register spi_master with the Linux kernel.

The APIs are as follows:

spi_alloc_master 函数:申请 spi_master。
spi_master_put 函数:释放 spi_master。

spi_register_master函数:注册 spi_master。
spi_unregister_master 函数:注销 spi_master。

spi_bitbang_start函数:注册 spi_master。
spi_bitbang_stop 函数:注销 spi_master。

SPI host driver loading

Take MTK as an example, the source code comes from the Xiaomi open source project

https://github.com/MiCode/Xiaomi_Kernel_OpenSource

Every time Xiaomi makes a project, it will open the kernel part, because it needs to follow the Linux GPL open source agreement.

[Device] declared in the device tree

kernel-4.14/arch/arm64/boot/dts/mediatek/mt6885.dts

【drive】

kernel-4.14/drivers/spi/spi-mt65xx.c

After matching, the probe function executes, applies for spi_master, initializes spi_master, and finally registers spi_master with the Linux kernel.

4. Software process

After understanding this picture, you will have a complete understanding of the SPI driver framework.

1, 2, 3 are executed in sequence, first there is the registration of the spi bus, then the loading of the spi controller driver, and then the loading of the device driver. The difference is that when the spi controller driver is loaded, it relies on the platform bus to match the device (controller) and driver. When the spi device driver is loaded, it depends on the spi bus to match the device (peripheral IC) and the driver.

init flow

Call sequence diagram of spi_register_master

The working mechanism and process of queuing

When the protocol driver initiates a message request through spi_async, the queuing and worker threads are activated, triggering a series of operations, and finally completing the message transmission operation.

spi_sync is similar to spi_async, but has a waiting process.

5. SPI device driver

[Device] declared in the device tree

Note: The declaration of the device, the slave device node should be included under the &spi node you want to mount, and bind the device to the master. Then specify GPIO through pinctrl, and operate the pinctrl handle in the driver.

【Driver】demo

The Linux kernel uses the spi_driver structure to represent the spi device driver, and we need to implement spi_driver when writing the SPI device driver. The spi_driver structure is defined in the include/linux/spi/spi.h file.

spi_register_driver:注册 spi_driver
spi_unregister_driver:销掉 spi_driver
/* probe 函数 */
static int xxx_probe(struct spi_device *spi)
{

 /* 具体函数内容 */
 return 0;
}

/* remove 函数 */
static int xxx_remove(struct spi_device *spi)
{

 /* 具体函数内容 */
 return 0;
}

/* 传统匹配方式 ID 列表 */
static const struct spi_device_id xxx_id[] = {

 {"xxx", 0},
 {}
};

/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {

 { .compatible = "xxx" },
 { /* Sentinel */ }
};

/* SPI 驱动结构体 */
static struct spi_driver xxx_driver = {

 .probe = xxx_probe,
 .remove = xxx_remove,
 .driver = {
  .owner = THIS_MODULE,
  .name = "xxx",
  .of_match_table = xxx_of_match,
  },
 .id_table = xxx_id,
};

/* 驱动入口函数 */
static int __init xxx_init(void)
{

 return spi_register_driver(&xxx_driver);
}

/* 驱动出口函数 */
static void __exit xxx_exit(void)
{

 spi_unregister_driver(&xxx_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);

Call spi_register_driver in the driver entry function to register spi_driver.

Call spi_unregister_driver in the driver exit function to unregister spi_driver.

spi read and write data demo

/* SPI 多字节发送 */
static int spi_send(struct spi_device *spi, u8 *buf, int len)
{
 int ret;
 struct spi_message m;
 
 struct spi_transfer t = {
  .tx_buf = buf,
  .len = len,
 };
 
 spi_message_init(&m); /* 初始化 spi_message */
 spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
 ret = spi_sync(spi, &m); /* 同步传输 */
 return ret;
}
/* SPI 多字节接收 */
static int spi_receive(struct spi_device *spi, u8 *buf, int len)
{
 int ret;
 struct spi_message m;
 
 struct spi_transfer t = {
  .rx_buf = buf,
  .len = len,
 };
 
 spi_message_init(&m); /* 初始化 spi_message */
 spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
 ret = spi_sync(spi, &m); /* 同步传输 */
 return ret;
}

In addition to the init, exit, probe, remove, read, write functions, other functions depend on the implementation of requirements, these are the most basic.

6. Summary

Linux is a framework for buses, devices, and drivers. If you understand this framework, you can understand all module driver frameworks.

The SPI driver is much simpler than the I2C driver.

Guess you like

Origin blog.csdn.net/weixin_41114301/article/details/131070768