Linux spi驱动框架分析(一)

news/2024/7/5 18:52:15

本次的spi专题,主要参考:这位大佬的博客
感觉大佬们的无私奉献,而我写博客的初衷除了记录自己的学习经历之外,同时也分享给大家,分享知识。

系列文章
Linux spi驱动框架分析(一)
Linux spi驱动框架分析(二)
Linux spi驱动框架分析(三)
Linux spi驱动框架分析(四)

Linux spi驱动框架

Linux下spi的驱动架构如下图:
在这里插入图片描述
从图中可以观察到spi系统的整个框架,发现跟i2c的框架很十分相似;spi驱动框架主要分为三个部分,spi控制器驱动,spi设备驱动,和spi核心。

内核把spi控制器抽象为struct spi_master结构体,设备驱动抽象为spi_driver,spi设备抽象spi_device结构体。
spi与i2c内核空间的类比:
(1)spi_master就相当于i2c中的i2c_adapter
(2)spi_driver即i2c_driver
(3)spi_device即i2c中的i2c_client
(4)spi core 即i2c core

主要相关结构体的定义
spi_driver,描述一个spi驱动,定义如下:

struct spi_driver {

	//id匹配表
	const struct spi_device_id *id_table;

	//与设备匹配时,会调用probe函数
	int			(*probe)(struct spi_device *spi);

	int			(*remove)(struct spi_device *spi);
	void			(*shutdown)(struct spi_device *spi);
	struct device_driver	driver;
};

spi_device ,描述一个spi设备:

struct spi_device {
	struct device		dev;

	//所属spi_master	
	struct spi_master	*master;

	//该设备支持的最大时钟频率
	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 */
	int			irq;
	void			*controller_state;
	void			*controller_data;

	//设备的name
	char			modalias[SPI_NAME_SIZE];

	//片选对应的gpio
	int			cs_gpio;	/* chip select gpio */

	/* the statistics */
	struct spi_statistics	statistics;

};

spi_transfer,描述一个spi的读/写缓存区:

struct spi_transfer {

	//发送buf
	const void	*tx_buf;

	//接收buf
	void		*rx_buf;

	//数据长度
	unsigned	len;

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

	//节点,用于链接到spi_message的transfers链表上
	struct list_head transfer_list;
};

tx_buf和rx_buf提供了非dma模式下的数据缓冲区地址,tx_dma和rx_dma则给出了dma模式下的缓冲区地址。

对于spi_master来说,一次数据传输的单位由spi_message结构体描述,多个spi_transfer会链接到spi_message的transfers链表,可以认为spi_message就是一次SPI数据交换的原子操作。spi_master接收了一个spi_message,其中的spi_transfer应该按顺序被发送,并且不能被其它spi_message打断。

struct spi_message {
	
	//链表头,链接该spi_message的所有spi_transfer
	struct list_head	transfers;

	struct spi_device	*spi;

	unsigned		is_dma_mapped:1;

	//该message下的所有spi_transfer都被传输完成时complete会被调用
	void			(*complete)(void *context);

	void			*context;
	unsigned		frame_length;
	unsigned		actual_length;
	int			status;

	//通过该节点,把spi_message加入到消息队列上
	struct list_head	queue;

	void			*state;

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

spi_master,描述一个spi控制器,定义如下:

struct spi_master {
	struct device	dev;

	//系统上的spi_master,会链在一个全局链表上
	struct list_head list;

	//总线号
	s16			bus_num;

	//片选信号的个数
	u16			num_chipselect;

	......

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

	//支持的最小时钟频率 
	u32			min_speed_hz;
	
	//支持的最大时钟频率
	u32			max_speed_hz;

	/* 用于设定某些限制条件的标志位 */
	u16			flags;
#define SPI_MASTER_HALF_DUPLEX	BIT(0)		/* can't do full duplex */
#define SPI_MASTER_NO_RX	BIT(1)		/* can't do buffer read */
#define SPI_MASTER_NO_TX	BIT(2)		/* can't do buffer write */
#define SPI_MASTER_MUST_RX      BIT(3)		/* requires rx */
#define SPI_MASTER_MUST_TX      BIT(4)		/* requires tx */

	......

	//用于设置模式、工作时钟等
	int			(*setup)(struct spi_device *spi);

