Linux SPI 驱动分析(1)— 结构框架

Linux SPI 驱动分析(1)— 结构框架

目录

1、总线互联拓扑结构

1.1、硬件拓扑

1.2、软件抽象

2、SPI 软件驱动层次

3、数据结构

3.1、spi_controller (spi_master)

3.2、spi_device

3.3、spi_driver

3.4、spi_board_info

3.5、spi_transfer

3.6、spi_message

3.7、数据结构之间的关系

4、对外接口 APIs


SPI 属于高速串行全双工的接口,在 SoC 中广泛存在,关于更多 SPI 的内容可以参考《STM32F103ZET6 — SPI》,这里介绍了关于 SPI 的一些基本内容。

本章主要分析 Linux SPI 驱动框架部分。

SPI 和 platform 一样(Linux设备模型(5) — platform bus/device/driver),属于总线的一类,所以他们类似,不过 SPI 有他自己的框架,我们依然从数据结构和相关的 API 入手,着重分析 SPI 相关的架构。

Linux SPI 驱动结构中,将 SPI 相关的驱动分为了几部分:

1、SPI 主机以及主机驱动:SoC 的 SPI Controller 部分的驱动

2、SPI 外设驱动描述:比如 SPI Flash 驱动

3、SPI 从设备描述:比如 SPI Flash 设备

4、SPI 传输层描述:spi_transfer 和 spi_message 组成

1、总线互联拓扑结构

1.1、硬件拓扑

首先需要明确的是,硬件上来说,对于一款 SoC 来说,一般的,都会集成不止一路 SPI Controller(比如,SPI_0/SPI_1/SPI_2),用于控制不同的外设;而 SPI 是一种总线规范,每一路 SPI 都可以接 N 个不同的外设,通过 CS 片选信号,来选择具体与哪一个连接到这个 SPI 总线的设备通信;总线拓扑结构如下:

img

1.2、软件抽象

软件上,对硬件进行了合理的抽象:

1、**spi_master(spi_controller):**对 SoC 的 SPI 控制器的抽象

2、**spi_bus_type:**spi 的 bus_type,代表了硬件上的 SPI Bus

3、**spi_device:**spi 从设备

4、**spi_driver:**spi 具体设备的驱动

2、SPI 软件驱动层次

首先需要明确的一点是,SPI 主机控制器部分是整个 SPI 系统的核心存在,它并不属于 SPI 下的 bus、device、drvier 这一组结构,因为他并不是挂接到 bus 上的 device,更不是对应挂接在 bus 上 device 的 driver,而是相对独立的一个存在,所以 SPI 控制器部分,是连接到 platform 下的,并执行 platform 的 probe;

注:这里可能会不好理解,因为做过单片机的朋友都知道,单片机上的 SPI 驱动,其实就是按照时序去配置寄存器,其实就是对 SPI 控制器的操作,这就是单片机上的 driver,在 Linux driver 结构下,这个 driver 并不单单的指代对控制器的寄存器配置的过程,而是将其抽象到了一个 deivce 对应到一个实际 driver 的这个角度。所以在这里,主机控制器的抽象其实在内核中是属于一个单独的 device,所以你可以把它挂接到 platform_device 上,并套用 platform 这套东西;

img

可以看到结构如上图所示,比如一个 SPI Flash 它就属于 spi_device 的范畴,针对这个 SPI Flash 的驱动具体业务,就属于 spi_driver 的范畴,而 SPI 主机控制器,只是提供了一组操作的通道,可以属于 platform device 的范畴。

3、数据结构

3.1、spi_controller (spi_master)

Linux driver 中,最新的内核代码中使用 *spi_controller* (以前是 spi_master 结构)来描述一个 SPI 的控制器,为了兼容以前的代码,直接:

#define spi_master            spi_controller

所以我们直接看最新的定义 spi_controller,他在 *include/linux/spi/spi.h*

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

源码中,注释写的非常详细,我们根据结构体的注释能够大致的知道他的含义,这里仅仅看几个关键的成员,其他成员用到的时候,加以解释:

**dev:**spi_controller 是一个 device,所以包含了一个 device 的实例,设备模型使用

**list:**链接到全局的 spi_controller list

