Linux SPI驱动框架

  Linux SPI驱动框架分为主机控制器驱动和设备驱动,主机驱动是SOC上的SPI控制器接口,不管是什么SPI设备,控制器部分的驱动都是一样的,因此大多数情况下我们只需要实现自己对应的SPI设备驱动。

1.SPI主机驱动

  SPI主机驱动就是SOC的SPI控制器驱动,Linux使用spi_master表示主机驱动,spi_master是定义在include/linux/spi/spi.h中的结构体:其中重要的属性有

/*控制器数据传输函数*/
int (*transfer)(struct spi_device *spi, struct spi_message *mesg); 
/*用于发送一个spi_message数据,通过队列方式发送出去,SPI的数据会打包成spi_message*/
int (*transfer_one_message)(struct spi_master *master, struct spi_message *mesg); 

 SPI最终会通过transfer函数与SPI设备进行通信。SPI主机驱动的核心就是申请spi_master,然后初始化spi_master,最后向Linux内核注册spi_master

(1)spi_master申请与释放

SPI申请

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

SPI释放

void spi_master_put(struct spi_master *master) 
(2)spi_master的注册属于注销

 spi_master初始化完成之后需要注册到linux内核中,注册函数为

int spi_register_master(struct spi_master *master) 

注销spi_master

void spi_unregister_master(struct spi_master *master) 

 spi_bitbang_start 注册 spi_master 的话就要使用 spi_bitbang_stop 来注销掉spi_master

2.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_driver结构体初始化完成之后,调用spi_register_driver向内核注册,注销使用spi_unregister_driver,函数原型为:

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

3.SPI设备和驱动匹配过程

 SPI设备和驱动的匹配过程是由SPI总线来完成的,SPI总线为spi_bus_type,定义在drivers/spi/spi.c中:

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

 SPI设备和驱动的匹配函数为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);

	/* 用于完成设备树设备与驱动的匹配,比较SPI设备节点的compatible属性和of_device_id中的compatible属性是否相等*/
	if (of_driver_match_device(dev, drv))
		return 1;

	/* 用于ACPI形式的匹配*/
	if (acpi_driver_match_device(dev, drv))
		return 1;
	
	/*用于传统的,无设备树的SPI设备和驱动匹配过程,比较SPI设备名字和spi_deviice_id中的name字段是否相等*/
	if (sdrv->id_table)
		return !!spi_match_id(sdrv->id_table, spi);
	
	/*比较 spi_device 中 modalias 成员变量和 device_driver 中的 name 成员变量是否相等*/
	return strcmp(spi->modalias, drv->name) == 0;
}

4.SPI设备驱动编写流程

 SPI设备驱动最主要的工作就是初始化spi_driver结构体并向内核中注册,spi_driver中最主要的是要实现probe函数,并根据设备树中的配置信息进行匹配。

/* 在设备树中进行匹配的compatible属性 */
static struct of_device_id icm20608_of_match[] = {
   {.compatible = "wang,icm20608",},
   {},
};
/* spi_driver的实现*/
static struct spi_driver icm20608_driver = {
   .probe = icm20608_probe,
   .remove = icm20608_remove,
   .driver = {
       .name = "wang,icm20608",
       .owner = THIS_MODULE,
       .of_match_table = of_match_ptr(icm20608_of_match),
   },
};

 probe函数主要的功能是进行初始化操作和字符设备的申请,为了更好的操作spi设备,将其注册为字符设备,后续在读写操作是只需对字符设备进行读写操作。
 当成功注册spi_driver到Linux内核之后,就可以使用SPI核心层提供的API接口进行读写操作了,spi_transfer结构体用于描述SPI传输信息,结构体内容如下:

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

 spi_transfer需要组织成spi_message,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;
   /* 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在使用之前要进行一些操作,具体如下:

/* 首先要进行初始化操作 */
void spi_message_init(struct spi_message *m) 
/* 初始化完成之后要将spi_transfer添加到spi_message队列中 */
void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m);
/*数据传输分为同步传输和异步传输
   同步传输:传输会阻塞直到SPI数据传输完成 spi_sync
   异步传输:不会阻塞  spi_async*/
int spi_sync(struct spi_device *spi, struct spi_message *message)
int spi_async(struct spi_device *spi, struct spi_message *message)

 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 多字节发送 */ 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值