Linux中SPI驱动简介及其简单编写流程

26 篇文章 31 订阅

1、SPI 驱动框架简介

SPI 驱动框架和 I2C 很类似,都分为主机控制器驱动和设备驱动,主机控制器也就是 SOC的 SPI 控制器接口。不管是什么 SPI 设备, SPI 控制器部分的驱动都是一样,我们的重点就落在了种类繁多的 SPI 设备驱动。

Linux内核将 SPI 驱动分为两部分:

①、 SPI 主机驱动

②、SPI 设备驱动

SPI 协议相关内容参考:SPI协议详解

1.1、SPI 主机驱动

SPI 主机驱动就是 SOC 的 SPI 控制器驱动,类似 I2C 驱动里面的适配器驱动。 Linux 内核使用 spi_master 表示 SPI 主机驱动, spi_master 是个结构体,定义在 include/linux/spi/spi.h 文件中,内容如下:

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;

	/* chipselects will be integral to many controllers; some others
	 * might use board-specific GPIOs.
	 */
	u16			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
	 */
	int			(*transfer)(struct spi_device *spi,
						struct spi_message *mesg);

	/* called on release() to free memory provided by spi_master */
	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.
	 */
	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.
	 */
	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;

	int (*prepare_transfer_hardware)(struct spi_master *master);
	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);
	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;

	/* 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 函数和 i2c_algorithm 中的 master_xfer 函数一样,控制器数据传输函数
transfer_one_message 函数用于 SPI 数据发送,用于发送一个 spi_message,SPI 的数据会打包成 spi_message,然后以队列方式发送出去

SPI 主机端最终会通过 transfer 函数与 SPI 设备进行通信,因此对于 SPI 主机控制器的驱动编写者而言 transfer 函数是需要实现的,因为不同的 SOC 其 SPI 控制器不同,寄存器都不一样。和 I2C 适配器驱动一样, SPI 主机驱动一般都是 SOC 厂商去编写的,所以我们作为 SOC 的使用者,这一部分的驱动就不用操心了,除非你是在 SOC 原厂工作,内容就是写 SPI 主机驱动。

SPI 主机驱动的核心就是申请 spi_master,然后初始化 spi_master,最后向 Linux 内核注册spi_master。

1.1.1、申请 spi_master

spi_alloc_master 函数用于申请 spi_master,函数原型如下:

struct spi_master *spi_alloc_master(struct device *dev,unsigned size)

函数参数和返回值含义如下:
dev:设备,一般是 platform_device 中的 dev 成员变量。
size: 私有数据大小,可以通过 spi_master_get_devdata 函数获取到这些私有数据。
返回值: 申请到的 spi_master。

当然申请与释放是一组API,有申请就有释放嘛

spi_master 的释放通过 spi_master_put 函数来完成,当我们删除一个 SPI 主机驱动的时候就需要释放掉前面申请的 spi_master, spi_master_put 函数原型如下:

释放 spi_master

void spi_master_put(struct spi_master *master)

函数参数和返回值含义如下:
master:要释放的 spi_master。
返回值: 无。

1.1.2、注册 spi_master

当 spi_master 初始化完成以后就需要将其注册到 Linux 内核, spi_master 注册函数为spi_register_master,函数原型如下:

int spi_register_master(struct spi_master *master)

函数参数和返回值含义如下:

master:要注册的 spi_master。

返回值: 0,成功;负值,失败。

当然注册与注销是一组API,有注册就有注销嘛

如果要注销 spi_master 的话可以使用 spi_unregister_master 函数,此函数原型为;

void spi_unregister_master(struct spi_master *master)

函数参数和返回值含义如下:

master:要注销的 spi_master。

返回值: 无。

1.2、SPI设备驱动

Linux 内核使用 spi_driver 结构体来表示 spi 设备驱动,我们在编写 SPI 设备驱动的时候需要实现 spi_driver。 spi_driver 结构体定义在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;
};

当 SPI 设备和驱动匹配成功以后 probe 函数就会执行。

spi_driver 初始化完成以后需要向 Linux 内核注册

spi_driver 注册函数为spi_register_driver,函数原型如下:

int spi_register_driver(struct spi_driver *sdrv)

函数参数和返回值含义如下:

sdrv: 要注册的 spi_driver。

返回值: 0,注册成功;赋值,注册失败。

注销 SPI 设备驱动以后也需要注销掉前面注册的 spi_driver,使用 spi_unregister_driver 函数完成 spi_driver 的注销,函数原型如下:

void spi_unregister_driver(struct spi_driver *sdrv)

函数参数和返回值含义如下:

sdrv: 要注销的 spi_driver。

返回值: 无。

1.3、spi_driver 注册示例

/* probe 函数 */
static int xxx_probe(struct spi_device *spi)
{
	/* 具体函数内容 */
	return 0;
}

/* remove 函数 */
static int xxx_remove(struct spi_device *spi)
{
	/* 具体函数内容 */
	return 0;
}
/* 传统匹配方式 ID 列表 */
static const struct spi_device_id xxx_id[] = {
	{"xxx", 0},
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
	{ .compatible = "xxx" },
	{ /* Sentinel */ }
};

/* SPI 驱动结构体 */
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)
{
	 return spi_register_driver(&xxx_driver);
}

 /* 驱动出口函数 */
static void __exit xxx_exit(void)
{
	spi_unregister_driver(&xxx_driver);
}