**bus_num:**spi bus 的编号,比如某 SoC有3个 SPI 控制,那么这个结构描述的是第几个

**num_chipselect:**片选数量,决定该控制器下面挂接多少个SPI设备,从设备的片选号不能大于这个数量

**mode_bits:**SPI 控制器支持的 slave 的模式

**min_speed_hz/max_speed_hz:**最大最小速率

**slave:**是否是 slave

**(*setup):**主要设置SPI控制器和工作方式、clock等

**(*transfer):**添加消息到队列的方法。这个函数不可睡眠。它的职责是安排发生的传送并且调用注册的回调函 complete()。这个不同的控制器要具体实现,传输数据最后都要调用这个函数

**(*cleanup):**在spidev_release函数中被调用,spidev_release被登记为spi dev的release函数

3.2、spi_device

spi_device 用于描述一个挂接到 SPI 总线上的一个设备,他的结构是:

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 */
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];
	const char		*driver_override;
	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
	 *  - chipselect delays
	 *  - ...
	 */
};

他的成员相对 spi_controller 要少很多:

**dev:**device 结构,设备模型使用

**controller:**这个 spi device 挂在那个 SPI Controller 下

**max_speed_hz:**通讯时钟最大频率

**chip_select:**片选号,每个 master 支持多个 spi_device

**mode:**SPI device 的模式,时钟极性和时钟相位

**bits_per_word:**每个通信字的字长的比特数,默认是 8

**irq:**使用到的中断号

**modalias:**设备驱动的名字

由于一个SPI总线上可以有多个SPI设备,因此需要片选号来区分它们,SPI控制器根据片选号来选择不同的片选线,从而实现每次只同一个设备通信。

spi_device的mode成员有两个比特位含义很重要。SPI_CPHA选择对数据线采样的时机,0选择每个时钟周期的第一个沿跳变时采样数据,1选择第二个时钟沿采样数据;SPI_CPOL选择每个时钟周期开始的极性,0表示时钟以低电平开始,1选择高电平开始。这两个比特有四种组合,对应SPI_MODE_0~SPI_MODE_3。

另一个比较重要的成员是bits_per_word。这个成员指定每次读写的字长,单位是比特。虽然大部分SPI接口的字长是8或者16,仍然会有一些特殊的例子。需要说明的是,如果这个成员为零的话,默认使用8作为字长。
最后一个成员并不是设备的名字,而是需要绑定的驱动的名字。

3.3、spi_driver

*spi_driver* 代表一个驱动,他也挂接到了 spi bus 上,与对应的 spi_device 结构进行匹配后调用 probe,他的定义是:

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

可以看到,其实就是最简单的成员,他并不提供任何与硬件的实际交互

3.4、spi_board_info

spi_device 的板信息用 *spi_board_info* 结构体描述,该结构体记录着SPI外设使用的主机控制器序号、片选序号、数据比特率、SPI传输模式(即CPOL、CPHA)等。ARM Linux3.x之后的内核在改为设备树之后,不再需要在arch/arm/mach-xxx中编码SPI的板级信息了,而倾向于在SPI控制器节点下填写子节点

如果定义了这个结构,然后在调用他的 register,并会分配并新建一个对应 spi_device,他的结构如下:

struct spi_board_info {
	/* the device name and module name are coupled, like platform_bus;
	 * "modalias" is normally the driver name.
	 *
	 * platform_data goes to spi_device.dev.platform_data,
	 * controller_data goes to spi_device.controller_data,
	 * device properties are copied and attached to spi_device,
	 * irq is copied too
	 */
	char		modalias[SPI_NAME_SIZE];
	const void	*platform_data;
	const struct property_entry *properties;
	void		*controller_data;
	int		irq;
 
	/* slower signaling on noisy or low voltage boards */
	u32		max_speed_hz;
 
 
	/* bus_num is board specific and matches the bus_num of some
	 * spi_controller that will probably be registered later.
	 *
	 * chip_select reflects how this chip is wired to that master;
	 * it's less than num_chipselect.
	 */
	u16		bus_num;
	u16		chip_select;
 
	/* mode becomes spi_device.mode, and is essential for chips
	 * where the default of SPI_CS_HIGH = 0 is wrong.
	 */
	u16		mode;
 
