当前位置: 首页 > ds >正文

【Linux】regmap子系统

简介

前面我们介绍了可以直接使用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);//是否不能读回调函数,当寄存器不能读,就返回treebool (*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;elsereturn -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;elsereturn -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;elsereturn -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;elsemap->reg_stride = 1;if (is_power_of_2(map->reg_stride))map->reg_stride_order = ilog2(map->reg_stride);elsemap->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;elsereturn 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),
};	
http://www.xdnf.cn/news/13915.html

相关文章:

  • 智慧工厂物联网解决方案:纺织厂边缘计算网关应用
  • 图像处理控件Aspose.Imaging教程:图像处理控件Aspose.Imaging教程:在Java中构建 SVG 图像调整器
  • vanna多表关联的实验
  • 将idea的目录结构以文本导出
  • MySQL 8.0的数据库root用户默认无法远程登录,需要修改root的远程授权
  • 使用AkShare获取大A列表
  • ( github actions + workflow 03 ) 手动添加 token, 防止权限不够
  • 运营商实名验证接口如何用Python实现调用?
  • 新疆大学具身导航新范式!DOPE:基于双重对象感知增强网络的视觉语言导航
  • golang -- map实现原理
  • 单片机队列功能模块的实战和应用
  • Elasticsearch的数据同步
  • 在线机考|2025年华为暑期实习春招秋招编程题(最新)——第2题_网络整改
  • 基于mapreduce的气候分析系统
  • Dify实战案例:AI邮件批量发送器!
  • Unit 3 Q-Learning 简介
  • 06-Python流程控制
  • [论文阅读] 人工智能 | ComfyUI-R1: Exploring Reasoning Models for Workflow Generation
  • JDBC接口开发指南
  • kali系统 windows Linux靶机入侵演练
  • 《Qt5.14.1与Mingw C++:打造可发布程序的技术之旅》
  • 实时监控、秒级决策:镜舟科技如何重塑融资融券业务数据处理模式
  • @SchedulerLock处理Spring Task在分布式环境下的重复执行问题
  • Transformer模型详解
  • leetcode 169. 多数元素
  • 数据结构-为什么双指针法可以用来解决环形链表?-使用O(1)的空间复杂度去解决环形链表的思路
  • React 基础状态管理方案
  • 基于Orange Pi Zero3的音频管理系统搭建与远程访问实现
  • ⭐ Unity 实现屏幕涟漪效果:自动生成 \ 点击交互生成涟漪
  • F5深化与Red Hat战略合作 ,赋能企业AI规模化安全部署