linux Regmap API寄存器映射抽象

        在引入ragmap API之前,SPI内核和I2C内核是独立的,二者有需要冗余代码。在引入Regmap API之后,无论是SPI还是I2C设备,只需要初始化配置Regmap,旧可以处理所有的读写修改操作。

        Regmap API很简单,其中最重要的是两个结构struct regmap_config(代表regmap配置) 和struct regmap(代表regmap实例本身)。

1. regmap_config结构

        struct regmap_config在驱动程序生命周期内保存Regmap配置,这些设置会影响读/写操作。

struct regmap_config {
    char *name;
    int reg_bits;  // 寄存器地址的位数
    int reg_stride;
    int pad_bits;
    int val_bits; // 寄存器值的位数

    bool (*writable_reg)(struct device *dev, uint reg); // 在写入Reg之前,会调用检查是否可写
    bool (*readable_reg)(struct device *dev, uint reg);// 在写入Reg之前,会调用检查是否可读
    bool (*volatile_reg)(struct device *dev, uint reg); // 检查寄存器是否是易失的,确定是否使用缓存
    bool (*precious_reg)(struct device *dev, uint reg);
    regmap_lock lock;
    regmap_unlock unlock;
    void *lock_arg;

    int (*reg_read)(void *context, uint reg, uint *val); // 在寄存器不支持简单的I2c/SPI读取操作时,需要自定义read函数
    int (*reg_write)(void *context, uint reg, uint val);
    bool fast_io;

    uint max_register;
    const struct regmap_access_table *wr_table; // 在没有writable_reg会调用函数时,使用该值来判断是否可写,该结构体中有yes_range和no_range,其类型为struct regmap_range{}
    const struct regmap_access_table *rd_table;
    const struct regmap_access_table *volatile_table;
    const struct regmap_access_table *precious_table;
    const struct reg_default *reg_defaults;
    uint num_reg_defaults;
    enum regcache_type cache_type;
    const void *reg_defaults_raw;
    uint num_reg_defaults_raw;

    u8 read_flag_mask;
    u8 write_flag_mask;
    bool use_single_rw;
    bool can_multi_write;

    enum regmap_endian reg_format_endian;
    enum regmap_endian val_format_endian;
    const struct regmap_range_cfg *ranges;
    uint num_ranges;
};


// 函数writeable_reg回调函数实现示例:
static bool foo_writeable_register(struct device *dev, uint reg) {
    switch (reg) {
    case 0x30 ... 0x38:
    case 0x40 ... 0x45:
    case 0x50 ... 0x57:
    case 0x60 ... 0x69:
    case 0x70 ... 0x7b:
    case 0x80 ... 0x8c:
    case 0x90 ... 0x9e:
    case 0xa0 ... 0xa9:
    case 0xb0 ... 0xb3:
        return true;
    default:
        return false;
    };
};

// 下面是一个简单的regmap_config{}实例化的示例:
static const struct regmap_config regmap_cfg = {
    .reg_bits = 8,
    .val_bits = 8,
    .max_register = LM3533_REG_MAX,
    .reagable_reg = lm3533_readable_reg,
    .writeable_reg = lm3533_writeable_reg,
    .volatile_reg = lm3533_volatile_reg,
    .precious_reg = lm3533_precious_reg,
};

2. Regmap初始化

        Regmap支持SPI和I2C协议,根据支持的协议的不同,在I2C中必须调用regmap_init_i2c()或regmap_init_spi(),如果要编写通用的驱动程序,使用regmap是最佳选择。regmap是通用和同质的,这两类总线只是初始化不同,其他功能完全相同。

        在remove函数中,调用regmap_exit()释放先前分配的寄存器。

// 初始化和释放函数原型
struct regmap* regmap_init_i2c(struct i2c_client *i2c,
                        const struct regmap_config);
struct regmap* regmap_init_i2c(struct spi_device *spi,
                        const struct regmap_config);