	/* ... may need additional spi_device chip config data here.
	 * avoid stuff protocol drivers can set; but include stuff
	 * needed to behave without being bound to a driver:
	 *  - quirks like clock rate mattering when not selected
	 */
};

他成员的内容几乎和 spi_device 一致,不再多说;

3.5、spi_transfer

*spi_transfer* 代表一个读写缓冲对,包含接收缓冲区及发送缓冲区,其实,spi_transfer的发送是通过构建spi_message实现,通过将spi_transfer中的链表transfer_list链接到spi_message中的transfers,再以spi_message形势向底层发送数据。每个spi_transfer都可以对传输的一些参数进行设置,使得master controller按照它要求的参数进行数据发送

struct spi_transfer {
	/* it's ok if tx_buf == rx_buf (right?)
	 * for MicroWire, one buffer must be null
	 * buffers must work with dma_*map_single() calls, unless
	 *   spi_message.is_dma_mapped reports a pre-existing mapping
	 */
	const void	*tx_buf;
	void		*rx_buf;
	unsigned	len;
 
	dma_addr_t	tx_dma;
	dma_addr_t	rx_dma;
	struct sg_table tx_sg;
	struct sg_table rx_sg;
 
	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;
	u16		word_delay;
 
	struct list_head transfer_list;
};

**tx_buf:**发送缓冲区,要写入设备的数据(必须是dma_safe),或者为NULL

**rx_buf:**接收缓冲区,要读取的数据缓冲(必须是dma_safe),或者为NULL

**len:**缓冲区长度,tx和rx的大小(字节数)。这里不是指它的和,而是各自的长度,它们总是相等的

**tx_dma:**如果spi_message.is_dma_mapped是真,这个是tx的dma地址

**rx_dma:**如果spi_message.is_dma_mapped是真,这个是rx的dma地址

**cs_change:1 :**当前spi_transfer发送完成之后重新片选。影响此次传输之后的片选。指示本次transfer结束之后是否要重新片选并调用setup改变设置。这个标志可以减少系统开销

**bits_per_word:**每个字长的比特数,0代表使用spi_device中的默认值 8

**delay_usecs:**发送完成一个spi_transfer后延时时间,此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息

**speed_hz:**通信时钟。如果是0,使用默认值

**transfer_list:**用于链接到spi_message,用来连接的双向链接节点

控制器驱动会先写入tx 的数据,然后读取同样长度的数据。长度指示是 len

如果tx_buff是空指针,填充rx_buff的时候会输出0(为了产生接收的时钟),如果rx_buff是NULL,接收到的数据将被丢弃

只有 len 长度的数据会被输出和接收

3.6、spi_message

spi_message 代表 spi 消息,由多个spi_transfer段组成。
spi_message用来原子的执行spi_transfer表示的一串数组传输请求。
这个传输队列是原子的,这意味着在这个消息完成之前不会有其它消息占用总线。
消息的执行总是按照FIFO的顺序。
向底层提交spi_message的代码要负责管理它的内存空间。未显示初始化的内存需要使用0来初始化。

struct spi_message {
	struct list_head	transfers;
 
	struct spi_device	*spi;
 
	unsigned		is_dma_mapped:1;
 
	/* REVISIT:  we might want a flag affecting the behavior of the
	 * last transfer ... allowing things like "read 16 bit length L"
	 * immediately followed by "read L bytes".  Basically imposing
	 * a specific message scheduling algorithm.
	 *
	 * Some controller drivers (message-at-a-time queue processing)
	 * could provide that as their default scheduling algorithm.  But
	 * others (with multi-message pipelines) could need a flag to
	 * tell them about such special cases.
	 */
 
	/* completion is reported through a callback */
	void			(*complete)(void *context);
	void			*context;
	unsigned		frame_length;
	unsigned		actual_length;
	int			status;
 
	/* for optional use by whatever driver currently owns the
	 * spi_message ...  between calls to spi_async and then later
	 * complete(), that's the spi_controller controller driver.
	 */
	struct list_head	queue;
	void			*state;
 
	/* list of spi_res reources when the spi message is processed */
	struct list_head        resources;
};

**transfer:**这个 mesage 含的 transfer 链表

**spi:**传输的目标设备

**is_dma_mapped:**spi_transfer 中 tx_dma 和 rx_dma 是否已经 mapped

