firefly-rk3288开发板Linux驱动——W25Q32 SPI驱动

本文深入探讨了Linux SPI驱动框架,包括SPI核心、主机控制器、设备及驱动的详细解析。并通过w25q32 NOR Flash的具体案例,展示了设备树配置、驱动编写及测试APP的全过程。

一、Linux SPI驱动框架

  Linux下的spi驱动和i2c驱动十分类似,也可以分为三个部分:SPI核心,spi主机控制器(i2c中叫做i2c适配器),spi设备。

1.spi核心

  spi核心提供了主机控制器的注册与注销方法、spi设备注册与注销方法、以及spi通信方法。源码位置位于kernel/drivers/spi/spi.c

2.spi主机控制器

  spi的主机控制器用spi_master结构体描述:

//有部分删减
struct spi_master {
   
   
	struct device	dev;
	struct list_head list;
	s16			bus_num;
	u16			num_chipselect;
	u16			dma_alignment;
	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;

	int			(*setup)(struct spi_device *spi);

	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);
	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;
	bool				idling;
	bool				busy;
	bool				running;
	bool				rt;
	bool				auto_runtime_pm;
	bool                            cur_msg_prepared;
	bool				cur_msg_mapped;
	struct completion               xfer_completion;
	size_t				max_dma_len;

	void (*set_cs)(struct spi_device *spi, bool enable);
	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;

	/* statistics */
	struct spi_statistics	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;
};

transfer函数就是spi的通信函数,和 i2c_algorithm 中的 master_xfer 函数一样,控制器数据传输函数。

spi的主机端最终会和硬件上的spi控制器打交道,因此spi主机控制器的驱动一般都是由芯片厂商完成的,他们的主要工作就是实现transfer函数。

spi核心提供的常用spi主机控制器的API函数如下:

/*申请一个spi主机控制器*/
struct spi_master *spi_alloc_master(struct device *dev, unsigned size)

/*释放一个spi主机控制器*/
static inline void spi_master_put(struct spi_master *master)

/*向内核注册一个spi主机控制器*/
int spi_register_master(struct spi_master *master)

/*从内核注销一个spi主机控制器*/
void spi_unregister_master(struct spi_master *master)

RK3288开发板的spi0主机控制器的设备树节点如下:

	spi0: spi@ff110000 {
   
   
		compatible = "rockchip,rk3288-spi", "rockchip,rk3066-spi";
		clocks = <&cru SCLK_SPI0>, <&cru PCLK_SPI0>;
		clock-names = "spiclk", "apb_pclk";
		dmas = <&dmac_peri 11>, <&dmac_peri 12>;
		dma-names = "tx", "rx";
		interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
		pinctrl-names = "default";
		pinctrl-0 = <&spi0_clk &spi0_tx &spi0_rx &spi0_cs0>;
		reg = <0x0 0xff110000 0x0 0x1000>;
		#address-cells = <1>;
		#size-cells = <0>;
		status = "disabled";
	};
	
    /*spi0引脚配置节点*/
	spi0 {
   
   
			spi0_clk: spi0-clk {
   
   
				rockchip,pins = <5 12 RK_FUNC_1 &pcfg_pull_up>;
			};
			spi0_cs0: spi0-cs0 {
   
   
				rockchip,pins = <5 13 RK_FUNC_1 &pcfg_pull_up>;
			};
			spi0_tx: spi0-tx {
   
   
				rockchip,pins = <5 14 RK_FUNC_1 &pcfg_pull_up>;
			};
			spi0_rx: spi0-rx {
   
   
				rockchip,pins = <5 15 RK_FUNC_1 &pcfg_pull_up>;
			};
			spi0_cs1: spi0-cs1 {
   
   
				rockchip,pins = <5 16 RK_FUNC_1 &pcfg_pull_up>;
			};
		};

与之对应的spi0主机控制器的驱动源码位于kernel/drivers/spi/spi-rockchip.c

static const struct of_device_id rockchip_spi_dt_match[] = {
   
   
	{
   
    .compatible = "rockchip,px30-spi",   },
	{
   
    .compatible = "rockchip,rv1108-spi", },
	{
   
    .compatible = "rockchip,rk3036-spi", },
	{
   
    .compatible = "rockchip,rk3066-spi", },
	{
   
    .compatible = "rockchip,rk3188-spi", },
	{
   
    .compatible = "rockchip,rk3228-spi", },
	{
   
    .compatible = "rockchip,rk3288-spi", },
	{
   
    .compatible = "rockchip,rk3368-spi", },
	{
   
    .compatible = "rockchip,rk3399-spi", },
	{
   
    },
};
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);

static struct platform_driver rockchip_spi_driver = {
   
   
	.driver = {
   
   
		.name	= DRIVER_NAME,
		.pm = &rockchip_spi_pm,
		.of_match_table = of_match_ptr(rockchip_spi_dt_match),
	},
	.probe = rockchip_spi_probe,
	.remove = rockchip_spi_remove,
};

可以看到rockchip_spi_dt_match匹配表中有很多芯片的compatible属性,只要设备树中有一个与之匹配,那么rockchip_spi_driver结构体的rockchip_spi_probe函数就会执行。

RK3288开发板的spi的通信函数为rockchip_spi_transfer_one。

static int rockchip_spi_transfer_one(
		struct spi_master *master,
		struct spi_device *spi,
		struct spi_transfer *xfer)

rockchip_spi_transfer_one函数最终会调用下面两个函数来完成与硬件的数据交互。

static int rockchip_spi_pio_transfer(struct rockchip_spi *rs)
static void rockchip_spi_pio_reader(struct rockchip_spi *rs)

3.spi设备和驱动

spi设备通过spi_device结构体描述。

struct spi_device {
   
   
	struct device		dev;
	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;
	char			modalias[SPI_NAME_SIZE];
	int			cs_gpio;	/* chip select gpio */

	/* the statistics */
	struct spi_statistics	statistics;
};

spi设备驱动通过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;
};

当spi设备和驱动匹配后,spi_driver结构体的probe函数就会执行。

spi_driver的常用的API函数如下:

/*spi_driver注册函数*/
#define spi_register_driver(driver)

/*spi_driver注销函数*/
static inline void spi_unregister_driver(struct spi_driver *sdrv)

4.spi设备和驱动匹配过程

spi设备和驱动的匹配是由spi总线完成的,spi总线定义如下:

struct bus_type spi_bus_type = {
   
   
	.name		= "spi",
	.dev_groups	= spi_dev_groups,
	.match		= spi_match_device,
	.uevent		= spi_uevent,
};
EXPORT_SYMBOL_GPL(spi_bus_type);

spi_match_device就是匹配函数,分析如下:

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

	/* 设备树匹配方式 */
	if (of_driver_match_device(dev,
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

知否,知否

来一杯冰美式把

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值