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

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

使开发更加方便

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒2 天前
TShark:基础知识
linux
AlfredZhao2 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334663 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪3 天前
linux 拷贝文件或目录到指定的位置
linux
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式