SPI_Master驱动程序框架

518 阅读7分钟

本文已参与【新人创作礼】活动,一起开启掘金创作之路。 参考内核源码:drivers\spi\spi.c

一、SPI传输概述

1.1 数据组织方式 使用SPI传输时,最小的传输单位是spi_transfer 对于一个设备,可以发起多个spi_transfer 这些spi_transfer,会放入一个spi_message

  • spi_transfer : 指定tx_buf、rx_buf、len
  • 同一个SPI设备的spi_transfer,使用spi_message管理
  • 同一个SPI Master下的spi_message,如在一个队列里:

反过来传输也是类似的:

  • 从spi_master的队列里取出每一个spi_message
  • 从spi_message的队列里取出一个spi_transfer
  • 处理spi_transfer

1.2 SPI控制器数据结构

参考文件:include\linux\spi\spi.h Linux中使用spi_master结构体描述SPI控制器,有两套传输方法:

struct spi_controller {
	struct device	dev;

	struct list_head list;

	/* other than negative (== assign one dynamically), bus_num is fully
	 * board-specific.  usually that simplifies to being SOC-specific.
	 * example:  one SOC has three SPI controllers, numbered 0..2,
	 * and one board's schematics might show it using SPI-2.  software
	 * would normally use bus_num=2 for that controller.
	 */
	s16			bus_num;

	/* chipselects will be integral to many controllers; some others
	 * might use board-specific GPIOs.
	 */
	u16			num_chipselect;

	/* some SPI controllers pose alignment requirements on DMAable
	 * buffers; let protocol drivers know about these requirements.
	 */
	u16			dma_alignment;

	/* spi_device.mode flags understood by this controller driver */
	u16			mode_bits;

	/* bitmask of supported bits_per_word for transfers */
	u32			bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BIT_MASK(bits) (((bits) == 32) ? ~0U : (BIT(bits) - 1))
#define SPI_BPW_RANGE_MASK(min, max) (SPI_BIT_MASK(max) - SPI_BIT_MASK(min - 1))

	/* limits on transfer speed */
	u32			min_speed_hz;
	u32			max_speed_hz;

	/* other constraints relevant to this driver */
	u16			flags;
#define SPI_CONTROLLER_HALF_DUPLEX	BIT(0)	/* can't do full duplex */
#define SPI_CONTROLLER_NO_RX		BIT(1)	/* can't do buffer read */
#define SPI_CONTROLLER_NO_TX		BIT(2)	/* can't do buffer write */
#define SPI_CONTROLLER_MUST_RX		BIT(3)	/* requires rx */
#define SPI_CONTROLLER_MUST_TX		BIT(4)	/* requires tx */

#define SPI_MASTER_GPIO_SS		BIT(5)	/* GPIO CS must select slave */

	/* flag indicating this is an SPI slave controller */
	bool			slave;

	/*
	 * on some hardware transfer / message size may be constrained
	 * the limit may depend on device transfer settings
	 */
	size_t (*max_transfer_size)(struct spi_device *spi);
	size_t (*max_message_size)(struct spi_device *spi);

	/* I/O mutex */
	struct mutex		io_mutex;

	/* lock and mutex for SPI bus locking */
	spinlock_t		bus_lock_spinlock;
	struct mutex		bus_lock_mutex;

	/* flag indicating that the SPI bus is locked for exclusive use */
	bool			bus_lock_flag;

	/* Setup mode and clock, etc (spi driver may call many times).
	 *
	 * IMPORTANT:  this may be called when transfers to another
	 * device are active.  DO NOT UPDATE SHARED REGISTERS in ways
	 * which could break those transfers.
	 */
	int			(*setup)(struct spi_device *spi);

