【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;
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

奶油芝士汉堡包

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值