美文网首页嵌入式 Linux C ARM Linux
嵌入式系统砖家_寄存器读写抽象之regmap

嵌入式系统砖家_寄存器读写抽象之regmap

作者: 老吴的嵌入式之旅 | 来源:发表于2019-08-24 10:25 被阅读0次

    目录:

    一、regmap子系统的引入

    二、regmap子系统的内部实现

    一、regmap子系统的引入

    没有regmap子系统之前

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

    以I2C设备为例

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

    xxx_i2c_read_reg()/xxx_i2c_write_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对象,有了这个对象,就可以调用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之间的关系如下:

    我在Linux-4.14内核里搜索了一下,目前大约有491个地方使用了regmap子系统,将来可能会更多。

    二、regmap子系统的内部实现

    regmap的拓扑结构

    在Linux-4.14内核中,regmap分为3层:

    源码文件:

    regmap的缓存功能

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

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

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

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

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

    相关结构体:

    struct regmap_config

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

    struct regmap_config重要成员变量的含义如下:

    struct regmap_bus

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

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

    比如:

    static struct regmap_bus regmap_i2c

    [...]

    static struct regmap_bus regmap_i2c_smbus_i2c_block

    static struct regmap_bus regmap_spi

    static struct regmap_bus regmap_spmi_base

    [...]

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

    struct regmap_bus重要成员变量的含义如下:

    定义一个regmap_bus结构体时不需要初始化其所有成员变量,例如某些 i2c 芯片的寄存器是16bit(2Byte)的,其使用的regmap_bus如下:

    常用的regmap API

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

    可以猜测上述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 的操作次数。给我的感觉是:内核越来越强大了,但是学习的难度也越来越大,前路漫漫啊,求老司机带路...

    你和我各有一个苹果,如果我们交换苹果的话,我们还是只有一个苹果。但当你和我各有一个想法,我们交换想法的话,我们就都有两个想法了。如果你也对嵌入式系统开发有兴趣,并且想和更多人互相交流学习的话,请关注我的公众号:ESexpert,一起来学习吧,欢迎各种收藏/转发/批评。

    相关文章

      网友评论

        本文标题:嵌入式系统砖家_寄存器读写抽象之regmap

        本文链接:https://www.haomeiwen.com/subject/houfectx.html