Linux SPI设备分析3

Linux SPI设备分析3(基于Linux6.6)---SPI master /device/driver介绍

 

 

一、spi驱动框架与i2c框架的区别

特性SPI 驱动框架I2C 驱动框架
通信方式全双工(Full-duplex),同时支持数据的发送和接收半双工(Half-duplex),一次只能发送或接收数据
信号线数量4 根:MOSI(主设备发送)、MISO(主设备接收)、SCK(时钟)、CS(片选)2 根:SDA(数据线)、SCL(时钟线)
总线结构支持多从设备,通常每个从设备使用一个单独的片选(CS)信号支持多设备,所有设备共享同一条总线,地址区分不同设备
通信速率通常较高,支持更高的传输速率(几 Mbps 至几十 Mbps)通常较低,速度一般在 100 kbps、400 kbps 或 1 Mbps 左右
硬件复杂度硬件支持较为复杂,需要多条信号线并且通常每个从设备需要独立的片选线硬件支持较为简单,只需要两条信号线即可实现多设备通信
设备地址管理没有统一的设备地址管理机制,通过片选(CS)线区分设备每个设备都有唯一的 7 位或 10 位地址,用于在总线上进行寻址
传输模式支持全双工,主设备与从设备可以同时进行数据交换仅支持半双工,主设备发送数据后等待从设备响应(或反之)
协议复杂度相对较复杂,涉及时序、片选信号等多个参数的配置协议简单,基于地址进行通信,时序由 I2C 协议定义
数据传输方式由主设备通过 SCK 时钟同步控制数据传输,适用于高速数据交换基于时钟拉高与拉低的同步传输,适用于慢速数据交换
数据校验通常不支持硬件校验(需要应用层进行校验)支持硬件校验(如 CRC 校验),能够检测数据传输的错误
设备数量支持的设备数量相对较少,通常受限于主设备的片选线数量可以支持更多设备,设备地址是唯一标识,且支持多个设备同时存在
时序控制时钟信号由主设备产生(SCK),从设备根据该时钟信号进行同步传输由主设备生成时钟信号(SCL),从设备同步接收数据
支持的操作模式SPI 模式有 4 种(CPOL 和 CPHA 配置),需根据设备要求设置I2C 模式简单,主要分为标准模式(100 kbps)、快速模式(400 kbps)等
硬件资源使用需要多条信号线,并且每个设备使用独立的片选信号线(CS),占用较多 GPIO 资源只需要 2 根信号线 SDA 和 SCL,占用较少的硬件资源
数据帧格式数据传输通常是字节或更大的块,帧的格式更灵活数据传输是按字节进行,帧格式固定,通常是设备地址 + 数据
典型应用场景用于高速通信、传感器、存储器、显示屏等设备间的通信用于低速通信、传感器、EEPROM、RTC、微控制器间的通信等

 

二、spi驱动-总线-控制器-设备模块之间的关联

这四个子模块通过借助LINUX设备-总线-驱动模型提供的数据结构以及接口函数,完成了子模块间的关联(这四个子模块关联关系的建立以及解除,即通过这几个模块间的注册与注销接口完成的)。简要说下这几个模块间的关联,然后再分别对这几个模块进行分析说明。下面通过一张简略图描述这四者之间的关系。

716738167af14d6e84114ba4658f39ba.png

 

  1. spi总线通过其bus_kset成员,链接至系统的总线集bus_kset上;
  2. spi中心概念的klist_devices、klist_drivers链表,完成了总线与spi设备、spi驱动之间的关联;
  3. Spi master借助device类型变量中的父子链表成员,完成与spi device的关联(父子关系关联);
  4. Spi master与spi device借助其spi模块定义的成员,完成了spi master与spi device的绑定;
  5. Spi master、spi device借助其device类型,完成了与系统设备集devices_kset的关联;
  6. Spi master借助其device类型成员变量,完成与spi_master_class类的关联(此图未画出)。