void regmap_exit(struct regmap *regmap);

// 初始化SPI设备的示例
static int foo_spi_probe(struct spi_device *spi) {
    int err;
    struct regmap *my_regmap;
    struct regmap_config foo_regmap_config;
    [...]// 省略了初始化和填充foo_regmap_config
    my_regmap = regmap_init_spi(spi, &foo_regmap_config);
    if (IS_ERR(my_regmap)) {
        err = PTR_ERR(my_regmap);
        return err;
    }
    [...]// 其他初始化流程
}

// 初始化I2C设备的示例
static int bar_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) {
    struct regmap *my_regmap;
    struct regmap_config bar_regmap_config;
    [...]// 省略了初始化和填充bar_regmap_config
    spi->bit_per_word = 8;
    my_regmap = regmap_init_i2c(i2c, &bar_regmap_config);
    if (IS_ERR(my_regmap)) {
        err = PTR_ERR(my_regmap);
        return err;
    }
    [...]// 其他初始化流程
}

3. 使用Regmap之后的设备访问函数

        Regmap API内部处理数据的解析、格式化和传输。提供了3个重要函数regmap_write, regmap_read, regmap_update_bits,其原型如下:

int regmap_write(struct regmap *map, uint reg, uint *val);
int regmap_read(struct regmap *map, uint reg, uint val);
int regmap_update_bits(struct regmap *map, uint reg, uint mask, uint val);
// 其中regmap_update_bits会写入mask所标识的那些位,在哪些位写入 mask & val;

这些函数会检查对应的reg是否可读写,是否可缓存再进行相应操作,以regmap_write函数为例,检查流程如下:

  • 如果参数reg大于regmap_config.max_register,则返回-EIO;否则继续执行
  • 如果调用regmap_config.writeable_reg(reg)返回false,则返回-EIO,否则继续执行;
  • 如果设置了wr_table,如果reg在yes_range内则继续执行,否则返回-EIO
  • 如果cache_type != RGECACHE_NONE,则启用缓存。启用缓存时,写操作会先更新缓存,之后执行硬件的写操作;
  • 如果提供了regmap_config.reg_write回调函数,则调用它执行写入操作;

Regmap API也一次写入多个寄存器的函数regmap_multi_reg_write()

int regmap_multi_reg_write(struct regmap* map,
            const struct reg_sequence *regs, int num_regs);
// 其中结构体struct reg_sequece{}
struct reg_sequence {
    uint reg;
    uint def;  // 寄存器写入的值
    uint delay_us;  // 在寄存器写入后的延迟,us
};


// 使用示例:
static const struct reg_sequence foo_regs[] = {
{FOO_REG1, 0XB8},
{BAR_REG1, 0X23},
{REG_INIT, 0X12},
{REG_POWER, 0X00},
{REG_RESET, 0X00},
};

int ret = regmap_multi_reg_write(map, foo_regs, ARRAY_SIZE(foo_regs));

Regmap API还提供了一些其他的bulk写入函数,用于多个寄存器的写入和读取

int regmap_bulk_read(struct regmap *map, uint reg,
                        void *val, size_t val_count);
int regmap_bulk_write(struct regmap *map, uint reg,
                const void *val, size_t val_count);

4. Regmap和缓存

        Regmap API支持缓存,是否使用缓存取决于regmap_config.cache_type字段的值。

enum regcache_type {
    REGCACHE_NONE,  // 不启用缓存
    REGCACHE_RBTREE, // 使用红黑树方式储存缓存
    REGCACHE_COMPRESSED, // 使用压缩方式
    REGCACHE_FLAT, // 使用数组方式储存缓存
};

        缓存只影响读的性能,任何写操作都会同时写入缓存和寄存器中。设备的某些寄存器可能具有预定义的加电复位值,使用regmap_config.reg_defaults字段来设置储存的这些初始值。

struct reg_default {
    uint reg;
    uint def;
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值