module_init(xxx_init);
module_exit(xxx_exit);

2、SPI驱动的数据收发

SPI 设备驱动的核心是 spi_driver。当我们向 Linux 内核注册成功 spi_driver 以后就可以使用 SPI 核心层提供的 API 函数来对设备进行读写操作了。首先是 spi_transfer 结构体,此结构体用于描述 SPI 传输信息,include\linux\spi\spi.h 结构体内容如下:

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;
	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;
};
成员描述
tx_buf保存着要发送的数据。
rx_buf用于保存接收到的数据。
len是要进行传输的数据长度,

SPI 是全双工通信,因此在一次通信中发送和接收的字节数都是一样的,所以 spi_transfer 中也就没有发送长度和接收长度之分。

spi_transfer 需要组织成 spi_message, spi_message 也是一个结构体,内容如下:

struct spi_message {
	struct list_head	transfers;

	struct spi_device	*spi;

	unsigned		is_dma_mapped:1;

	/* 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;
	unsigned		frame_length;
	unsigned		actual_length;
	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;
};

在使用spi_message之前需要对其进行初始化, spi_message初始化函数为spi_message_init,函数原型如下:

void spi_message_init(struct spi_message *m)

函数参数和返回值含义如下:

m: 要初始化的 spi_message。

返回值: 无

spi_message 初始化完成以后需要将 spi_transfer 添加到 spi_message 队列中,这里我们要用到 spi_message_add_tail 函数,此函数原型如下:

void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)

函数参数和返回值含义如下:

t: 要添加到队列中的 spi_transfer。

m: spi_transfer 要加入的 spi_message。

返回值: 无。

spi_message 准备好以后既可以进行数据传输了,数据传输分为同步传输和异步传输,同步传输会阻塞的等待 SPI 数据传输完成,同步传输函数为 spi_sync,函数原型如下:

int spi_sync(struct spi_device *spi, struct spi_message *message)

函数参数和返回值含义如下:

spi: 要进行数据传输的 spi_device。

message:要传输的 spi_message。

返回值: 无。

异步传输不会阻塞的等到 SPI 数据传输完成,异步传输需要设置 spi_message 中的 complete成员变量, complete 是一个回调函数,当 SPI 异步传输完成以后此函数就会被调用。 SPI 异步传输函数为 spi_async,函数原型如下:

int spi_async(struct spi_device *spi, struct spi_message *message)

函数参数和返回值含义如下:

spi: 要进行数据传输的 spi_device。

message:要传输的 spi_message。

返回值: 无。

2.1、总结

SPI 数据传输步骤如下(采用同步传输方式来完成 SPI 数据的传输工作):

  • ①、申请并初始化 spi_transfer,设置 spi_transfer 的 tx_buf 成员变量, tx_buf 为要发送的数据。然后设置 rx_buf 成员变量,rx_buf 保存着接收到的数据。最后设置 len 成员变量,也就是要进行数据通信的长度。
  • ②、使用 spi_message_init 函数初始化 spi_message。
  • ③、使用spi_message_add_tail函数将前面设置好的spi_transfer添加到spi_message队列中。
  • ④、使用 spi_sync 函数完成 SPI 数据同步传输。

传输框架如下:


/* SPI 多字节发送 */
static int spi_send(struct spi_device *spi, u8 *buf, int len)
{
	int ret;
	struct spi_message m;
	struct spi_transfer t = {
		.tx_buf = buf,
		.len = len,
	};
	spi_message_init(&m); /* 初始化 spi_message */
	spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
	ret = spi_sync(spi, &m); /* 同步传输 */
	return ret;
}
/* SPI 多字节接收 */
static int spi_receive(struct spi_device *spi, u8 *buf, int len)
{
	int ret;
	struct spi_message m;
	struct spi_transfer t = {
		.rx_buf = buf,
		.len = len,
	};
	spi_message_init(&m); /* 初始化 spi_message */
	spi_message_add_tail(t, &m);/* 将 spi_transfer 添加到 spi_message 队列 */
	ret = spi_sync(spi, &m); /* 同步传输 */
	return ret;
}

内核中也有很多封装好的函数供我们使用比如:

static inline int spi_write(struct spi_device *spi, const void *buf, size_t len)

static inline int spi_read(struct spi_device *spi, void *buf, size_t len)

extern int spi_write_then_read(struct spi_device *spi,const void *txbuf, unsigned n_tx,void *rxbuf, unsigned n_rx);

static inline ssize_t spi_w8r8(struct spi_device *spi, u8 cmd)

static inline ssize_t spi_w8r16(struct spi_device *spi, u8 cmd)

static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)

其实向这些函数也是如我们上面的事例一样封装的,不过内核做好了我们可以直接拿来用。

spi_write原型如下:

static inline int
spi_write(struct spi_device *spi, const void *buf, size_t len)
{
	struct spi_transfer	t = {
			.tx_buf		= buf,
			.len		= len,
		};
	struct spi_message	m;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);
	return spi_sync(spi, &m);
}

spi_read原型如下:

static inline int
spi_read(struct spi_device *spi, void *buf, size_t len)
{
	struct spi_transfer	t = {
			.rx_buf		= buf,
			.len		= len,
		};
	struct spi_message	m;

	spi_message_init(&m);
	spi_message_add_tail(&t, &m);
	return spi_sync(spi, &m);
}
  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值