在引入ragmap API之前,SPI内核和I2C内核是独立的,二者有需要冗余代码。在引入Regmap API之后,无论是SPI还是I2C设备,只需要初始化配置Regmap,旧可以处理所有的读写修改操作。
Regmap API很简单,其中最重要的是两个结构struct regmap_config(代表regmap配置) 和struct regmap(代表regmap实例本身)。
1. regmap_config结构
struct regmap_config在驱动程序生命周期内保存Regmap配置,这些设置会影响读/写操作。
struct regmap_config {
char *name;
int reg_bits; // 寄存器地址的位数
int reg_stride;
int pad_bits;
int val_bits; // 寄存器值的位数
bool (*writable_reg)(struct device *dev, uint reg); // 在写入Reg之前,会调用检查是否可写
bool (*readable_reg)(struct device *dev, uint reg);// 在写入Reg之前,会调用检查是否可读
bool (*volatile_reg)(struct device *dev, uint reg); // 检查寄存器是否是易失的,确定是否使用缓存
bool (*precious_reg)(struct device *dev, uint reg);
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg;
int (*reg_read)(void *context, uint reg, uint *val); // 在寄存器不支持简单的I2c/SPI读取操作时,需要自定义read函数
int (*reg_write)(void *context, uint reg, uint val);
bool fast_io;
uint max_register;
const struct regmap_access_table *wr_table; // 在没有writable_reg会调用函数时,使用该值来判断是否可写,该结构体中有yes_range和no_range,其类型为struct regmap_range{}
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
const struct reg_default *reg_defaults;
uint num_reg_defaults;
enum regcache_type cache_type;
const void *reg_defaults_raw;
uint num_reg_defaults_raw;
u8 read_flag_mask;
u8 write_flag_mask;
bool use_single_rw;
bool can_multi_write;
enum regmap_endian reg_format_endian;
enum regmap_endian val_format_endian;
const struct regmap_range_cfg *ranges;
uint num_ranges;
};
// 函数writeable_reg回调函数实现示例:
static bool foo_writeable_register(struct device *dev, uint reg) {
switch (reg) {
case 0x30 ... 0x38:
case 0x40 ... 0x45:
case 0x50 ... 0x57:
case 0x60 ... 0x69:
case 0x70 ... 0x7b:
case 0x80 ... 0x8c:
case 0x90 ... 0x9e:
case 0xa0 ... 0xa9:
case 0xb0 ... 0xb3:
return true;
default:
return false;
};
};
// 下面是一个简单的regmap_config{}实例化的示例:
static const struct regmap_config regmap_cfg = {
.reg_bits = 8,
.val_bits = 8,
.max_register = LM3533_REG_MAX,
.reagable_reg = lm3533_readable_reg,
.writeable_reg = lm3533_writeable_reg,
.volatile_reg = lm3533_volatile_reg,
.precious_reg = lm3533_precious_reg,
};
2. Regmap初始化
Regmap支持SPI和I2C协议,根据支持的协议的不同,在I2C中必须调用regmap_init_i2c()或regmap_init_spi(),如果要编写通用的驱动程序,使用regmap是最佳选择。regmap是通用和同质的,这两类总线只是初始化不同,其他功能完全相同。
在remove函数中,调用regmap_exit()释放先前分配的寄存器。
// 初始化和释放函数原型
struct regmap* regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config);
struct regmap* regmap_init_i2c(struct spi_device *spi,
const struct regmap_config);
void regmap_exit(struct regmap *regmap);
// 初始化SPI设备的示例
static int foo_spi_probe(struct spi_device *spi) {
int err;
struct regmap *my_regmap;
struct regmap_config foo_regmap_config;
[...]// 省略了初始化和填充foo_regmap_config
my_regmap = regmap_init_spi(spi, &foo_regmap_config);
if (IS_ERR(my_regmap)) {
err = PTR_ERR(my_regmap);
return err;
}
[...]// 其他初始化流程
}
// 初始化I2C设备的示例
static int bar_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) {
struct regmap *my_regmap;
struct regmap_config bar_regmap_config;
[...]// 省略了初始化和填充bar_regmap_config
spi->bit_per_word = 8;
my_regmap = regmap_init_i2c(i2c, &bar_regmap_config);
if (IS_ERR(my_regmap)) {
err = PTR_ERR(my_regmap);
return err;
}
[...]// 其他初始化流程
}
3. 使用Regmap之后的设备访问函数
Regmap API内部处理数据的解析、格式化和传输。提供了3个重要函数regmap_write, regmap_read, regmap_update_bits,其原型如下:
int regmap_write(struct regmap *map, uint reg, uint *val);
int regmap_read(struct regmap *map, uint reg, uint val);
int regmap_update_bits(struct regmap *map, uint reg, uint mask, uint val);
// 其中regmap_update_bits会写入mask所标识的那些位,在哪些位写入 mask & val;
这些函数会检查对应的reg是否可读写,是否可缓存再进行相应操作,以regmap_write函数为例,检查流程如下:
- 如果参数reg大于regmap_config.max_register,则返回-EIO;否则继续执行
- 如果调用regmap_config.writeable_reg(reg)返回false,则返回-EIO,否则继续执行;
- 如果设置了wr_table,如果reg在yes_range内则继续执行,否则返回-EIO
- 如果cache_type != RGECACHE_NONE,则启用缓存。启用缓存时,写操作会先更新缓存,之后执行硬件的写操作;
- 如果提供了regmap_config.reg_write回调函数,则调用它执行写入操作;
Regmap API也一次写入多个寄存器的函数regmap_multi_reg_write()
int regmap_multi_reg_write(struct regmap* map,
const struct reg_sequence *regs, int num_regs);
// 其中结构体struct reg_sequece{}
struct reg_sequence {
uint reg;
uint def; // 寄存器写入的值
uint delay_us; // 在寄存器写入后的延迟,us
};
// 使用示例:
static const struct reg_sequence foo_regs[] = {
{FOO_REG1, 0XB8},
{BAR_REG1, 0X23},
{REG_INIT, 0X12},
{REG_POWER, 0X00},
{REG_RESET, 0X00},
};
int ret = regmap_multi_reg_write(map, foo_regs, ARRAY_SIZE(foo_regs));
Regmap API还提供了一些其他的bulk写入函数,用于多个寄存器的写入和读取
int regmap_bulk_read(struct regmap *map, uint reg,
void *val, size_t val_count);
int regmap_bulk_write(struct regmap *map, uint reg,
const void *val, size_t val_count);
4. Regmap和缓存
Regmap API支持缓存,是否使用缓存取决于regmap_config.cache_type字段的值。
enum regcache_type {
REGCACHE_NONE, // 不启用缓存
REGCACHE_RBTREE, // 使用红黑树方式储存缓存
REGCACHE_COMPRESSED, // 使用压缩方式
REGCACHE_FLAT, // 使用数组方式储存缓存
};
缓存只影响读的性能,任何写操作都会同时写入缓存和寄存器中。设备的某些寄存器可能具有预定义的加电复位值,使用regmap_config.reg_defaults字段来设置储存的这些初始值。
struct reg_default {
uint reg;
uint def;
};