regmap

一、regmap的引入

没有regmap子系统之前

在内核代码里,有成千上万的以I2C / SPI为通讯接口的设备驱动。

以I2C设备为例

各种I2C接口的设备驱动都需要通过I2C子系统的API(i2c_transfer())来进行读写寄存器的操作。在对应的设备驱动中,I2C读写寄存器的操作通常会被封装成2个静态函数:

static int fsi_i2c_read_reg(struct fsi_device *fsi, unsigned int reg, u32 *data);
static int fsi_i2c_write_reg(struct fsi_device *fsi, unsigned int reg,u32 *data);
static inline unsigned char imx_i2c_read_reg(struct imx_i2c_struct *i2c_imx,unsigned int reg);
static inline void imx_i2c_write_reg(unsigned int val,struct imx_i2c_struct *i2c_imx, unsigned int reg);

没有regmap抽象层之前,内核里充斥着大量类似的代码,这些代码都是多余的,使用I2C总线来读写寄存器的操作是有共性的,应该被抽象出来,形成一份统一的代码。驱动开发人员应使用该抽象层的API来读写I2C设备的寄存器,然后把更多的精力放在驱动的逻辑设计上。

同样的,没有regmap子系统时,spi设备的寄存器读写操作也是散落在各个spi设备驱动。

i2c/spi drvier、i2c/spi device、i2c/spi subsystem之间的关系如下:
在这里插入图片描述

有了regmap子系统后

1. 同样以I2C设备为例,先定义struct regmap_config:

struct regmap_config里包含了读写芯片寄存器所需所有信息,例如寄存器数据位宽、地址位宽等。

2. 将struct regmap_config注册给regmap子系统:

struct regmap * devm_regmap_init_i2c(i2c, config);
得到一个struct regmap对象,有了这个对象,就可以调用regmap子系统提供的用于读写寄存器的API了。

3. 使用regmap API读写寄存器:

int regmap_read(struct regmap *map, unsigned int reg,unsigned int *val);
int regmap_write(struct regmap *map, unsigned int reg,unsigned int val);
int regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val);

读写寄存器的操作已经抽象到regmap子系统里了,完整的API位于include/linux/regmap.h。

有了regmap后,i2c/spi drvier、i2c/spi device、i2c/spi subsystem之间的关系如下:

在这里插入图片描述

二、regmap子系统的内部实现

regmap的拓扑结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gg98bFrk-1668320247630)(https:upload-images.jianshu.io/upload_images/13906732-7786c99038848e61?imageMogr2/auto-orient/strip|imageView2/2/w/501/format/webp)]

源码文件:

├──regmap.c
	核心文件:注册/卸载总线、读写寄存器API

├── regcache.c
├── regcache-flat.c
├── regcache-lzo.c
├── regcache-rbtree.c
	缓存相关

├── regmap-debugfs.c
	调试接口

├── regmap-i2c.c
├── regmap-i3c.c
├── regmap-ac97.c
├── regmap-spi.c
	总线相关

├── regmap-irq.c
├── regmap-mmio.c
├── regmap-sccb.c
├── regmap-sdw.c
├── regmap-slimbus.c
├── regmap-spmi.c
├── regmap-w1.c
└── trace.h

regmap的缓存功能

在regmap子系统里,可以选择是否使用缓存功能:

enum regcache_type {
	REGCACHE_NONE,
	REGCACHE_RBTREE,
	REGCACHE_COMPRESSED,
	REGCACHE_FLAT,
};

regmap内支持3 种缓存类型:数组(flat)、LZO 压缩和红黑树(rbtree)

  1. 数组是最简单的缓存类型,当设备寄存器很少时,可以用这种类型来缓存寄存器值。

  2. LZO(Lempel–Ziv–Oberhumer) 是 Linux 中经常用到的一种压缩算法,Linux 编译后就会用这个算法来压缩。这个算法有 3 个特性:压缩快,解压不需要额外内存,压缩比可以自动调节。在这里,你可以理解为一个数组缓存,套了一层压缩,来节约内存。当设备寄存器数量中等时,可以考虑这种缓存类型。

  3. 红黑树特性就是索引快,所以当设备寄存器数量比较大,或者对寄存器操作延时要求低时,就可以用这种缓存类型。

相关结构体:

regcache_ops

struct regcache_ops {
	const char *name;
	enum regcache_type type;
	int (*init)(struct regmap *map);
	int (*exit)(struct regmap *map);
#ifdef CONFIG_DEBUG_FS
	void (*debugfs_init)(struct regmap *map);
#endif
	int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
	int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
	int (*sync)(struct regmap *map, unsigned int min, unsigned int max);
	int (*drop)(struct regmap *map, unsigned int min, unsigned int max);
};
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);
	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 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;
};

