简介
前面我们介绍了可以直接使用i2c子系统中数据传输接口就可以进行i2c通信,那为什么又引入了regmap子系统呢?
Linux下大部分设备的驱动开发都是操作其内部寄存器,比如I2C/SPI设备的本质都是一样的,通过I2C/SPI 接口读写芯片内部寄存器,使用i2c_transfer来读写I2C设备中的寄存器,SPI接口的话使用 spi_write/spi_read读写SPI设备中的寄存器。regmap
是为了方便操作寄存器而设计的,它将所有模块的寄存器(包括soc上模块的寄存器和外围设备的寄存器等)抽象出来,用一套统一接口来操作寄存器,统一操作 i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq
等。
regmap
框架分为三层:
①、底层物理总线: regmap
支持的物理总线有 i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq、spmi 和 w1
。
②、regmap
核心层:用于实现 regmap
核心。
③、regmapAPI
抽象层,regmap
向驱动编写人员提供的 API 接口。
其源代码在kernel/drivers/base/regmap
目录下。
重要数据结构
struct regmap {
union {
struct mutex mutex;
struct {
spinlock_t spinlock;
unsigned long spinlock_flags;
};
};
regmap_lock lock;
regmap_unlock unlock;
void *lock_arg; /* This is passed to lock/unlock functions */
struct device *dev; /* Device we do I/O on */
void *work_buf; /* Scratch buffer used to format I/O */
struct regmap_format format; /* Buffer format */
const struct regmap_bus *bus;
void *bus_context;
const char *name;
bool async;
spinlock_t async_lock;
wait_queue_head_t async_waitq;
struct list_head async_list;
struct list_head async_free;
int async_ret;
...
unsigned int max_register;
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);
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;
int (*reg_read)(void *context, unsigned int reg,
unsigned int *val);
int (*reg_write)(void *context, unsigned int reg,
unsigned int val);
......
struct rb_root range_tree;
void *selector_work_buf; /* Scratch buffer used for selector */
}; // drivers/base/regmap/internal.h
struct regmap_config {
const char *name;//名字
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);
//是否不能读回调函数,当寄存器不能读,就返回tree
bool (*precious_reg)(struct device *dev, unsigned int reg);
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;//快速 I/O,使用 spinlock 替代 mutex 来提升锁性能。
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 reg_default *reg_defaults;//寄存器默认值,有两个成员变量:reg是寄存器地址,def是默认值
unsigned int num_reg_defaults;//默认寄存器表中的元素个数
enum regcache_type cache_type;
const void *reg_defaults_raw;
unsigned int 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;
unsigned int num_ranges;
};
API
regmap
支持多种物理总线,比如 I2C 和 SPI,我们需要根据所使用的接口来选 择合适的 regmap
初始化函数。
struct regmap * devm_regmap_init_spi(struct spi_device *spi, const struct regmap_config *config);
struct regmap * devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config);
void regmap_exit(struct regmap *map);//不管是什么物理接口,退出都用regmap_exit
regmap 设备访问
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
/* regmap_update_bits函数,看名字就知道,此函数用来修改寄存器指定的 bit。
* mask:掩码,需要更新的位必须在掩码中设置为 1。
* val:需要更新的位值。
* 比如要将寄存器的 bit1 和 bit2 置 1,那么 mask 应该设置为 0X00000011,此时 val 的 bit1
和 bit2 应该设置为 1,也就是 0Xxxxxxx11。如果要清除寄存器的 bit4 和 bit7,那么 mask 应该
设置为 0X10010000,val 的 bit4 和 bit7 设置为 0,也就是 0X0xx0xxxx。
*/
int regmap_update_bits (struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val);
//读取写入多个寄存器的值
//map:要操作的 regmap。
//reg:要读写的第一个寄存器。
//val:要读写的寄存器数据缓冲区。
//val_count:要读写的寄存器数量
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count);
int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count)
//原始字节操作
//直接读取原始字节流,不进行任何格式转换
//数据按总线原始字节顺序存储(大端/小端保持不变)
//只使用指定的起始寄存器地址reg,不会自动递增寄存器地址
int regmap_raw_write(struct regmap *map, int reg, void *val, size_t val_len);
int regmap_raw_read(struct regmap *map, int reg, void *val, size_t val_len);
源码解读
解读__devm_regmap_init_i2c
函数
struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __devm_regmap_init(&i2c->dev, bus, &i2c->dev, config,
lock_key, lock_name);
}
这个是初始化函数,我们先看regmap_get_i2c_bus
。
static int regmap_i2c_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
int ret;
ret = i2c_master_send(i2c, data, count);
if (ret == count)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static int regmap_i2c_gather_write(void *context,
const void *reg, size_t reg_size,
const void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
int ret;
/* If the I2C controller can't do a gather tell the core, it
* will substitute in a linear write for us.
*/
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_NOSTART))
return -ENOTSUPP;
xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
xfer[0].len = reg_size;
xfer[0].buf = (void *)reg;
xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_NOSTART;
xfer[1].len = val_size;
xfer[1].buf = (void *)val;
ret = i2c_transfer(i2c->adapter, xfer, 2);
if (ret == 2)
return 0;
if (ret < 0)
return ret;
else
return -EIO;
}
static int regmap_i2c_read(void *context,
const void *reg, size_t reg_size,
void *val, size_t val_size)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
struct i2c_msg xfer[2];
int ret;
xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
xfer[0].len = reg_size;
xfer[0].buf = (void *)reg;
xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = val_size;
xfer[1].buf = val;
ret = i2c_transfer(i2c->adapter, xfer, 2);
if (ret == 2)
return 0;
else if (ret < 0)
return ret;
else
return -EIO;
}
static const struct regmap_bus regmap_i2c = {
.write = regmap_i2c_write,
.gather_write = regmap_i2c_gather_write,
.read = regmap_i2c_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
const struct regmap_config *config)
{
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
return ®map_i2c;
else if (config->val_bits == 8 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
return ®map_i2c_smbus_i2c_block;
else if (config->val_bits == 8 && config->reg_bits == 16 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_I2C_BLOCK))
return ®map_i2c_smbus_i2c_block_reg16;
else if (config->val_bits == 16 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_WORD_DATA))
switch (regmap_get_val_endian(&i2c->dev, NULL, config)) {
case REGMAP_ENDIAN_LITTLE:
return ®map_smbus_word;
case REGMAP_ENDIAN_BIG:
return ®map_smbus_word_swapped;
default: /* everything else is not supported */
break;
}
else if (config->val_bits == 8 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_BYTE_DATA))
return ®map_smbus_byte;
return ERR_PTR(-ENOTSUPP);
}
在这里会先判断I2C是否支持I2C_FUNC_I2C
的功能,我们的I2C肯定是支持的(这一部分具体可以看I2C子系统,然后返回regmap_bus
的值,这个regmap_bus
对象提供了read和write函数。
我们再看__regmap_init
函数,该函数中会将很多regmap_config
中的字段赋值给regmap
,其中会判断reg_bits
和val_bits
的值,然后给map->format
赋值。
map->format.format_val = regmap_format_8;
map->format.parse_val = regmap_parse_8;
map->format.parse_inplace = regmap_parse_inplace_noop;
还有一段是配置reg_stride
的,这个有啥用,后续再分析。
if (config->reg_stride)
map->reg_stride = config->reg_stride;
else
map->reg_stride = 1;
if (is_power_of_2(map->reg_stride))
map->reg_stride_order = ilog2(map->reg_stride);
else
map->reg_stride_order = -1;
解读regmap_read
函数
/**
* regmap_read() - Read a value from a single register
*
* @map: Register map to read from
* @reg: Register to be read from
* @val: Pointer to store read value
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
{
int ret;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
map->lock(map->lock_arg);
ret = _regmap_read(map, reg, val);
map->unlock(map->lock_arg);
return ret;
}
这个读单个寄存器的函数,其中IS_ALIGNED
函数是为什么校验reg值的,并且用到了stride
字段。我们在regmap_config中配置这个字段,就是器件中实际寄存器地址的排列间隔,默认值为1,也就是有0x00,0x01,0x02。如果配置为2,那么就是0x00,0x02,0x04,以此类推。所以,如果你传入一个非法的地址,那会直接返回。再继续看_regmap_read
。
static int _regmap_read(struct regmap *map, unsigned int reg,
unsigned int *val)
{
int ret;
void *context = _regmap_map_get_context(map);
if (!map->cache_bypass) {
ret = regcache_read(map, reg, val);
if (ret == 0)
return 0;
}
if (map->cache_only)
return -EBUSY;
if (!regmap_readable(map, reg))
return -EIO;
ret = map->reg_read(context, reg, val);
if (ret == 0) {
if (regmap_should_log(map))
dev_info(map->dev, "%x => %x\n", reg, *val);
trace_regmap_reg_read(map, reg, *val);
if (!map->cache_bypass)
regcache_write(map, reg, *val);
}
return ret;
}
这里会先判断是否bypass cache和是否可读,然后会直接调用map的读函数。regmap_should_log
这个函数可以看一下:
/*
* Sometimes for failures during very early init the trace
* infrastructure isn't available early enough to be used. For this
* sort of problem defining LOG_DEVICE will add printks for basic
* register I/O on a specific device.
*/
// #undef LOG_DEVICE
#define LOG_DEVICE "4-0068"
#ifdef LOG_DEVICE
static inline bool regmap_should_log(struct regmap *map)
{
return (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0);
}
#else
static inline bool regmap_should_log(struct regmap *map) { return false; }
#endif
源代码中默认是undef LOG_DEVICE
,如果需要开启日志,则需要宏定义LOG_DEVICE
为你的i2c设备,或者设备地址经常改变的时候,可以直接return true。
解读regmap_raw_read
函数
/**
* regmap_raw_read() - Read raw data from the device
*
* @map: Register map to read from
* @reg: First register to be read from
* @val: Pointer to store read value
* @val_len: Size of data to read
*
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
size_t val_len)
{
size_t val_bytes = map->format.val_bytes;
size_t val_count = val_len / val_bytes;
unsigned int v;
int ret, i;
if (!map->bus)
return -EINVAL;
if (val_len % map->format.val_bytes)
return -EINVAL;
if (!IS_ALIGNED(reg, map->reg_stride))
return -EINVAL;
if (val_count == 0)
return -EINVAL;
map->lock(map->lock_arg);
if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
map->cache_type == REGCACHE_NONE) {
size_t chunk_count, chunk_bytes;
size_t chunk_regs = val_count;
if (!map->bus->read) {
ret = -ENOTSUPP;
goto out;
}
if (map->use_single_read)
chunk_regs = 1;
else if (map->max_raw_read && val_len > map->max_raw_read)
chunk_regs = map->max_raw_read / val_bytes;
chunk_count = val_count / chunk_regs;
chunk_bytes = chunk_regs * val_bytes;
/* Read bytes that fit into whole chunks */
for (i = 0; i < chunk_count; i++) {
ret = _regmap_raw_read(map, reg, val, chunk_bytes, false);
if (ret != 0)
goto out;
reg += regmap_get_offset(map, chunk_regs);
val += chunk_bytes;
val_len -= chunk_bytes;
}
/* Read remaining bytes */
if (val_len) {
ret = _regmap_raw_read(map, reg, val, val_len, false);
if (ret != 0)
goto out;
}
} else {
/* Otherwise go word by word for the cache; should be low
* cost as we expect to hit the cache.
*/
for (i = 0; i < val_count; i++) {
ret = _regmap_read(map, reg + regmap_get_offset(map, i),
&v);
if (ret != 0)
goto out;
map->format.format_val(val + (i * val_bytes), v, 0);
}
}
out:
map->unlock(map->lock_arg);
return ret;
}
注意该函数的第三个参数,是读取的字节数大小。然后会根据cache分两路。如果不管cache,一次性读取的字节数是有一个最大限制的max_raw_read
,根据这个值分批次读。_regmap_raw_read
函数中主要是调用regmap_bus
的读函数。
这里还有关键的地方regmap_get_offset
,我们轮询读的时候,地址并不是累计的,而是计算了一个offset,这个是根据索引计算实际寄存器地址。
static inline unsigned int regmap_get_offset(const struct regmap *map,
unsigned int index)
{
if (map->reg_stride_order >= 0)
return index << map->reg_stride_order;
else
return index * map->reg_stride;
}
reg_stride_order
是reg_stride
取2的对数,然后index
左移reg_stride_order
位,也就是乘以reg_stride_order
个2,其实就是乘以reg_stride
,例如reg_stride
配置的值是4,那么offset就是4*reg_stride
。
示例
static bool haptic_readable_register(struct device *dev, unsigned int reg)
{
return true;
}
static bool haptic_writeable_register(struct device *dev, unsigned int reg)
{
return true;
}
static bool haptic_volatile_register(struct device *dev, unsigned int reg)
{
return true;
}
static const struct reg_default haptic_reg_defaults[] = {
};
static const struct regmap_config haptic_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xff,
.volatile_reg = haptic_volatile_register,
.readable_reg = haptic_readable_register,
.writeable_reg = haptic_writeable_register,
.cache_type = REGCACHE_NONE,
.reg_defaults = haptic_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(haptic_reg_defaults),
};