赞
踩
1)实验平台:正点原子ATK-DLRK3568开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=731866264428
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban
我们在前面学习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 };
要使用regmap,肯定要先给驱动分配一个具体的regmap结构体实例,一会讲解如何分配regmap实例。大家可以看到示例代码36.1.2.1中第90~101行有很多的函数以及table,这些需要驱动编写人员根据实际情况选择性的初始化,regmap的初始化通过结构体regmap_config来完成。
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 };
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内核中的相关描述。
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)
函数参数和返回值含义如下:
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)
函数参数和返回值含义如下:
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)
函数参数和返回值含义如下:
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
regmap_iic.h就是AP3216C器件寄存器宏定义。然后在regmap_iic.c输入如下内容:
示例代码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 = ® /* 读取的首地址 */ 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");
第27行,regmap指针变量,regmap我们需要使用regmap_init_i2c函数来申请和初始化,所以这里是指针类型。
第28行,regmap_config结构体成员变量,要来配置regmap。
第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设备内部寄存器。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。