20.SPI深入剖析

目录

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_controller结构体框图

20.SPI深入剖析_第1张图片

核心函数

spi_imx_probe()函数

此函数是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控制器回调函数

spi_bitbang_start()函数

该函数存放在内核/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);

spi_setup()函数

该函数存放在内核/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;
}

20.SPI深入剖析_第2张图片

spi_message_init()函数

该函数存放在内核/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中的两个链表节点成员
}

20.SPI深入剖析_第3张图片

spi_message_add_tail()函数

该函数存放在内核/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结构体

spi_sync()函数

该函数存放在内核/drivers/spi/spi.c文件。同步传输数据,阻塞当前线程。

int spi_sync(struct spi_device *spi, struct spi_message *message);

spi_async()函数

该函数存放在内核/drivers/spi/spi.c文件。异步传输数据,不会阻塞当前线程。

int spi_async(struct spi_device *spi, struct spi_message *message);

SPI数据传输剖析:同步、异步

spi_register_master()宏

该宏存放在内核/include/linux/spi/spi.h文件。

调用流程:

        spi_imx_probe → spi_bitbang_start → spi_register_master

#define spi_register_master(_ctlr)	spi_register_controller(_ctlr)

20.SPI深入剖析_第4张图片

spi_sync()函数

该函数存放在内核/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;
}

20.SPI深入剖析_第5张图片

spi_async()函数

该函数存放在内核/drivers/spi/spi.c文件。异步传输数据。

int spi_async(struct spi_device *spi, struct spi_message *message)
{
	...
	ret = __spi_async(spi, message);
	...
}

20.SPI深入剖析_第6张图片

你可能感兴趣的:(#,野火i.mx,6ull内核驱动进阶,linux,嵌入式硬件)