• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

regmap

武飞扬头像
Yisnow.
帮助3

一、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 的操作次数。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfheeje
系列文章
更多 icon
同类精品
更多 icon
继续加载