Linux SPI驱动框架(1)——核心层

概述

  linux SPI驱动框架主要分为核心层,控制器驱动层以及设备驱动层。具体结构可参考下图
SPI驱动框架图
  图中,最下层是硬件空间,SPI总线控制器,总线控制器负责硬件上的数据交互。内核空间中,需要有对应的控制器驱动,对硬件进行操作。
  核心层的作用是,向控制器驱动层提供注册SPI控制器驱动的接口,并提供一些需要控制器驱动实现的回调函数。
  核心层向上,对SPI设备驱动,提供标准的spi收发API,以及设备注册函数。
  所以当有SPI设备驱动发起一次传输时,设备驱动会调用SPI核心层的收发函数(spi_sync/spi_async),核心层的收发函数,会回调控制器驱动层实现的硬件相关的发送回调函数。从而实现SPI数据的收发。
  本文主要介绍SPI核心层相关API及数据结构。

SPI核心层

核心层目录说明

  SPI核心层代码位于(drivers/spi/spi.c),头文件位于(include/linux/spi/spi.h)
  spi.c一方面对SPI子系统进行初始化工作,注册spi bus,注册spi_master class,同事提供spi设备驱动对spi总线进行操作的API。
  spi.h包含了spi核心层的一些重要数据结构,struct spi_master; struct spi_transfer; struct spi_message,以及一些实现比较简单的函数等。

子系统初始化

struct bus_type spi_bus_type = {
	.name		= "spi",
	.dev_groups	= spi_dev_groups,
	.match		= spi_match_device,
	.uevent		= spi_uevent,
};

static struct class spi_master_class = {
	.name		= "spi_master",
	.owner		= THIS_MODULE,
	.dev_release	= spi_master_release,
	.dev_groups	= spi_master_groups,
};

  spi_bus_type为spi总线类型,通过bus_register()函数将SPI 总线注册进总线,成功注册后,在/sys/bus 下即可找到spi 文件目录。
  spi_master_class为spi控制器设备类,通过调用class_register()函数注册设备类,成功注册后,在/sys/class目录下即可找到spi_master文件目录。
  然后来看spi子系统初始化函数,仅仅是注册了spi bus,以及spi_master class。

spi_init函数

static int __init spi_init(void)
{
	int	status;

	buf = kmalloc(SPI_BUFSIZ, GFP_KERNEL);
	if (!buf) {
		status = -ENOMEM;
		goto err0;
	}
	status = bus_register(&spi_bus_type);			/*注册spi bus*/
	if (status < 0)
		goto err1;
	status = class_register(&spi_master_class);		/* 注册spi_master类 */
	if (status < 0)
		goto err2;
	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
		WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
	if (IS_ENABLED(CONFIG_ACPI))
		WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));
	return 0;
err2:
	bus_unregister(&spi_bus_type);
err1:
	kfree(buf);
	buf = NULL;
err0:
	return status;
}

  再来看一下spi从设备和从设备驱动匹配函数,也即是spi_bus_type.match函数。

spi_match_device函数

static int spi_match_device(struct device *dev, struct device_driver *drv)
{
	const struct spi_device	*spi = to_spi_device(dev);
	const struct spi_driver	*sdrv = to_spi_driver(drv);

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))1return 1;

	/* Then try ACPI */
	if (acpi_driver_match_device(dev, drv))2return 1;

	if (sdrv->id_table)3return !!spi_match_id(sdrv->id_table, spi);

	return strcmp(spi->modalias, drv->name) == 0;4}

说明:
(1)比较驱动中的of_match_table的compatible和device的of_node的compatible,判断两者是否相等
(2)比较驱动中的acpi_match_table的compatible和device的of_node的compatible,判断两者是否相等
(3)判断驱动中是否支持id数组,如果支持,查找匹配此id的spi_device。
(4)比较设备的名字的和驱动的名字是否相同。

核心数据结构

struct spi_master

