【Linux】regmap子系统

发布于:2025-06-13 ⋅ 阅读:(20) ⋅ 点赞:(0)

简介

前面我们介绍了可以直接使用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 &regmap_i2c;
	else if (config->val_bits == 8 && config->reg_bits == 8 &&
		 i2c_check_functionality(i2c->adapter,
					 I2C_FUNC_SMBUS_I2C_BLOCK))
		return &regmap_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 &regmap_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 &regmap_smbus_word;
		case REGMAP_ENDIAN_BIG:
			return &regmap_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 &regmap_smbus_byte;

	return ERR_PTR(-ENOTSUPP);
}

在这里会先判断I2C是否支持I2C_FUNC_I2C的功能,我们的I2C肯定是支持的(这一部分具体可以看I2C子系统,然后返回regmap_bus的值,这个regmap_bus对象提供了read和write函数。

我们再看__regmap_init函数,该函数中会将很多regmap_config中的字段赋值给regmap,其中会判断reg_bitsval_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_orderreg_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),
};	

网站公告

今日签到

点亮在社区的每一天
去签到