spi--linux驱动框架介绍(二)

linux驱动框架分为三部分: 框架核心 ,主控制器驱动,从设备驱动。

  • 框架核心:driver/spi/spi.c
  • 主机控制器驱动:driver/spi/spi-rockchip.c (举例rk3399的spi)
  • 从设备驱动:driver/spi/spidev.c (linux中自带的从设备驱动,功能有限)

这些三部分的实现 离不开 如下结构体:
(路径:include/linux/spi/spi.h)

  • struct spi_device
  • struct spi_master
  • struct spi_driver
  • struct spi_transfer
  • struct spi_message
  • struct spi_board_info

详解介绍:

struct spi_device:

struct spi_device {
	struct device		dev;
	struct spi_master	*master;	//挂接到哪个spi主控制器上。
	u32			max_speed_hz;		//从设备支持的最大通讯速率。
	u8			chip_select;		//片选号,对应struct master ->cs_gpios[]的下标。cs_gpios[]中存放的是用于cs功能的gpio号。
	u8			bits_per_word;		//每个通讯字的字长,默认是8bit。
	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;
	char			modalias[SPI_NAME_SIZE];		//spi从设备的名字
	int			cs_gpio;	/* chip select gpio 本设备片选使用的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从设备。
注意:

  • spi总线中,同一个时间,spi控制器只能跟一个从设备进行沟通。(这点类似I2C总线)
  • spi控制器是通过cs 片选引脚的高低来控制和那个设备进行沟通。片选号对应片选引脚。
  • struct spi_device ->mode 非常重要,主要是相位(CPHA)和极性(CPOL)的搭配方式。
  • struct spi_device ->bits_per_word,指定每次读写的字长,单位bit.如果该值为0,则默认使用8bit为字长。

struct spi_board_info

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,
	 * irq is copied too
	 */
	char		modalias[SPI_NAME_SIZE];			//模块别名
	const void	*platform_data;
	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_master 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;		//总线号,对应spi主控制器
	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从设备的板信息,这个现在不常用。它的内容基本和struct spi_device一样。

struct spi_master

struct spi_master {
	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;    //总线号,一个soc内部会有多个spi主控制器,每一个主控制器可以组成一个spi总线,也就是说每一个总线号就表示一个spi主控制器

	/* chipselects will be integral to many controllers; some others
	 * might use board-specific GPIOs.
	 */
	u16			num_chipselect;		//主控制器最大连接 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_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 */

	/* 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 master's main job is to process its message queue,
	 *   selecting a chip 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

	 这个函数不可睡眠。
	 功能:添加消息到队列。
	 spi框架已经给我们实现了一个transfer函数(spi_queued_transfer()),而且推荐我们使用它提供的transfer函数。
	 */
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);

	/* called on release() to free memory provided by spi_master
	在spidev_release函数中被调用。
	*/
	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.

	 * 用于启用对DMA处理的核心支持,如果can_dma()存在并返回true,
	  *则在调用transfer_one()之前将映射传输。驱动程序不应该修改或存储xfer和dma_tx,在准备设备时必须设置dma_rx。
	 */
	bool			(*can_dma)(struct spi_master *master,
					   struct spi_device *spi,
					   struct spi_transfer *xfer);

	/*
	 * These hooks are for drivers that want to use the generic
	 * master 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.
	 这些钩子用于希望使用通用主传输队列机制的驱动程序。如果使用了这些函数,驱动程序就不能指定上面的transfer()函数。
	 随着时间的推移,我们希望SPI驱动程序逐步过渡到这个API,也就是内核系统我们使用内核自己提供的spi_master->transfer函数。
	 */
	bool				queued;					//是否已经入队的标志
	struct kthread_worker		kworker;		//类似于worker,用于创建一个内核线程。
	struct task_struct		*kworker_task;
	struct kthread_work		pump_messages;		//将kthread_work 类似于一个work,只是这个kthread_work用于内核线程的处理,这个kthread_work想要处理必须添加到thread_worker的工作链表中,然后被线程处理。
	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;				//spi主控制器的运行时,电源管理是否执行的标志
	bool                            cur_msg_prepared;
	bool				cur_msg_mapped;
	struct completion               xfer_completion;
	size_t				max_dma_len;

	int (*prepare_transfer_hardware)(struct spi_master *master);
	/* 如果我们没有实现这个函数,spi框架给我们提供了一个:spi_transfer_one_message()。
	spi_transfer_one_message()最终调用struct master -> transfer_one()函数。
	所以,如果我们使用spi_transfer_one_message()函数,则必须提供 transfer_one()函数。
	 */
	int (*transfer_one_message)(struct spi_master *master,
				    struct spi_message *mesg);
	int (*unprepare_transfer_hardware)(struct spi_master *master);
	int (*prepare_message)(struct spi_master *master,
			       struct spi_message *message);
	int (*unprepare_message)(struct spi_master *master,
				 struct spi_message *message);

	/*
	 * 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);
	/* rk3399实现了这个函数 */
	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 chip select */
	int			*cs_gpios;			//存放作为cs功能的引脚的gpio号。这里是一个数组,这里可以存放 num_chipselect个gpio号。

