0%

Linux驱动之Regmap子系统

概述

内核版本3.1中引入了Regmap API,用于统一内核开发人员访问SPI/IIC设备的方式,无论是SPI设备还是IIC设备,只需要初始化,配置Regmap就可以通过Regmap读写设备。Regmap子系统主要提供如下两种功能:

  • 第一是为IIC/SPI/MMIO等提供统一的访问接口,linux中大量的iic和spi设备就可以通过统一的接口进行访问尤其对于那些同时支持iic和spi接口的设备
  • 第二是提供缓存访问机制用于加速设备访问设备,对于支持缓存的设备这将大大加快设备的当问速度

下面看一下Regmapz子系统在驱动中的位置,如下:原图

数据结构

regmap子系统的实现比较简单,数据结构也是很简单的,整体结构如下:原图

  • regmap_bus
    regmap_bus数据结构用于对具体的bus进行封装,例如对IIC或SPI总线进行封装,这些bus下可以挂载很多设备,他们都从属于一个总线那么就有很多共性操作,对这个结构的封装就用regmap_bus结构。该结构主要提供同步读写、异步读写、格式化数据等操作接口

  • regmap
    在上面的结构中讲到regmap_bus用于封装具体的bus,这些bus下会挂载各种不同的设备,这些设备的操作有很多不一样的,针对每一个设备进行封装就使用regmap结构体。该结构主要提供的功能有:缓存区和缓存操作、异步读写链表和队列操作、读写权限管理(哪些地址的寄存器是可读的、哪些地址范围是可写的以及哪些地址范围是可读写的)、读写页的支持及访问操作以及对寄存器和值的格式化操作等

  • regmap_config
    regmap_config结构是最接近用户的,因此上图中也对该结构做了最详细的注释,该结构主要用于用户通过该结构来初始化regmap结构体

  • regmap_range_cfg
    regmap_range_cfg数据结构用于描述页范围和页选择

  • regmap_format
    regmap_format数据结构就是上面提到的用户格式化数据的结构,对于某些寄存器是12bit或其他非常规位数的寄存器进行格式化数据的结构

  • regmap_access_table
    regmap_access_table数据结构用于描述对寄存器访问权限的控制,yes_ranges是可访问地址范围而no_range是不可访问地址访问

  • regcache_ops
    regcache_ops结构提供缓存操作相关接口描述

实现

regmap_bus的实现

这里以spi总线的regmap_bus为例,iic等其他总线是相同的逻辑,regmap_spi的实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

static const struct regmap_bus regmap_spi = {
.write = regmap_spi_write,
.gather_write = regmap_spi_gather_write,
.async_write = regmap_spi_async_write,
.async_alloc = regmap_spi_async_alloc,
.read = regmap_spi_read,
.read_flag_mask = 0x80,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};

struct regmap *__regmap_init_spi(struct spi_device *spi,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
return __regmap_init(&spi->dev, &regmap_spi, &spi->dev, config,
lock_key, lock_name);
}
可以看到其实实现很简单就是构造一个regmap_bus数据结构regmap_spi,并提供spi的相关操作,这里以write为例看下里面的实现,其他类似:
1
2
3
4
5
6
7
static int regmap_spi_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct spi_device *spi = to_spi_device(dev);

return spi_write(spi, data, count);
}
其实就是标准的spi接口的调用,这也证明了regmap其实就是对一些底层bus进行进一步封装并对外提供统一的接口,同时还提供页操作缓存等非常好用的接口。

regmap的实现

在上面介绍的__regmap_init_spi函数就会返回一个regmap结构体也就是regmap结构体的创建过程

1
2
__devm_regmap_init
__regmap_init
__regmap_init函数的流程比较繁琐这里就不贴代码了,大致的流程是初始化需要两个重要的数据结构regmap_bus和regmap_config。每一个regmap都需要一个regmap_bus数据结构,regmap的初始化就是设置其结构体的成员,而设置的依据就是根据regmap_config结构体的内容来设置。

其实到这里regmap的主体结构和实现就大概清楚了,至于其具体的实现细节像页的访问操作,缓存的操作权限的管理感兴趣的可以去driver/base/regmap/目录下去看源码,我也没去详细了解具体的实现过程,只是梳理了一下regmap的大致实现过程。

参考文献

https://blog.csdn.net/lickylin/article/details/107595373?spm=1001.2014.3001.5501
https://blog.csdn.net/lickylin/article/details/107595340?spm=1001.2014.3001.5501
《Linux设备驱动开发》