	/* bidirectional bulk transfers
	 *
	 * + The transfer() method may not sleep; its main role is
	 *   just to add the message to the queue.
	 * + For now there's no remove-from-queue operation, or
	 *   any other request management
	 * + To a given spi_device, message queueing is pure fifo
	 *
	 * + The controller's main job is to process its message queue,
	 *   selecting a chip (for masters), then transferring data
	 * + If there are multiple spi_device children, the i/o queue
	 *   arbitration algorithm is unspecified (round robin, fifo,
	 *   priority, reservations, preemption, etc)
	 *
	 * + Chipselect stays active during the entire message
	 *   (unless modified by spi_transfer.cs_change != 0).
	 * + The message transfers use clock and SPI mode parameters
	 *   previously established by setup() for this device
	 */
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);			//这里是老方法

	/* called on release() to free memory provided by spi_controller */
	void			(*cleanup)(struct spi_device *spi);

	/*
	 * Used to enable core support for DMA handling, if can_dma()
	 * exists and returns true then the transfer will be mapped
	 * prior to transfer_one() being called.  The driver should
	 * not modify or store xfer and dma_tx and dma_rx must be set
	 * while the device is prepared.
	 */
	bool			(*can_dma)(struct spi_controller *ctlr,
					   struct spi_device *spi,
					   struct spi_transfer *xfer);

	/*
	 * These hooks are for drivers that want to use the generic
	 * controller transfer queueing mechanism. If these are used, the
	 * transfer() function above must NOT be specified by the driver.
	 * Over time we expect SPI drivers to be phased over to this API.
	 */
	bool				queued;
	struct kthread_worker		kworker;
	struct task_struct		*kworker_task;
	struct kthread_work		pump_messages;
	spinlock_t			queue_lock;
	struct list_head		queue;
	struct spi_message		*cur_msg;
	bool				idling;
	bool				busy;
	bool				running;
	bool				rt;
	bool				auto_runtime_pm;
	bool                            cur_msg_prepared;
	bool				cur_msg_mapped;
	struct completion               xfer_completion;
	size_t				max_dma_len;

	int (*prepare_transfer_hardware)(struct spi_controller *ctlr);
	int (*transfer_one_message)(struct spi_controller *ctlr,
				    struct spi_message *mesg);
	int (*unprepare_transfer_hardware)(struct spi_controller *ctlr);
	int (*prepare_message)(struct spi_controller *ctlr,
			       struct spi_message *message);
	int (*unprepare_message)(struct spi_controller *ctlr,
				 struct spi_message *message);
	int (*slave_abort)(struct spi_controller *ctlr);

	/*
	 * These hooks are for drivers that use a generic implementation
	 * of transfer_one_message() provied by the core.
	 */
	void (*set_cs)(struct spi_device *spi, bool enable);
	int (*transfer_one)(struct spi_controller *ctlr, struct spi_device *spi,
			    struct spi_transfer *transfer);				//这里是新方法
	void (*handle_err)(struct spi_controller *ctlr,
			   struct spi_message *message);

	/* Optimized handlers for SPI memory-like operations. */
	const struct spi_controller_mem_ops *mem_ops;

	/* gpio chip select */
	int			*cs_gpios;

	/* statistics */
	struct spi_statistics	statistics;

	/* DMA channels for use with core dmaengine helpers */
	struct dma_chan		*dma_tx;
	struct dma_chan		*dma_rx;

	/* dummy data for full duplex devices */
	void			*dummy_rx;
	void			*dummy_tx;

	int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs);
};

每个spi设备,会构造一个spi_message。 对于每一个spi_master,都有一个queue,队列里可以放多个spi_message。spi_message结构体中有一个spi_device,用来表示要传输给哪一个设备,这些spi_message里又有一个或者多个spi_transfer。

二、SPI传输函数的两种方法

//driver/spi/spi.c
//发起一次同步的spi传输,
//1.根据spi_device找到master
//2.把spi_message放入spi_master的队列中
//3.会用线程去触发一次传输,会从队列里执行取出message,启动一次传输,会等待执行完毕(schedule_work)
	//执行完毕通过中断唤醒。唤醒应用程序
//4.等待message完成,或者出错
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;
}
EXPORT_SYMBOL_GPL(spi_sync);
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_controller *ctlr = spi->controller;
	unsigned long flags;

	status = __spi_validate(spi, message);
	if (status != 0)
		return status;

	message->complete = spi_complete;	//提供了一个完成函数,等线程或者中断完成后,执行这个完成函数。(在上面)
	message->context = &done;
	message->spi = spi;

	SPI_STATISTICS_INCREMENT_FIELD(&ctlr->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 (ctlr->transfer == spi_queued_transfer) {			//走这个分支使用新方法,spi_queued_transfer是管理队列的函数
		spin_lock_irqsave(&ctlr->bus_lock_spinlock, flags);

		trace_spi_message_submit(message);			

		status = __spi_queued_transfer(spi, message, false);	//新方法放入队列

		spin_unlock_irqrestore(&ctlr->bus_lock_spinlock, flags);
	} else {		//走这个分支使用老方法
		status = spi_async_locked(spi, message);		//自己实现transfer函数,自己管理队列
	}

	if (status == 0) {
		/* Push out the messages in the calling context if we
		 * can.
		 */
		if (ctlr->transfer == spi_queued_transfer) {
			SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics,
						       spi_sync_immediate);
			SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,
						       spi_sync_immediate);
			__spi_pump_messages(ctlr, false);			//新方法使用pump取出队列
		}

		wait_for_completion(&done);			//这里等待结果
		status = message->status;
	}
	message->context = NULL;
	return status;
}

