Linux驱动开发SPI

Posted XXX_UUU_XXX

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动开发SPI相关的知识,希望对你有一定的参考价值。

Linux内核将SPI驱动分成两部分

  • SPI主机驱动:SOC的SPI控制器驱动。半导体厂商编写。
  • SPI设备驱动:具体SPI设备的驱动。SOC使用者编写。

SPI主机驱动

Linux定义spi_master结构体表示SPI主机驱动,include/linux/spi.spi.h。SPI主机驱动申请spi_master,初始化spi_master,向内核注册spi_master。

使用spi_alloc_master申请spi_master。

struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
  • dev:platform_device中的成员变量。
  • size:私有数据private_data大小。
  • 返回值:申请的spi_master。

使用spi_master_put释放spi_master。

void spi_master_put(struct spi_master *master)
  • master:要释放的spi_master。
  • 返回值:无。

使用spi_register_master注册spi_master。

int spi_register_master(struct spi_master *master)
  • master:要注册的spi_master。
  • 返回值:0,成功;负值,失败。

使用spi_unregister_master注销spi_master。

void spi_unregister_master(struct spi_master *master)
  • master:要注销的spi_master。
  • 返回自:无。

SPI设备驱动

Linux使用spi_driver结构体表示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;
;

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

使用spi_register_driver注册spi_driver。

int spi_register_driver(struct spi_driver *sdrv)
  • sdrv:要注册的spi_driver。
  • 返回值:0,成功;负值,失败。

使用spi_unregister_driver注销spi_driver。

void spi_unregister_driver(struct spi_driver *sdrv)
  • sdrv:要注销的spi_driver。
  • 返回值:无。

SPI设备和驱动匹配

SPI设备和驱动匹配是由SPI总线完成,SPI总线为spi_bus_type,drivers/spi/spi.c。

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;


struct bus_type spi_bus_type = 
	.name		= "spi",
	.dev_groups	= spi_dev_groups,
	.match		= spi_match_device,
	.uevent		= spi_uevent,
;

SPI设备和驱动的匹配函数为spi_match_device。of_driver_match_device完成设备树设备和驱动匹配,acpi_driver_match_device用于ACPI形式匹配,spi_match_id用于无设备树匹配。

SPI设备数据收发

SPI数据收发步骤

  1. 申请并初始化spi_transfer,设置spi_transfer的tx_buf成员变量、rx_buf成员变量、len成员变量。
  2. 使用spi_message_init初始化spi_message。
  3. 使用spi_message_add_tail将spi_transfer添加到spi_message队列中。
  4. 使用spi_sync或spi_async完成数据同步/异步传输。

Linux定义spi_transfer结构体描述SPI传输信息。

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;
;
  • tx_buf:保存要发送的数据。
  • rx_buf:保存接收到的数据。
  • len:数据传输长度。

spi_transfer需要组织成spi_message结构体。

struct spi_message 
	struct list_head	transfers;

	struct spi_device	*spi;

	unsigned		is_dma_mapped:1;

	/* completion is reported through a callback */
	void			(*complete)(void *context);
	void			*context;
	unsigned		frame_length;
	unsigned		actual_length;
	int			status;

	struct list_head	queue;
	void			*state;
;

使用spi_message_init初始化spi_message。

void spi_message_init(struct spi_message *m)
  • m:要初始化的spi_message。
  • 返回值:无。

使用spi_message_add_tail将spi_transfer添加到spi_message队列中。

void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m)
  • t:要添加到队列的spi_transfer。
  • m:spi_transfer要加入的spi_message。
  • 返回值:无。 

使用spi_sync进行同步数据传输。

int spi_sync(struct spi_device *spi, struct spi_message *message)
  • spi:要进行数据传输的spi_device。
  • message:要传输的spi_message。
  • 返回值:无。

使用spi_async进行异步数据传输,异步传输完成后执行complete回调函数。

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

  • spi:要进行数据传输的spi_device。
  • message:要传输的spi_message。
  • 返回值:无。

通过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_add_tail(t, &m);
    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_add_tail(t, &m);
    ret = spi_sync(spi, &m);

    return ret;

以上是关于Linux驱动开发SPI的主要内容,如果未能解决你的问题,请参考以下文章

Linux驱动开发SPI

Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤

Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤

Linux——Linux驱动之玩转SPI(上)Linux下SPI驱动框架简析及SPI设备驱动代码框架实现步骤

i.MX6ULL驱动开发 | 13 - Linux SPI 驱动框架

i.MX6ULL驱动开发 | 13 - Linux SPI 驱动框架