基本上通过这些关联关系,即完成了spi框架中这几个子模块间的关联操作。

三、spi master device的注册与注销以及相关结构体说明

Spi master用于抽象一个spi控制器,该控制器提供spi总线的通信方法等信息,同时该spi控制器的device类型的成员变量是该控制器下所有spi device的父设备。下面分析下该控制器的注册与注销。

先分析下spi master的定义,对该结构体变量熟悉了以后,对它的的注册与注销也就基本上熟悉了。对主要成员进行说明:

  1. dev用于使用LINUX设备-总线-驱动模型;
  2. Bus_num、num_chipselect用于说明总线号及支持的cs个数;
  3. mode_bits表示该控制器支持的模式(即spi总线支持的四种模式);
  4. setup接口用于设置总线模式、时钟等内容;
  5. transfer说明该控制器提供的通信方法(目前在新的内核中已不推荐使用该接口,推荐使用队列方式,队列模式由spi核心提供,我们会在后面进行详细说明);
  6. kworker、kworker_task、queued、pump_messages、prepare_transfer_hardware、unprepare_transfer_hardware、unprepare_transfer_hardware这几个成员变量均是用于spi核心提供的队列通信模式。
  7. list成员用于将该spi_master链接至系统的spi_master_list链表上

include/linux/spi/spi.h 

/* Compatibility layer */

#define spi_master spi_controller 

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 */
	u32			mode_bits;

	/* spi_device.mode flags override flags for this controller */
	u32			buswidth_override_bits;

	/* Bitmask of supported bits_per_word for transfers */
	u32			bits_per_word_mask;
#define SPI_BPW_MASK(bits) BIT((bits) - 1)
#define SPI_BPW_RANGE_MASK(min, max) GENMASK((max) - 1, (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_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS must select slave */
#define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently suspended */

	/* Flag indicating if the allocation of this struct is devres-managed */
	bool			devm_allocated;

	union {
		/* Flag indicating this is an SPI slave controller */
		bool			slave;
		/* Flag indicating this is an SPI target controller */
		bool			target;
	};

	/*
	 * 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;

	/* Used to avoid adding the same CS twice */
	struct mutex		add_lock;

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

	/*
	 * set_cs_timing() method is for SPI controllers that supports
	 * configuring CS timing.
	 *
	 * This hook allows SPI client drivers to request SPI controllers
	 * to configure specific CS timing through spi_set_cs_timing() after
	 * spi_setup().
	 */
	int (*set_cs_timing)(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);
	struct device *dma_map_dev;
	struct device *cur_rx_dma_dev;
	struct device *cur_tx_dma_dev;

	/*
	 * 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 kthread_work		pump_messages;
	spinlock_t			queue_lock;
	struct list_head		queue;
	struct spi_message		*cur_msg;
	struct completion               cur_msg_completion;
	bool				cur_msg_incomplete;
	bool				cur_msg_need_completion;
	bool				busy;
	bool				running;
	bool				rt;
	bool				auto_runtime_pm;
	bool				cur_msg_mapped;
	char				last_cs;
	bool				last_cs_mode_high;
	bool                            fallback;
	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);
	union {
		int (*slave_abort)(struct spi_controller *ctlr);
		int (*target_abort)(struct spi_controller *ctlr);
	};

	/*
	 * These hooks are for drivers that use a generic implementation
	 * of transfer_one_message() provided 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;
	const struct spi_controller_mem_caps *mem_caps;

	/* GPIO chip select */
	struct gpio_desc	**cs_gpiods;
	bool			use_gpio_descriptors;
	s8			unused_native_cs;
	s8			max_native_cs;

	/* Statistics */
	struct spi_statistics __percpu	*pcpu_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);

	/*
	 * Driver sets this field to indicate it is able to snapshot SPI
	 * transfers (needed e.g. for reading the time of POSIX clocks)
	 */
	bool			ptp_sts_supported;

	/* Interrupt enable state during PTP system timestamping */
	unsigned long		irq_flags;

	/* Flag for enabling opportunistic skipping of the queue in spi_sync */
	bool			queue_empty;
	bool			must_async;
};

