Linux驱动:SPI设备驱动(基于Freescale i.MX6ULL平台了解SPI的驱动框架,顺便写个W25Q32驱动)

1、简介

  Linux中的SPI驱动框架和I2C驱动框架类似,都是分为控制器驱动设备驱动,同样地,前者也是SoC厂商写好的,后者才是针对某个具体的外设。

2、SPI控制器、设备驱动的结构体定义

2.1 结构体定义–SPI控制器

SPI控制器驱动的结构体在include/linux/spi/spi.h中定义:

struct spi_master {
    
    
	struct device	dev;
	struct list_head list;
	// ...
	int			(*setup)(struct spi_device *spi);

	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);

	void			(*cleanup)(struct spi_device *spi);
	// ...
	int (*transfer_one_message)(struct spi_master *master,
				    struct spi_message *mesg);
	// ...
	int (*transfer_one)(struct spi_master *master, struct spi_device *spi,
			    struct spi_transfer *transfer);
	// ...
};

同样地,也需要将结构体告诉内核,相关函数如下:

struct spi_master *spi_alloc_master(struct device *dev, unsigned size);	 // 用于分配一个spi_master,dev为platform设备
void spi_master_put(struct spi_master *master);			// 与前者相反,用于释放

int spi_register_master(struct spi_master *master);		// 将spi_master注册进内核
void spi_unregister_master(struct spi_master *master);	// 与前者相反,注销

2.2 结构体定义–SPI设备

SPI设备的结构体在include/linux/spi/spi.h中定义:

struct spi_device {
    
    
	struct device		dev;
	struct spi_master	*master;  // 包含“SPI控制器”成员
	u32			max_speed_hz;
	u8			chip_select;
	u8			bits_per_word;
	u16			mode;
	// ...
	int			irq;
	void			*controller_state;
	void			*controller_data;
	char			modalias[SPI_NAME_SIZE];
	int			cs_gpio;
};

  如果使用设备树的写法,则SPI设备在系统解析设备树的时候会配置该结构体,不需要我们手动配置。而传统不使用设备树则可以通过以下函数来注册进内核:

// 注册方法1
struct spi_device *spi_new_device(struct spi_master *master, struct spi_board_info *board_info);

// 注册方法2(两阶段操作,与前者不同,注册之前可以先配置spi_device)
struct spi_device *spi_alloc_device(struct spi_master *master);
int spi_add_device(struct spi_device *spi);

// 注销
void spi_unregister_device(struct spi_device *spi);

2.3 结构体定义–SPI驱动

SPI驱动的结构体在include/linux/spi/spi.h中定义:

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

驱动的结构体也要注册进内核,相关函数如下:

int spi_register_driver(struct spi_driver *sdrv);
void spi_unregister_driver(struct spi_driver *sdrv);

3、SPI总线、设备、驱动、硬件操作的联系

3.1 SPI控制器驱动加载到内核的过程

以Freescale i.MX6ULL,Linux 4.1.15版本内核为例,在设备树文件imx6ull.dtsi中可以看到SPI节点如下:

ecspi1: ecspi@02008000 {
    
    
	#address-cells = <1>;
	#size-cells = <0>;
	compatible = "fsl,imx6ul-ecspi", "fsl,imx51-ecspi";
	reg = <0x02008000 0x4000>;
	interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
	clocks = <&clks IMX6UL_CLK_ECSPI1>,
		 <&clks IMX6UL_CLK_ECSPI1>;
	clock-names = "ipg", "per";
	dmas = <&sdma 3 7 1>, <&sdma 4 7 2>;
	dma-names = "rx", "tx";
	status = "disabled";
};

  直接grep搜索compatible属性"fsl,imx6ul-ecspi",发现在drivers/spi/spi-imx.c中有使用,如果查看整体架构就会发现SPI控制器驱动也还是使用的platform平台设备驱动方式:

static const struct of_device_id spi_imx_dt_ids[] = {
    
    
	// ...
	{
    
     .compatible = "fsl,imx6ul-ecspi", .data = &imx6ul_ecspi_devtype_data, },
	{
    
     /* sentinel */ }
};

static struct platform_driver spi_imx_driver = {
    
    
	.driver = {
    
    
		   .name = DRIVER_NAME,
		   .of_match_table = spi_imx_dt_ids,
		   .pm = IMX_SPI_PM,
	},
	.id_table = spi_imx_devtype,
	.probe = spi_imx_probe,
	.remove = spi_imx_remove,
};
module_platform_driver(spi_imx_driver);

3.2 SPI控制器驱动如何操作硬件

以Freescale i.MX6ULL,Linux 4.1.15版本内核为例:

spi_imx->bitbang.txrx_bufs = spi_imx_transfer;
	spi_imx_pio_transfer(spi, transfer);
		struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);
		spi_imx->tx_buf = transfer->tx_buf;
		spi_imx->rx_buf = transfer->rx_buf;
		spi_imx_push(spi_imx);	// 启动传输

3.3 SPI设备 <-----> SPI驱动

  不管是SPI控制器、SPI设备、还是SPI驱动,注册时都会指定总线类型,而总线类型结构体里包含match匹配函数。以SPI驱动方面为例:

