目录
spi_controller结构体框图
核心函数
spi_imx_probe()函数
spi_bitbang_start()函数
spi_setup()函数
spi_message_init()函数
spi_message_add_tail()函数
spi_sync()函数
spi_async()函数
SPI数据传输剖析:同步、异步
spi_register_master()宏
spi_sync()函数
spi_async()函数
此函数是spi主机控制器的平台设备驱动的probe成员。该函数存放在内核/drivers/spi/spi-imx.c文件。
static struct platform_driver spi_imx_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = spi_imx_dt_ids,
.pm = IMX_SPI_PM,
},
.id_table = spi_imx_devtype,
.probe = spi_imx_probe,
.remove = spi_imx_remove,
};
module_platform_driver(spi_imx_driver);
static int spi_imx_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id = of_match_device(spi_imx_dt_ids, &pdev->dev);
struct spi_imx_master *mxc_platform_info = dev_get_platdata(&pdev->dev);
struct spi_master *master;
struct spi_imx_data *spi_imx;
struct resource *res;
const struct spi_imx_devtype_data *devtype_data = of_id ? of_id->data : (struct spi_imx_devtype_data *)pdev->id_entry->driver_data;
bool slave_mode;
...
// 判断当前spi主控制器节点工作在主模式还是从模式
// 若是设置了这个属性,那么bool值返回1,为从模式
// 然后根据主从模式进行不同的分配内存操作
slave_mode = devtype_data->has_slavemode && of_property_read_bool(np, "spi-slave");
if (slave_mode)
master = spi_alloc_slave(&pdev->dev, sizeof(struct spi_imx_data));
else
master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data));
if (!master)
return -ENOMEM;
...
// 读取此属性,获取片选信号的数量,保存在num_cs
ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
if (ret < 0) {
if (mxc_platform_info) {
num_cs = mxc_platform_info->num_chipselect;
master->num_chipselect = num_cs;
}
} else {
// 读取成功则赋值
master->num_chipselect = num_cs;
}
// 获取 spi_controller->device->spi_imx_data
spi_imx = spi_master_get_devdata(master);
// bitbang结构体的存在是为了让我们能够用普通gpio来模拟spi的时序
// 简单了解一下
spi_imx->bitbang.master = master;
spi_imx->dev = &pdev->dev;
spi_imx->slave_mode = slave_mode;
spi_imx->devtype_data = devtype_data;
// 记录每个片选信号所使用的gpio引脚
master->cs_gpios = devm_kzalloc(&master->dev, sizeof(int) * master->num_chipselect, GFP_KERNEL);
spi_imx->bitbang.chipselect = spi_imx_chipselect;
spi_imx->bitbang.setup_transfer = spi_imx_setupxfer;
spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
spi_imx->bitbang.master->setup = spi_imx_setup;
spi_imx->bitbang.master->cleanup = spi_imx_cleanup;
spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message;
spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;
spi_imx->bitbang.master->slave_abort = spi_imx_slave_abort;
spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \
...
init_completion(&spi_imx->xfer_done);
// 获取spi控制器对应的寄存器组的基地址
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
spi_imx->base = devm_ioremap_resource(&pdev->dev, res);
...
// 详见下
ret = spi_bitbang_start(&spi_imx->bitbang);
...
}
主要内容
获取设备树节点信息,初始化spi时钟、dma等
保存spi寄存器起始地址,填充spi控制器回调函数
该函数存放在内核/drivers/spi/spi-bitbang.c文件。
int spi_bitbang_start(struct spi_bitbang *bitbang)
{
struct spi_master *master = bitbang->master;
int ret;
if (!master || !bitbang->chipselect)
return -EINVAL;
mutex_init(&bitbang->lock);
if (!master->mode_bits)
master->mode_bits = SPI_CPOL | SPI_CPHA | bitbang->flags;
if (master->transfer || master->transfer_one_message)
return -EINVAL;
// 填充的函数都和bitbang结构体相关,都是用来模拟spi时序
master->prepare_transfer_hardware = spi_bitbang_prepare_hardware;
master->unprepare_transfer_hardware = spi_bitbang_unprepare_hardware;
master->transfer_one = spi_bitbang_transfer_one;
master->set_cs = spi_bitbang_set_cs;
if (!bitbang->txrx_bufs) {
bitbang->use_dma = 0;
bitbang->txrx_bufs = spi_bitbang_bufs;
if (!master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer = spi_bitbang_setup_transfer;
master->setup = spi_bitbang_setup;
master->cleanup = spi_bitbang_cleanup;
}
}
/* driver may get busy before register() returns, especially
* if someone registered boardinfo for devices
*/
// 将spi主控制器注册到linux系统
ret = spi_register_master(spi_master_get(master));
if (ret)
spi_master_put(master);
return ret;
}
EXPORT_SYMBOL_GPL(spi_bitbang_start);
该函数存放在内核/drivers/spi/spi.c文件。设置spi设备的片选信号、传输单位、最大传输速率等。
int spi_setup(struct spi_device *spi)
{
unsigned bad_bits, ugly_bits;
int status;
...
// 对spi的传输单位进行设置,只能是8位或者16位
status = __spi_validate_bits_per_word(spi->controller, spi->bits_per_word);
...
// 用spi控制器的最大传输速率来限制spi设备的最大传输速率
spi->max_speed_hz = spi->controller->max_speed_hz;
...
if (spi->controller->setup)//详见下
status = spi->controller->setup(spi);
...
return status;
}
该函数存放在内核/include/linux/spi/spi.h文件。初始化一个spi信息。
static inline void spi_message_init(struct spi_message *m)
{
memset(m, 0, sizeof *m);
spi_message_init_no_memset(m); // 初始化m中的两个链表节点成员
}
该函数存放在内核/include/linux/spi/spi.h文件。把一个一个spi具体的消息存放在spi_message来保存。
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
// 把参数1链表节点加入到参数2的末尾
list_add_tail(&t->transfer_list, &m->transfers);
}
struct spi_transfer {
const void *tx_buf; // 指向想要发送的spi消息的buf
void *rx_buf; // 指向用来接收spi消息的buf
unsigned len;
...
u32 speed_hz;
struct list_head transfer_list;
}; //此结构体是spi传输数据的最基本的单位,多个spi_transfer结构体可以组成一个spi_message结构体
该函数存放在内核/drivers/spi/spi.c文件。同步传输数据,阻塞当前线程。
int spi_sync(struct spi_device *spi, struct spi_message *message);
该函数存放在内核/drivers/spi/spi.c文件。异步传输数据,不会阻塞当前线程。
int spi_async(struct spi_device *spi, struct spi_message *message);
该宏存放在内核/include/linux/spi/spi.h文件。
调用流程:
spi_imx_probe → spi_bitbang_start → spi_register_master
#define spi_register_master(_ctlr) spi_register_controller(_ctlr)
该函数存放在内核/drivers/spi/spi.c文件。同步传输数据。新创建一个内核线程去传输数据,在传输时会阻塞当前线程,数据在传输完成时唤醒当前线程。
int spi_sync(struct spi_device *spi, struct spi_message *message)
{
int ret;
// 互斥锁相关
mutex_lock(&spi->controller->bus_lock_mutex);
ret = __spi_sync(spi, message);
mutex_unlock(&spi->controller->bus_lock_mutex);
return ret;
}
该函数存放在内核/drivers/spi/spi.c文件。异步传输数据。
int spi_async(struct spi_device *spi, struct spi_message *message)
{
...
ret = __spi_async(spi, message);
...
}