当前位置:   article > 正文

【正点原子Linux连载】 第三十六章 Regmap API实验 摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

【正点原子Linux连载】 第三十六章 Regmap API实验 摘自【正点原子】ATK-DLRK3568嵌入式Linux驱动开发指南

1)实验平台:正点原子ATK-DLRK3568开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=731866264428
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban

第三十六章 Regmap API实验

我们在前面学习I2C驱动的时候,针对I2C设备寄存器的操作都是通过相关的API函数进行操作的。这样Linux内核中就会充斥着大量的重复、冗余代码,但是这些本质上都是对寄存器的操作,所以为了方便内核开发人员统一访问I2C设备的时候,为此引入了Regmap子系统,本章我们就来学习一下如何使用Regmap API函数来读写I2C设备寄存器。

36.1 Regmap API简介
36.1.1 什么是Regmap
Linux下大部分设备的驱动开发都是操作其内部寄存器,比如I2C/SPI设备的本质都是一样的,通过I2C/SPI接口读写芯片内部寄存器。芯片内部寄存器也是同样的道理,比如ATK-DLRK3568的PWM、TIM等外设初始化,最终都是要落到寄存器的设置上。
Linux下使用i2c_transfer来读写I2C设备中的寄存器,SPI接口的话使用spi_write/spi_read等。I2C/SPI芯片又非常的多,因此Linux内核里面就会充斥了大量的i2c_transfer这类的冗余代码,再者,代码的复用性也会降低。
基于代码复用的原则,Linux内核引入了regmap模型,regmap将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用SPI或者I2C接口API函数,统一使用regmap API函数。这样的好处就是统一使用regmap,降低了代码冗余,提高了驱动的可以移植性。regmap模型的重点在于:
通过regmap模型提供的统一接口函数来访问器件的寄存器,SOC内部的寄存器也可以使用regmap接口函数来访问。
regmap是Linux内核为了减少慢速I/O在驱动上的冗余开销,提供了一种通用的接口来操作硬件寄存器。另外,regmap在驱动和硬件之间添加了cache,降低了低速I/O的操作次数,提高了访问效率,缺点是实时性会降低。
什么情况下会使用regmap:
①、硬件寄存器操作,比如选用通过I2C/SPI接口来读写设备的内部寄存器,或者需要读写SOC内部的硬件寄存器。
②、提高代码复用性和驱动一致性,简化驱动开发过程。
③、减少底层I/O操作次数,提高访问效率。
本章教程我们就来重点学习一下如何将《第二十八章 Linux I2C驱动实验》中编写的I2C接口的AP3216C驱动改为使用regmap API。
36.1.2 Regmap驱动框架
1、regmap框架结构
regmap驱动框架如下图所示:
在这里插入图片描述

图36.1.2.1 regmap框架
regmap框架分为三层:
①、底层物理总线:regmap就是对不同的物理总线进行封装,目前regmap支持的物理总线有i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq、spmi和w1。
②、regmap核心层,用于实现regmap,我们不用关心具体实现。
③、regmap API抽象层,regmap向驱动编写人员提供的API接口,驱动编写人员使用这些API接口来操作具体的芯片设备,也是驱动编写人员重点要掌握的。
2、regmap结构体
Linux内核将regmap框架抽象为regmap结构体,这个结构体定义在文件drivers/base/regmap/internal.h中,结构体内容如下(有缩减):
示例代码36.1.2.1 regmap结构体

49  struct regmap {
50      union {
51          struct mutex mutex;
52          struct {
53              spinlock_t spinlock;
54              unsigned long spinlock_flags;
55          };
56      };
57      regmap_lock lock;
58      regmap_unlock unlock;
59      void *lock_arg; /* This is passed to lock/unlock functions */
60      gfp_t alloc_flags;
......
89      unsigned int max_register;
90      bool (*writeable_reg)(struct device *dev, unsigned int reg);
91      bool (*readable_reg)(struct device *dev, unsigned int reg);
92      bool (*volatile_reg)(struct device *dev, unsigned int reg);
93      bool (*precious_reg)(struct device *dev, unsigned int reg);
94      bool (*writeable_noinc_reg)(struct device *dev, 
unsigned int reg);
95      bool (*readable_noinc_reg)(struct device *dev, 
unsigned int reg);
96      const struct regmap_access_table *wr_table;
97      const struct regmap_access_table *rd_table;
98      const struct regmap_access_table *volatile_table;
99      const struct regmap_access_table *precious_table;
100     const struct regmap_access_table *wr_noinc_table;
101     const struct regmap_access_table *rd_noinc_table;
102 
103     int (*reg_read)(void *context, unsigned int reg, 
unsigned int *val);
104     int (*reg_write)(void *context, unsigned int reg, 
unsigned int val);
105     int (*reg_update_bits)(void *context, unsigned int reg,
106                    unsigned int mask, unsigned int val);
......
159 
160     struct rb_root range_tree;
161     void *selector_work_buf;  /* Scratch buffer used for selector */
162 
163     struct hwspinlock *hwlock;
164 };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
要使用regmap,肯定要先给驱动分配一个具体的regmap结构体实例,一会讲解如何分配regmap实例。大家可以看到示例代码36.1.2.1中第90~101行有很多的函数以及table,这些需要驱动编写人员根据实际情况选择性的初始化,regmap的初始化通过结构体regmap_config来完成。
  • 1

3、regmap_config结构体
顾名思义,regmap_config结构体就是用来初始化regmap的,这个结构体也定义在include/linux/regmap.h文件中,结构体内容如下:
示例代码36.1.2.2 regmap_config结构体

352 struct regmap_config {
353     const char *name;
354 
355     int reg_bits;
356     int reg_stride;
357     int pad_bits;
358     int val_bits;
359 
360     bool (*writeable_reg)(struct device *dev, unsigned int reg);
361     bool (*readable_reg)(struct device *dev, unsigned int reg);
362     bool (*volatile_reg)(struct device *dev, unsigned int reg);
363     bool (*precious_reg)(struct device *dev, unsigned int reg);
364     bool (*writeable_noinc_reg)(struct device *dev, 
unsigned int reg);
365     bool (*readable_noinc_reg)(struct device *dev, 
unsigned int reg);
366 
367     bool disable_locking;
368     regmap_lock lock;
369     regmap_unlock unlock;
370     void *lock_arg;
371 
372     int (*reg_read)(void *context, unsigned int reg, 
unsigned int *val);
373     int (*reg_write)(void *context, unsigned int reg, 
unsigned int val);
374 
375     bool fast_io;
376 
377     unsigned int max_register;
378     const struct regmap_access_table *wr_table;
379     const struct regmap_access_table *rd_table;
380     const struct regmap_access_table *volatile_table;
381     const struct regmap_access_table *precious_table;
382     const struct regmap_access_table *wr_noinc_table;
383     const struct regmap_access_table *rd_noinc_table;
384     const struct reg_default *reg_defaults;
385     unsigned int num_reg_defaults;
386     enum regcache_type cache_type;
387     const void *reg_defaults_raw;
388     unsigned int num_reg_defaults_raw;
389 
390     unsigned long read_flag_mask;
391     unsigned long write_flag_mask;
392     bool zero_flag_mask;
393 
394     bool use_single_read;
395     bool use_single_write;
396     bool can_multi_write;
397 
398     enum regmap_endian reg_format_endian;
399     enum regmap_endian val_format_endian;
400 
401     const struct regmap_range_cfg *ranges;
402     unsigned int num_ranges;
403 
404     bool use_hwlock;
405     unsigned int hwlock_id;
406     unsigned int hwlock_mode;
407 };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
Linux内核里面已经对regmap_config各个成员变量进行了详细的讲解,这里我们只看一些比较重要的:
第353行name:名字。
第355行reg_bits:寄存器地址位数,必填字段。
第356行reg_stride:寄存器地址步长。
第357行pad_bits:寄存器和值之间的填充位数。
第358行val_bits:寄存器值位数,必填字段。
第360行writeable_reg:可选的可写回调函数,寄存器可写的话此回调函数就会被调用,并返回true。
第361行readable_reg:可选的可读回调函数,寄存器可读的话此回调函数就会被调用,并返回true。
第362行volatile_reg:可选的回调函数,当寄存器值不能缓存的时候此回调函数就会被调用,并返回true。
第363行precious_reg:当寄存器值不能被读出来的时候此回调函数会被调用,比如很多中断状态寄存器读清零,读这些寄存器就可以清除中断标志位,但是并没有读出这些寄存器内部的值。
第372行reg_read:可选的读操作回调函数,所有读寄存器的操作此回调函数就会执行。
第373行reg_write:可选的写操作回调函数,所有写寄存器的操作此回调函数就会执行。
第375行fast_io:快速I/O,使用spinlock替代mutex来提升锁性能。
第377行max_register:有效的最大寄存器地址,可选。
第378行wr_table:可写的地址范围,为regmap_access_table结构体类型。后面的rd_table、volatile_table、precious_table、wr_noinc_table和rd_noinc_table同理。
第384行reg_defaults:寄存器模式值,为reg_default结构体类型,此结构体有两个成员变量:reg和def,reg是寄存器地址,def是默认值。
第385行num_reg_defaults:默认寄存器表中的元素个数。
第390行read_flag_mask:读标志掩码。
第391行write_flag_mask:写标志掩码。
关于regmap_config结构体成员变量就介绍这些,其他没有介绍的自行查阅Linux内核中的相关描述。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

36.1.3 Regmap操作函数
1、Regmap申请与初始化
前面说了,regmap支持多种物理总线,比如I2C和SPI,我们需要根据所使用的接口来选择合适的regmap初始化函数。Linux内核提供了针对不同接口的regmap初始化函数,SPI接口初始化函数为regmap_init_spi,函数原型如下:

struct regmap * regmap_init_spi(struct spi_device 			*spi, 
					 	   const struct regmap_config 	*config)
  • 1
  • 2

函数参数和返回值含义如下:
spi:需要使用regmap的spi_device。
config:regmap_config结构体,需要程序编写人员初始化一个regmap_config实例,然后将其地址赋值给此参数。
返回值:申请到的并进过初始化的regmap。
I2C接口的regmap初始化函数为regmap_init_i2c,函数原型如下:

struct regmap * regmap_init_i2c(struct i2c_client		 		*i2c,
				 		   const struct regmap_config 	*config)
  • 1
  • 2

函数参数和返回值含义如下:
i2c:需要使用regmap的i2c_client。
config:regmap_config结构体,需要程序编写人员初始化一个regmap_config实例,然后将其地址赋值给此参数。
返回值:申请到的并进过初始化的regmap。
还有很多其他物理接口对应的regmap初始化函数,这里就不介绍了,大家直接查阅Linux内核即可,基本和SPI/I2C的初始化函数相同
在退出驱动的时候需要释放掉申请到的regmap,不管是什么接口,全部使用regmap_exit这个函数来释放regmap,函数原型如下:
void regmap_exit(struct regmap *map)
函数参数和返回值含义如下:
map:需要释放的regmap
返回值:无。
我们一般会在probe函数中初始化regmap_config,然后申请并初始化regmap。
2、regmap设备访问API函数
不管是I2C还是SPI等接口,还是SOC内部的寄存器,对于寄存器的操作就两种:读和写。regmap提供了最核心的两个读写操作:regmap_read和regmap_write。这两个函数分别用来读/写寄存器,regmap_read函数原型如下:
int regmap_read(struct regmap *map,
unsigned int reg,
unsigned int *val)
函数参数和返回值含义如下:
map:要操作的regmap。
reg:要读的寄存器。
val:读到的寄存器值。
返回值:0,读取成功;其他值,读取失败。

