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

【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),
};	

相关文章:

  • WEB JWT
  • Java程序员如何设计一个高并发系统?
  • Go 语言安装指南:并解决 `url.JoinPath` 及 `Exec format error` 问题
  • 全栈监控系统架构
  • 大白话解释蓝牙的RPC机制
  • LeetCode 2917.找出数组中的K-or值
  • Linux612 chroot_list开放;FTP服务ftp:get put,指定上传路径报错553;ftp查看文件夹权限
  • Vulkan学习笔记4—图形管线基础
  • ubuntu20.04 安装Mujoco 及 Isaac Gym 仿真器
  • 紫光展锐完成优化升级,支持Android 16,以科技创新共赴智能体验新篇章
  • 常见的测试工具及分类
  • 系统功耗管理
  • 从零搭建智能家居:香橙派+HomeAssistant实战指南
  • 【android bluetooth 框架分析 04】【bt-framework 层详解 6】【Properties介绍】
  • Springboot仿抖音app开发之消息业务模块后端复盘及相关业务知识总结
  • php反序列化漏洞学习
  • [安卓按键精灵辅助工具]一些安卓端可以用的雷电模拟器adb命令
  • 关于安卓dialogFragment中,EditText无法删除文字的问题
  • Android NTP自动同步时间机制
  • 展开说说Android之Glide详解_使用篇
  • 做网站自己买服务器好还是用别人的/怎么做网站
  • 广告创意设计案例/贵阳seo网站推广
  • 云南网站建设/南昌seo招聘信息
  • 免费推广网站建设/快速排名刷
  • 烟台建设联合会网站/双11销售数据
  • 制作网站注册登录模块的思维导图/在线智能识图