Linux SPI子系统(2):SPI核心层

目录

SPI核心层 

1. SPI子系统初始化

2. 重要的数据结构

2.1 struct spi_controller

2.2 struct spi_driver

2.3 struct spi_device

2.4 struct spi_transfer和struct spi_message

3. 重要的API

3.1 spi_register_controller

3.2 数据准备函数:spi_message_init和spi_message_add_tail

3.3 数据传输函数:spi_sync和spi_async

4. 参考文章


SPI核心层 

上次简单介绍了下Linux SPI子系统的系统结构,主要有3部分组成,分别是SPI核心、SPI总线驱动(控制器驱动)以及SPI设备驱动。

SPI核心层代码位于drivers/spi/spi.c,头文件位于include/linux/spi/spi.h,SPI核心提供了SPI总线驱动和设备驱动的注册、注销方法,并提供一些需要控制器驱动实现的回调函数。

本文主要介绍SPI核心层,包括重要的数据结构和API。

1. SPI子系统初始化

/* drivers/spi/spi.c */

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

/* 2 */
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))
		return 1;

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

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

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

/* 3 */
static struct class spi_master_class = {
	.name		= "spi_master",
	.owner		= THIS_MODULE,
	.dev_release	= spi_controller_release,
	.dev_groups	= spi_master_groups,
};

 1. struct bus_type是总线的类型,spi_bus_type即类型为SPI的总线,其中的match成员函数spi_match_device的作用是将SPI设备和设备驱动进行匹配。

2. 从四个方面进行匹配,分别从驱动中的of_match_table的compatible和acpi_match_table的compatible与dts中的compatible进行比较,看是否一致、如支持id数组,则查找与id数组中匹配的SPI设备、比较驱动和设备的名字是否一致。

3. struct class是设备的类,spi_master_class即SPI控制器设备的类。

/* 4 */
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);
	if (status < 0)
		goto err1;

	status = class_register(&spi_master_class);
	if (status < 0)
		goto err2;

	if (IS_ENABLED(CONFIG_SPI_SLAVE)) {
		status = class_register(&spi_slave_class);
		if (status < 0)
			goto err3;
	}

	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;

err3:
	class_unregister(&spi_master_class);
err2:
	bus_unregister(&spi_bus_type);
err1:
	kfree(buf);
	buf = NULL;
err0:
	return status;
}

4. spi_init是SPI子系统的初始化函数,起作用的注册了spi bus和spi master class。

2. 重要的数据结构

2.1 struct spi_controller

#define spi_master			spi_controller

struct spi_controller {
    /* SPI设备的设备结构体 */
	struct device	dev;
    /* 链表头 */
	struct list_head list;
	/* SPI总线序号 */
	s16			bus_num;
	/* 片选信号的数量 */
	u16			num_chipselect;
	/* SPI控制器DMA缓冲区对齐定义 */
	u16			dma_alignment;
	/* 工作模式位 */
	u16			mode_bits;
	/* 传输支持的 bits_per_word 的位掩码 */
	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))
	/* 传输速度限制 */
	u32			min_speed_hz;
	u32			max_speed_hz;

	/* 与此驱动程序相关的其他限制 */
	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 */

	/* 指示这是非devres托管控制器的标志 */
	bool			devm_allocated;

	/* 指示这是一个 SPI 从控制器的标志 */
	bool			slave;

	/* 在某些硬件传输/消息大小可能受到限制,该限制取决于设备传输设置 */
	size_t (*max_transfer_size)(struct spi_device *spi);
	size_t (*max_message_size)(struct spi_device *spi);

	/* I/O互斥锁 */
	struct mutex		io_mutex;

	/* 用于SPI总线锁定的lock和mutex */
	spinlock_t		bus_lock_spinlock;
	struct mutex		bus_lock_mutex;

	/* 指示 SPI 总线被锁定为独占使用的标志 */
	bool			bus_lock_flag;

	/* 设置模式和时钟等(spi驱动可能调用多次) */
	int			(*setup)(struct spi_device *spi);

	/* 双向批量传输 */
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);

	/* 调用 release()以释放spi_controller提供的内存 */
	void			(*cleanup)(struct spi_device *spi);

	/* 用于启用对DMA处理的核心支持,如果 can_dma()存在并返回true,则传输将在调用 
       transfer_one()之前进行映射 */
	bool			(*can_dma)(struct spi_controller *ctlr,
					   struct spi_device *spi,
					   struct spi_transfer *xfer);

	/* 下面的成员适用于想要使用通用控制器传输队列机制的驱动程序 */
	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);
	int (*spi_flash_read)(struct  spi_device *spi,
			      struct spi_flash_read_message *msg);
	bool (*spi_flash_can_dma)(struct spi_device *spi,
				  struct spi_flash_read_message *msg);
	bool (*flash_read_supported)(struct spi_device *spi);

	/* 下面的成员适用于使用核心提供的transfer_one_message()的通用实现的驱动程序 */
	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);

	/* 为类似SPI内存的操作优化了处理程序 */
	const struct spi_controller_mem_ops *mem_ops;
	const struct spi_controller_mem_caps *mem_caps;

	/* gpio片选 */
	int			*cs_gpios;

	/* 统计数据 */
	struct spi_statistics	statistics;

	/* DMA通道 */
	struct dma_chan		*dma_tx;
	struct dma_chan		*dma_rx;

	/* 全双工设备的虚拟数据 */
	void			*dummy_rx;
	void			*dummy_tx;

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

 struct spi_controller是SPI控制器驱动的核心数据结构,spi_controller代表一个SPI控制器,在编写控制器驱动时需要对其中的一些参数和回调函数进行填充或实现。