//kernel/schedule/completion.c
void complete(struct completion *x)
{
    unsigned long flags;

    spin_lock_irqsave(&x->wait.lock, flags);

    if (x->done != UINT_MAX)
        x->done++;
    __wake_up_locked(&x->wait, TASK_NORMAL, 1);		//唤醒当前的任务
    spin_unlock_irqrestore(&x->wait.lock, flags);
}
EXPORT_SYMBOL(complete);

2.1 老方法

在这里插入图片描述

int spi_async_locked(struct spi_device *spi, struct spi_message *message)
{
	struct spi_controller *ctlr = spi->controller;
	//...
	ret = __spi_async(spi, message);
	//...
	return ret;

}
EXPORT_SYMBOL_GPL(spi_async_locked);


static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
	struct spi_controller *ctlr = spi->controller;
	//...
	return ctlr->transfer(spi, message);		//使用transfer函数,自己提供的。自己管理队列,自己触发传输
}

transfer函数示例(spi_sh_transfer): 需要自己管理队列,自己实现work函数,work函数要从队列里取出数据,从message里获取xfer,然后触发传输。

2.2 新方法

在这里插入图片描述

//__spi_sync-->__spi_queued_transfer
static int __spi_queued_transfer(struct spi_device *spi,
				 struct spi_message *msg,
				 bool need_pump)
{
	struct spi_controller *ctlr = spi->controller;
	//....

	list_add_tail(&msg->queue, &ctlr->queue);				//把spi_message放入queue队列
	if (!ctlr->busy && need_pump)
		kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);

	spin_unlock_irqrestore(&ctlr->queue_lock, flags);
	return 0;
}

//__spi_sync-->__spi_pump_messages
static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
{
	unsigned long flags;
	bool was_busy = false;
	int ret;

    if (ctlr->cur_msg) {			//如果有message在使用,就唤醒下一次
        spin_unlock_irqrestore(&ctlr->queue_lock, flags);
        return;
    }

	//....
	/* Extract head of queue */
	ctlr->cur_msg =
		list_first_entry(&ctlr->queue, struct spi_message, queue);		//获取queue队列的头部message

	list_del_init(&ctlr->cur_msg->queue);		//把msg移出队列
	if (ctlr->busy)
		was_busy = true;
	else
		ctlr->busy = true;
	spin_unlock_irqrestore(&ctlr->queue_lock, flags);

	mutex_lock(&ctlr->io_mutex);

	//....

	if (!was_busy)
		trace_spi_controller_busy(ctlr);

	if (!was_busy && ctlr->prepare_transfer_hardware) {
		ret = ctlr->prepare_transfer_hardware(ctlr);		//准备操作,根据spi_master设置硬件(比如DMA等)
		//...
	}

	trace_spi_message_start(ctlr->cur_msg);

	if (ctlr->prepare_message) {
		ret = ctlr->prepare_message(ctlr, ctlr->cur_msg);	//准备操作,根据msg设置硬件(比如片选等)
		//...
		ctlr->cur_msg_prepared = true;
	}

	ret = spi_map_msg(ctlr, ctlr->cur_msg);			//分配dummy buf、为DMA映射内存
	//...

	ret = ctlr->transfer_one_message(ctlr, ctlr->cur_msg);		//传输一个msg,等待传输完成。这里的函数使用spi_transfer_one_message
	//...

out:
	mutex_unlock(&ctlr->io_mutex);

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

默认的传输一个message函数

static int spi_transfer_one_message(struct spi_controller *ctlr,
				    struct spi_message *msg)
{
	struct spi_transfer *xfer;
	bool keep_cs = false;
	int ret = 0;
	unsigned long long ms = 1;
	struct spi_statistics *statm = &ctlr->statistics;
	struct spi_statistics *stats = &msg->spi->statistics;

	spi_set_cs(msg->spi, true);			//设置片选信号

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

	list_for_each_entry(xfer, &msg->transfers, transfer_list) {				//对于msg中的每一个xfer
		trace_spi_transfer_start(msg, xfer);

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

		if (xfer->tx_buf || xfer->rx_buf) {
			reinit_completion(&ctlr->xfer_completion);			//似乎是重置信号量

			ret = ctlr->transfer_one(ctlr, msg->spi, xfer);			//传输一个xfer
			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;
			}

			if (ret > 0) {
				ret = 0;
				ms = 8LL * 1000LL * xfer->len;
				do_div(ms, xfer->speed_hz);
				ms += ms + 200; /* some tolerance */

				if (ms > UINT_MAX)
					ms = UINT_MAX;

				ms = wait_for_completion_timeout(&ctlr->xfer_completion,
								 msecs_to_jiffies(ms));		//等待xfer完成
			}

			if (ms == 0) {
				SPI_STATISTICS_INCREMENT_FIELD(statm,
							       timedout);
				SPI_STATISTICS_INCREMENT_FIELD(stats,
							       timedout);
				dev_err(&msg->spi->dev,
					"SPI transfer timed out\n");
				msg->status = -ETIMEDOUT;
			}
		} else {
			if (xfer->len)
				dev_err(&msg->spi->dev,
					"Bufferless transfer has length %u\n",
					xfer->len);
		}

		trace_spi_transfer_stop(msg, xfer);

		if (msg->status != -EINPROGRESS)
			goto out;

		if (xfer->delay_usecs) {
			u16 us = xfer->delay_usecs;

			if (us <= 10)
				udelay(us);
			else
				usleep_range(us, us + DIV_ROUND_UP(us, 10));
		}

		if (xfer->cs_change) {
			if (list_is_last(&xfer->transfer_list,
					 &msg->transfers)) {
				keep_cs = true;
			} else {
				spi_set_cs(msg->spi, false);			
				udelay(10);
				spi_set_cs(msg->spi, true);
			}
		}

		msg->actual_length += xfer->len;
	}

out:
	if (ret != 0 || !keep_cs)
		spi_set_cs(msg->spi, false);		//取消片选

	if (msg->status == -EINPROGRESS)
		msg->status = ret;

	if (msg->status && ctlr->handle_err)
		ctlr->handle_err(ctlr, msg);

	spi_res_release(ctlr, msg);

	spi_finalize_current_message(ctlr);		//结束当前msg

	return ret;
}

