152 SPI主控制器驱动和核心函数

一、spi_controller 结构体框图

在这里插入图片描述
在这里插入图片描述

二、spi_imx_probe()函数

此函数是spi主机控制器的平台设备驱动的probe成员

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);
  • 主要内容
    获取设备树节点信息,初始化spi时钟、dma…
    保存spi寄存器起始地址,填充spi控制器回调函数

drivers/spi/spi-imx.c

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_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);

三、核心函数

1、spi_setup()函数

设置spi设备的片选信号、传输单位、最大传输速率…

drivers/spi/spi.c

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;
}

2、spi_imx_setup()函数

drivers/spi/spi-imx.c

static int spi_imx_setup(struct spi_device *spi)
{
	dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__,
		 spi->mode, spi->bits_per_word, spi->max_speed_hz);
	// 若当前spi设备不支持片选信号模式,直接返回
	if (spi->mode & SPI_NO_CS)
		return 0;
	// 若gpio有效,设置为输出模式
	if (gpio_is_valid(spi->cs_gpio))
		gpio_direction_output(spi->cs_gpio,
				      spi->mode & SPI_CS_HIGH ? 0 : 1);
	// 详见下
	spi_imx_chipselect(spi, BITBANG_CS_INACTIVE);

	return 0;
}
spi_imx_chipselect()函数

drivers/spi/spi-imx.c

static void spi_imx_chipselect(struct spi_device *spi, int is_active)
{
	int active = is_active != BITBANG_CS_INACTIVE;
	// 判断spi的片选信号是否低有效
	int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH);
	
	if (spi->mode & SPI_NO_CS)
		return;

	if (!gpio_is_valid(spi->cs_gpio))
		return;
	// ^是异或运算符,相同为0相异为1,低电平有效则设置为低电平
	gpio_set_value(spi->cs_gpio, dev_is_lowactive ^ active);
}

3、spi_message_init()函数

include/linux/spi/spi.h
初始化一个spi信息

static inline void spi_message_init(struct spi_message *m)
{
	memset(m, 0, sizeof *m);
	// 初始化m中的两个链表节点成员
	spi_message_init_no_memset(m);
}

spi_message结构体

include/linux/spi/spi.h

struct spi_message {
	// 
	struct list_head	transfers;
	// 表示此spi消息属于哪一个spi设备
	struct spi_device	*spi;
	...
	// 当此spi消息传输完成回调使用的
	void			(*complete)(void *context);
	void			*context;
	unsigned		frame_length;
	unsigned		actual_length;
	int			status;
	...
	// 配合内核线程进行数据的异步传输
	struct list_head	queue;
	void			*state;

	/* list of spi_res reources when the spi message is processed */
	// 记录spi消息相关的资源
	struct list_head        resources;
};
spi_message_init_no_memset()函数

初始化两个链表节点

static inline void spi_message_init_no_memset(struct spi_message *m)
{
	INIT_LIST_HEAD(&m->transfers);
	INIT_LIST_HEAD(&m->resources);
}

4、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);
}
spi_transfer结构体

include/linux/spi/spi.h
此结构体是spi传输数据的最基本的单位,多个spi_transfer结构体可以组成一个spi_message结构体

struct spi_transfer {
	// 指向想要发送的spi消息的buf
	const void	*tx_buf;
	// 指向用来接收spi消息的buf
	void		*rx_buf;
	unsigned	len;
	...
	u32		speed_hz;
	struct list_head transfer_list;
};

5、spi_sync()函数

drivers/spi/spi.c

同步传输数据,阻塞当前线程
详见下一讲

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

6、spi_async()函数

drivers/spi/spi.c

异步传输数据,不会阻塞当前线程
详见下一讲

int spi_async(struct spi_device *spi, struct spi_message *message)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值