struct spi_master {
	struct device	dev;													/* SPI设备的device数据结构 */
	s16			bus_num;													/* SPI总线序号 */
	u16			num_chipselect;												/* 片选信号数量 */
	u16			dma_alignment;												/* SPI控制器DMA缓冲区对齐定义 */
	u16			mode_bits;													/* 工作模式位,由驱动定义 */
	u32			min_speed_hz;												/* 最小速度 */
	u32			max_speed_hz;												/* 最小速度 */
	u16			flags;														/* 限制条件标志 */
	int			(*setup)(struct spi_device *spi);							/* 设置SPI设备的工作参数 */
	int			(*transfer)(struct spi_device *spi,							/* SPI发送函数1 */
						struct spi_message *mesg);
	void		(*cleanup)(struct spi_device *spi);							/* SPI清除函数,当spi_master被释放时调用 */
	int (*transfer_one_message)(struct spi_master *master,					/* SPI发送函数2 */
	int (*transfer_one)(struct spi_master *master, struct spi_device *spi,	/* SPI发送函数3 */
			    struct spi_transfer *transfer);
	...
};

  上述为struct spi_master数据结构,结构体本身比较大,挑选了一些个人认为比较核心的成员。
  该结构体为spi控制器驱动核心数据结构。控制器驱动需要配置其中一些参数,填充一些回调函数。
  需要注意的是,上述代码段中,有三个transfer函数。这里先简单的说明一下,在spi_master注册时,会首先判断transfer函数是否实现,如果没实现,核心层会自动填充一个默认的transfer函数。当填充了默认的transfer函数后,会判断控制器驱动是否实现transfer_one_message,如果没实现,则核心层会填充一个默认的transfer_one_message函数,最后控制器驱动只需要实现transfer_one回调函数,就可以让驱动层实现spi数据收发了。
  当然,有些厂商会自己实现transfer或transfer_one_message函数,替代内核默认的逻辑。不过总结来说,三个transfer函数只需要实现其中一个就可以了。

struct spi_driver

struct spi_driver {
	const struct spi_device_id *id_table;
	int			(*probe)(struct spi_device *spi);
	int			(*remove)(struct spi_device *spi);
	void			(*shutdown)(struct spi_device *spi);
	struct device_driver	driver;
};

  spi_driver比较简单,根据上文介绍的spi_match_device逻辑,只需要实现of_match_table或acpi_match_table或id_table,或者最简单的driver->name,能让spi_match_device返回match成功即可。match成功会调用probe函数。remove函数在驱动卸载时调用。
  spi_device数据结构就不介绍了,一般用设备树,会自动生成。

struct spi_transfer & struct spi_message

struct spi_transfer {
	const void	*tx_buf;
	void		*rx_buf;
	unsigned	len;
	...
	u8		bits_per_word;
	u16		delay_usecs;
	u32		speed_hz;
	...
	struct list_head transfer_list;
};

struct spi_message {
	struct list_head	transfers;
	...
	struct spi_device	*spi;
	...
	void			(*complete)(void *context);
	void			*context;
	...
	struct list_head	queue;
};

  一个spi_message是一次数据交换的原子请求,而spi_message由多个spi_transfer结构组成,这些spi_transfer通过一个链表组织在一起。

  spi_message通过spi_sync函数或spi_async函数发送。

核心API

spi_register_master函数

int spi_register_master(struct spi_master *master)
{
	...
	status = of_spi_register_master(master);							(1)
	if (status)
		return status;
	...
	dev_set_name(&master->dev, "spi%u", master->bus_num);
	status = device_add(&master->dev);									(2)
	if (status < 0)
		goto done;
	dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
			dynamic ? " (dynamic)" : "");

	if (master->transfer)
		dev_info(dev, "master is unqueued, this is deprecated\n");
	else {
		status = spi_master_initialize_queue(master);					(3)
		if (status) {
			device_del(&master->dev);
			goto done;
		}
	}
	...
	mutex_lock(&board_lock);
	list_add_tail(&master->list, &spi_master_list);
	list_for_each_entry(bi, &board_list, list)
		spi_match_master_to_boardinfo(master, &bi->board_info);			(4)
	mutex_unlock(&board_lock);
	...
	of_register_spi_devices(master);									(5)
	...
done:
	return status;
}

  spi_register_master是控制器驱动中最核心的api,控制器驱动通过该api注册到系统中。