regmap_write函数原型如下:
int regmap_write(struct regmap *map,
unsigned int reg,
unsigned int val)
函数参数和返回值含义如下:
map:要操作的regmap。
reg:要写的寄存器。
val:要写的寄存器值。
返回值:0,写成功;其他值,写失败。
在regmap_read和regmap_write的基础上还衍生出了其他一些regmap的API函数,首先是regmap_update_bits函数,看名字就知道,此函数用来修改寄存器指定的bit,函数原型如下:
int regmap_update_bits (struct regmap *map,
unsigned int reg,
unsigned int mask,
unsigned int val,
函数参数和返回值含义如下:
map:要操作的regmap。
reg:要操作的寄存器。
mask:掩码,需要更新的位必须在掩码中设置为1。
val:需要更新的位值。
返回值:0,写成功;其他值,写失败。
比如要将寄存器的bit1和bit0置1,那么mask应该设置为0X00000011,此时val的bit1和bit0应该设置为1,也就是0Xxxxxxx11。如果要清除寄存器的bit4和bit7,那么mask应该设置为0X10010000,val的bit4和bit7设置为0,也就是0X0xx0xxxx。
接下来看一下regmap_bulk_read函数,此函数用于读取多个寄存器的值,函数原型如下:

int regmap_bulk_read(struct regmap 	*map, 
				  	  unsigned int 	reg, 
				      void 			*val,
		     	      size_t 			val_count)
函数参数和返回值含义如下:
map:要操作的regmap。
reg:要读取的第一个寄存器。
val:读取到的数据缓冲区。
val_count:要读取的寄存器数量。
返回值:0,写成功;其他值,读失败。
	另外也有多个寄存器写函数regmap_bulk_write,函数原型如下:
int regmap_bulk_write(struct regmap 	*map, 
				   unsigned int 	reg, 
				   const void 	*val,
		     		   size_t 		val_count)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

函数参数和返回值含义如下:
map:要操作的regmap。
reg:要写的第一个寄存器。
val:要写的寄存器数据缓冲区。
val_count:要写的寄存器数量。
返回值:0,写成功;其他值,读失败。
关于regmap常用到API函数就讲解到这里,还有很多其他功能的API函数,大家自行查阅Linux内核即可,内核里面对每个API函数都有详细的讲解。接下来学习如何将《第二十八章 Linux I2C驱动实验》中编写AP3216C驱动改为regmap框架。
36.2 实验程序编写
本实验对应的例程路径为:开发板光盘1、程序源码3、Linux 驱动例程23_regmap_iic。
新建名为“22_regmap_iic”的文件夹,然后在22_regmap_iic文件夹里面创建 vscode 工程,工作区命名为“iic”。工程创建好以后新建 regmap_iic.c 和 regmap_iic.h 这两个文件,regmap_iic.c 为 AP3216C 的驱动代码,regmap_iic.h 是 AP3216C 寄存器头文件。先在 regmap_iic.h 中定义好 AP3216C 的寄存器,输入如下内容,
示例代码36.2.1 regmap_iic.h 文件代码段

1 #ifndef AP3216C_H
2 #define AP3216C_H
3 /***************************************************************
4 Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
5 文件名 : regmap_iic.h
6 作者 : 正点原子 Linux 团队
7 版本 : V1.0
8 描述 : AP3216C 寄存器地址描述头文件
9 其他 : 无
10 论坛 : www.openedv.com
11 日志 : 初版 V1.0 2021/03/19 正点原子 Linux 团队创建
12 ***************************************************************/
13
14 #define AP3216C_ADDR 0X1E /* AP3216C 器件地址 */
15
16 /* AP3316C 寄存器 */
17 #define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
18 #define AP3216C_INTSTATUS 0X01 /* 中断状态寄存器 */
19 #define AP3216C_INTCLEAR 0X02 /* 中断清除寄存器 */
20 #define AP3216C_IRDATALOW 0x0A /* IR 数据低字节 */
21 #define AP3216C_IRDATAHIGH 0x0B /* IR 数据高字节 */
22 #define AP3216C_ALSDATALOW 0x0C /* ALS 数据低字节 */
23 #define AP3216C_ALSDATAHIGH 0X0D /* ALS 数据高字节 */
24 #define AP3216C_PSDATALOW 0X0E /* PS 数据低字节 */
25 #define AP3216C_PSDATAHIGH 0X0F /* PS 数据高字节 */
26
27 #endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
regmap_iic.h就是AP3216C器件寄存器宏定义。然后在regmap_iic.c输入如下内容:
  • 1

示例代码36.2.2 regmap_iic.c文件代码段

/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名	: regmap_iic.c
作者   	: 正点原子Linux团队
版本   	: V1.0
描述   	: AP3216C IIO驱动程序
其他   	: 无
论坛   	: www.openedv.com
日志    	: 初版 V1.0 2023/08/15 正点原子Linux团队创建
***************************************************************/
1   #include <linux/types.h>
2   #include <linux/kernel.h>
3   #include <linux/delay.h>
4   #include <linux/ide.h>
5   #include <linux/init.h>
6   #include <linux/module.h>
7   #include <linux/errno.h>
8   #include <linux/gpio.h>
9   #include <linux/cdev.h>
10  #include <linux/device.h>
11  #include <linux/of_gpio.h>
12  #include <linux/semaphore.h>
13  #include <linux/timer.h>
14  #include <linux/i2c.h>
15  #include <linux/miscdevice.h>
16  #include <asm/uaccess.h>
17  #include <asm/io.h>
18  #include <linux/regmap.h>
19  #include "regmap_iic.h"
20 
21  #define AP3216C_MINOR   255
22  #define AP3216C_NAME "ap3216c"
23 
24  typedef struct {
25  	struct i2c_client *client;
26  	unsigned short ir, als, ps;
27  	struct regmap *regmap;
28  	struct regmap_config regmap_config;
29  }ap3216c_dev;
30 
31  ap3216c_dev ap3216c;
32 
33  /*
34  * @description : 从 ap3216c 读取多个寄存器数据
35  * @param – dev : ap3216c 设备
36  * @param – reg : 要读取的寄存器首地址
37  * @param – val : 读取到的数据
38  * @param – len : 要读取的数据长度
39  * @return : 操作结果
40  */
41 static int ap3216c_read_regs(ap3216c_dev *dev, u8 reg,void *val, 
int len)
42  {
43 #if 0
44  int ret;
45  struct i2c_msg msg[2];
46  struct i2c_client *client = (struct i2c_client *)dev->client;
47 
48  /* msg[0]为发送要读取的首地址 */
49  msg[0].addr = client->addr; /* ap3216c 地址 */
50  msg[0].flags = 0; /* 标记为发送数据 */
51  msg[0].buf = &reg; /* 读取的首地址 */
52  msg[0].len = 1; /* reg 长度 */
53 
54  /* msg[1]读取数据 */
55  msg[1].addr = client->addr; /* ap3216c 地址 */
56  msg[1].flags = I2C_M_RD; /* 标记为读取数据 */
57  msg[1].buf = val; /* 读取数据缓冲区 */
58  msg[1].len = len; /* 要读取的数据长度 */
59 
60  ret = i2c_transfer(client->adapter, msg, 2);
61  if(ret == 2) {
62  ret = 0;
63  } else {
64  printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
65  ret = -EREMOTEIO;
66  }
67  #endif 
68  int ret = 0;
69  	reg = regmap_read(dev->regmap,reg,(unsigned int*)val);
70      return ret;
71  }
72 
73  /*
74  * @description: 向 ap3216c 多个寄存器写入数据
75  * @param - dev: ap3216c 设备
76  * @param - reg: 要写入的寄存器首地址
77  * @param - val: 要写入的数据缓冲区
78  * @param - len: 要写入的数据长度
79  * @return : 操作结果
80  */
81 static s32 ap3216c_write_regs(ap3216c_dev *dev, u8 reg,u8 *buf, 
u8 len)
82 {
83 #if 0
84 u8 b[256];
85 struct i2c_msg msg;
86 struct i2c_client *client = (struct i2c_client *)dev->client;
87 
88 b[0] = reg; /* 寄存器首地址 */
89 memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组 b 里面 */
90 
91 msg.addr = client->addr; /* ap3216c 地址 */
92 msg.flags = 0; /* 标记为写数据 */
93 
94 msg.buf = b; /* 要写入的数据缓冲区 */
95  msg.len = len + 1; /* 要写入的数据长度 */
96 
97  return i2c_transfer(client->adapter, &msg, 1);
98  #endif
99  	regmap_write(dev->regmap, reg, (unsigned int) *buf);
100 	return 0;
101 }
102
103 /*
104 * @description : 读取 AP3216C 的数据,包括 ALS,PS 和 IR, 注意!如果同时
105 * :打开 ALS,IR+PS 两次数据读取的时间间隔要大于 112.5ms
106 * @param – ir : ir 数据
107 * @param - ps : ps 数据
108 * @param - ps : als 数据
109 * @return : 无。
110 */
111 void ap3216c_readdata(ap3216c_dev *dev)
112 {
113     unsigned char i =0;
114     unsigned char buf[6];
115 
116     /* 循环读取所有传感器数据 */
117     for(i = 0; i < 6; i++) {
118         ap3216c_read_regs(dev, AP3216C_IRDATALOW + i, buf+i, 1); 
119     }
120
121     if(buf[0] & 0X80) /* IR_OF 位为 1,则数据无效 */
122         dev->ir = 0; 
123     else /* 读取 IR 传感器的数据 */
124         dev->ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 
125
126 		/* 读取ALS传感器的数据*/
127     dev->als = ((unsigned short)buf[3] << 8) | buf[2]; 
128 
129     if(buf[4] & 0x40) /* IR_OF 位为 1,则数据无效 */
130         dev->ps = 0; 
131     else /* 读取 PS 传感器的数据 */
132    dev->ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] &0X0F);
133 }
134
135 static int lcs_ap3216c_init(ap3216c_dev *dev)
136{
137 	unsigned char buf = 0;
138 	/* 复位ap3216c */
139 	buf = 0x04;
140 	ap3216c_write_regs(dev, AP3216C_SYSTEMCONG, &buf, 1);
141
142 	/* 延时 */
143 	mdelay(50);
144
145 	/* 开启ALS、PS、IR*/
146 	buf = 0x03;
147 	ap3216c_write_regs(dev, AP3216C_SYSTEMCONG, &buf, 1);
148
149 	return 0;
150 }
151
152 /*
153 * @description : 打开设备
154 * @param – inode : 传递给驱动的 inode
155 * @param - filp : 设备文件,file 结构体有个叫做 private_data 的成员变量
156 * 一般在 open 的时候将 private_data 指向设备结构体。
157 * @return : 0 成功;其他 失败
158 */
159 static int ap3216c_open(struct inode *inode, struct file *filp)
160 {
161 	/* 设置私有数据 */
162 	filp->private_data = &ap3216c;
163 
164 	/* 初始化ap216c */
165 	lcs_ap3216c_init(&ap3216c);
166
167 	return 0;
168 }
169
170 /*
171 * @description : 从设备读取数据
172 * @param - filp : 要打开的设备文件(文件描述符)
173 * @param - buf : 返回给用户空间的数据缓冲区
174 * @param - cnt : 要读取的数据长度
175 * @param - offt : 相对于文件首地址的偏移
176 * @return : 读取的字节数,如果为负值,表示读取失败
177 */
178 static ssize_t ap3216c_read(struct file *filp, char __user *buf,
size_t cnt, loff_t *off)
179 {
180     short data[3];
181     long err = 0;
182
183 ap3216c_dev *dev = (ap3216c_dev*)filp->private_data;
184 /* 读取ap3216c数据*/
185 ap3216c_readdata(dev);
186
187     data[0] = dev->ir;
188     data[1] = dev->als;
189     data[2] = dev->ps;
190     err = copy_to_user(buf, data, sizeof(data));
191     return 0;
192 }
193
194 /*
195 * @description : 关闭/释放设备
196 * @param - filp : 要关闭的设备文件(文件描述符)
197 * @return : 0 成功;其他 失败
198 */
199 static int ap3216c_release(struct inode *inode, struct file *filp)
200 {
201     return 0;
202 }
203
204 /* AP3216C 操作函数 */
205 static const struct file_operations ap3216c_ops = {
206     .owner = THIS_MODULE,
207     .open = ap3216c_open,
208     .read = ap3216c_read,
209     .release = ap3216c_release,
210 };
211
212 /* MISC设备结构体 */
213 static struct miscdevice ap3216c_miscdev = {
214 .minor = AP3216C_MINOR,
215 .name = AP3216C_NAME,
216 .fops = &ap3216c_ops,
217 };
218
219 /*
220 * @description : i2c 驱动的 probe 函数,当驱动与
221 * 设备匹配以后此函数就会执行
222 * @param – client : i2c 设备
223 * @param - id : i2c 设备 ID
224 * @return : 0,成功;其他负值,失败
225 */
226static int ap3216c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
227 {
228     int ret = 0;
229      
230 /* 注册MISC子系统 */
231 ret = misc_register(&ap3216c_miscdev);
232 if(ret < 0){
233     printk("ap3216 misc device register failed!\r\n");
234     ret = -EFAULT;
235     }
236
237 /* 获取I2C设备 */
238 ap3216c.client = client;
239
240 /* 初始化regmap_config设置 */
241 ap3216c.regmap_config.reg_bits = 8;     /* 寄存器长度8bit */
242 ap3216c.regmap_config.val_bits = 8;     /* 值长度8bit */
243
244 /* 初始化I2C接口的regmap */
245 ap3216c.regmap = regmap_init_i2c(client, &ap3216c.regmap_config);
246 if(IS_ERR(ap3216c.regmap)){
247     return PTR_ERR(ap3216c.regmap);
248 }
249
250 return 0;
251 
252 }
253
254 /*
255 * @description : i2c 驱动的 remove 函数,移除 i2c 驱动的时候此函数会执行
256 * @param - client : i2c 设备
257 * @return : 0,成功;其他负值,失败
258 */
259static int ap3216c_remove(struct i2c_client *client)
260 {
261 int ret = 0;
262 /*释放regmap*/
263 regmap_exit(ap3216c.regmap);
264 /*MISC 驱动框架卸载*/
265 misc_deregister(&ap3216c_miscdev);
266 return ret;
267 }
268
269 /* 传统匹配方式 ID 列表 */
270 static const struct i2c_device_id ap3216c_id[] = {
271 {"alientek,ap3216c", 0}, 
272 {}
273 };
274
275 /* 设备树匹配列表 */
276 static const struct of_device_id ap3216c_of_match[] = {
277 { .compatible = "alientek,ap3216c" },
278 { /* Sentinel */ }
279 };
280
281 /* i2c 驱动结构体 */ 
282 static struct i2c_driver ap3216c_driver = {
283     .probe = ap3216c_probe,
284     .remove = ap3216c_remove,
285     .driver = {
286         .owner = THIS_MODULE,
287         .name = "ap3216c",
288         .of_match_table = ap3216c_of_match,
289 },
290     .id_table = ap3216c_id,
291 };
292 
293 /*
294 * @description : 驱动入口函数
295 * @param : 无
296 * @return : 无
297 */
298 static int __init ap3216c_init(void)
299 {
300     int ret = 0;
301
302     ret = i2c_add_driver(&ap3216c_driver);
303     return ret;
304 }
305
306 /*
307 * @description : 驱动出口函数
308 * @param : 无
309 * @return : 无
310 */
311 static void __exit ap3216c_exit(void)
312 {
313     i2c_del_driver(&ap3216c_driver);
314 }
315
316 /* module_i2c_driver(ap3216c_driver) */
317
318 module_init(ap3216c_init);
319 module_exit(ap3216c_exit);
320 MODULE_LICENSE("GPL");
321 MODULE_AUTHOR("ALIENTEK");
322 MODULE_INFO(intree, "Y");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
第27行,regmap指针变量,regmap我们需要使用regmap_init_i2c函数来申请和初始化,所以这里是指针类型。
第28行,regmap_config结构体成员变量,要来配置regmap。
  • 1
  • 2

