当前位置:首页>Linux>Linux设备驱动 -- MPU6050 驱动(regmap子系统)

Linux设备驱动 -- MPU6050 驱动(regmap子系统)

  • 2026-09-02 19:36:19
Linux设备驱动 -- MPU6050 驱动(regmap子系统)

regmap子系统

在芯片驱动开发中,基本都是读写操作寄存器。常用到的接口都是如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框架

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:

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;};

2、struct regmap_config

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;};
  • 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
structregmap *__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在驱动卸载时会自动释放

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手动释放

voidregmap_exit(struct regmap *map);

2、regmap读写

读取单个寄存器:

intregmap_read(struct regmap *map, unsignedint reg, unsignedint *val);            intregmap_write(struct regmap *map, unsignedint reg, unsignedint val);
  • map ,申请初始化成功要操作的regmap
  • reg,寄存器地址
  • val,写入寄存器的值
  • *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);
  • 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)staticinlineintregmap_update_bits_base(struct regmap *map, unsignedint reg,unsignedint mask, unsignedint 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、驱动的修改

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;}

最新文章

随机文章