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 | static const struct regmap_bus regmap_spi = { |
可以看到其实实现很简单就是构造一个 regmap_bus 数据结构 regmap_spi,并提供 spi 的相关操作,这里以 write 为例看下里面的实现,其他类似:
1 | static int regmap_spi_write(void *context, const void *data, size_t count) |
其实就是标准的 spi 接口的调用,这也证明了 regmap 其实就是对一些底层 bus 进行进一步封装并对外提供统一的接口,同时还提供页操作缓存等非常好用的接口。
regmap 的实现
在上面介绍的__regmap_init_spi 函数就会返回一个 regmap 结构体也就是 regmap 结构体的创建过程
1 | __devm_regmap_init |
其实到这里 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 设备驱动开发》