drivers/spi/spi-bitbang.c 中查看示例:

static int spi_bitbang_transfer_one(struct spi_master *master,
				    struct spi_device *spi,
				    struct spi_transfer *transfer)
{
	struct spi_bitbang *bitbang = spi_master_get_devdata(master);
	int status = 0;

	if (bitbang->setup_transfer) {
		status = bitbang->setup_transfer(spi, transfer);	//需要实现的函数
		if (status < 0)
			goto out;
	}

	if (transfer->len)
		status = bitbang->txrx_bufs(spi, transfer);		//需要实现的函数,1启动传输,2等待传输完毕,通过中断唤醒

	if (status == transfer->len)
		status = 0;
	else if (status >= 0)
		status = -EREMOTEIO;

out:
	spi_finalize_current_transfer(master);			//这里会去执行complete函数,去唤醒wait_for_completion_timeout

	return status;
}
void spi_finalize_current_message(struct spi_controller *ctlr)
{
	struct spi_message *mesg;
	unsigned long flags;
	int ret;

	spin_lock_irqsave(&ctlr->queue_lock, flags);
	mesg = ctlr->cur_msg;
	spin_unlock_irqrestore(&ctlr->queue_lock, flags);

	spi_unmap_msg(ctlr, mesg);			//反向的操作,对应前面的map操作

	if (ctlr->cur_msg_prepared && ctlr->unprepare_message) {
		ret = ctlr->unprepare_message(ctlr, mesg);		//反向的操作,对应前面的prepare操作
		if (ret) {
			dev_err(&ctlr->dev, "failed to unprepare message: %d\n",
				ret);
		}
	}

	spin_lock_irqsave(&ctlr->queue_lock, flags);
	ctlr->cur_msg = NULL;
	ctlr->cur_msg_prepared = false;
	kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);		//如果还有其他消息,就唤醒一个内核线程,取出队列来处理
	spin_unlock_irqrestore(&ctlr->queue_lock, flags);

	trace_spi_message_done(mesg);

	mesg->state = NULL;
	if (mesg->complete)			//唤醒wait_for_completion
		mesg->complete(mesg->context);
}
EXPORT_SYMBOL_GPL(spi_finalize_current_message);


新方法需要提供一些传输函数