5

分析Linux内核中的SPI驱动源码

 1 year ago
source link: https://www.51cto.com/article/753305.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

分析Linux内核中的SPI驱动源码

作者:编码小哥 2023-04-28 08:42:08
本文分析了Linux内核中的SPI驱动源码,介绍了SPI驱动框架的基本结构、Spi.c的结构和作用以及SPI驱动中的重要数据结构和函数。
368875b056bfe04646c49383299e42837ee36a.png

本文将对Linux内核中的SPI驱动源码进行分析,包括SPI驱动框架的基本结构、各文件的作用、重要的数据结构和函数等。

SPI(Serial Peripheral Interface)是一种串行通信接口,常常用于与数字外设进行通信,如传感器、存储器、网卡等。Linux内核提供了SPI驱动框架,用于向上层应用程序提供SPI接口。本文将对该框架进行深入分析。

一、SPI驱动框架的基本结构

在Linux内核中,SPI驱动框架的代码位于/drivers/spi目录下。该目录下的源文件主要包括以下几个:

  • spi.c:SPI总线设备驱动程序。
  • spi-bitbang.c:位压缩SPI驱动程序。
  • spi-dw-dma.c:SPI DMA驱动程序。
  • spi-dw-mmio.c:SPI MMIO驱动程序。
  • spi-fsl-dspi.c:FSL DSPI驱动程序。
  • spi-imx.c:i.MX SPI驱动程序。
  • spi-pl022.c:ARM PrimeCell PL022 SPI驱动程序。
  • spi-s3c24xx.c:Samsung S3C24xx SPI驱动程序。
  • spi-tegra20-sflash.c:Nvidia SPI Flash驱动程序。
  • spi-ti-qspi.c:TI Quad SPI驱动程序。

这些驱动程序分别对应不同的SPI控制器。其中,spi.c是SPI驱动的核心文件,提供了SPI驱动框架的基本结构和主要函数。

二、spi.c的结构和作用

1、SPI驱动框架的初始化

SPI驱动框架的初始化主要在spi_init()函数中完成。该函数首先调用spi_bus_type_init()函数,注册SPI设备总线,然后向/sys/class下的spi_master目录中创建spi设备目录,最后调用probe_master()函数,搜索当前系统中的SPI设备并添加到bus层中。该函数的代码如下:

static int __init spi_init(void)
{
int status;
status = spi_bus_type_init();
if (status)
goto out;
status = class_register(&spi_master_class);
if (status)
goto bus_unregister;
status = spi_proc_init();
if (status)
goto class_unregister;
status = spi_gpio_register_board_info(NULL, 0);
if (status)
goto proc_cleanup;
status = spi_read_configfile();
if (status)
goto board_cleanup;
status = spi_master_probe_devices();
if (status)
goto board_cleanup;
printk(KERN_INFO "%s\n", spi_revision);
return 0;
board_cleanup:
spi_board_cleanup();
proc_cleanup:
spi_proc_cleanup();
class_unregister:
class_unregister(&spi_master_class);
bus_unregister:
spi_bus_type_exit();
out:
return status;
}

2、SPI总线设备的添加和删除

当SPI总线设备(spi_master)被发现并添加到bus层时,会自动调用spi_master_add()函数,该函数会为SPI总线设备创建一个spi_master结构体,并将其添加到bus层中。

static int spi_master_add(struct spi_master *master)
{
struct device *dev = master->dev.parent;
struct spi_controller *ctlr = master->controller;
mutex_lock(&spi_mutex);
/*
• Implementation restriction: each SPI MASTER talks with other
• devices at constant signal levels, which don't change once
• operation starts. We don't provide any synchronization
• primitives that would be necessary for anything else.
*/
if (master->num_chipselect)
dev_warn(dev, "num_chipselect should == 1 when !is_slave\n");
if (!ctlr) {
ctlr = kzalloc(sizeof(struct spi_controller), GFP_KERNEL);
if (!ctlr) {
mutex_unlock(&spi_mutex);
return -ENOMEM;
}
ctlr->master = master;
master->controller = ctlr;
master->bits_per_word_mask = 0xFFFF;
if (!spi_controller_is_slave(master)) {
ctlr->max_speed_hz = spi_max_speed_hz(&ctlr->dev, master);
ctlr->setup = spi_master_setup;
ctlr->transfer_one = spi_transfer_one;
} else {
ctlr->max_speed_hz = master->max_speed_hz;
ctlr->setup = spi_slave_setup;
ctlr->transfer_one = spi_transfer_one_slave;
}
ctlr->bits_per_word_mask = master->bits_per_word_mask;
ctlr->flags = 0;
ctlr->mode_bits = master->mode_bits;
if (spi_controller_is_slave(master)) {
ctlr->mode_bits = 0;
ctlr->flags = SPI_CONTROLLER_SLAVE;
ctlr->bus_num = spi_slave_controller_id++;
idr_init(&ctlr->idr);
} else {
ctlr->mode_bits &= ctlr->controller_ops->get_mode_bits;
ctlr->flags |= SPI_CONTROLLER_MASTER;
ctlr->bus_num = spi_master_controller_id++;
}
dev_set_drvdata(dev, master);
dev_info(dev, "registered, %s%s%s%s%s\n",
ctlr->flags & SPI_CONTROLLER_MASTER ? "master" : "",
ctlr->flags & SPI_CONTROLLER_SLAVE ? "slave" : "",
ctlr->flags & SPI_CONTROLLER_CS_WORD ? "cs-high" : "",
ctlr->flags & SPI_CONTROLLER_NEEDS_POLL ? ", polling" : "",
ctlr->mode_bits ? ", mode " : "");
list_add_tail(&ctlr->list, &ctlr_list);
}
mutex_unlock(&spi_mutex);
return 0;
}

