前言:
regmap 机制是在 Linux 3.1 加入的新特性。主要目的是减少 I/O 驱动上的重复逻辑代码,提供一种通用的接口来操作底层硬件上的寄存器。
比如,之前如果要操作 i2c 设备的寄存器,我们要调用 i2c_transfer 接口,要操作 spi 设备的寄存器,就要调用 spi_write/spi_read 等接口,如果把它们抽象为 regmap 结构,那么只要调用 regmap_read/regmap_write 就可以了。
一、重要结构体
内核版本:5.11。
1.1、regmap_config
struct regmap_config {
const char *name; //regmap名字,当一个设备有多个寄存器区域时使用
int reg_bits; //寄存器地址位数,必须配置
int reg_stride; //寄存器地址步进
int pad_bits; //寄存器和值之间的填充位数
int val_bits; //寄存器值的位数,必须配置
//判断寄存器状态,比如: 是否可读,是否可写等
bool (*writeable_reg)(struct device *dev, unsigned int reg);
bool (*readable_reg)(struct device *dev, unsigned int reg);
bool (*volatile_reg)(struct device *dev, unsigned int reg);
bool (*precious_reg)(struct device *dev, unsigned int reg);
bool (*writeable_noinc_reg)(struct device *dev, unsigned int reg);
bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
bool disable_locking;
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg;
//读写寄存器回调函数,大部分设备不使用
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
bool fast_io;
unsigned int max_register; //最大寄存器地址
const struct regmap_access_table *wr_table;
const struct regmap_access_table *rd_table;
const struct regmap_access_table *volatile_table;
const struct regmap_access_table *precious_table;
const struct regmap_access_table *wr_noinc_table;
const struct regmap_access_table *rd_noinc_table;
const struct reg_default *reg_defaults; //复位后默认值
unsigned int num_reg_defaults; //有默认值的寄存器数
enum regcache_type cache_type;
const void *reg_defaults_raw;
unsigned int num_reg_defaults_raw;
unsigned long read_flag_mask;
unsigned long write_flag_mask;
bool zero_flag_mask;
bool use_single_read;
bool use_single_write;
bool use_relaxed_mmio;
bool can_multi_write;
//寄存器和值的大小端
enum regmap_endian reg_format_endian;
enum regmap_endian val_format_endian;
const struct regmap_range_cfg *ranges; //虚拟地址范围
unsigned int num_ranges;
bool use_hwlock;
unsigned int hwlock_id;
unsigned int hwlock_mode;
bool can_sleep; //是否可以休眠
};
struct regmap_config 结构体代表一个设备的寄存器配置信息,初始化时需要配置。
二、API 函数
//1. 初始化接口
//i2c
#define devm_regmap_init_i2c(i2c, config) \
__regmap_lockdep_wrapper(__devm_regmap_init_i2c, #config, \
i2c, config)
//spi
#define devm_regmap_init_spi(dev, config) \
__regmap_lockdep_wrapper(__devm_regmap_init_spi, #config, \
dev, config)
//2.读写接口
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
//3. 释放
void regmap_exit(struct regmap *map);
上面只是列举了 I2C 和 SPI,regmap 还有 AC97、MMIO 和 SPMI 等其他总线的接口。
三、传统方式和 regmap
我们举个例子来看看传统方式和 regmap 的区别。以 I2C 为例:
传统方式:
static int xxx_i2c_read_reg(struct i2c_client *client, u8 reg, u8 *val)
{
struct i2c_msg msg[] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = ®,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = 1,
.buf = val,
},
};
return i2c_transfer(client->adapter, msg, 2);
}
regmap 方式:
// 1. 定义regmap_config
static const struct regmap_config xxx_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
// 2.初始化
regmap = devm_regmap_init_i2c(i2c_client, &xxx_regmap_config);
// 3.寄存器操作
regmap_read(regmap, XXX_REG, &value);
首先定义芯片的寄存器信息,位宽,地址位宽,寄存器总线等。初始化 regmap,不同总线调用对应的初始化函数。初始化完成之后就可以正常调用 regmap 的 API 进行读写了。