Linux regmap机制(一)

Regmap是Linux内核为了减少低速I/O在驱动上的冗余开销而引入的一种通用寄存器访问接口。它通过统一的API,降低了代码冗余,提高了驱动的可移植性。Regmap包含底层物理总线、核心层和API抽象层,支持多种物理总线如I2C、SPI、MMIO等。驱动开发中,使用regmap_config结构体初始化regmap,并通过regmap结构体实例来操作寄存器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Linux regmap机制

在Linux下开发WDT驱动时候参考某源代码时候发现devm_regmap_init_mmio_clk()函数的使用,故深入探究一下。

什么是 Regmap

Linux 下大部分设备的驱动开发都是操作其内部寄存器,比如 I2C/SPI 器件的本质都是一样的,通过 I2C/SPI 接口读写器件内部寄存器。芯片内部寄存器也是同样的道理,比如STM32的 PWM、TIM 等外设初始化,最终都是要落到寄存器的设置上。

例如,Linux 下使用 i2c_transfer 来读写 I2C 器件中的寄存器,SPI 接口的话使用 spi_write/spi_read等。I2C/SPI 芯片又非常的多,因此 Linux 内核里面就会充斥了大量的 i2c_transfer 这类的冗余代码。

再者,代码的复用性也会降低,如果一个器件同时支持SPI和IIC接口的话,在产品开发初期将器件设计为 SPI 接口,但是后面发现 SPI 接口不够用,或者 SOC 的引脚不够用,我们需要将器件改为 I2C 接口。这个时候器件的驱动就要大改,我们需要将 SPI 接口函数换为 I2C 的,工作量比较大。

基于代码复用的原则,Linux 内核引入了 regmap 模型,regmap 将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用 SPI 或者 I2C 接口 API 函数,统一使用 regmap API函数。这样的好处就是统一使用 regmap,降低了代码冗余,提高了驱动的可以移植性

  • regmap模型的重点在于:
  1. 通过 regmap 模型提供的统一接口函数来访问器件的寄存器,SOC 内部的寄存器也可以使用 regmap 接口函数来访问。
  2. regmap 是 Linux 内核为了减少低速 I/O 在驱动上的冗余开销,提供了一种通用的接口来操作硬件寄存器。另外,regmap 在驱动和硬件之间添加了 cache,降低了低速 I/O 的操作次数,提高了访问效率,缺点是实时性会降低。
  • 什么情况下会使用 regmap:
  1. 硬件寄存器操作,比如选用通过 I2C/SPI 接口来读写设备的内部寄存器,或者需要读写 SOC 内部的硬件寄存器。
  2. 提高代码复用性和驱动一致性,简化驱动开发过程。
  3. 减少底层 I/O 操作次数,提高访问效率。

Regmap 驱动框架

regmap 整体框架结构

regmap 驱动框架如下图所示:
在这里插入图片描述
regmap 框架分为三层:

  1. 底层物理总线:regmap 就是对不同的物理总线进行封装,目前 regmap 支持的物理总
    线有 i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq、spmi 和 w1。
  2. regmap 核心层,用于实现 regmap,感兴趣的可以自行去剖析。
  3. regmap API 抽象层,regmap 向驱动编写人员提供的 API 接口,驱动编写人员使用这些
    API 接口来操作具体的芯片设备,也是驱动编写人员重点要掌握的。

regmap 结构体

Linux 内核将 regmap 框架抽象为 regmap 结构体,这个结构体定义在文件drivers/base/regmap/internal.h 中,结构体内容如下(有缩减):

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 */
	gfp_t alloc_flags;

	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);
	bool (*writeable_noinc_reg)(struct device *dev, unsigned int reg);
	bool (*readable_noinc_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;
	const struct regmap_access_table *wr_noinc_table;
	const struct regmap_access_table *rd_noinc_table;

	int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
	int (*reg_write)(void *context, unsigned int reg, unsigned int val);
	int (*reg_update_bits)(void *context, unsigned int reg,
			       unsigned int mask, unsigned int val);
......
	struct rb_root range_tree;
	void *selector_work_buf;	/* Scratch buffer used for selector */

	struct hwspinlock *hwlock;
};

要使用 regmap,肯定要先给驱动分配一个具体的 regmap 结构体实例。

regmap_config 结构体

顾名思义,regmap_config 结构体就是用来初始化 regmap 的,这个结构体定义在include/linux/regmap.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);
	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;
};

Linux 内核里面已经对 regmap_config 各个成员变量进行了详细的讲解(函数上面有注释),这里我们只看一些比较重要的成员:

  • reg_bits:寄存器地址位数,必填字段。
  • reg_stride:寄存器地址步长,相邻寄存器的距离。
  • val_bits:寄存器值位数,必填字段。
  • max_register:有效的最大寄存器地址,可选。

关于 regmap_config 结构体成员变量就介绍这些,其他没有介绍的自行查阅 Linux 内核中的注释描述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁头小哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值