当SPI总线设备从bus层中删除时,会自动调用spi_master_del()函数,该函数会删除spi_master结构体并释放相关资源。

static int spi_master_del(struct spi_master *master)
{
int my_bus_num = master->controller->bus_num;
mutex_lock(&spi_mutex);
if (my_bus_num < 0) { /* not yet attached */
mutex_unlock(&spi_mutex);
return -EINVAL;
}
if (!spi_controller_is_slave(master)) {
if (spi_master_get(master)) {
mutex_unlock(&spi_mutex);
return -EINVAL;
}
}
dev_info(&master->dev, "removed\n");
spi_controller_cleanup(master->controller);
kfree(master->controller);
return 0;
}

三、重要的数据结构和函数

1、spi_device

spi_device结构体表示一个SPI设备,包含了设备的名称、片选信号、总线速率、数据位数、SPI传输设置等信息。该结构体被定义在include/linux/spi/spi.h头文件中,其定义如下:

struct spi_device {
struct device dev;
spinlock_t regs_lock;
const struct spi_device *next;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
u8 bit_order;
u16 flags;
u32 irq;
struct mutex io_mutex;
/* RT signal stuff */
struct rt_mutex rt;
struct spi_controller *controller;
};

spi_transfer结构体表示一次SPI传输,包含了传输的缓冲区、字节长度、传输设置等信息以及一个回调函数,用于在传输完成时通知上层应用程序。该结构体被定义在include/linux/spi/spi.h头文件中,其定义如下:

struct spi_transfer {
const void *tx_buf;
void *rx_buf;
unsigned len;
u32 speed_hz;
u16 delay_usecs;
u8 bits_per_word;
/* Used internally, by spi_sync() and the SPI core code */
u8 cs_change:1;
u8 do_read:1;
u8 tx_nbits:6; /* internal, for packing only */
u8 rx_nbits:6; /* internal, for packing only */
u16 rdy_for_tx:1;
u16 rdy_for_rx:1;
u16 cs_change_delay:14;
u16 large_buf:1;
u8 *tx_buf_wr;
u8 *rx_buf_wr;
void *private_data;
void (*complete)(void *private_data);
};

3、spi_sync()

spi_sync()函数用于同步传输数据,该函数会等待传输完成并返回传输结果。该函数的代码如下:

int spi_sync(struct spi_device *spi, struct spi_transfer *t)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
t->complete = spi_complete;
t->private_data = &done;
t->rdy_for_tx = t->rdy_for_rx = 0;
t->cs_change = spi->controller->cs_gpiod ? 1 : 0;
status = spi_async(spi, t);
if (status == 0) {
wait_for_completion(&done);
status = t->status;
if (status == -ETIMEDOUT)
status = -EIO;
}
return status;
}

4、spi_async()

spi_async()函数用于异步传输数据,该函数会启动SPI传输,并立即返回,不等待传输完成。该函数的代码如下:

int spi_async(struct spi_device *spi, struct spi_transfer *t)
{
struct spi_message msg;
int status;
memset(&msg, 0, sizeof(msg));
msg.spi = spi;
msg.complete = spi_complete;
msg.context = t;
msg.state = NULL;
msg.is_dma_mapped = false;
spi_prepare_message(&msg, t);
status = spi_async_locked(spi_get_parent_master(spi), &msg);
if (status == -EBUSY)
return -EAGAIN;
t->status = status;
if (msg.is_dma_mapped)
dma_unmap_sg(&spi->dev, msg.sgbuf, msg.nents, msg.direction);
if (msg.is_dma_mapped && msg.context && spi_need_dma_clean_up_on_error()) {
struct spi_controller *ctlr = spi->controller;
struct spi_transfer *xfer = msg.contexte
if (xfer->tx_buf && ctlr->dma_tx && ctlr->dma_tx->device->dev) {
dma_sync_sg_for_device(ctlr->dma_tx->device->dev,
msg.sgbuf,
msg.nents,
(ctlr->dma_tx_dir == DMA_MEM_TO_DEV) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE);
dma_unmap_sg(ctlr->dma_tx->device->dev,
msg.sgbuf,
msg.nents,
ctlr->dma_tx_dir);
}
if (xfer->rx_buf && ctlr->dma_rx && ctlr->dma_rx->device->dev) {
dma_sync_sg_for_device(ctlr->dma_rx->device->dev,
msg.sgbuf,
msg.nents,
(ctlr->dma_rx_dir == DMA_MEM_TO_DEV) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE);
dma_unmap_sg(ctlr->dma_rx->device->dev,
msg.sgbuf,
msg.nents,
ctlr->dma_rx_dir);
}
}
if (status == -EINPROGRESS || status == -EBUSY) {
status = 0;
} else if (unlikely(status)) {
dev_err(spi->dev.parent, "%s: spi_sync failed with status %d\n",
func, status);
}
return status;
}

本文分析了Linux内核中的SPI驱动源码,介绍了SPI驱动框架的基本结构、spi.c的结构和作用以及SPI驱动中的重要数据结构和函数。通读本文后,读者应该了解了SPI设备的工作原理和Linux内核中提供的SPI驱动框架的实现方式,理解了相关代码的运行过程和涉及的系统调用,有助于读者熟练掌握SPI驱动的编写技巧。

责任编辑:姜华 来源: 今日头条

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK