1.针对 I2C 和 SPI 设备寄存器的操作都是通过相关的 API 函数进行操作的。这样 Linux 内核中就会充斥着大量的重复、冗余代码,但是这些本质上都是对寄存器的操作,所以为了方便内核开发人员统一访问I2C/SPI 设备的时候,为此引入了 Regmap 子系统,使用 Regmap API 函数来读写 I2C/SPI 设备寄器。
2.Linux 下大部分设备的驱动开发都是操作其内部寄存器,比如 I2C/SPI 设备的本质都是一样的,通过I2C/SPI 接口读写芯片内部寄存器。芯片内部寄存器也是同样的道理,比如 I.MX6ULL的 PWM、定时器等外设初始化,最终都是要落到寄存器的设置上。
3.Linux 下使用 i2c_transfer 来读写 I2C 设备中的寄存器, SPI 接口的话使用 spi_write/spi_read等。 I2C/SPI 芯片又非常的多,因此 Linux 内核里面就会充斥了大量的 i2c_transfer 这类的冗余代码,再者,代码的复用性也会降低。
4.基于代码复用的原则, 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 操作次数,提高访问效率。
5.regmap 框架结构
regmap 框架分为三层:
①、底层物理总线: regmap 就是对不同的物理总线进行封装,目前 regmap 支持的物理总线有 i2c、 i3c、 spi、 mmio、 sccb、 sdw、 slimbus、 irq、 spmi 和 w1。
②、 regmap 核心层,用于实现 regmap,我们不用关心具体实现。
③、 regmap API 抽象层, regmap 向驱动编写人员提供的 API 接口,驱动编写人员使用这些API 接口来操作具体的芯片设备,也是驱动编写人员重点要掌握的。
6.相关函数
/* regmap申请和初始化 */
icm20608dev.regmap_config.reg_bits = 8; /* 寄存器长度 8bit */
icm20608dev.regmap_config.val_bits = 8; /* 值长度 8bit */
icm20608dev.regmap_config.read_flag_mask = 0x80; /* 读掩码 */
icm20608dev.regmap = regmap_init_spi(spi, &icm20608dev.regmap_config);
static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg)
{
u8 ret = 0;
unsigned int data;
ret = regmap_read(dev->regmap, reg, &data);
return (u8)data;
}
static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value)
{
u8 ret = 0;
ret = regmap_write(dev->regmap, reg, value);
}