栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

嵌入式linux SPI驱动

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

嵌入式linux SPI驱动

        SPI驱动框架和I2C很类似,都是分为主机控制器和设备驱动,主机控制器也就是SOC的SPI控制器接口。这个也是半导体原厂已经写好了的,所以我们只需要负责种类繁多的SPI设备驱动,使用控制器提供的API函数对驱动设备的寄存器进行配置。

SPI主机驱动

        linux内核中使用spi_master表示SPI主机驱动,i2c_adapter代表I2C主机驱动。spi_master是个结构体,定义在在 include/linux/spi/spi.h 文件中,内容如下(有缩减):

         第393行,transfer函数,和i2c_algorithm中的master_xfer函数一样,控制器数据传输函数。

        第434行,tranfer_one_message函数,也用于SPI数据传输,用于发送一个spi_message,SPI的数据会打包成spi_message,然后以队列方式发送出去。也就是SPI主机端最终会通过transfer函数与SPI设备进行通讯,这个transfer函数就不需要我们管了,这个是原厂已经写好的了,我们只要会用就好。

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

1,spi_master的申请与释放

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

         dev:设备,一般是platform_device中的dev成员变量

        size:私有数据大小,可以通过spi_master_get_devdata函数获取到这些私有数据

        返回值:申请到的spi_master

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

2,spi_master的注册与注销

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

         master:要注册的spi_master

        返回值:0代表成功,负值代表失败

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

 SPI设备驱动

        spi设备驱动也和i2c设备驱动也很类似,linux内核使用spi_driver结构体来表示spi设备驱动,我们再编写SPI设备驱动的时候需要实现spi_driver。spi_driver结构体定义在include/linux/spi/spi.h 文件中,结构体内容如下:

         可以看出,spi_driver,i2c_driver和platform_driver基本一样,当SPI设备和驱动匹配成功以后probe函数就会执行。

        同样的,spi_driver初始化完后需要向linux内核注册,spi_driver注册函数为spi_register-driver,函数原型如下:

         sdrv:要注册的spi_driver。

        返回值:0代表注册成功,负值代表注册失败

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

         spi_driver注册实例程序如下:

SPI设备和驱动匹配过程     

        SPI设备和驱动的匹配过程是由SPi总线来完成的,这点和platform,I2C等驱动一样,SPI总线为spi_bus_type定义如下:

         可以看出spi_match_device就是SPI设备和驱动的匹配函数。

         spi_match_device 函数和 i2c_match_device 函数的对于设备和驱动的匹配过程基本一样。

SPI设备驱动编写流程

        1,IO的pinctrl子节点创建与修改

        首先肯定是根据所使用的IO来创建或修改pinctrl子节点,唯独要注意的是我们需要注意检查相应的IO有没有被其他设备使用

        2,spi设备节点的创建与修改

        采用设备树的情况下,SPI 设备信息描述就通过创建相应的设备子节点来完成,我们可以打开 imx6qdl-sabresd.dtsi 这个设备树头文件,在此文件里面找到如下所示内容:

         第 309 行,设置“fsl,spi-num-chipselects”属性为 1,表示只有一个设备。

        第 310 行,设置“cs-gpios”属性,也就是片选信号为 GPIO4_IO09。

         第 311 行,设置“pinctrl-names”属性,也就是 SPI 设备所使用的 IO 名字。

        第 312 行,设置“pinctrl-0”属性,也就是所使用的 IO 对应的 pinctrl 节点。

        第 313 行,将 ecspi1 节点的“status”属性改为“okay”。

        第 315~320 行,ecspi1 下的 m25p80 设备信息,每一个 SPI 设备都采用一个子节点来描述其设备信息。第 315 行的“m25p80@0”后面的“0”表示 m25p80 的接到了 ECSPI 的通道 0上。这个要根据自己的具体硬件来设置。

        第 318 行,SPI 设备的 compatible 属性值,用于匹配设备驱动。

        第 319 行,“spi-max-frequency”属性设置 SPI 控制器的最高频率,这个要根据所使用的SPI 设备来设置,比如在这里将 SPI 控制器最高频率设置为 20MHz。

        第 320 行,reg 属性设置 m25p80 这个设备所使用的 ECSPI 通道,和“m25p80@0”后面的“0”一样。

        3,SPi设备数据收发处理流程

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

         第 609 行,tx_buf 保存着要发送的数据。

        第 610 行,rx_buf 用于保存接收到的数据。

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

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

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

         m:要初始化的spi_massage

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

        t:要添加到队列中的api_transfer        

        m:spi_transfer要加入的spi_massage 

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

        spi:要进行数据传输的spi_device        

        message:要传输的spi_message 

  •  申请并初始化 spi_transfer,设置 spi_transfer 的 tx_buf 成员变量,tx_buf 为要发送的数据。然后设置rx_buf成员变量,rx_buf保存着接收到的数据,最后设置len成员变量,也就是要进行数据通信的长度
  • 使用spi_message_init函数初始化spi_message
  • 使用spi_message_add_tail函数将前面设置好的spi_transfer添加到api_message队列中
  • 使用spi_sync函数完成SPI数据同步传输
static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg, void *buf, int len)
{

	int ret = -1;
	unsigned char txdata[1];
	unsigned char * rxdata;
	struct spi_message m;
	struct spi_transfer *t;
	struct spi_device *spi = (struct spi_device *)dev->private_data;
    
	t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);	
	if(!t) {
		return -ENOMEM;
	}

	rxdata = kzalloc(sizeof(char) * len, GFP_KERNEL);	
	if(!rxdata) {
		goto out1;
	}

	
	txdata[0] = reg | 0x80;					
	t->tx_buf = txdata;			
    t->rx_buf = rxdata;			
	t->len = len+1;				
	spi_message_init(&m);		
	spi_message_add_tail(t, &m);
	ret = spi_sync(spi, &m);	
	if(ret) {
		goto out2;
	}
	
    memcpy(buf , rxdata+1, len);  

out2:
	kfree(rxdata);					
out1:	
	kfree(t);						
	
	return ret;
}


static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, u8 len)
{
	int ret = -1;
	unsigned char *txdata;
	struct spi_message m;
	struct spi_transfer *t;
	struct spi_device *spi = (struct spi_device *)dev->private_data;
	
	t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);	
	if(!t) {
		return -ENOMEM;
	}
	
	txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
	if(!txdata) {
		goto out1;
	}
	
	
	*txdata = reg & ~0x80;	
    memcpy(txdata+1, buf, len);	
	t->tx_buf = txdata;			
	t->len = len+1;				
	spi_message_init(&m);		
	spi_message_add_tail(t, &m);
	ret = spi_sync(spi, &m);	
    if(ret) {
        goto out2;
    }
	
out2:
	kfree(txdata);				
out1:
	kfree(t);					
	return ret;
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/963027.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号