Regmap API (IIC,SPI)

Regmap API

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

什么是 Regmap ?

Linux 下大部分设备的驱动开发都是操作其内部寄存器,比如 I2C/SPI 设备的本质都是一样的,通过 I2C/SPI 接口读写芯片内部寄存器。
Linux 下使用 i2c_transfer 来读写 I2C 设备中的寄存器,SPI 接口的话使用 spi_write/spi_read等。I2C/SPI 芯片又非常的多,因此 Linux 内核里面就会充斥了大量的 i2c_transfer 这类的冗余代码,再者,代码的复用性也会降低。比如 icm20608 这个芯片既支持 I2C 接口,也支持 SPI 接口。假设我们在产品设计阶段一开始将 icm20608 设计为 SPI 接口,但是后面发现 SPI 接口不够用,或者 SOC 的引脚不够用,我们要将 icm20608 改为 I2C 接口。这个时候 icm20608 驱动就要大改,我们要将 SPI 接口换为 I2C 的,工作量比较大。
基于代码复用的原则,Linux 内核引入了 regmap 模型,regmap 将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用 SPI 或者 I2C 接口 API 函数,统一使用 regmapAPI函数。这样的好处就是统一使用 regmap,降低了代码冗余,提高了驱动的可以移植性。
通过 regmap 模型提供的统一接口函数来访问器件的寄存器,SOC 内部的寄存器也可以使用 regmap 接口函数来访问。
接下来说说regmap 模型的优缺点:
1.减少慢速 I/O 在驱动上的冗余开销
2.降低了低速 I/O 的操作次数,提高了访问效率,缺点是实时性会降低。

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
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:需要更新的位值。
比如要将寄存器的 bit1 和 bit2 置 1,那么 mask 应该设置为 0X00000011,此时 val 的 bit1 和 bit2 应该设置为 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_config 掩码设置

结构体 regmap_config 里面有三个关于掩码的成员变量:其中read_flag_mask 和 write_flag_mask,这二个掩码非常重要,接下来我们来学习一下如何使用这三个掩码。在使用 spi 接口的时候,读取外设器件寄存器的时候地址最高位必须置 1,写内部寄存器的是时候地址最高位要设置为 0。因此这里就涉及到对寄存器地址最高位的操作。
比如我们在使用 SPI 接口函数读取icm20608 内部寄存器的时候手动将寄存器地址的最高位置 1,代码如下所示:

1 static int icm20608_read_regs(struct icm20608_dev *dev, u8 reg,
void *buf, int len) 
2 { 
3 
......
21 txdata[0] = reg | 0x80; /* 写数据的时候首寄存器地址 bit7 要置 1 */ 
22 t->tx_buf = txdata; /* 要发送的数据 */
23 t->rx_buf = rxdata; /* 要读取的数据 */
24 t->len = len+1; /* t->len=发送的长度+读取的长度 */
25 spi_message_init(&m); /* 初始化 spi_message */
26 spi_message_add_tail(t, &m);
27 ret = spi_sync(spi, &m); /* 同步发送 */
......
39 return ret;
40 }

代码其中第 21 行将寄存器的地址 bit7 置 1,表示这是一个读操作。
当我们使用 regmap 的时候就不需要手动将寄存器地址的 bit7 置 1,在初始化 regmap_config的时候直接将 read_flag_mask 设置为 0X80 即可,这样通过 regmap 读取 SPI 内部寄存器的时候就会将寄存器地址与 read_flag_mask 进行或运算,结果就是将 bit7 置 1,但是整个过程不需要我们来操作,全部由 regmap 框架来完成的。
同理 write_flag_mask 用法也一样,只是 write_flag_mask 用于写寄存器的。

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...