天天看點

Linux spi驅動架構分析(一)Linux spi驅動架構

本次的spi專題,主要參考:這位大佬的部落格

感覺大佬們的無私奉獻,而我寫部落格的初衷除了記錄自己的學習經曆之外,同時也分享給大家,分享知識。

系列文章:

Linux spi驅動架構分析(一)

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

	......

};
           

繼續閱讀