第41~71行,ap3216c_read_regs函数用于读取ap3216c内部寄存器,这里直接使用regmap_read函数来完成寄存器的读取操作。
第81~101行,ap3216c_write_regs函数用于向ap3216c指定寄存器写入数据,这里也直接使用regmap_write函数来完成写操作。
第111~133行,ap3216c_readdata函数用于读取ap3216c内部环境光强度(ALS)、接近距离(PS)和红外线强度(IR)的数据。
第238行,获取I2C的设备。
第241~242行,regmap_config的初始化,ap3216c的寄存器地址地址长度为8bit,寄存器值也是8bit,因此reg_bits和val_bits都设置为8。
第245行,通过regmap_init_i2c函数来申请并初始化I2C总线的regmap。
剩下的就不一一描述了,都是我们前面学习过的框架内容。
对比《第二十八章节 Linux I2C驱动实验》中的I2C驱动,采用了regmap API和MISC驱动框架,驱动程序精简了很多。具体涉及到I2C总线的部分全部由regmap来处理了,驱动编写人员不用管,极大的方便了我们的驱动编写,而且驱动的可以执行提高了很多。
36.3 运行测试
测试APP直接第二十八章节编写的ap3216cApp.c文件即可。测试方法也和二十八章一样。输入如下命令:
depmod //第一次加载驱动的时候需要运行此命令
modprobe regmap_iic //加载驱动模块
./ap3216App /dev/ap3216c //app读取内部数据
如果regmapAPI工作正常,那么就会正确的初始化AP3216C,并且读出传感器数据,结果和二十八章一样,如图36.3.1所示:
在这里插入图片描述

图 36.3.1 获取的数据
SPI总线的regmap框架基本和II2C一样,只是需要使用regmap_init_spi来申请并初始化对应的regmap,同样都是使用regmap_read和regmap_write来读写SPI设备内部寄存器。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/634172
推荐阅读
相关标签
  

闽ICP备14008679号