3.1、Spi master的注册

上面分析了spi_master的数据结构定义,现在分析下其注册接口spi_register_master的实现。在之前的文档中,已经说明spi驱动框架是按照LINUX设备-总线-驱动模型进行实现的,并没有将spi master注册到spi总线上,而仅仅将spi master注册到系统的devices_kset中,隶属于LINUX系统设备集,同时隶属于spi_master_class类(spi master device并没有注册到spi总线上,而不像i2c模块,将i2c adapter也注册到i2c总线上,从这一点上而言,spi模块还是比较好的遵守了LINUX设备-总线-驱动模型的)。

Spi master注册接口的流程图如下,主要实现的功能如下:

  1. 调用device_add,将该master对应的device成员注册到系统的设备集devices_kset上,以及链接至spi_master_class类,此处主要是使用系统的设备模型对应的接口;
  2. 针对新版内核,spi模块提供队列传输模式,为该master创建worker kthread,并与master的进行绑定(该部分在后面会进行详细说明);
  3. 将该master链接到系统的spi_master_list链表上(该变量主要是在调用spi模块的设备注册接口时,根据传递的bus_num与该链表上的master进行匹配检测,从而实现将注册的spi device与对应的master的绑定);
  4. 对系统上board_list链表上注册的spi board info(尚未创建对应的spi device),若其属于该master,则为其创建spi device并注册到spi总线上,同时完成spi device与该master的绑定操作;
  5. 对于通过设备树创建的master,针对该master的of_node节点上所有的设备子节点,均创建对应的spi device,并将其注册到spi总线上,同时完成spi device与master的绑定操作。

针对spi device,其父设备均为其所绑定的spi master。

797cd332796d42ea94f6f8c4a9d96002.png

 

 

3.2、Spi master的注销

以上为spi master注册相关的处理流程分析,而针对spi master注销的流程,其主要流程与上

面的流程刚发相反,主要内容如下:

  1. 将该master从spi_master_list链表上移除;
  2. 针对该spi master上所有的子设备,调用device_unregister将其从spi总线上移除,并完成与spi driver的解绑;
  3. 若该spi master支持队列传输模式,则关闭该master对应的worker kthread。
  4. 针对该spi master对应的device,调用device_unregister,移除该设备(此处仅仅将该设备从系统设备集devices_kset上注销以及从spi_master_class类上解除对该设备的链接)

3.3、Spi device的注册与注销