说明:
(1)of_spi_register_master函数内部,根据设备树节点中的"cs-gpios",向struct spi_master添加gpio cs引脚。
(2)将device注册到设备模型中。
(3)如果控制器驱动没有自己实现transfer函数,则初始化发送队列。(核心层填充默认transfer函数)
(4)老的方式,遍历所有spi_board_info数据结构,并注册spi_device
(5)新的设备树方式,遍历spi控制器节点下所有子节点,并注册成对应的spi_device设备。
  再来仔细看下spi_master_initialize_queue函数

static int spi_master_initialize_queue(struct spi_master *master)
{
	......
	master->queued = true;
	master->transfer = spi_queued_transfer;
	if (!master->transfer_one_message)
		master->transfer_one_message = spi_transfer_one_message;
	/* Initialize and start queue */
	ret = spi_init_queue(master);
	......
	ret = spi_start_queue(master);
	......
}

  该函数把master->transfer回调字段设置为默认的实现函数:spi_queued_transfer,如果控制器驱动没有实现transfer_one_message回调,用默认的spi_transfer_one_message函数进行赋值。然后分别调用spi_init_queue和spi_start_queue函数初始化队列并启动工作线程。spi_init_queue函数最主要的作用就是建立一个内核工作线程。
  然后来一起看下spi_init_queue和spi_start_queue函数

static int spi_init_queue(struct spi_master *master)
{
	......
	kthread_init_worker(&master->kworker);
	master->kworker_task = kthread_run(kthread_worker_fn,		&master->kworker, "%s", dev_name(&master->dev));
	......
	kthread_init_work(&master->pump_messages, spi_pump_messages);
	......
	return 0;
}

static int spi_start_queue(struct spi_master *master)
{
	......
	master->running = true;
	master->cur_msg = NULL;
	......
	kthread_queue_work(&master->kworker, &master->pump_messages);
	......
	return 0;
}

  spi_init_queue函数先初始化kthread_worker,为kthread_worker创建一个内核线程来处理work,随后初始化kthread_work,设置work执行函数,work执行函数为spi_pump_messages
  spi_start_queue就相对简单了,只是唤醒该工作线程而已;自此,队列化的相关工作已经完成,系统等待message请求被发起,然后在工作线程中处理message的传送工作。

spi_message_init函数

static inline void spi_message_init_no_memset(struct spi_message *m)
{
	INIT_LIST_HEAD(&m->transfers);
	INIT_LIST_HEAD(&m->resources);
}
static inline void spi_message_init(struct spi_message *m)
{
	memset(m, 0, sizeof *m);
	spi_message_init_no_memset(m);
}

  spi_message_init函数用于对spi_massage进行初始化,并初始化链表头。

spi_message_add_tail函数

static inline void
spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
{
	list_add_tail(&t->transfer_list, &m->transfers);
}

  spi_message_add_tail函数用于把spi_transfer添加到spi_message传输链表中。
一般spi设备驱动的发送函数,会定义spi_message和若干个spi_transfer,设置spi_transfer的txbuff/rxbuff,len等,调用spi_message_init函数初始化spi_message,调用spi_message_add_tail把spi_transfer添加到spi_message链表中。最后调用spi_sync或spi_async发送。

spi_async

  spi_async是异步执行的,不会等待传输是否完成,就直接返回。调用关系为:

spi_async->
		__spi_async->
				master->transfer

  在上文中的spi_register_master函数中,我们已经介绍过,当控制器驱动没有实现transfer函数时,内核会自己填充默认的transfer函数(spi_queued_transfer)。这里我们假设控制器驱动既没有实现transfer,也没有实现transfer_one_message,只实现了transfer_one函数。来看一下内核默认transfer函数的逻辑。