	/* 如果想采用消息队列机制,注册spi_matser时该成员要设置为NULL
	  之后会设置为spi_queued_transfer,该函数一般把spi_message加入到消息队列上
	*/
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);

	//spi_master被释放时,该函数会调用
	void			(*cleanup)(struct spi_device *spi);

	//如果spi_master支持DMA,则设置该成员
	bool			(*can_dma)(struct spi_master *master,
					   struct spi_device *spi,
					   struct spi_transfer *xfer);

	//如果spi_master提供消息队列机制则设置该成员
	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;

	......


	//单个spi_message传输函数 
	int (*transfer_one_message)(struct spi_master *master,
				    struct spi_message *mesg);

	/* 这两个回调函数用于在发起一个数据传送过程前和后,给控制器驱动一个
	机会,申请或释放某些必要的硬件资源,例如DMA资源和内存资源等等
	 */
	int (*prepare_transfer_hardware)(struct spi_master *master);
	int (*unprepare_transfer_hardware)(struct spi_master *master);

	/* 这两个回调函数也是用于在发起一个数据传送过程前和后,给控制器驱动
	一个机会,对message进行必要的预处理或后处理,如设定控制器的正确工作时钟、字长和工作模式等 */
	int (*prepare_message)(struct spi_master *master,
			       struct spi_message *message);
	int (*unprepare_message)(struct spi_master *master,
				 struct spi_message *message);

	......

	//片选函数
	void (*set_cs)(struct spi_device *spi, bool enable);

	//用于传输单个spi_transfer 
	int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
			    struct spi_transfer *transfer);

	void (*handle_err)(struct spi_master *master,
			   struct spi_message *message);

	/* 片选信号所用到的gpio引脚 */
	int			*cs_gpios;

	/* statistics */
	struct spi_statistics	statistics;

	......

};

http://www.niftyadmin.cn/n/3657306.html

相关文章

软件架构师

软件企业中有一个角色叫做软件架构师,不同公司或者不同的环境下,对该职位的定位可能不尽相同。微软首席架构师Ray Ozzie 对自己职位的一些看法,倒是给人很多启发:1. 不管是设计一座桥梁还是一幢大厦,你是在特定的情况下…

Linux spi驱动框架分析(二)

系列文章: Linux spi驱动框架分析(一) Linux spi驱动框架分析(二) Linux spi驱动框架分析(三) Linux spi驱动框架分析(四) spi core spi核心(dervers/spi/s…

Linux spi驱动框架分析(三)

系列文章: Linux spi驱动框架分析(一) Linux spi驱动框架分析(二) Linux spi驱动框架分析(三) Linux spi驱动框架分析(四) spi_master驱动 spi_master驱动负责最底层的…

Visual Studio International Pack对汉字的支持

还在重复发明轮子吗?看看微软在汉字方面的新支持:Simplified Chinese Pin-Yin Conversion Library - 支持获取简体中文字符的常用属性比如拼音,多音字,同音字,笔画数。 Traditional Chinese to Simplified Chinese Con…

Hyper-V: 一台服务器变二百台服务器

微软未来的virtualization技术会带来。。。?来自微软的邮件摘录:Q: My customer has a significant investment of 32-bit servers and wants to know why Hyper-V won’t be developed for Windows Server 2008 32-bit editions.A: Developing Hyper-V …

Linux spi驱动框架分析(四)

系列文章: Linux spi驱动框架分析(一) Linux spi驱动框架分析(二) Linux spi驱动框架分析(三) Linux spi驱动框架分析(四) spi_master的消息队列机制 SPI数据传输可以有…

Linux 输入子系统分析(一)

Linux 输入子系统分析(一) Linux 输入子系统分析(二) 分析一个内核提供的input_handler Linux内核输入子系统 输入设备(如按键、键盘、触摸屏、鼠标等)是典型的字符设备,其一般的工作机理是底…

文件的上传和下载

在Web编程中,我们常需要把一些本地文件上传到Web服务器上,上传后,用户可以通过浏览器方便地浏览这些文件,应用十分广泛。那么使用C#如何实现文件上传的功能呢?首先,在你的Visual C# web project 中增加一个上传用的Web Form,为了要上传文件,需要在ToolBox中选择HTM…