53.Linux regmap驱动框架

不需要在使用复杂的spi、i2c各种协议要求的代码,使用regmap提供的API函数就可以实现相关的功能,非常有利于开发
regmap结构体
c
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;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
const char *debugfs_name;
unsigned int debugfs_reg_len;
unsigned int debugfs_val_len;
unsigned int debugfs_tot_len;
struct list_head debugfs_off_cache;
struct mutex cache_lock;
#endif
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);
bool defer_caching;
u8 read_flag_mask;
u8 write_flag_mask;
/* number of bits to (left) shift the reg value when formatting*/
int reg_shift;
int reg_stride;
/* regcache specific members */
const struct regcache_ops *cache_ops;
enum regcache_type cache_type;
/* number of bytes in reg_defaults_raw */
unsigned int cache_size_raw;
/* number of bytes per word in reg_defaults_raw */
unsigned int cache_word_size;
/* number of entries in reg_defaults */
unsigned int num_reg_defaults;
/* number of entries in reg_defaults_raw */
unsigned int num_reg_defaults_raw;
/* if set, only the cache is modified not the HW */
u32 cache_only;
/* if set, only the HW is modified not the cache */
u32 cache_bypass;
/* if set, remember to free reg_defaults_raw */
bool cache_free;
struct reg_default *reg_defaults;
const void *reg_defaults_raw;
void *cache;
/* if set, the cache contains newer data than the HW */
u32 cache_dirty;
/* if set, the HW registers are known to match map->reg_defaults */
bool no_sync_defaults;
struct reg_default *patch;
int patch_regs;
/* if set, converts bulk rw to single rw */
bool use_single_rw;
/* if set, the device supports multi write mode */
bool can_multi_write;
struct rb_root range_tree;
void *selector_work_buf; /* Scratch buffer used for selector */
};
- 初始化regmap
c
regmap_init_spi()
regmap_init_i2c()
需要添加配置regmap的结构体regmap_config作为参数
c
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;
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;
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;
};
其中
c
u8 read_flag_mask;
u8 write_flag_mask;
这两个是读写标志掩码非常重要
区分不同的接口使用regmap_bus来完成
如regmap_bus中的各种操作集合使用spi的就调用
->regmap_spi_read,regmap_spi_write,
-> 调用spi_read ,spi_write等
或者i2c的...
释放就直接使用
c
void regmap_exit(struct regmap *map)
直接进行释放即可。
初始化之后就可以使用regmap提供的API函数直接读写寄存器
不管是 I2C 还是 SPI 等接口,还是 SOC 内部的寄存器,对于寄存器的操作就两种:读和写。 regmap 提供了最核心的两个读写操作: regmap_read 和 regmap_write。这两个函数分别用来读/写寄存器
regmap_read 函数
原型如下:
c
int regmap_read(struct regmap *map,
unsigned int reg,
unsigned int *val)
map: 要操作的 regmap。
reg: 要读的寄存器。
val:读到的寄存器值。
返回值:0,读取成功;其他值,读取失败。
regmap_write 函数
原型如下:
c
int regmap_write(struct regmap *map,
unsigned int reg,
unsigned int val)
map: 要操作的 regmap。
reg: 要写的寄存器。
val:要写的寄存器值。
返回值: 0,写成功;其他值,写失败。
在 regmap_read 和 regmap_write 的基础上还衍生出了其他一些 regmap 的 API 函数,首先是regmap_update_bits 函数,看名字就知道,此函数用来修改寄存器指定的 bit,函数原型如下:
c
int regmap_update_bits (struct regmap *map,
unsigned int reg,
unsigned int mask,
unsigned int val)
map: 要操作的 regmap。
reg: 要操作的寄存器。
mask: 掩码,需要更新的位必须在掩码中设置为 1。
val:需要更新的位值。
返回值: 0,写成功;其他值,写失败
比如要将寄存器的 bit1 和 bit2 置 1,那么 mask 应该设置为 0X00000011,此时 val 的 bit1和 bit2 应该设置为 1,也就是 0Xxxxxxx11。如果要清除寄存器的 bit4 和 bit7,那么 mask 应该设置为 0X10010000, val 的 bit4 和 bit7 设置为 0,也就是 0X0xx0xxxx。
接下来看一下 regmap_bulk_read 函数,此函数用于读取多个寄存器的值,函数原型如下:
c
int regmap_bulk_read(struct regmap *map,
unsigned int reg,
void *val,
size_t val_count)
map: 要操作的 regmap。
reg: 要读取的第一个寄存器。
val: 读取到的数据缓冲区。
val_count:要读取的寄存器数量。
返回值: 0,写成功;其他值,读失败
多个寄存器写函数 regmap_bulk_write,函数原型如下
c
int regmap_bulk_write(struct regmap *map,
unsigned int reg,
const void *val,
size_t val_count)
map: 要操作的 regmap。
reg: 要写的第一个寄存器。
val: 要写的寄存器数据缓冲区。
val_count:要写的寄存器数量。
返回值: 0,写成功;其他值,读失败
关于 regmap 常用到 API 函数就讲解到这里,还有很多其他功能的 API 函数,大家自行查阅 Linux 内核即可,内核里面对每个 API 函数都有详细的讲解。
regmap_config 掩码设置
当我们使用 regmap 的时候就不需要手动将寄存器地址的 bit7 置 1,在初始化 regmap_config的时候直接将 read_flag_mask 设置为 0X80 即可,这样通过 regmap 读取 SPI 内部寄存器的时候就会将寄存器地址与 read_flag_mask 进行或运算,结果就是将 bit7 置 1,但是整个过程不需要我们来操作,全部由 regmap 框架来完成的
实战
添加头文件
c
#include <linux/regmap.h>
设备结构体中,添加regmap相关变量
c
struct icm20608_dev
{
dev_t devid;
int MAJOR;
int MINOR;
struct cdev cdev;
....
struct regmap *regmap;
struct regmap_config regmap_config;
};
在probe函数中spi初始化之前
c
//regmap_config初始化
icm20608.regmap_config.reg_bits = 8;
icm20608.regmap_config.val_bits = 8;
icm20608.regmap_config.read_flag_mask = 0x80;
//regmap初始化
icm20608.regmap = regmap_init_spi(spi,&icm20608.regmap_config);
注销则在remove中
c
regmap_exit(icm20608.regmap);
重新实现spi读写单个寄存器
c
//读取单个寄存器
static unsigned char icm20608_read_Onereg(struct icm20608_dev *dev , u8 reg){
unsigned int data = 0;
int ret = 0;
ret = regmap_read(dev->regmap,reg,&data);
return (u8)ret;
}
//写入单个寄存器
static void icm20608_write_Onereg(struct icm20608_dev *dev , u8 reg , u8 value){
u8 buf = value;
int ret = 0;
ret = regmap_write(dev->regmap,reg,buf);
}
同时:
c
static int icm20608_read_regs(struct icm20608_dev *dev , u8 reg , void *buf , int len){
struct spi_device *spi = dev->private_data;
u8 data =0;
data = reg | 0x80;
spi_write_then_read(spi,&data,1,buf,len);
return 0;
}
static int icm20608_write_regs(struct icm20608_dev *dev , u8 reg, u8 *buf , int len){
struct spi_device *spi = dev->private_data;
u8 *txdata;
txdata = kzalloc(len+1,GFP_KERNEL);
txdata[0] = reg &~ 0x80;
memcpy(&txdata[1],buf,len);
spi_write(spi,txdata,len+1);
kfree(txdata);
return 0;
}
//调用
icm20608_read_regs(dev, ICM20_ACCEL_XOUT_H, data, 14);
连续读写多个寄存器的值也不需要这样实现
就可以直接使用一句代码即可:
c
regmap_bulk_read(dev->regmap,ICM20_ACCEL_XOUT_H,data,14);
对于ap3216c这样的i2c设备驱动改写
c
struct ap3216c_dev
{
dev_t devid;
int MAJOR;
int MINOR;
...
unsigned short ir;
unsigned short ps;
unsigned short als;
struct regmap *regmap;
struct regmap_config regmap_config;
};
在i2c的probe函数中
c
//初始化regmap
ap3216c.regmap_config.reg_bits = 8;
ap3216c.regmap_config.val_bits = 8;
ap3216c.regmap = regmap_init_i2c(client,&ap3216c.regmap_config);
在remove中
c
regmap_exit(ap3216c.regmap);
使用regmap
直接将下面代码进行改写
c
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)dev->private_data;
/* msg[0]为发送要读取的首地址 */
msg[0].addr = client->addr; /* ap3216c地址 */
msg[0].flags = 0; /* 标记为发送数据 */
msg[0].buf = ® /* 读取的首地址 */
msg[0].len = 1; /* reg长度*/
/* msg[1]读取数据 */
msg[1].addr = client->addr; /* ap3216c地址 */
msg[1].flags = I2C_M_RD; /* 标记为读取数据*/
msg[1].buf = val; /* 读取数据缓冲区 */
msg[1].len = len; /* 要读取的数据长度*/
ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
ret = 0;
} else {
printk("i2c rd failed=%d reg=%06x len=%d\n",ret, reg, len);
ret = -EREMOTEIO;
}
return ret;
}
/*
* @description : 向ap3216c多个寄存器写入数据
* @param - dev: ap3216c设备
* @param - reg: 要写入的寄存器首地址
* @param - val: 要写入的数据缓冲区
* @param - len: 要写入的数据长度
* @return : 操作结果
*/
static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->private_data;
b[0] = reg; /* 寄存器首地址 */
memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组b里面 */
msg.addr = client->addr; /* ap3216c地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = b; /* 要写入的数据缓冲区 */
msg.len = len + 1; /* 要写入的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
}
改写如下:
c
static int ap3216c_read_regs(struct ap3216c_dev *dev, u8 reg, void *val, int len)
{
return regmap_bulk_read(dev->regmap,reg,val,len);
}
static s32 ap3216c_write_regs(struct ap3216c_dev *dev, u8 reg, u8 *buf, u8 len)
{
return regmap_bulk_write(dev->regmap,reg,buf,len);
}
读写单个寄存器
c
/*
* @description : 读取ap3216c指定寄存器值,读取一个寄存器
* @param - dev: ap3216c设备
* @param - reg: 要读取的寄存器
* @return : 读取到的寄存器值
*/
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev, u8 reg)
{
u8 data = 0;
ap3216c_read_regs(dev, reg, &data, 1);
return data;
#if 0
struct i2c_client *client = (struct i2c_client *)dev->private_data;
return i2c_smbus_read_byte_data(client, reg);
#endif
}
/*
* @description : 向ap3216c指定寄存器写入指定的值,写一个寄存器
* @param - dev: ap3216c设备
* @param - reg: 要写的寄存器
* @param - data: 要写入的值
* @return : 无
*/
static void ap3216c_write_reg(struct ap3216c_dev *dev, u8 reg, u8 data)
{
u8 buf = 0;
buf = data;
ap3216c_write_regs(dev, reg, &buf, 1);
}
则改写成
c
//读取单个寄存器
static unsigned char ap3216c_read_reg(struct ap3216c_dev *dev,u8 reg){
unsigned int data = 0;
regmap_read(dev->regmap,reg,&data);
return (u8)data;
}
//写入单个寄存器
static void ap3216c_write_reg(struct ap3216c_dev *dev,u8 reg,u8 data){
u8 buf = 0;
buf = data;
regmap_write(dev->regmap,reg,data);
}
可以看到大量减少了重复性的工作
使开发更加方便