	/* statistics */
	struct spi_statistics	statistics;

	/* DMA channels for use with core dmaengine helpers */
	struct dma_chan		*dma_tx;		//主控制器工作使用的发送通道。当然这些通道也是需要spi主控制器在驱动中申请才能获得,而且最后还要释放
	struct dma_chan		*dma_rx;		//主控制器工作使用的发送通道。当然这些通道也是需要spi主控制器在驱动中申请才能获得,而且最后还要释放

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

struct spi_master用来描述一个 spi主控制器。

struct spi_driver

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

struct spi_driver 用来描述一个spi设备驱动。

struct spi_transfer

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;		//发送缓存,要发送数据的buff首地址
	void		*rx_buf;		//接受缓存
	unsigned	len;

	dma_addr_t	tx_dma;			//如果spi_message.is_dma_mapped是真,这个是tx的dma地址
	dma_addr_t	rx_dma;			//如果spi_message.is_dma_mapped是真,这个是rx的dma地址
	struct sg_table tx_sg;		//用于传输的Scatterlist,目前不用于客户端
	struct sg_table rx_sg;		//用于接受的Scatterlist,目前不用于客户端

	unsigned	cs_change:1;	//当前spi_transfer发送完成之后重新片选。影响此次传输之后的片选。指示本次transfer结束之后是否要重新片选并调用setup改变设置。这个标志可以减少系统开销
	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;		//一个通讯字的bit位,默认8bit.
	u16		delay_usecs;		//发送完成一个spi_transfer后延时时间,此次传输结束和片选改变之间的延时,之后就会启动另一个传输或者结束整个消息
	u32		speed_hz;			//通信时钟。如果是0,使用默认值

	struct list_head transfer_list; //用于链接到spi_message,用来连接的双向链接节点
};

struct spi_tranfer 表示一个读写缓存对。spi_transfer的传送是通过构建一个spi_message来实现的。
spi_transfer的特点如下:

  • 一个或多个spi_transfer组成一个spi_message.
  • 一个spi_message 就是一个完整的spi传输,也就是一个CS 由高到低,再由低到高的过程。
  • spi_transfer 代表一个读写缓冲对,包含接收缓冲区及发送缓冲区,其实,spi_transfer的发送是通过构建spi_message实现。
  • 通过将spi_transfer中的链表transfer_list链接到spi_message中的transfers,再以spi_message形势向底层发送数据。
  • 每个spi_transfer都可以对传输的一些参数进行设置,使得spi主控制器按照它要求的参数进行数据发送。
  • 每一个spi_transfer 都有自己的通讯速率,字宽 的要求

struct spi_message

struct spi_message {
	struct list_head	transfers;   //transfersl链表中挂接的都是struct spi_transfer

	struct spi_device	*spi;		//传输的目标从设备

	unsigned		is_dma_mapped:1;	//spi_transfer 中 tx_dma 和 rx_dma 是否已经 mapped

	/* 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;						//提供给complete的可选参数
	unsigned		frame_length;
	unsigned		actual_length;					//spi_message已经传输了的字节数
	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_master controller driver.
	 */
	struct list_head	queue;
	void			*state;
};

struct spi_message 描述一次完整的传输,即cs信号从高->底->高的传输。
spi_message 代表 spi 消息,由多个spi_transfer段组成。而且是原子执行,再消息传送完成前不会释放spi总线。

struct spi_transfer 和 struct spi_message的 关系如下:
1.一个spi_message 表示一个完整的传输,也就是CS片选线,由高到低,再由低到高的完整过程。
2.一个spi_message 由一个spi_transfer 或者多个spi_transfer 组成。
一个spi_transfer的的发送过程是:先使能spi主控制器,然后发送,最后禁止spi主控器。
	(禁止或使能spi主控制器,不会影响cs片选引脚的电平变化,cs是我们单独控制的。)
但是我们spi驱动的发送是以spi_message为单位进行传输的。
所以:
	一个完整的spi传输,如下:
cs	---|_________________________________________|------
	   |——————————|——————————|——————————|————————|    传输了4个spi_transfer.每个spi_transfer都伴随着spi控制器的使能于禁止。但cs线时钟保持低电平。
	   4个spi_transfer 传完成,表示spi_message传输完成,则cs拉高。
	      

注意:	
	1.CS片选引脚的高与低,看spi主控制器,有的是spi控制器的,有的是我们驱动开发者控制的。
	2.一个spi_message 类似于一个人跟两一个人的完整的聊天记录。
		而spi_transfer 类似于这个聊天过程中的 每一句话。
	3.在一个spi_message中的不同spi_transfer 可以设置不同的通讯字宽,速率,缓存,模式,dma.
	4.也就是说,一个完整的spi_message的传输过程,中间的速率很可能是不同的哦。
	5.所以spi主控制器的配置实在 进行spi_transfer发送前设置的。跟spi_message没有直接的关系

spi驱动框架使用的主要结构体介绍完毕。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值