**complete:**数据传输完成的回调函数

**context:**提供给complete的可选参数

**actual_length:**spi_message已经传输了的字节数

status:出错与否,错误时返回 errorcode

queue 、**state:**供controller驱动内部使用

spi_message:描述一次完整的传输,即cs信号从高->底->高的传输

spi_transfer:多个 spi_transfer 够成一个 spi_message

3.7、数据结构之间的关系

上面的数据结构,基本上组成了 SPI 框架的内容,在软件层次上我们将 SPI Controller,Bus,Driver,Slave Device 结构抽象为如下:

img

在数据发送的结构部分,内核将其抽象如下:

img

4、对外接口 APIs

数据结构理清楚了,暂时把 SPI Framework 看成一个黑盒子,先关心到底怎么用这个 SPI Framework,首先来看看他提供出来的接口 APIs,后面我们再来打开黑盒看看!

SPI Framework 提供的所有 APIs,几乎都可以在 *include/linux/spi/spi.h* 中找到,主要都是围绕着数据结构进行的,我们按照使用的流程来一步一步看看;首先先从定义注册 SPI Controller 开始入手;

1、分配 spi_controller (spi_master)

struct spi_controller *spi_alloc_master(struct device *host, unsigned int size)

这个接口用于分配并返回一个 spi_controller (spi_master)结构,前面也说过,SPI 控制器部分,可以挂接到 platform 上,所以这个 API 的使用场景是 SoC 的 spi 控制器 probe 的时候,调用 *spi_alloc_master* 来获得一个 spi_controller,并根据 SoC 自身的资源情况,来填充 spi_controller 下的各个成员。

注意:这里传入的 *host 便是 xxx_spi_probe(struct platform_device *pdev) 中的 *&pdev->dev* 部分,后面的 size,可以指的是私有的数据,并且包含了 spi_controller 的实例;

2、注册 spi_controller (spi_master)

int spi_register_controller(struct spi_controller *ctlr);
int spi_register_master(struct spi_controller *ctlr);

在第一步的 probe 中,将分配好并且已经完成了必要初始化的 spi_controller 结构注册到内核。在这个函数中,其实是进行了一些必要的设备模型的操作和钩子函数的 check,通过后,挂接到全局的 *spi_controller_list* 链表上。

这里需要注意一下,由于 SoC 一般存在多个 SPI Controller,所以在资源定义的时候(不管是 DTS 还是 platform_device),这个 probe 会被调用多次,所有的 spi_controller 都挂到了 *spi_controller_list* 链表上。

3、分配 spi_device

分配一个并简单初始化 spi_device

struct spi_device *spi_alloc_device(struct spi_controller *ctlr);

4、添加 spi_device

将 spi_device 添加的 device 系统

int spi_add_device(struct spi_device *spi);

5、分配并添加 spi_device

这个接口是上面两个接口的结合,往指定的 spi_controller 上添加一个 spi_borad_info:

struct spi_device *spi_new_device(struct spi_controller *ctlr,
				  struct spi_board_info *chip)

6、注册 spi_driver

#define spi_register_driver(driver) \
	__spi_register_driver(THIS_MODULE, driver)

往 SPI BUS 上添加一个 driver

7、初始化 spi_message

void spi_message_init(struct spi_message *m);

用于传输之前,进行 spi_message 结构的初始化

8、添加 transfer 到指定的 spi_message

void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m);

将希望传输的 transfer 到指定的已经完成初始化的 spi_message;

9、初始化一个 spi_message 并添加 N 个 transfer

void spi_message_init_with_transfers(struct spi_message *m,
            struct spi_transfer *xfers, unsigned int num_xfers)

这个接口是上面两个的合并版本,初始化一个 spi_message,并将 num_xfers 个 xfers 挂接到 spi_message 上;

10、分配 1个 spi_message 和 N 个其下的 spi_transfer

struct spi_message *spi_message_alloc(unsigned ntrans, gfp_t flags);

这个接口也是一个高级版本,先分配 1 个 spi_message 和 ntrans 个 spi_transfer,并对其进行初始化,将 spi_transfer 全部挂接到 spi_message;

11、控制器 setup

int spi_setup(struct spi_device *spi);

