Linux / 软件积累 · 2023年7月31日

[LINUX 内核] REGMAP框架模型

一.REGMAP子系统作用

基于代码代码复用的原则之一,Linux在3.1内核后引入了regmap模型,将寄存器访问的共同逻辑抽象出来,只需初始化时指定总线类型、寄存器位宽等关键参数,即可通过regmap模型接口来操作器件寄存器。当然,regmap同样适用于操作cpu自身的寄存器。

如针对i2c设备我们通过接口i2c_transfer、i2c_master_send等接口进行读写操作,而针对spi 设备我们则通过接口spi_write、spi_sync、spi_async等接口实现读写操作。尤其在hwmon子系统、regulator子系统中,大部分的设备基本上就是i2c、spi设备。虽然直接调用i2c或spi设备的操作接口也很方便,但这些子系统中的读写接口中充斥着大量的i2c、spi操作,那能不能让通过设计接口减少直接对i2c、spi等操作呢?

1.1 什么是REGMAP

regmap是在 linux 内核为减少慢速 I/O 驱动上的重复逻辑,提供一种通用的接口来操作底层硬件寄存器的模型框架。此外,regmap在驱动和硬件寄存器之间增加了cache,减少底层低速 I/O 的操作次数,提高访问效率;当然实时性会有所降低。

1.2为什么要用regmap

regmap的特性和优点决定了我们为什么要用regmap。

  • 统一寄存器操作接口
  • 提高代码重用性和驱动一致性
  • 减少底层I/O操作次数,提高访问效率
  • 简化驱动开发过程

1.3REGMAP框架模型


/*
@	Linux内核将regmap框架抽象为regmap结构体
@	定义在文件drivers/base/regmap/internal.h 中
@	regmap 结构体 
*/
  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 */
        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); 
    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; 
 
    int (*reg_read)(void *context, unsigned int reg,  unsigned int *val); 
     int (*reg_write)(void *context, unsigned int reg, 
                      unsigned int val); 
... 
     struct rb_root range_tree; 
     void *selector_work_buf;  /* Scratch buffer used for selector */
 };   
 
/***************************************************************************************
*上面介绍了结构体,我们要实例化一个结构体也就是实例化regmap框架,实例化就是一系列的初始化
*****************************************************************************************/
/*
@	regmap_config 结构体就是用来初始化 regmap结构体即时初始化regmap框架
@	定义在include/linux/regmap.h文件中
@	regmap_config 结构体
*/
 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); /*当寄存器值不能被读出来的时候此回调函数会被调用*/
     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; /*快速 I/O,使用spinlock替代mutex来提升锁性能*/
  
     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 reg_default *reg_defaults; /*寄存器模式值,为reg_default结构体类型*/
     unsigned int num_reg_defaults; /*默认寄存器表中的元素个数*/
     enum regcache_type cache_type; 
     const void *reg_defaults_raw; 
     unsigned int 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; 
     unsigned int num_ranges; 
 };   


/*********************************************************************
*上面我们已经说了我们主要就是针对regmapAPI抽象层来下功夫==
*我们一般会在probe 函数中初始化regmap_config,然后申请并初始化 regmap
**********************************************************************/
/*
@	Linux内核提供了针对不同接口的regmap初始化函数,一下举例2个
@	SPI接口初始化函数为regmap_init_spi
@	I2C 接口的regmap初始化函数为regmap_init_i2c
@
@	spi:需要使用regmap的spi_device
@	i2c:需要使用regmap的i2c_client
@	config:regmap_config结构体,需要程序编写人员初始化一个regmap_config实例,然后将其地址赋值给此参数
@   返回值:申请到的并进过初始化的 regmap
*/
struct regmap * regmap_init_spi(struct spi_device  *spi,  const struct regmap_config  *config) 

struct regmap * regmap_init_i2c(struct i2c_client  *i2c,  const struct regmap_config    *config) 

/*
@	退出驱动的时候需要释放掉申请到的 regmap,不管是什么接口,全部使用 regmap_exit这个函数来释放regmap
@	map:需要释放的 regmap   
@	返回值:无
*/
void regmap_exit(struct regmap *map) 



/*******************************************************************
*对于寄存器的操作就两种:读和写。regmap提供了最核心的两个读写操作****
*********************************************************************/
/*
@	读寄存器操作
@	map:要操作的 regmap。
@	reg:要读的寄存器
@	val:读到的寄存器值
@	返回值:0,读取成功;其他值,读取失败
*/
int regmap_read(struct regmap    *map,   unsigned int    reg,  unsigned int      *val) 
/*
@	写寄存器操作
@	map:要操作的 regmap。
@	reg:要写的寄存器
@	val:要写的寄存器值
@	返回值:0,读取成功;其他值,写失败
*/
int regmap_write(struct regmap    *map,   unsigned int    reg,  unsigned int   *val) 
/*
@	读写的衍生API=用于读取多个寄存器的值
@	reg:要读取的第一个寄存器。
@	map:要操作的 regmap
@	val:读取到的数据缓冲区。 
@	val_count:要读取的寄存器数量。
@	返回值:0,写成功;其他值,读失败
*/
int regmap_bulk_read(struct regmap *map, unsigned int reg, void   *val, size_t     val_count) 
/*
@	读写的衍生API=用于写多个寄存器的值
@	reg:要写的第一个寄存器。
@	map:要操作的 regmap
@	val:要写的寄存器数据缓冲区。 
@	val_count:要写的寄存器数量。
@	返回值:0,写成功;其他值,写失败
*/
int regmap_bulk_write(struct regmap *map, unsigned int reg, void   *val, size_t     val_count) 

/*
@	读写的衍生API==修改寄存器指定的bit
@	map:要操作的 regmap
@	val:需要更新的位值。
@	返回值:0,写成功;其他值,写失败
*/
int regmap_update_bits (struct regmap    *map,   unsigned int   reg,  unsigned int    mask, unsigned int   val)
打赏作者