在芯片驱动开发中,基本都是读写操作寄存器。常用到的接口都是如SPI、I2C,对于一些硬件则可能会支持两种接口,对于Linux的SPI、I2C总线,它们读写操作接口是不同,但是针对这些硬件而言无论使用哪种接口它们最终读写寄存器的方式都是一样。比如一般OLED支持I2C和SPI接口,开始使用SPI接口,后续要改成I2C,针对这种更换接口就要将驱动改动很大的做法在Linux下是不可想象,基于分层分离的思想,代码复用等原则,Linux下引入了regmap模型,将SPI、I2C这些常用接口统一抽象成regmap APISOC内部寄存器也可以使用Regmap进行访问使用regmap的情况:
1、硬件寄存器操作,如i2c接口的mpu6050芯片的寄存器读写,SOC内部寄存器操作2、代码复用,简化代码开发3、减少底层 I/O 操作次数,提高访问效率regmap框架实现分为三层,从中也可以看出分层分离的思想。

1、硬件物理总线,对不同物理总线操作进行封装 2、regmap core, 实现regmap子系统 3、regmap API,抽象出regmap子系统统一的接口
对于一般的驱动工程师只需要关注regmap API即可。
regmap支持的物理总线:
i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq、spmi 和 w1drivers/base/regmap/internal.h:
structregmap {union {structmutexmutex;struct {spinlock_t spinlock;unsignedlong spinlock_flags; }; }; regmap_lock lock; regmap_unlock unlock;void *lock_arg; /* This is passed to lock/unlock functions */gfp_t alloc_flags;structdevice *dev;/* Device we do I/O on */void *work_buf; /* Scratch buffer used to format I/O */structregmap_formatformat;/* Buffer format */conststructregmap_bus *bus;void *bus_context;constchar *name;bool async;spinlock_t async_lock;wait_queue_head_t async_waitq;structlist_headasync_list;structlist_headasync_free;int async_ret; ......unsignedint max_register;bool (*writeable_reg)(struct device *dev, unsignedint reg);bool (*readable_reg)(struct device *dev, unsignedint reg);bool (*volatile_reg)(struct device *dev, unsignedint reg);bool (*precious_reg)(struct device *dev, unsignedint reg);bool (*readable_noinc_reg)(struct device *dev, unsignedint reg);conststructregmap_access_table *wr_table;conststructregmap_access_table *rd_table;conststructregmap_access_table *volatile_table;conststructregmap_access_table *precious_table;conststructregmap_access_table *rd_noinc_table;int (*reg_read)(void *context, unsignedint reg, unsignedint *val);int (*reg_write)(void *context, unsignedint reg, unsignedint val);int (*reg_update_bits)(void *context, unsignedint reg,unsignedint mask, unsignedint val);bool defer_caching;unsignedlong read_flag_mask;unsignedlong write_flag_mask; ......structrb_rootrange_tree;void *selector_work_buf; /* Scratch buffer used for selector */ ......structhwspinlock *hwlock;};include\linux\regmap.h: 该结构用于配置regmap
structregmap_config {constchar *name;int reg_bits;int reg_stride;int pad_bits;int val_bits;bool (*writeable_reg)(struct device *dev, unsignedint reg);bool (*readable_reg)(struct device *dev, unsignedint reg);bool (*volatile_reg)(struct device *dev, unsignedint reg);bool (*precious_reg)(struct device *dev, unsignedint reg);bool (*readable_noinc_reg)(struct device *dev, unsignedint reg);bool disable_locking; regmap_lock lock; regmap_unlock unlock;void *lock_arg;int (*reg_read)(void *context, unsignedint reg, unsignedint *val);int (*reg_write)(void *context, unsignedint reg, unsignedint val);bool fast_io;unsignedint max_register;conststructregmap_access_table *wr_table;conststructregmap_access_table *rd_table;conststructregmap_access_table *volatile_table;conststructregmap_access_table *precious_table;conststructregmap_access_table *rd_noinc_table;conststructreg_default *reg_defaults;unsignedint num_reg_defaults;enumregcache_typecache_type;constvoid *reg_defaults_raw;unsignedint num_reg_defaults_raw;unsignedlong read_flag_mask;unsignedlong write_flag_mask;bool zero_flag_mask;bool use_single_rw;bool can_multi_write;enumregmap_endianreg_format_endian;enumregmap_endianval_format_endian;conststructregmap_range_cfg *ranges;unsignedint num_ranges;bool use_hwlock;unsignedint hwlock_id;unsignedint hwlock_mode;};reg_bits必填字段val_bits必填字段#define regmap_init_xxx(i2c, config) \ __regmap_lockdep_wrapper(__regmap_init_xxx, #config, \ xxx, config)#define regmap_init_spi(dev, config) \ __regmap_lockdep_wrapper(__regmap_init_spi, #config, \ dev, config)#ifdef CONFIG_LOCKDEP#define __regmap_lockdep_wrapper(fn, name, ...) \( \ ({ \ static struct lock_class_key _key; \ fn(__VA_ARGS__, &_key, \ KBUILD_BASENAME ":" \ __stringify(__LINE__) ":" \"(" name ")->lock"); \ }) \)#else#define __regmap_lockdep_wrapper(fn, name, ...) fn(__VA_ARGS__, NULL, NULL)#endifstructregmap *__regmap_init_spi(structspi_device *dev,conststructregmap_config *config,structlock_class_key *lock_key,constchar *lock_name);经过 一系列替换,最终调用的是__regmap_init_spi
同类型的宏:devm_regmap_init_spi
#define devm_regmap_init_spi(dev, config) \ __regmap_lockdep_wrapper(__devm_regmap_init_spi, #config, \ dev, config)申请的regmap在驱动卸载时会自动释放
#define regmap_init_i2c(i2c, config) \ __regmap_lockdep_wrapper(__regmap_init_i2c, #config, \ i2c, config)i2c总线封装的接口和spi等都是一样的,不同的是regmap_init_i2c的第一个参数是struct i2c_client
voidregmap_exit(struct regmap *map);读取单个寄存器:
intregmap_read(struct regmap *map, unsignedint reg, unsignedint *val); intregmap_write(struct regmap *map, unsignedint reg, unsignedint val);读写多个寄存器的值:
intregmap_bulk_read(struct regmap *map, unsignedint reg, void *val,size_t val_count);intregmap_bulk_write(struct regmap *map, unsignedint reg, constvoid *val,size_t val_count);更改寄存器某个位:
#define regmap_update_bits(map, reg, mask, val) \ regmap_update_bits_base(map, reg, mask, val, NULL, false, false)staticinlineintregmap_update_bits_base(struct regmap *map, unsignedint reg,unsignedint mask, unsignedint val,bool *change, bool async, bool force)设备树节点沿用mpu6050 IIC驱动的节点structmpu6050_device {int irq; /* 中断号 */int gpio;dev_t dev_no; /* 设备号 */structcdevchrdev;structclass *class;structmutexm_lock;wait_queue_head_t wq; /* 等待队列 */structmpu6050_datadata;structregmap *rmap;structregmap_configcfg;};增加regmap相关数据结构:
structregmap *rmap;structregmap_configcfg;修改读写寄存器函数:
staticintmpu6050_i2c_write_reg(struct regmap *rmap, uint32_t reg_addr, uint32_t data){int ret = 0; regmap_write(rmap, reg_addr, data);return0;}staticintmpu6050_i2c_read_reg(struct regmap *rmap, uint32_t reg_addr, uint32_t *data){ regmap_read(rmap, reg_addr, data);return0;}只是将第一个参数类型从struct i2c_client改成struct regmap
修改mpu6050驱动函数:
staticintmpu6050_init(void){uint8_t reg_val = 0;int ret = 0;/* 解除休眠状态 */ ret = mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_PWR_MGMT_1, 0x00); /* *//* 陀螺仪采样频率输出设置 */ ret = mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_SMPLRT_DIV, 0x07); ret = mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_CONFIG, 0x06);/* 配置加速度传感器工作在 16G 模式, 不自检 */ ret = mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_ACCEL_CONFIG, 0x18);/* 陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s) */ ret = mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_GYRO_CONFIG, 0x18);#if MPU6050_USE_INT /* 配置中断产生时中断引脚为低电平 */// ret = mpu6050_i2c_read_reg(mpu6050_dev->rmap, MPU6050_INT_PIN_CFG, ®_val);// reg_val |= 0x80; mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_INT_PIN_CFG, 0x9C);/* 开启数据就绪中断 */ ret = mpu6050_i2c_read_reg(mpu6050_dev->rmap, MPU6050_INT_ENABLE, ®_val); reg_val |= 0x01; mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_INT_ENABLE, reg_val);#endifreturn ret;}staticintmpu6050_data_ready_interrupt_on_off(uint8_t on_off){uint32_t reg_val = 0;int ret = 0; ret = mpu6050_i2c_read_reg(mpu6050_dev->rmap, MPU6050_INT_ENABLE, ®_val);if (on_off) { reg_val |= 0x01; ret = mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_INT_ENABLE, reg_val); }else { reg_val &= 0xFE; ret = mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_INT_ENABLE, reg_val); }return ret;}/* mpu6050复位, 寄存器恢复默认值 */staticintmpu6050_deinit(void){int ret = 0; ret = mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_PWR_MGMT_1, 0x80);return ret;}/* 读取mpu6050 ID */staticintmpu6050_read_id(uint8_t *id){uint32_t data;int ret = 0; ret = mpu6050_i2c_read_reg(mpu6050_dev->rmap, MPU6050_WHO_AM_I, &data);if (id != NULL) *id = (uint8_t)data;if (data != MPU6050_IIC_ADDR) ret = -1;return ret;}/* 读取加速度 */staticintmpu6050_read_accel(struct mpu6050_accel *acc){int i = 0;int ret = 0;uint32_t data[6] = {0};for (i = 0; i < 6; i++) { ret = mpu6050_i2c_read_reg(mpu6050_dev->rmap, MPU6050_ACCEL_XOUT_H+i, &data[i]); } acc->x = (uint8_t)(data[0] << 8) + (uint8_t)data[1]; acc->y = (uint8_t)(data[2] << 8) + (uint8_t)data[3]; acc->z = (uint8_t)(data[4] << 8) + (uint8_t)data[5];return ret;}/* 读取陀螺仪数据 */staticintmpu6050_read_gyro(struct mpu6050_gyro *gyro){int i = 0;int ret = 0;uint32_t data[6] = {0};for (i = 0; i < 6; i++) { ret = mpu6050_i2c_read_reg(mpu6050_dev->rmap, MPU6050_GYRO_XOUT_H+i, &data[i]); } gyro->x = (uint8_t)(data[0] << 8) + (uint8_t)data[1]; gyro->y = (uint8_t)(data[2] << 8) + (uint8_t)data[3]; gyro->z = (uint8_t)(data[4] << 8) + (uint8_t)data[5];return ret;}staticintmpu6050_read_temp(short *temp){int i = 0;int ret = 0;uint32_t data[2] = {0};for (i = 0; i < 2; i++) { ret = mpu6050_i2c_read_reg(mpu6050_dev->rmap, MPU6050_TEMP_OUT_H+i, &data[i]); } *temp = (uint8_t)(data[0] << 8) + (uint8_t)data[1];return ret;}