spi_queued_transfer(struct spi_device *spi, struct spi_message *msg)->
		__spi_queued_transfer(spi, msg, true)

static int __spi_queued_transfer(struct spi_device *spi,
				 struct spi_message *msg,
				 bool need_pump)
{
	struct spi_master *master = spi->master;
	unsigned long flags;

	spin_lock_irqsave(&master->queue_lock, flags);
	...
	list_add_tail(&msg->queue, &master->queue);
	if (!master->busy && need_pump)
		kthread_queue_work(&master->kworker, &master->pump_messages);
	spin_unlock_irqrestore(&master->queue_lock, flags);
	return 0;
}

  这里我们可以看到,__spi_queued_transfer(spi, msg, true)函数中,注意这里第三个参数是true,把message添加到master的发送链表中。随后仅仅是唤醒工作线程,完成该步后,spi_async即返回,不等待发送完成。

spi_sync

  与spi_async不同,spi_sync是同步执行的,会等待传输完成,随后返回。调用关系为:

spi_sync->
		__spi_sync

  然后看一下__spi_sync函数代码,这里我们也假设控制器驱动既没有实现transfer,也没有实现transfer_one_message,只实现了transfer_one函数:

static void spi_complete(void *arg)
{
	complete(arg);
}

static int __spi_sync(struct spi_device *spi, struct spi_message *message)
{
	DECLARE_COMPLETION_ONSTACK(done);
	int status;
	struct spi_master *master = spi->master;
	unsigned long flags;
	...
	message->complete = spi_complete;									(1)
	message->context = &done;
	message->spi = spi;

	SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync);
	SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync);

	/* If we're not using the legacy transfer method then we will
	 * try to transfer in the calling context so special case.
	 * This code would be less tricky if we could remove the
	 * support for driver implemented message queues.
	 */
	if (master->transfer == spi_queued_transfer) {
		spin_lock_irqsave(&master->bus_lock_spinlock, flags);

		trace_spi_message_submit(message);

		status = __spi_queued_transfer(spi, message, false);			(2)

		spin_unlock_irqrestore(&master->bus_lock_spinlock, flags);
	} else {
		status = spi_async_locked(spi, message);
	}

	if (status == 0) {
		/* Push out the messages in the calling context if we
		 * can.
		 */
		if (master->transfer == spi_queued_transfer) {
			SPI_STATISTICS_INCREMENT_FIELD(&master->statistics,
						       spi_sync_immediate);
			SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
						       spi_sync_immediate);
			__spi_pump_messages(master, false);							(3)
		}

		wait_for_completion(&done);										(4)
		status = message->status;
	}
	message->context = NULL;
	return status;
}

说明:
(1)设置spi_message完成后回调函数,回调函数中仅仅调用complete函数,唤醒等待唤醒的线程。意思就是,当发送spi_message的工作线程完成发送后,唤醒在等待的spi发送完成的spi_sync函数所在的线程。
(2)由于使用内核默认的spi_queued_transfer,所以会调用__spi_queued_transfer(spi, message, false);这个函数上文介绍spi_async时已经介绍过了,注意这里第三个传参是false。所以所做的操作仅仅是把spi_message添加到master的发送链表中。随即返回
(3)__spi_pump_messages(master, false),false意思是标记不在工作线程中执行该函数。调用该函数,把spi_message发出去,该函数中会判断当前状态,如果可以直接发,则直接在当前线程中发送,如果不能直接发,则唤醒工作线程,稍后发送。
(4)睡眠等待发送完成函数释放的完成信号量,当接收到信号量时,证明发送完成唤醒并结束spi_sync函数。

  工作线程的工作函数是spi_pump_messages来看一下该函数实现:

static void spi_pump_messages(struct kthread_work *work)
{
	struct spi_master *master =
		container_of(work, struct spi_master, pump_messages);

	__spi_pump_messages(master, true);
}

  上文中不管是spi_sync,还是spi_async中,不管是直接调用该函数中的__spi_pump_messages(sync)还是通过工作线程,作为线程服务函数调用,最终发送数据都是通过__spi_pump_messages实现的。
  与__spi_sync中不同的是工作函数中调用的__spi_pump_messages(master, true);第二个参数为true,标记为是否在工作线程中。
  最后来看一下__spi_pump_messages函数的实现:

static void __spi_pump_messages(struct spi_master *master, bool in_kthread)
{
	unsigned long flags;
	bool was_busy = false;
	int ret;

	/* Lock queue */
	spin_lock_irqsave(&master->queue_lock, flags);

	/* Make sure we are not already running a message */
	if (master->cur_msg) {
		spin_unlock_irqrestore(&master->queue_lock, flags);
		return;
	}

	/* If another context is idling the device then defer */
	if (master->idling) {
		kthread_queue_work(&master->kworker, &master->pump_messages);
		spin_unlock_irqrestore(&master->queue_lock, flags);
		return;
	}

	/* Check if the queue is idle */
	if (list_empty(&master->queue) || !master->running) {
		if (!master->busy) {
			spin_unlock_irqrestore(&master->queue_lock, flags);
			return;
		}

		/* Only do teardown in the thread */
		if (!in_kthread) {
			kthread_queue_work(&master->kworker,
					   &master->pump_messages);
			spin_unlock_irqrestore(&master->queue_lock, flags);
			return;
		}

		master->busy = false;
		master->idling = true;
		spin_unlock_irqrestore(&master->queue_lock, flags);

		kfree(master->dummy_rx);
		master->dummy_rx = NULL;
		kfree(master->dummy_tx);
		master->dummy_tx = NULL;
		if (master->unprepare_transfer_hardware &&
		    master->unprepare_transfer_hardware(master))
			dev_err(&master->dev,
				"failed to unprepare transfer hardware\n");
		if (master->auto_runtime_pm) {
			pm_runtime_mark_last_busy(master->dev.parent);
			pm_runtime_put_autosuspend(master->dev.parent);
		}
		trace_spi_master_idle(master);

		spin_lock_irqsave(&master->queue_lock, flags);
		master->idling = false;
		spin_unlock_irqrestore(&master->queue_lock, flags);
		return;
	}

	/* Extract head of queue */
	master->cur_msg =
		list_first_entry(&master->queue, struct spi_message, queue);

	list_del_init(&master->cur_msg->queue);
	if (master->busy)
		was_busy = true;
	else
		master->busy = true;
	spin_unlock_irqrestore(&master->queue_lock, flags);

	mutex_lock(&master->io_mutex);

	if (!was_busy && master->auto_runtime_pm) {
		ret = pm_runtime_get_sync(master->dev.parent);
		if (ret < 0) {
			dev_err(&master->dev, "Failed to power device: %d\n",
				ret);
			mutex_unlock(&master->io_mutex);
			return;
		}
	}

	if (!was_busy)
		trace_spi_master_busy(master);

	if (!was_busy && master->prepare_transfer_hardware) {
		ret = master->prepare_transfer_hardware(master);
		if (ret) {
			dev_err(&master->dev,
				"failed to prepare transfer hardware\n");

			if (master->auto_runtime_pm)
				pm_runtime_put(master->dev.parent);
			mutex_unlock(&master->io_mutex);
			return;
		}
	}

	trace_spi_message_start(master->cur_msg);

	if (master->prepare_message) {
		ret = master->prepare_message(master, master->cur_msg);
		if (ret) {
			dev_err(&master->dev,
				"failed to prepare message: %d\n", ret);
			master->cur_msg->status = ret;
			spi_finalize_current_message(master);
			goto out;
		}
		master->cur_msg_prepared = true;
	}

	ret = spi_map_msg(master, master->cur_msg);
	if (ret) {
		master->cur_msg->status = ret;
		spi_finalize_current_message(master);
		goto out;
	}

	ret = master->transfer_one_message(master, master->cur_msg);
	if (ret) {
		dev_err(&master->dev,
			"failed to transfer one message from queue\n");
		goto out;
	}

out:
	mutex_unlock(&master->io_mutex);

	/* Prod the scheduler in case transfer_one() was busy waiting */
	if (!ret)
		cond_resched();
}

  函数源码比较多,总结一下大概可以概括为,作为工作线程服务函数时,会调用master->transfer_one_message函数。当__spi_sync中调用该函数时,如果资源没有冲突,可以直接在当前线程中调用transfer_one_message则调用,如果不能,则添加到工作线程队列中,通过工作线程再次调用__spi_pump_messages,把数据发送出去。
  在上文介绍spi_register_master->spi_master_initialize_queue中,我们已经看到,内核默认填充的master->transfer_one_message函数为spi_transfer_one_message,下面来看一下源码:

static int spi_transfer_one_message(struct spi_master *master,
				    struct spi_message *msg)
{
	struct spi_transfer *xfer;
	...
	spi_set_cs(msg->spi, true);									/* cs引脚拉高 */

	SPI_STATISTICS_INCREMENT_FIELD(statm, messages);
	SPI_STATISTICS_INCREMENT_FIELD(stats, messages);

	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
		trace_spi_transfer_start(msg, xfer);

		spi_statistics_add_transfer_stats(statm, xfer, master);
		spi_statistics_add_transfer_stats(stats, xfer, master);

		if (xfer->tx_buf || xfer->rx_buf) {
			reinit_completion(&master->xfer_completion);

			ret = master->transfer_one(master, msg->spi, xfer);		(1)
			if (ret < 0) {
				SPI_STATISTICS_INCREMENT_FIELD(statm,
							       errors);
				SPI_STATISTICS_INCREMENT_FIELD(stats,
							       errors);
				dev_err(&msg->spi->dev,
					"SPI transfer failed: %d\n", ret);
				goto out;
			}
			...
		}
out:
	if (ret != 0 || !keep_cs)
		spi_set_cs(msg->spi, false);						/* 拉低cs */

	spi_finalize_current_message(master);							(2)

	return ret;
}

void spi_finalize_current_message(struct spi_master *master)
{
	...
	kthread_queue_work(&master->kworker, &master->pump_messages);
	...

	mesg->state = NULL;
	if (mesg->complete)
		mesg->complete(mesg->context);
}

  提炼了一下代码,(1)可以看到函数中先是遍历了message中的transfer链表,然后一个个调用控制器驱动的transfer_one函数。随后(2)调用spi_finalize_current_message函数,函数代码也在代码段中贴出来了。重要的两个操作一个是,唤醒下一次工作线程,然后判断message的回调函数是否填充,如果存在,则回调。对于spi_sync来说,message的回调函数是complete(&done),spi_sync阻塞的wait_for_completion(&done)接收到信号量会被唤醒,意味着数据以及收发完成,结束本次spi_sync。

spi_sync&spi_async总结

  spi_sync和spi_async是spi设备驱动通过spi总线发送数据的重要函数。经过上文的分析。简单的总结下。
  spi_sync函数是同步的,把spi_message发送出去,会等待发送完成,发送完成才会返回,正常情况下调用的都是spi_sync函数。
  spi_async函数本身的异步的,把spi_message推到工作线程中,就不管了,所以试想一下,当发送数据时,可以不管是否发送完成。但是读取数据时,如果一次发送(数据交换)没有完成就返回了,那么rx_buff中是否有数据时不能保障的。那么,spi_async是不是就不能用来读取数据了呢?
  答案是否定的,可以仿照spi_sync,在spi_async调用之前,手动设置spi_message的逻辑,不过有点麻烦就是了,还不如直接调用spi_sync。。

static void spi_complete(void *arg)
{
	complete(arg);
}

{
...
message->complete = spi_complete;
message->context = &done;
...
spi_async(spi, message);
...
wait_for_completion(&done);
...
}

SPI驱动框架链接:
Linux SPI驱动框架(2)——控制器驱动层
Linux SPI驱动框架(3)——设备驱动层

发布了9 篇原创文章 · 获赞 0 · 访问量 394

猜你喜欢

转载自blog.csdn.net/weixin_42262944/article/details/102944484