2.2 struct spi_driver

struct spi_driver {
    /* 支持的SPI device的设备表 */
	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设备驱动,实现id_table或者of_match_table等,使得匹配成功即可;匹配成功后调用probe函数,驱动卸载时调用remove函数。

2.3 struct spi_device

struct spi_device {
	struct device		dev;
	struct spi_controller	*controller;
	struct spi_controller	*master;	/* compatibility layer */
	u32			max_speed_hz;
	u8			chip_select;
	u8			bits_per_word;
	u16			mode;
#define	SPI_CPHA	0x01			/* clock phase */
#define	SPI_CPOL	0x02			/* clock polarity */
#define	SPI_MODE_0	(0|0)			/* (original MicroWire) */
#define	SPI_MODE_1	(0|SPI_CPHA)
#define	SPI_MODE_2	(SPI_CPOL|0)
#define	SPI_MODE_3	(SPI_CPOL|SPI_CPHA)
#define	SPI_CS_HIGH	0x04			/* chipselect active high? */
#define	SPI_LSB_FIRST	0x08			/* per-word bits-on-wire */
#define	SPI_3WIRE	0x10			/* SI/SO signals shared */
#define	SPI_LOOP	0x20			/* loopback mode */
#define	SPI_NO_CS	0x40			/* 1 dev/bus, no chipselect */
#define	SPI_READY	0x80			/* slave pulls low to pause */
#define	SPI_TX_DUAL	0x100			/* transmit with 2 wires */
#define	SPI_TX_QUAD	0x200			/* transmit with 4 wires */
#define	SPI_RX_DUAL	0x400			/* receive with 2 wires */
#define	SPI_RX_QUAD	0x800			/* receive with 4 wires */
#define	SPI_CS_WORD	0x1000			/* toggle cs after each word */
#define	SPI_TX_OCTAL	0x2000			/* transmit with 8 wires */
#define	SPI_RX_OCTAL	0x4000			/* receive with 8 wires */
#define	SPI_3WIRE_HIZ	0x8000			/* high impedance turnaround */
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];
	int			cs_gpio;	/* chip select gpio */

	/* the statistics */
	struct spi_statistics	statistics;

	/*
	 * likely need more hooks for more protocol options affecting how
	 * the controller talks to each chip, like:
	 *  - memory packing (12 bit samples into low bits, others zeroed)
	 *  - priority
	 *  - drop chipselect after each word
	 *  - chipselect delays
	 *  - ...
	 */
};

spi_device就不多介绍了,现在一般都是用dts来描述,由系统自动生成spi_device。

2.4 struct spi_transfer和struct spi_message

struct spi_transfer {
	const void	*tx_buf;
	void		*rx_buf;
	unsigned int len;

