一.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)
打赏作者
近期评论