regmap子系统
在芯片驱动开发中,基本都是读写操作寄存器。
常用到的接口都是如SPI、I2C,对于一些硬件则可能会支持两种接口,
对于Linux的SPI、I2C总线,它们读写操作接口是不同,
但是针对这些硬件而言无论使用哪种接口它们最终读写寄存器的方式都是一样。
比如一般OLED支持I2C和SPI接口,开始使用SPI接口,后续要改成I2C,针对这种更换接口就要将驱动改动很大的做法在Linux下是不可想象,
基于分层分离的思想,代码复用等原则,
Linux下引入了regmap模型,将SPI、I2C这些常用接口统一抽象成regmap API
SOC内部寄存器也可以使用Regmap进行访问
使用regmap的情况:
1、硬件寄存器操作,如i2c接口的mpu6050芯片的寄存器读写,SOC内部寄存器操作
2、代码复用,简化代码开发
3、减少底层 I/O 操作次数,提高访问效率
regmap框架
regmap框架实现分为三层,从中也可以看出分层分离的思想。
1、硬件物理总线,对不同物理总线操作进行封装
2、regmap core, 实现regmap子系统
3、regmap API,抽象出regmap子系统统一的接口
对于一般的驱动工程师只需要关注regmap API即可。
regmap支持的物理总线:
i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq、spmi 和 w1
regmap相关数据结构
1、struct regmap
drivers/base/regmap/internal.h:
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 */
gfp_t alloc_flags;
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);
bool (*readable_noinc_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;
const struct regmap_access_table *rd_noinc_table;
int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
int (*reg_write)(void *context, unsigned int reg, unsigned int val);
int (*reg_update_bits)(void *context, unsigned int reg,
unsigned int mask, unsigned int val);
bool defer_caching;
unsigned long read_flag_mask;
unsigned long write_flag_mask;
......
struct rb_root range_tree;
void *selector_work_buf; /* Scratch buffer used for selector */
......
struct hwspinlock *hwlock;
};
2、struct regmap_config
include\linux\regmap.h: 该结构用于配置regmap
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);
bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
bool disable_locking;
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;
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 regmap_access_table *rd_noinc_table;
const struct reg_default *reg_defaults;
unsigned int num_reg_defaults;
enum regcache_type cache_type;
const void *reg_defaults_raw;
unsigned int num_reg_defaults_raw;
unsigned long read_flag_mask;
unsigned long write_flag_mask;
bool zero_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;
bool use_hwlock;
unsigned int hwlock_id;
unsigned int hwlock_mode;
};
- name,名字
reg_bits
,寄存器的位数,必填字段
- reg_stride,寄存器地址步长
- pad_bits,寄存器和值之间的填充位数
val_bits
,寄存器值的位数,必填字段
- fast_io,使用spinlock歹徒mutex提升锁性能
- read_flag_mask,读标志位掩码,有些芯片读取寄存器时某个位是1,就可以设置这个掩码字段
- write_flag_mask,写标志位掩码
regmap相关函数
1、regmap申请与初始化
regmap申请接口
#define regmap_init_xxx(i2c, config) \
__regmap_lockdep_wrapper(__regmap_init_xxx, #config, \
xxx, config)
- xxx,表示协议名字,如spi、i2c、sccb、slimbus
spi总线相关regmap申请
#define regmap_init_spi(dev, config) \
__regmap_lockdep_wrapper(__regmap_init_spi, #config, \
dev, config)
- dev, struct spi_device,需要使用regmap的spi_device
- config,struct regmap_config,配置regmap
- 返回值,struct regmap对象
#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)
#endif
struct regmap *__regmap_init_spi(struct spi_device *dev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *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在驱动卸载时会自动释放
i2c regmap申请
#define regmap_init_i2c(i2c, config) \
__regmap_lockdep_wrapper(__regmap_init_i2c, #config, \
i2c, config)
i2c总线封装的接口和spi等都是一样的,不同的是regmap_init_i2c
的第一个参数是struct i2c_client
regmap手动释放
void regmap_exit(struct regmap *map);
2、regmap读写
读取单个寄存器:
int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
- map ,申请初始化成功要操作的regmap
- reg,寄存器地址
- val,写入寄存器的值
- *val,存放读出来的值
读写多个寄存器的值:
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_count);
int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
size_t val_count);
- map ,申请初始化成功要操作的regmap
- *reg,寄存器地址
- *val,写入/读取数据存放缓冲区
- val_count,写入/读取寄存器的数量
更改寄存器某个位:
#define regmap_update_bits(map, reg, mask, val) \
regmap_update_bits_base(map, reg, mask, val, NULL, false, false)
static inline int regmap_update_bits_base(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,
bool *change, bool async, bool force)
- map ,申请初始化成功要操作的regmap
- *reg,寄存器地址
- mask,掩码,标志哪个位需要清除或设置
- val,需要更新的位值,将val的值对应的mask标志的位置1或置0,置1表示设置寄存器该位为1,置0表示设置寄存器该位为0
mpu6050 regmap驱动实现
1、设备树编写
设备树节点沿用mpu6050 IIC驱动的节点
2、驱动的修改
struct mpu6050_device {
int irq; /* 中断号 */
int gpio;
dev_t dev_no; /* 设备号 */
struct cdev chrdev;
struct class *class;
struct mutex m_lock;
wait_queue_head_t wq; /* 等待队列 */
struct mpu6050_data data;
struct regmap *rmap;
struct regmap_config cfg;
};
增加regmap相关数据结构:
struct regmap *rmap;
struct regmap_config cfg;
修改读写寄存器函数:
static int mpu6050_i2c_write_reg(struct regmap *rmap, uint32_t reg_addr, uint32_t data)
{
int ret = 0;
regmap_write(rmap, reg_addr, data);
return 0;
}
static int mpu6050_i2c_read_reg(struct regmap *rmap, uint32_t reg_addr, uint32_t *data)
{
regmap_read(rmap, reg_addr, data);
return 0;
}
只是将第一个参数类型从struct i2c_client
改成struct regmap
修改mpu6050驱动函数:
static int mpu6050_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);
#endif
return ret;
}
static int mpu6050_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复位, 寄存器恢复默认值 */
static int mpu6050_deinit(void)
{
int ret = 0;
ret = mpu6050_i2c_write_reg(mpu6050_dev->rmap, MPU6050_PWR_MGMT_1, 0x80);
return ret;
}
/* 读取mpu6050 ID */
static int mpu6050_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;
}
/* 读取加速度 */
static int mpu6050_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;
}
/* 读取陀螺仪数据 */
static int mpu6050_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;
}
static int mpu6050_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;
}