// 驱动注册函数里面指定总线类型spi_bus_type:drivers/spi/spi.c
int spi_register_driver(struct spi_driver *sdrv)
{
    
    
	sdrv->driver.bus = &spi_bus_type;		// ------->
	// ...
	return driver_register(&sdrv->driver);
}

struct bus_type spi_bus_type = {
    
    
	.name		= "spi",
	.dev_groups	= spi_dev_groups,
	.match		= spi_match_device,			// ------->
	.uevent		= spi_uevent,
};

static int spi_match_device(struct device *dev, struct device_driver *drv)
{
    
    
	const struct spi_device	*spi = to_spi_device(dev);
	const struct spi_driver	*sdrv = to_spi_driver(drv);

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then try ACPI */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	if (sdrv->id_table)
		return !!spi_match_id(sdrv->id_table, spi);

	return strcmp(spi->modalias, drv->name) == 0;
}

3.4 SPI设备 <-----> SPI控制器

设备树方式:设备放在哪个节点下就是使用哪个控制器。

// imx6qdl-sabresd.dtsi
&ecspi1 {
    
    
	fsl,spi-num-chipselects = <1>;
	cs-gpios = <&gpio4 9 0>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi1>;
	status = "okay";

	flash: m25p80@0 {
    
    
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "st,m25p32";
		spi-max-frequency = <20000000>;
		reg = <0>;
	};
};

传统方式:创建设备时已经指定了使用哪个spi_master控制器。

struct spi_device *spi_new_device(struct spi_master *, struct spi_board_info *);

3.5 SPI驱动 <-----> SPI控制器

  事实上,SPI驱动无法“指定”哪个SPI控制器,它是通过probe函数参数里“被动”地从SPI设备的结构体里获得SPI控制器的信息,这也就是将设备、驱动分离的目的:

int spi_register_driver(struct spi_driver *sdrv);
	spi_driver.probe // int(*probe)(struct spi_device *spi);
		spi_device.master

4、SPI驱动传输数据API函数

struct spi_message {
    
    
	struct list_head	transfers;
	struct spi_device	*spi;
	unsigned		is_dma_mapped:1;
	void			(*complete)(void *context);
	void			*context;
	unsigned		frame_length;
	unsigned		actual_length;
	int			status;
	struct list_head	queue;
	void			*state;
};

struct spi_transfer {
    
    
	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;
	struct list_head transfer_list;
};

void spi_message_init(struct spi_message *m);							// 初始化消息
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m); // 将消息添加到队列尾部
int spi_sync(struct spi_device *spi, struct spi_message *message);		// 启动同步传输:阻塞到传输完毕
int spi_async(struct spi_device *spi, struct spi_message *message)		// 启动异步传输:调用后立马返回,但需要设置传输完成的回调函数complete

5、SPI设备驱动框架

5.1、SPI设备(设备树形式)

// imx6qdl-sabresd.dtsi
&ecspi1 {
    
    
	fsl,spi-num-chipselects = <1>;
	cs-gpios = <&gpio4 9 0>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi1>;
	status = "okay";

	flash: m25p80@0 {
    
    
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "st,m25p32";
		spi-max-frequency = <20000000>;
		reg = <0>;
	};
};

5.2、SPI驱动

static int xxx_open(struct inode *inode, struct file *filp)
{
    
    	
	// ...
	return 0;
}

static int xxx_close(struct inode *inode, struct file *filp)
{
    
    
	// ...
	return 0;
}

static struct file_operations xxx_fops = {
    
    
	.owner 	 = THIS_MODULE,
	.open  	 = xxx_open,
	.release = xxx_close,
};

static int xxx_probe(struct spi_device *spi)
{
    
    
	// ...
	cdev_init(&cdev, &xxx_fops);
	return 0;
}

static int xxx_remove(struct spi_device *spi)
{
    
    
	// ...
	cdev_del(&cdev);
	return 0;
}

/* 传统不使用设备树的匹配列表 */
static const struct spi_device_id xxx_id[] = {
    
    
	{
    
    "xxx", 0},				/* <用于匹配dev的名字> <私有数据> */
	{
    
    }
};

/* 使用设备树的匹配列表 */
static const struct of_device_id xxx_of_match[] = {
    
    
	{
    
    .compatible = "xxx"},	/* 与设备树节点匹配的名字 */
	{
    
    }
};

static struct spi_driver xxx_driver = {
    
    
	.probe = xxx_probe,
	.remove = xxx_remove,
	.driver = {
    
    
		.owner = THIS_MODULE,
		.name = "xxx",
		.of_match_table = xxx_of_match,
	},
	.id_table = xxx_id,
};

static int __init xxx_init(void)
{
    
    
	spi_register_driver(&xxx_driver);
	return 0;
}

static void __exit xxx_exit(void)
{
    
    
	spi_unregister_driver(&xxx_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");


6、W25Q32驱动示例

后续更新…

7、W25Q32测试程序

后续更新…

8、W25Q32测试结果

后续更新…

猜你喜欢

转载自blog.csdn.net/weixin_44498318/article/details/111400313