struct regmap_config里包含了读写芯片寄存器所需的所有信息,它是regmap API里最核心的结构体。使用regmap子系统的第一步就是填充该结构体。这个结构体看着庞大,但是大多数情况下只要初始化几个成员变量就足够了,例如:

const struct regmap_config tps65912_regmap_config = {
    .reg_bits = 8,
    .val_bits = 8,
    .cache_type = REGCACHE_RBTREE,
    .volatile_table = &tps65912_volatile_table,
};

regmap_bus

struct regmap_bus {
	bool fast_io;
	regmap_hw_write write;
	regmap_hw_gather_write gather_write;
	regmap_hw_async_write async_write;
	regmap_hw_reg_write reg_write;
	regmap_hw_reg_update_bits reg_update_bits;
	regmap_hw_read read;
	regmap_hw_reg_read reg_read;
	regmap_hw_free_context free_context;
	regmap_hw_async_alloc async_alloc;
	u8 read_flag_mask;
	enum regmap_endian reg_format_endian_default;
	enum regmap_endian val_format_endian_default;
	size_t max_raw_read;
	size_t max_raw_write;
};

该结构体用于描述一种硬件总线的寄存器读写操作(a hardware bus for the register map infrastructure)。

regmap_bus并不是面向用户的API,也就是说使用regmap子系统并不要求一定要了解该结构体,但是理解该结构体有助于我们了解regmap是如何抽象寄存器读写操作的。无论是i2c还是spi,在调用devm_regmap_init_i2c将struct regmap_config注册给regmap子系统后,在子系统内部都会根据remap_config里的配置信息找到对应的regmap_bus。

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_config 和 regmap_bus,regmap子系统就有了读写该芯片寄存器的能力,然后就会返回一个struct regmap供设备驱动来使用了,struct regmap类似一个大管家,包含了所有信息,负责统筹一切。

常用的regmap API

regmap提供出来的读写寄存器的API非常多,最常用的3个API如下:

regmap_read
regmap_write
regmap_update_bits_base

可以猜测上述API会利用struct regmap_config + struct regmap_bus完成寄存器的读写操作。

简单看下regmap_read()的实现:

regmap_read

struct regmap *map

map->reg_read(context, reg, val);

​ _regmap_bus_reg_read

​ map->bus->reg_read

总结

regmap 是在 Linux 3.1 加入进来的特性,其最初的目的是减少i2c/spi等设备驱动里的重复逻辑,提供一种通用的接口来操作芯片内寄存器,随着版本的更迭,regmap 支持的bus越来越多,并且除了能做到统一的 寄存器I/O 接口,还可以在驱动和硬件 IC 之间做一层缓存,从而能减少底层 I/O 的操作次数。

作者:老吴的嵌入式之旅
参考链接:https://www.jianshu.com/p/600a3a4c78e9

Linux regmap是一个内核模块,主要用于管理嵌入式设备中硬件寄存器的访问和配置。它是基于设备树规范设计的,允许内核模块轻松、高效地与多种类型的硬件通信,特别是那些支持I2C总线协议的低级外设。 ### 主要组件 #### Device Tree `regmap`依赖于设备树描述硬件资源,如寄存器地址空间、读写权限、中断等。设备树文件提供了一个清晰的方式去描述设备及其连接结构,使得`regmap`能直接引用这些信息来设置硬件操作。 #### 数据结构 `struct regmap` `regmap`的主要数据结构定义了寄存器映射表,其中包含: - 寄存器的物理位置 - 高效的数据结构用于查找和访问特定寄存器 - 支持多种操作模式,如直接读写、中断处理等 #### 写注册表函数 `regmap`提供了一组API供用户空间程序或内核模块使用,这些API可以安全地读取和写入硬件寄存器。它们封装了底层细节,简化了硬件交互。 ### 功能特点 1. **通用性**:`regmap`的设计考虑到了各种硬件平台的需求,因此可以广泛应用于不同类型的嵌入式系统上。 2. **安全性**:通过设备树的安全属性,可以限制哪些进程能访问特定的寄存器,增强了系统的安全性。 3. **性能优化**:内部实现了高效的寻址机制,减少了不必要的内存访问延迟。 4. **易用性**:提供了一套标准的API,使得硬件驱动的编写变得更加简单和标准化。 ### 使用场景 - **I2C 设备驱动**: 对于需要通过 I2C 总线控制的硬件设备,如传感器、LED 控制板等,`regmap` 提供了一个强大的框架来进行交互。 - **GPIO 和 PWM**: 管理 GPIO 引脚的状态和 PWM 调整,对于控制 LED 或其他数字信号处理任务非常有用。 - **ADC 和 DAC**: 集成模拟转换器和数模转换器的操作,用于信号调理和数字化。 `regmap`的灵活性和高效性使其成为Linux内核管理和控制低级别硬件资源的重要组成部分之一。对于开发者来说,掌握`regmap`不仅能够更有效地开发驱动程序,还能增强对系统底层架构的理解。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值