根据与 spi_device 的属性,setup 我们的 SPI Controller 的 mode 和 clock rate

12、异步传输数据

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

往 spi_device 提价 spi_message 的数据

因为是异步的,一提交就立马返回了,这也就是说需要同步机制(complete 就是了)。他确保不会睡眠,使用 spinlock,可安全的在中断 handler 或其他不可休眠的代码中调用。

使用 spi_async() 需要注意的是,在complete()未返回前不要轻易访问你一提交的 spi_transfer 中的buffer。也不能释放SPI系统正在使用的buffer。一旦你的complete返回了,这些buffer就又是你的了。

13、同步传输数据

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

同步数据传输,完成后返回,也就是说,允许睡眠,使用的是 mutex 锁,返回后,可立即访问 buffer

14、添加若干 spi_transfer 并同步传输

int spi_sync_transfer(struct spi_device *spi, struct spi_transfer *xfers,
	unsigned int num_xfers);

这个是前面同步传输的高级版本,往一个 spi_message 中添加已经初始化好的 num_xfers 个 spi_transfer,并嗲用 spi_sync ,往 指定的 spi_device 进行数据传输,返回 0,代表完成;允许睡眠

15、同步写一次数据

int spi_write(struct spi_device *spi, const void *buf, size_t len);

向指定的 spi 设备,同步写一个长为 len 的 buf 数据,注意,这里是往一个 spi_message 中填充一个 spi_transter

15、同步读一次

int spi_read(struct spi_device *spi, const void *buf, size_t len);

向指定的 spi 设备,同步读一个长为 len 的 buf 数据,注意,这里是往一个 spi_message 中填充一个 spi_transter

16、同步写一次同时读一次

/* this copies txbuf and rxbuf data; for small transfers only! */
int spi_write_then_read(struct spi_device *spi,
		const void *txbuf, unsigned n_tx,
		void *rxbuf, unsigned n_rx);

SPI 是全双工的,这个接口是向指定的 spi 设备写入 n_tx 个数据,同时读取 n_rx 数据,接口调用允许睡眠。

注:看注释知道,这个只为小型读写专用

17、同步写一个 8-bits 命令,读 8-bits 状态

/**
 * spi_w8r8 - SPI synchronous 8 bit write followed by 8 bit read
 * @spi: device with which data will be exchanged
 * @cmd: command to be written before data is read back
 * Context: can sleep
 *
 * Callable only from contexts that can sleep.
 *
 * Return: the (unsigned) eight bit number returned by the
 * device, or else a negative error code.
 */
 
ssize_t spi_w8r8(struct spi_device *spi, u8 cmd);

这个接口,有点带专用性,比如你有一个 SPI Flash,他支持使用 SPI 命令配置 SPI Flash,并返回 SPI Flash 状态码的话,那么就可以使用这个接口,这个接口其实是封装了 spi_write_then_read 接口,将 cmd 写入 SPI,将 rx 的 buffer (比如 SPI Flash 收到配置 cmd 后,通过 SPI 传输了一个状态码到 SoC)返回。

17、同步写一个 8-bits 命令,读 16-bits 状态

/**
 * spi_w8r16 - SPI synchronous 8 bit write followed by 16 bit read
 * @spi: device with which data will be exchanged
 * @cmd: command to be written before data is read back
 * Context: can sleep
 *
 * The number is returned in wire-order, which is at least sometimes
 * big-endian.
 *
 * Callable only from contexts that can sleep.
 *
 * Return: the (unsigned) sixteen bit number returned by the
 * device, or else a negative error code.
 */
 
ssize_t spi_w8r16(struct spi_device *spi, u8 cmd);

这个接口和上面一个类似,只不过返回的是 2 个 Bytes,也就是针对对方要返回 16bits 状态的情况,不再多说。

参考文献:

https://blog.csdn.net/wuhzossibility/article/details/7832438

https://www.jianshu.com/p/60d2d5849db9

http://emb.hqyj.com/Column/Column367.htm

https://blog.csdn.net/yaolanshu_june/article/details/52356138

https://www.linuxidc.com/Linux/2012-07/64942.htm

https://www.linuxidc.com/Linux/2012-07/64942p2.htm

https://www.linuxidc.com/Linux/2012-07/64942p3.htm

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值