53.Linux regmap驱动框架

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 */
};
  1. 初始化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 = &reg;					/* 读取的首地址 */
	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);
}

可以看到大量减少了重复性的工作

使开发更加方便

相关推荐
python百炼成钢1 小时前
54.Linux IIO驱动框架
linux·运维·服务器·驱动开发
纷飞梦雪1 小时前
ubuntu22开启root
linux·运维·ubuntu
Konwledging1 小时前
linux debug工具集合
linux
星哥说事1 小时前
恶意团伙利用 PHP-FPM 未授权访问漏洞发起大规模攻击
linux·服务器
Evan芙1 小时前
shell编程求10个随机数的最大值与最小值
java·linux·前端·javascript·网络
再睡一夏就好1 小时前
进程调度毫秒之争:详解Linux O(1)调度与进程切换
linux·运维·服务器·c++·算法·哈希算法
咬_咬1 小时前
C++仿muduo库高并发服务器项目:EventLoop模块
服务器·c++·muduo·eventloop
BS_Li1 小时前
【Linux系统编程】库制作与原理
linux·运维·服务器