上面分析了spi总线的注册与注销,此处分析spi设备的注册与注销,首先分析下spi device对应数据结构定义。主要包含该设备所依附的master、传输模式、传输速率、片选、device类型的成员变量用于实现LINUX设备模型,完成注册到spi总线上等。

 struct spi_device {
	struct device		dev;
    /*该设备所依附的master*/
	struct spi_master	*master;
    /*传输速率*/
	u32			max_speed_hz;
    /*该设备对应的片选*/
	u8			chip_select;
    /*数据传输模式(4种模式之一)*/
	u8			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 */
    /*单次传输的bit数*/
	u8			bits_per_word;
    /*中断号*/
	int			irq;
    /*spi控制器相关的私有状态与私有数据等信息*/
	void			*controller_state;
	void			*controller_data;
    /*该设备的名称,用于设备与驱动的匹配检测*/
	char			modalias[SPI_NAME_SIZE];
    /*若使用gpio作为cs,此处标注cs的序号,*/
	int			cs_gpio;	/* chip select gpio */
 
	/*
	 * 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主要是借助设备模型的device_add接口,完成将该设备注册到spi总线上。spi设备的添加接口spi_add_device的流程图如下,也就是增加了spi_setup的调用设置该设备的通信模式与通信速率,然后就调用device_add完成spi设备的注册了。

82dafe0a2c0f4bd1a94f2306dc938ec1.png

 

下面看下spi device添加的方式,主要包括如下几个方式:

  1. 在spi master注册时,完成spi 设备的创建与注册(1. 通过设备树模式,为该master下的设备完成创建与注册 2.通过注册到board_list链表上的设备信息,完成spi设备的创建与注册);
  2. 针对spi master已创建的情况,通过调用spi_register_board_info接口,实现spi device的创建与注册操作。

针对spi device,其注销接口则主要是调用device_unregister完成设备的注销操作。

四、Spi driver的注册与注销

进行spi driver的注册与注销操作,照例先分析spi driver的数据结构的定义:

include/linux/spi/spi.h 

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

该接口基本上没有定义spi私有的相关信息,基本上是对device_driver类型的重载,因此spi driver的注册应该也不会复杂,基本上也就是调用driver_register实现驱动的注册。

4.1、spi_register_driver接口实现

如下为spi_register_driver的实现,设置该驱动属于spi总线、同时设置probe、remove等函数指针,最后调用driver_register完成spi总线的注册。

drivers/spi/spi.c 

int __spi_register_driver(struct module *owner, struct spi_driver *sdrv)
{
	sdrv->driver.owner = owner;
	sdrv->driver.bus = &spi_bus_type;

	/*
	 * For Really Good Reasons we use spi: modaliases not of:
	 * modaliases for DT so module autoloading won't work if we
	 * don't have a spi_device_id as well as a compatible string.
	 */
	if (sdrv->driver.of_match_table) {
		const struct of_device_id *of_id;

		for (of_id = sdrv->driver.of_match_table; of_id->compatible[0];
		     of_id++) {
			const char *of_name;

			/* Strip off any vendor prefix */
			of_name = strnchr(of_id->compatible,
					  sizeof(of_id->compatible), ',');
			if (of_name)
				of_name++;
			else
				of_name = of_id->compatible;

			if (sdrv->id_table) {
				const struct spi_device_id *spi_id;

				spi_id = spi_match_id(sdrv->id_table, of_name);
				if (spi_id)
					continue;
			} else {
				if (strcmp(sdrv->driver.name, of_name) == 0)
					continue;
			}

			pr_warn("SPI driver %s has no spi_device_id for %s\n",
				sdrv->driver.name, of_id->compatible);
		}
	}

	return driver_register(&sdrv->driver);
}
EXPORT_SYMBOL_GPL(__spi_register_driver);

关于driver_register接口的实现流程,想详细了解的同学请查看之前写的设备驱动模型相关的文档,此处就不附上链接了,下图为driver_register以及device_register中实现设备与驱动匹配的简要流程(因spi总线未定义probe,因此当完成spi设备与驱动匹配检测后,则直接调用spi driver的probe进行探测操作)。

aa9356fa6df3471f87fc6d529bbddde1.png

 

 

4.2、spi_unregister_driver接口

该接口直接调用driver_unregister,实现驱动的注销以及与device的解绑等操作,此接口为LINUX设备-总线-驱动模型中的接口。

以上即为spi driver的注册与注销,相对来说还是比较简单的。那我们在实现一个spi驱动时,应该注意哪些事情呢:

  1. 确认该spi设备支持的通信方式、片选;
  2. 确认该spi设备驱动在进行探测时,需要对哪些寄存器进行初始化(针对初始化操作,在driver的probe接口中实现);
  3. 为了应用层程序可以和spi设备进行通信,提供字符设备文件(read、write、ioctl),并实现相应的操作接口(在driver的probe接口中实现字符设备的注册)。

 

include/linux/spi/spi.h 

/**
 * spi_unregister_driver - reverse effect of spi_register_driver
 * @sdrv: the driver to unregister
 * Context: can sleep
 */
static inline void spi_unregister_driver(struct spi_driver *sdrv)
{
	if (sdrv)
		driver_unregister(&sdrv->driver);
}

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值