	dma_addr_t	tx_dma;
	dma_addr_t	rx_dma;
	struct sg_table tx_sg;
	struct sg_table rx_sg;

	unsigned	dummy_data:1;
	unsigned	cs_change:1;
	unsigned	tx_nbits:3;
	unsigned	rx_nbits:3;
#define	SPI_NBITS_SINGLE	0x01 /* 1bit transfer */
#define	SPI_NBITS_DUAL		0x02 /* 2bits transfer */
#define	SPI_NBITS_QUAD		0x04 /* 4bits transfer */
	u8		bits_per_word;
	u16		delay_usecs;
	u32		speed_hz;

    /*用于链接到spi_message,用来连接的双向链接节点*/
	struct list_head transfer_list;
};
struct spi_message {
    /*spi_transfer链表队列 */
	struct list_head	transfers;
    /*传输的目的设备*/
	struct spi_device	*spi;
    /*如果为真,此次调用提供dma和cpu虚拟地址 */
	unsigned		is_dma_mapped:1;

	/* 通过回调告知完成 */
	void			(*complete)(void *context);
	void			*context;
	unsigned int	frame_length;
	unsigned int	actual_length;
	int			status;

	struct list_head	queue;
	void			*state;

	/* list of spi_res reources when the spi message is processed */
	struct list_head        resources;
};

spi_message用于执行数据传输的原子序列,每个数据传输由spi_transfer表示。该序列是“原子的”,因为在该序列完成之前没有其他spi_message可以使用该SPI总线。在某些系统上,许多这样的序列可以作为单个编程的DMA传输执行。在所有系统上,这些消息都是排队的,并且可能在与其他设备进行事务处理后完成。发送到给定spi_device的消息始终按FIFO顺序执行。 

3. 重要的API

3.1 spi_register_controller

#define spi_register_master(_ctlr)	spi_register_controller(_ctlr)

int spi_register_controller(struct spi_controller *ctlr)

作用是将spi控制器驱动注册到内核。

3.2 数据准备函数:spi_message_init和spi_message_add_tail

/* include/linux/spi/spi.h */
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);
}

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_init函数作用是初始化spi_message,将message清零,初始化transfers链表头。

spi_message_add_tail函数作用是将spi_transfer尾部加入到spi_message链表中。

3.3 数据传输函数:spi_sync和spi_async

SPI数据传输分为同步方式和异步方式,同步方式即发起数据传输后,必须等待传输完成后才能返回,期间不能做其他事情;异步方式即发起数据传输后,不用管传输是否完成,直接返回,期间可以做其他事情。

/* 调用关系 */
spi_sync(struct spi_device *spi, struct spi_message *message)
    |-> __spi_sync(struct spi_device *spi, struct spi_message *message)

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

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

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

 spi_sync:首先对spi_message中的complete回调函数进行初始化;之后调用__spi_queued_transfer函数,这里第三个参数传的是false,所以只将message尾部加入到ctlr链表中后返回;随后调用__spi_pump_messages,将spi_message发出去;最后调用wait_for_completion函数,等待完成的信号量,当接收到信号量时说明传输完成并返回。

/* 调用关系 */
spi_async(struct spi_device *spi, struct spi_message *message)
    |-> __spi_async(struct spi_device *spi, struct spi_message *message)
        |-> ctlr->transfer(spi, message)

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

	spin_lock_irqsave(&ctlr->queue_lock, flags);

	if (!ctlr->running) {
		spin_unlock_irqrestore(&ctlr->queue_lock, flags);
		return -ESHUTDOWN;
	}
	msg->actual_length = 0;
	msg->status = -EINPROGRESS;

	list_add_tail(&msg->queue, &ctlr->queue);
	if (!ctlr->busy && need_pump)
		kthread_queue_work(&ctlr->kworker, &ctlr->pump_messages);

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

spi_async:如果控制器驱动没有实现transfer函数,内核会自动将transfer函数初始化为spi_queued_transfer函数,此函数中,将message尾部加入到ctlr链表中,随后唤醒工作线程,把具体的工作交付给worker后返回,不等待传输完成。

4. 参考文章

http://t.csdn.cn/Ppqna

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值