精华内容
下载资源
问答
  • regmap
    2022-07-04 19:41:56

    1.什么是regmap

    Linux 下大部分设备的驱动开发都是操作其内部寄存器,比如 I2C/SPI 设备的本质都是一样的,通过 I2C/SPI 接口读写芯片内部寄存器。芯片内部寄存器也是同样的道理,比如 I.MX6ULL的 PWM、定时器等外设初始化,最终都是要落到寄存器的设置上。
    Linux 下使用 i2c_transfer 来读写 I2C 设备中的寄存器,SPI 接口的话使用 spi_write/spi_read等。I2C/SPI 芯片又非常的多,因此 Linux 内核里面就会充斥了大量的 i2c_transfer 这类的冗余代码,再者,代码的复用性也会降低。比如 icm20608 这个芯片既支持 I2C 接口,也支持 SPI 接口。假设我们在产品设计阶段一开始将 icm20608 设计为 SPI 接口,但是后面发现 SPI 接口不够用,或者 SOC 的引脚不够用,我们需要将 icm20608 改为 I2C 接口。这个时候 icm20608 的驱动就要大改,我们需要将 SPI 接口函数换为 I2C 的,工作量比较大。
    基于代码复用的原则,Linux 内核引入了 regmap 模型,regmap 将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用 SPI 或者 I2C 接口 API 函数,统一使用 regmapAPI函数。这样的好处就是统一使用 regmap,降低了代码冗余,提高了驱动的可以移植性。regmap模型的重点在于:
    通过 regmap 模型提供的统一接口函数来访问器件的寄存器,SOC 内部的寄存器也可以使用 regmap 接口函数来访问。
    regmap 是 Linux 内核为了减少慢速 I/O 在驱动上的冗余开销,提供了一种通用的接口来操作硬件寄存器。另外,regmap 在驱动和硬件之间添加了 cache,降低了低速 I/O 的操作次数,提高了访问效率,缺点是实时性会降低。
    什么情况下会使用 regmap:
    ①、硬件寄存器操作,比如选用通过 I2C/SPI 接口来读写设备的内部寄存器,或者需要读写 SOC 内部的硬件寄存器。
    ②、提高代码复用性和驱动一致性,简化驱动开发过程。
    ③、减少底层 I/O 操作次数,提高访问效率。

    2.驱动代码

    #include <linux/types.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/delay.h>
    #include <linux/init.h>
    #include <linux/fs.h>
    #include <linux/ide.h>
    #include <linux/slab.h>
    #include <linux/uaccess.h>
    #include <linux/io.h>
    #include <linux/errno.h> 
    #include <linux/gpio.h>
    #include <linux/of_gpio.h>
    #include <linux/cdev.h>
    #include <linux/device.h>
    #include <linux/of.h>
    #include <linux/of_address.h>
    #include <linux/of_irq.h>
    #include <asm/mach/map.h>
    #include <linux/timer.h>
    #include <linux/jiffies.h>
    #include <linux/wait.h>
    #include <linux/poll.h>
    #include <linux/fcntl.h>                        /* 异步通知 */
    #include <linux/input.h>
    #include <linux/spi/spi.h>
    #include "icm20608reg.h"
    #include <linux/regmap.h>
    
    #define ICM20608_CNT	        1
    #define ICM20608_NAME	        "icm20608_"
    
    struct icm20608_dev {
    	dev_t devid;				/* 设备号 	 */
    	struct cdev cdev;			/* cdev 	*/
    	struct class *class;		/* 类 		*/
    	struct device *device;		/* 设备 	 */
    	struct device_node	*nd; 	/* 设备节点 */
    	int major;					/* 主设备号 */
    
    	signed int gyro_x_adc;		/* 陀螺仪X轴原始值 	 */
    	signed int gyro_y_adc;		/* 陀螺仪Y轴原始值		*/
    	signed int gyro_z_adc;		/* 陀螺仪Z轴原始值 		*/
    	signed int accel_x_adc;		/* 加速度计X轴原始值 	*/
    	signed int accel_y_adc;		/* 加速度计Y轴原始值	*/
    	signed int accel_z_adc;		/* 加速度计Z轴原始值 	*/
    	signed int temp_adc;		/* 温度原始值 			*/
    
    	struct regmap *regmap;      /* regmap 指针变量,regmap 我们需要使用 regmap_init_spi 函数来申请和初始化 */
    	struct regmap_config regmap_config; /* 用来配置regmap */
    };
    
    static struct icm20608_dev icm20608dev;
    
    
    
    /*
     * @description	: 读取icm20608指定寄存器值,读取一个寄存器
     * @param - dev:  icm20608设备
     * @param - reg:  要读取的寄存器
     * @return 	  :   读取到的寄存器值
     */
    static unsigned char icm20608_read_onereg(struct icm20608_dev *dev, u8 reg)
    {
    	// u8 data = 0;
    	// icm20608_read_regs(dev, reg, &data, 1);
    	// return data;
    	u8 ret;
    	unsigned int data;
    
    	ret = regmap_read(dev->regmap, reg, &data);
    	return (u8)data;
    }
    
    /*
     * @description	: 向icm20608指定寄存器写入指定的值,写一个寄存器
     * @param - dev:  icm20608设备
     * @param - reg:  要写的寄存器
     * @param - data: 要写入的值
     * @return   :    无
     */	
    
    static void icm20608_write_onereg(struct icm20608_dev *dev, u8 reg, u8 value)
    {
    	regmap_write(dev->regmap, reg, value);
    }
    
    /*
     * @description	: 读取ICM20608的数据,读取原始数据,包括三轴陀螺仪、
     * 				: 三轴加速度计和内部温度。
     * @param - dev	: ICM20608设备
     * @return 		: 无。
     */
    void icm20608_readdata(struct icm20608_dev *dev)
    {
    	u8 ret;
    	unsigned char data[14] = {0};
    	ret = regmap_bulk_read(dev->regmap, ICM20_ACCEL_XOUT_H, data, 14);
    
    	dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]); 
    	dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]); 
    	dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]); 
    	dev->temp_adc    = (signed short)((data[6] << 8) | data[7]); 
    	dev->gyro_x_adc  = (signed short)((data[8] << 8) | data[9]); 
    	dev->gyro_y_adc  = (signed short)((data[10] << 8) | data[11]);
    	dev->gyro_z_adc  = (signed short)((data[12] << 8) | data[13]);
    }
    
    /*
     * @description		: 打开设备
     * @param - inode 	: 传递给驱动的inode
     * @param - filp 	: 设备文件,file结构体有个叫做pr似有ate_data的成员变量
     * 					  一般在open的时候将private_data似有向设备结构体。
     * @return 			: 0 成功;其他 失败
     */
    static int icm20608_open(struct inode *inode, struct file *filp)
    {
    	filp->private_data = &icm20608dev; /* 设置私有数据 */
    	return 0;
    }
    
    /*
     * @description		: 从设备读取数据 
     * @param - filp 	: 要打开的设备文件(文件描述符)
     * @param - buf 	: 返回给用户空间的数据缓冲区
     * @param - cnt 	: 要读取的数据长度
     * @param - offt 	: 相对于文件首地址的偏移
     * @return 			: 读取的字节数,如果为负值,表示读取失败
     */
    static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
    {
    	signed int data[7];
    	long err = 0;
    	struct icm20608_dev *dev = (struct icm20608_dev *)filp->private_data;
    
    	icm20608_readdata(dev);
    	data[0] = dev->gyro_x_adc;
    	data[1] = dev->gyro_y_adc;
    	data[2] = dev->gyro_z_adc;
    	data[3] = dev->accel_x_adc;
    	data[4] = dev->accel_y_adc;
    	data[5] = dev->accel_z_adc;
    	data[6] = dev->temp_adc;
    	err = copy_to_user(buf, data, sizeof(data));
    	return 0;
    }
    
    /*
     * @description		: 关闭/释放设备
     * @param - filp 	: 要关闭的设备文件(文件描述符)
     * @return 			: 0 成功;其他 失败
     */
    static int icm20608_release(struct inode *inode, struct file *filp)
    {
    	return 0;
    }
    
    /* icm20608操作函数 */
    static const struct file_operations icm20608_ops = {
    	.owner = THIS_MODULE,
    	.open = icm20608_open,
    	.read = icm20608_read,
    	.release = icm20608_release,
    };
    
    /*
     * ICM20608内部寄存器初始化函数 
     * @param  	: 无
     * @return 	: 无
     */
    void icm20608_reginit(void)
    {
    	u8 value = 0;
    	
    	icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x80);
    	mdelay(50);
    	icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_1, 0x01);
    	mdelay(50);
    
    	value = icm20608_read_onereg(&icm20608dev, ICM20_WHO_AM_I);
    	printk("ICM20608 ID = %#X\r\n", value);	
    
    	icm20608_write_onereg(&icm20608dev, ICM20_SMPLRT_DIV, 0x00); 	/* 输出速率是内部采样率					*/
    	icm20608_write_onereg(&icm20608dev, ICM20_GYRO_CONFIG, 0x18); 	/* 陀螺仪±2000dps量程 				*/
    	icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG, 0x18); 	/* 加速度计±16G量程 					*/
    	icm20608_write_onereg(&icm20608dev, ICM20_CONFIG, 0x04); 		/* 陀螺仪低通滤波BW=20Hz 				*/
    	icm20608_write_onereg(&icm20608dev, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz 			*/
    	icm20608_write_onereg(&icm20608dev, ICM20_PWR_MGMT_2, 0x00); 	/* 打开加速度计和陀螺仪所有轴 				*/
    	icm20608_write_onereg(&icm20608dev, ICM20_LP_MODE_CFG, 0x00); 	/* 关闭低功耗 						*/
    	icm20608_write_onereg(&icm20608dev, ICM20_FIFO_EN, 0x00);		/* 关闭FIFO						*/
    }
    
     /*
      * @description     : spi驱动的probe函数,当驱动与
      *                    设备匹配以后此函数就会执行
      * @param - client  : spi设备
      * @param - id      : spi设备ID
      * 
      */	
    static int icm20608_probe(struct spi_device *spi)
    {
    
    	int ret;
    	struct icm20608_dev *icm20608dev;
    
    	/* 分配 icm20608dev 对象的空间 */
    	icm20608dev = devm_kzalloc(&spi->dev, sizeof(*icm20608dev), GFP_KERNEL);
    	if(!icm20608dev)
    		return -ENOMEM;
    
    	/* 初始化 regmap_config 设置 */
    	icm20608dev->regmap_config.reg_bits = 8; /* 寄存器长度 8bit */
    	icm20608dev->regmap_config.val_bits = 8; /* 值长度 8bit */
    	icm20608dev->regmap_config.read_flag_mask = 0x80; /* 读掩码 */
    
    	/* 初始化 SPI 接口的 regmap */
    	icm20608dev->regmap = regmap_init_spi(spi, &icm20608dev->regmap_config);
    	if (IS_ERR(icm20608dev->regmap)) {
    		return PTR_ERR(icm20608dev->regmap);
    	}
    
    	/* 1、构建设备号 */
    	ret = alloc_chrdev_region(&icm20608dev->devid, 0, ICM20608_CNT, ICM20608_NAME);
    	if(ret < 0) {
    		pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", ICM20608_NAME, ret);
    		goto del_regmap;
    	}
    
    	/* 2、注册设备 */
    	cdev_init(&icm20608dev->cdev, &icm20608_ops);
    	cdev_add(&icm20608dev->cdev, icm20608dev->devid, ICM20608_CNT);
    
    	/* 3、创建类 */
    	icm20608dev->class = class_create(THIS_MODULE, ICM20608_NAME);
    	if (IS_ERR(icm20608dev->class)) {
    		return PTR_ERR(icm20608dev->class);
    	}
    
    	/* 4、创建设备 */
    	icm20608dev->device = device_create(icm20608dev->class, NULL, icm20608dev->devid, NULL, ICM20608_NAME);
    	if (IS_ERR(icm20608dev->device)) {
    		return PTR_ERR(icm20608dev->device);
    	}
    
    	// /*初始化spi_device */
    	// spi->mode = SPI_MODE_0;	/*MODE0,CPOL=0,CPHA=0*/
    	// spi_setup(spi);
    	// icm20608dev.private_data = spi; /* 设置私有数据 */
    
    	// /* 初始化ICM20608内部寄存器 */
    	// icm20608_reginit();		
    	return 0;
    del_regmap:
    	regmap_exit(icm20608dev->regmap);
    }
    
    /*
     * @description     : spi驱动的remove函数,移除spi驱动的时候此函数会执行
     * @param - client 	: spi设备
     * @return          : 0,成功;其他负值,失败
     */
    static int icm20608_remove(struct spi_device *spi)
    {
    	/* 删除设备 */
    	cdev_del(&icm20608dev.cdev);
    	unregister_chrdev_region(icm20608dev.devid, ICM20608_CNT);
    
    	/* 注销掉类和设备 */
    	device_destroy(icm20608dev.class, icm20608dev.devid);
    	class_destroy(icm20608dev.class);
    	/* 5、删除 regmap */
    	regmap_exit(icm20608dev.regmap);
    	return 0;
    }
    
    /* 传统匹配方式ID列表 */
    static const struct spi_device_id icm20608_id[] = {
    	{"shaozheming,icm20608", 0},  
    	{}
    };
    
    /* 设备树匹配列表 */
    static const struct of_device_id icm20608_of_match[] = {
    	{ .compatible = "shaozheming,icm20608" },
    	{ /* Sentinel */ }
    };
    
    /* SPI驱动结构体 */	
    static struct spi_driver icm20608_driver = {
    	.probe = icm20608_probe,
    	.remove = icm20608_remove,
    	.driver = {
    			.owner = THIS_MODULE,
    		   	.name = "icm20608",
    		   	.of_match_table = icm20608_of_match, 
    		   },
    	.id_table = icm20608_id,
    };
    		   
    /*
     * @description	: 驱动入口函数
     * @param 		: 无
     * @return 		: 无
     */
    static int __init icm20608_init(void)
    {
    	return spi_register_driver(&icm20608_driver);
    }
    
    /*
     * @description	: 驱动出口函数
     * @param 		: 无
     * @return 		: 无
     */
    static void __exit icm20608_exit(void)
    {
    	spi_unregister_driver(&icm20608_driver);
    }
    
    module_init(icm20608_init);
    module_exit(icm20608_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Shao Zheming");
    更多相关内容
  • regmap 一些众所周知的好计算机架构,例如Microchip PIC产品线,或许多AVR处理器家族,都非常幸运,可以让架构师了解处理器寄存器文件之间战略性对接的力量。和主存储器。 在这些体系结构上,具有远见卓识的寄存器和...
  • regmap

    2021-08-06 15:18:26
    regmap是在 linux 内核为减少慢速 I/O 驱动上的重复逻辑,提供一种通用的接口来操作底层硬件寄存器的模型框架。此外,regmap在驱动和硬件寄存器之间增加了cache,减少底层低速 I/O 的操作次数,提高访问效率;当然...

    一、前言

    regmap是在 linux 内核为减少慢速 I/O 驱动上的重复逻辑,提供一种通用的接口来操作底层硬件寄存器的模型框架。此外,regmap在驱动和硬件寄存器之间增加了cache,减少底层低速 I/O 的操作次数,提高访问效率;当然实时性会有所降低。

    基于代码代码复用的原则之一,Linux内核后引入了regmap模型,将寄存器访问的共同逻辑抽象出来,只需初始化时指定总线类型、寄存器位宽等关键参数,即可通过regmap模型接口来操作器件寄存器。当然,regmap同样适用于操作cpu自身的寄存器。将i2c、spi、mmio、irq都抽象出统一的接口regmap_read、regmap_write、regmap_update_bits等接口 ,从而提高代码的可重用性,并且使得在使用如上内核基础组件时变得更为简单易用。

    二、regmap 框架模型

    regmap整体上分为三层,从下到上分别为物理总线、regmap核心、regmap api。

    在这里插入图片描述

    • 底层,对接的是具体物理总线,目前regmap框架支持i2c、spi、mmio、spmi、ac97总线
    • 核心层,regmap 核心实现
    • api,抽象通用接口

    对于使用regmap框架来说,可以不用关心regmap核心的实现过程,只需根据物理总线类型,配置好相关参数信息,即可调用regmap api访问芯片寄存器。

    二、怎样使用regmap

    使用regmap比较简单,使用前,只需根据外设属性配置总线类型、寄存器位宽、缓存类型、读写属性等参数;接着注册一个regmap实例;然后调用抽象访问接口访问寄存器。

    • 第一步,配置regmap信息
    • 第二步,注册regmap实例
    • 第三步,访问寄存器
    • 第四步,释放regmap实例

    三、regmap的结构体(可以仅对自己需要的部分赋值)

    struct regmap_config {
    	const char *name;
    
    	int reg_bits;// 寄存器地址的位数,必须配置,例如I2C寄存器地址位数为 8
    	int reg_stride;
    	int pad_bits;// 寄存器值的位数,必须配置
    	int val_bits;
    
    	bool (*writeable_reg)(struct device *dev, unsigned int reg);// 可写寄存器回调,maintain一个可写寄存器表
    	bool (*readable_reg)(struct device *dev, unsigned int reg); // 可读寄存器回调, maintain一个可读寄存器表
    	bool (*volatile_reg)(struct device *dev, unsigned int reg); // 可要求读写立即生效的寄存器回调,不可以被cache,maintain一个可立即生效寄存器表
    	bool (*precious_reg)(struct device *dev, unsigned int reg); // 要求寄存器数值维持在一个数值范围才正确,maintain一个数值准确表
    	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;  // cache数据类型,支持三种:flat、rbtree、Izo 
    	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;// 寄存器地址大小端,大于8位时需设置
    	enum regmap_endian val_format_endian;// 寄存器值大小端,大于8位时需设置 
    
    	const struct regmap_range_cfg *ranges;
    	unsigned int num_ranges;
    };
    

    目前linux内核支持三种cache数据类型,如下:

    /* An enum of all the supported cache types */
    enum regcache_type {
    	REGCACHE_NONE,
    	REGCACHE_RBTREE,//红黑树类型
    	REGCACHE_COMPRESSED,//压缩类型
    	REGCACHE_FLAT,//普通数据类型
    };
    

    在最新 Linux 4.0 版本中,已经有 3 种缓存类型,分别是数组(flat)、LZO 压缩和红黑树(rbtree)。数组好理解,是最简单的缓存类型,当设备寄存器很少时,可以用这种类型来缓存寄存器值。LZO(Lempel–Ziv–Oberhumer) 是 Linux 中经常用到的一种压缩算法,Linux 编译后就会用这个算法来压缩。这个算法有 3 个特性:压缩快,解压不需要额外内存,压缩比可以自动调节。在这里,你可以理解为一个数组缓存,套了一层压缩,来节约内存。当设备寄存器数量中等时,可以考虑这种缓存类型。而最后一类红黑树,它的特性就是索引快,所以当设备寄存器数量比较大,或者对寄存器操作延时要求低时,就可以用这种缓存类型。

    缓存的类型是在 regmap 初始化时,由.cache_type = REGCACHE_RBTREE 来指定的。对于 regmap_read 来说,会先判断当前缓存是否有值,然后再检查是否需要 bypass,若没有,则可以直接从缓存里面取值,调用regcache_read来获取值,若需要从硬件上读取,则调用具体协议的读写函数,若是 I2C,调用i2c_transfer。写的过程也是大同小异。

    例如我用到REGCACHE_RBTREE

     static struct regmap_config es8388_regmap = {
    	.reg_bits = 8,
    	.val_bits = 8,	
    
        .max_register = 0x34,
    	.volatile_reg = es8388_volatile_register,
        .readable_reg = es8388_readable_register,
    
        .cache_type = REGCACHE_RBTREE,
    	.reg_defaults = es8388_reg_defaults,
    	.num_reg_defaults = ARRAY_SIZE(es8388_reg_defaults),
    
    	
    };
    
    

    该数据结构主要在创建一个regmap时,实现对regmap的初始化,主要信息如下:

    • 寄存器的位数、寄存器值的位数;
    • 寄存器读写权限判断的回调函数、读写范围的定义;
    • 加锁、解锁函数;
    • 读写flag;
    • 字节序相关的配置;
    • 是否支持page读写方式,若需要定义regmap_range_cfg类型的变量,说明page select reg、page
      reg范围等内容。

    四、注册并初始化regmap

    regmap_init_i2c(struct i2c_client *i2c, struct regmap_config *config);   
    regmap_init_spi(struct spi_device *spi, strcut regmap_config *config);
    regmap_init_mmio(struct device *dev, struct regmap_config *config);   
    regmap_init_spmi_base(struct spmi_device *dev, strcut regmap_config *config);
    regmap_init_spmi_ext(struct spmi_device *dev, strcut regmap_config *config);
    regmap_init_ac97(struct snd_ac97 *ac97, strcut regmap_config *config);
    regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, int irq_base, struct regmap_irq_chip *chip, struct regmap_irq_chip_data **data);
    

    注意:regmap_add_irq_chip:关联后的regmap上注册 irq

    初始化函数声明位于kernel/include/linux/regmap.h中,原型中linux内核通过宏定义实现,展开后即是上面函数声明。具体转化过程如下

    
    #define regmap_init_i2c(i2c, config)					\
    	__regmap_lockdep_wrapper(__regmap_init_i2c, #config,i2c, config)
    				
    #define __regmap_lockdep_wrapper(fn, name, ...) fn(__VA_ARGS__, NULL, NULL)
    
    struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
    				 const struct regmap_config *config,
    				 struct lock_class_key *lock_key,
    				 const char *lock_name);
    转化为				 				 
    regmap_init_i2c(struct i2c_client *i2c, struct regmap_config *config);
     
    

    五、 使用regmap

    配置和注册regmap实例后,我们就可以使用抽象接口来访问寄存器,摈弃之前那套繁琐的数据结构和函数api。接口比较通俗,根据函数名称和入口参数即可知道函数功能。接口分为两大类,设置类(与初始化配置信息不同)和访问类,访问类根据访问过程又分为两种:

    经过regmap cache,提高访问效率,对于写操作,待cache存在一定数据量或者超出时间后写入物理寄存器;但降低实时性
    
    不经过regmap cache,对于写操作,立即写入物理寄存器,实时性好;对于读操作,则经过cache,减少拷贝时间
    

    在初始化好regmap之后,就可以调用regmap提供的read/write/update等操作了。

    
    int regmap_write(struct regmap *map, int reg, int val); //向单个reg写入val  
    int regmap_raw_write(struct regmap *map, int reg, void *val, size_t val_len); //向单个reg写入指定长度的数据,数据存放在val中
    int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,size_t val_count); // 写多个reg
    int regmap_multi_reg_write_bypassed(struct regmap *map, const struct reg_sequence *regs,int num_regs);// 直接写入reg,不经过regmap cache
    int regmap_raw_write_async(struct regmap *map, unsigned int reg,const void *val, size_t val_len);//写多个reg,并立即刷新cache写入
    int regmap_read(struct regmap *map, int reg, int *val); // 读取单个reg的数据到val中/
    int regmap_raw_read(struct regmap *map, int reg, void *val, size_t val_len);  // 读取单个reg中指定长度的数据 
    int regmap_bulk_read(struct regmap *map, int reg, void *val, size_t val_count); // 读取从reg开始之后val_count个寄存器的数据到val中
    int regmap_update_bits(struct regmap *map, int reg, int mask, int val); 	// 更新reg寄存器中mask指定的位
    int regmap_write_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val);//写入寄存器值指定bit *
    void regcache_cache_bypass(arizona->regmap, true); // 设置读写寄存器不通过cache模式而是bypass模式,读写立即生效,一般在audio等确保时序性驱动中用到
    

    六、释放regmap

    在驱动注销时一定要释放已注册的regmap。

    void regmap_exit(struct regmap *map);
    

    七、举例

    /* 第一步配置信息 */
    static const struct regmap_config regmap_config =
    {
    .reg_bits = 8,
    .val_bits = 8,
    .max_register = 255,
    .cache_type = REGCACHE_RBTREE,
    .volatile_reg = false,
    };

    /* 第二步,注册regmap实例 */
    regmap = regmap_init_i2c(i2c_client, &regmap_config);

    /* 第三步,访问操作 */
    static int read_regs(uint8_t reg, uint8_t *pdata, int size)
    {
    return regmap_raw_read(regmap, reg, pdata, size);
    }

    regmap方式将i2c的数据结构、传输api隐藏,使用者无需关心i2c内部实现,简化驱动开发过程,提高代码的复用性。如果将该器件物理接口更换为spi,只需修改配置信息即可,寄存器访问过程无需更改。

    展开全文
  • Linux内核之 regmap 子系统


    前言

    在 Linux 驱动中很多涉及设备寄存器等的操作都是通过 I2C / SPI 等常用接口,针对i2c设备我们通过接口 i2c_transferi2c_master_send 等接口进行读写操作,而针对spi 设备我们则通过接口 spi_writespi_syncspi_async 等接口实现读写操作。虽然直接调用 i2c 或 spi 设备的操作接口也很方便,但这些子系统中的读写接口中充斥着大量的 i2c、spi 操作,因此引入了 Regmap 子系统来方便内核开发人员统一操作 I2C/SPI 设备。


    一、regmap 子系统介绍

    Linux 内核提供了 regmap 子系统来针对 spi、i2c 等设备的操作提供了一次抽象,对外提供对 spi、i2c 等设备相关寄存器的统一访问接口( regmap_write、regmap_read )等,而在 regmap 子系统内部再通过调用 i2c、spi 等设备的寄存器读写接口,实现对具体设备寄存器的读写操作。我们常常会在一些驱动中使用到 regmap 子系统接口 regmap_write/read 等来操作设备寄存器,同时 regmap 子系统提供缓存机制,也可以减小对设备的访问次数。

    1.什么情况下会使用 regmap

    • 硬件寄存器操作,比如选用通过 I2C/SPI 接口来读写设备的内部寄存器,或者需要读写 SOC 内部的硬件寄存器。
    • 提高代码复用性和驱动一致性,简化驱动开发过程。
    • 减少底层 I/O 操作次数,提高访问效率。

    二、regmap 子系统框架

    regmap 子系统主要包含 regmapregmap_bus 两大部分,其中 regmap 表示一个慢速 i/o 设备的 reg 操作的映射,reg_bus 则表示一类慢速 i/o 设备的 reg 操作(如i2c设备可定义对应的regmap_bus,提供寄存器的读写操作接口、spi设备也定义对应的remap_bus,提供寄存器的读写接口)。

    针对regmap子系统而言,regmap_bus的实现由内核层完成、属于regmap子系统的一部分(目前实现了regmap_i2c、regmap_spi、regmap_mmio等regmap_bus),内核层通过实现remap_i2c、regmap_spi,只需要使用i2c、spi的regmap创建接口,然后就可使用regmap提供的操作接口,实现对这两类设备的访问操作。

    regmap 驱动框架如下:
    在这里插入图片描述
    通过regmap子系统的抽象,具体的设备驱动模块只需要调用regmap_write、regmap_read等通用接 口,即可实现对i2c/spi设备的访问。

    三、regmap 子系统重要数据结构

    regmap 子系统主要包含 3 个重要的数据结构, struct regmap、struct regmap_config、struct regmap_bus,其中,

    • regmap数据结构即为具体设备的 map (如一个 i2c 设备、spi 设备均需要一个 regmap),而 regmap 里面则包含 regcache 相关的支持、该 regmap 关联的 regmap、寄存器是否可读写等接口;
    • regmap_config - Configuration for the register map of a device,实现对 regmap 的初始化;
    • regmap_bus 数据结构即对具体总线控制器 map 的抽象(i2s、spi 模块均完成了 regmap_bus 的定义,其中定义了对 i2c 设备、spi 设备的统一读写接口)。

    regmap子系统的数据结构间的定义相对来说并不复杂,并且regmap子系统的设计相对来说也不算复杂(比如并没有提供remap_bus的注册接口,也不需要关注系统中已经定义了多少个regmap_bus以及系统中已经创建了多少个regmap等等),相比于设备驱动模型、pinctrl子系统、input子系统而言,算是较简单的子系统设计。针对regmap子系统,只要我们理解了regmap、regmap_bus的定义,基本上也可以大概了解regmap子系统的实现了。下面我们详细说明下。

    1. struct regmap

    regmap 数据结构定义在 drivers/base/regmap/internal.h

    struct regmap {
    	...
    	struct device *dev; /* Device we do I/O on */
    	void *work_buf;     /* Scratch buffer used to format I/O */
    	/* 针对有些设备而言,其寄存器的位数、寄存器值的位数可能有所不同
    	 *(有的设备寄存器为8位、寄存器值也是8位;有的设备寄存器为16位、
    	 * 寄存器值也是16位等等),而且存在字节序的问题,因此提供struct 
    	 * regmap_format类型的变量,实现寄存器、寄存器值位数设定、寄存
    	 * 器及其值的格式化操作接口等。
    	 */
    	struct regmap_format format;  /* Buffer format */
    	const struct regmap_bus *bus;
    	
    	/* 相关的异步写链表以及异步写相关的等待队列 */
    	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 (*writeable_noinc_reg)(struct device *dev, unsigned int reg);
    	bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
    
    	/* 该regmap相关的寄存器读写权限(提供寄存器是否可写接口、是否可
    	 * 读接口、是否为volatile等),并提供可写寄存器table、可读寄存
    	 * 器table、volatile_table等等,regmap子系统实现了寄存器的访问
    	 * 权限控制,这个设计还是很好的。 
    	 */
    	const struct regmap_access_table *wr_table;
    	const struct regmap_access_table *rd_table;
    	const struct regmap_access_table *volatile_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);
    
    	/* 该regmap相关的cache操作接口、是否支持cache操作等 */
    	/* 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 */
    	bool cache_only;
    	/* if set, only the HW is modified not the cache */
    	bool 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 */
    	bool cache_dirty;
    	/* if set, the HW registers are known to match map->reg_defaults */
    	bool no_sync_defaults;
    
    	/* if set, raw reads/writes are limited to this size */
    	size_t max_raw_read;
    	size_t max_raw_write;
    	
    	/* 该regmap是否支持按页访问操作(针对某类设备而言,如phy寄存器,
    	 * 协议上规则只支持32个寄存器,但有些phy设备提供的功能比较复杂,
    	 * 对外提供的寄存器个数超过了32个,那它就提供按页访问操作,比如
    	 * 定义向寄存器27写入值进行页的选择,这样的话支持的寄存器就好超
    	 * 过32个(如marvell的88exxxx系统的芯片,基本上都是支持按页访问
    	 * 的)),若该设备支持按页访问,则需要设定page 访问的范围、page 
    	 * 选择寄存器号及其偏移等等,并会将对应的信息存放在红黑树
    	 * range_tree中。
    	 */
    	struct rb_root range_tree;
    	void *selector_work_buf;	/* Scratch buffer used for selector */
    
    	/* if set, the regmap core can sleep */
    	bool can_sleep;
    };
    

    regmap 基本上则为数据结构中描述的功能,围绕这 regmap 又定义了 regmap_formatregmap_access_table 等数据结构。

    1.1 struct regmap_format

    regmap_format 数据结构定义在 drivers/base/regmap/internal.h,该数据结构主要实现寄存器及其值的位数、格式化操作等接口。

    针对寄存器值不是 8 bit 整数倍的情形,则需要实现 format_write 接口(如 reg 占用 4 bit、value 占用 12 bit 情况);而针对寄存器值是 8 bit 整数倍的情况,则只需要实现 format_regformat_valparse_valparse_inplace 接口。

    struct regmap_format {
    	size_t buf_size;
    	size_t reg_bytes;
    	size_t pad_bytes;
    	size_t reg_downshift;
    	size_t val_bytes;
    	void (*format_write)(struct regmap *map,
    			     unsigned int reg, unsigned int val);
    	void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
    	void (*format_val)(void *buf, unsigned int val, unsigned int shift);
    	unsigned int (*parse_val)(const void *buf);
    	void (*parse_inplace)(void *buf);
    };
    

    1.2 struct regmap_access_table

    该数据结构主要用于描述regmap的寄存器读写权限控制的,该数据结构内部包含struct regmap_ranage类型的变量。

    • 支持访问的寄存器的范围;
    • 不支持访问的寄存器的范围等。
    /**
     * struct regmap_access_table - A table of register ranges for access checks
     *
     * @yes_ranges : pointer to an array of regmap ranges used as "yes ranges"
     * @n_yes_ranges: size of the above array
     * @no_ranges: pointer to an array of regmap ranges used as "no ranges"
     * @n_no_ranges: size of the above array
     *
     * A table of ranges including some yes ranges and some no ranges.
     * If a register belongs to a no_range, the corresponding check function
     * will return false. If a register belongs to a yes range, the corresponding
     * check function will return true. "no_ranges" are searched first.
     */
    struct regmap_access_table {
    	const struct regmap_range *yes_ranges;
    	unsigned int n_yes_ranges;
    	const struct regmap_range *no_ranges;
    	unsigned int n_no_ranges;
    };
    

    2. struct regmap_config

    该数据结构主要在创建一个 regmap 时,实现对 regmap 的初始化,主要信息如下:

    1. 寄存器的位数、寄存器值的位数;
    2. 寄存器读写权限判断的回调函数、读写范围的定义;
    3. 加锁、解锁函数;
    4. 读写flag;
    5. 字节序相关的配置;
    6. 是否支持page读写方式,若需要定义regmap_range_cfg类型的变量,说明page select reg、page reg范围等内容;
    /**
     1. struct regmap_config - Configuration for the register map of a device.
     2.  3. @name: Optional name of the regmap. Useful when a device has multiple
     4.        register regions.
     5.  6. @reg_bits: Number of bits in a register address, mandatory.
     7. @reg_stride: The register address stride. Valid register addresses are a
     8.              multiple of this value. If set to 0, a value of 1 will be
     9.              used.
     10. @reg_downshift: The number of bits to downshift the register before
     11. 	   performing any operations.
     12. @reg_base: Value to be added to every register address before performing any
     13.       operation.
     14. @pad_bits: Number of bits of padding between register and value.
     15. @val_bits: Number of bits in a register value, mandatory.
     16.  17. @writeable_reg: Optional callback returning true if the register
     18. 	   can be written to. If this field is NULL but wr_table
     19. 	   (see below) is not, the check is performed on such table
     20.                 (a register is writeable if it belongs to one of the ranges
     21.                  specified by wr_table).
     22. @readable_reg: Optional callback returning true if the register
     23. 	  can be read from. If this field is NULL but rd_table
     24. 	   (see below) is not, the check is performed on such table
     25.                 (a register is readable if it belongs to one of the ranges
     26.                  specified by rd_table).
     27. @volatile_reg: Optional callback returning true if the register
     28. 	  value can't be cached. If this field is NULL but
     29. 	  volatile_table (see below) is not, the check is performed on
     30.                such table (a register is volatile if it belongs to one of
     31.                the ranges specified by volatile_table).
     32. @precious_reg: Optional callback returning true if the register
     33. 	  should not be read outside of a call from the driver
     34. 	  (e.g., a clear on read interrupt status register). If this
     35.                field is NULL but precious_table (see below) is not, the
     36.                check is performed on such table (a register is precious if
     37.                it belongs to one of the ranges specified by precious_table).
     38. @writeable_noinc_reg: Optional callback returning true if the register
     39. 		supports multiple write operations without incrementing
     40. 		the register number. If this field is NULL but
     41. 		wr_noinc_table (see below) is not, the check is
     42. 		performed on such table (a register is no increment
     43. 		writeable if it belongs to one of the ranges specified
     44. 		by wr_noinc_table).
     45. @readable_noinc_reg: Optional callback returning true if the register
     46. 		supports multiple read operations without incrementing
     47. 		the register number. If this field is NULL but
     48. 		rd_noinc_table (see below) is not, the check is
     49. 		performed on such table (a register is no increment
     50. 		readable if it belongs to one of the ranges specified
     51. 		by rd_noinc_table).
     52. @disable_locking: This regmap is either protected by external means or
     53.                   is guaranteed not to be accessed from multiple threads.
     54.                   Don't use any locking mechanisms.
     55. @lock:	  Optional lock callback (overrides regmap's default lock
     56. 	  function, based on spinlock or mutex).
     57. @unlock:	  As above for unlocking.
     58. @lock_arg:	  this field is passed as the only argument of lock/unlock
     59. 	  functions (ignored in case regular lock/unlock functions
     60. 	  are not overridden).
     61. @reg_read:	  Optional callback that if filled will be used to perform
     62.           	  all the reads from the registers. Should only be provided for
     63. 	  devices whose read operation cannot be represented as a simple
     64. 	  read operation on a bus such as SPI, I2C, etc. Most of the
     65. 	  devices do not need this.
     66. @reg_write:	  Same as above for writing.
     67. @reg_update_bits: Optional callback that if filled will be used to perform
     68. 	     all the update_bits(rmw) operation. Should only be provided
     69. 	     if the function require special handling with lock and reg
     70. 	     handling and the operation cannot be represented as a simple
     71. 	     update_bits operation on a bus such as SPI, I2C, etc.
     72. @fast_io:	  Register IO is fast. Use a spinlock instead of a mutex
     73.      	  to perform locking. This field is ignored if custom lock/unlock
     74.      	  functions are used (see fields lock/unlock of struct regmap_config).
     75. 	  This field is a duplicate of a similar file in
     76. 	  'struct regmap_bus' and serves exact same purpose.
     77. 	   Use it only for "no-bus" cases.
     78. @max_register: Optional, specifies the maximum valid register address.
     79. @wr_table:     Optional, points to a struct regmap_access_table specifying
     80.                valid ranges for write access.
     81. @rd_table:     As above, for read access.
     82. @volatile_table: As above, for volatile registers.
     83. @precious_table: As above, for precious registers.
     84. @wr_noinc_table: As above, for no increment writeable registers.
     85. @rd_noinc_table: As above, for no increment readable registers.
     86. @reg_defaults: Power on reset values for registers (for use with
     87.                register cache support).
     88. @num_reg_defaults: Number of elements in reg_defaults.
     89.  90. @read_flag_mask: Mask to be set in the top bytes of the register when doing
     91.                  a read.
     92. @write_flag_mask: Mask to be set in the top bytes of the register when doing
     93.                   a write. If both read_flag_mask and write_flag_mask are
     94.                   empty and zero_flag_mask is not set the regmap_bus default
     95.                   masks are used.
     96. @zero_flag_mask: If set, read_flag_mask and write_flag_mask are used even
     97.                   if they are both empty.
     98. @use_relaxed_mmio: If set, MMIO R/W operations will not use memory barriers.
     99.                    This can avoid load on devices which don't require strict
     100.                    orderings, but drivers should carefully add any explicit
     101.                    memory barriers when they may require them.
     102. @use_single_read: If set, converts the bulk read operation into a series of
     103.                   single read operations. This is useful for a device that
     104.                   does not support  bulk read.
     105. @use_single_write: If set, converts the bulk write operation into a series of
     106.                    single write operations. This is useful for a device that
     107.                    does not support bulk write.
     108. @can_multi_write: If set, the device supports the multi write mode of bulk
     109.                   write operations, if clear multi write requests will be
     110.                   split into individual write operations
     111.  112. @cache_type: The actual cache type.
     113. @reg_defaults_raw: Power on reset values for registers (for use with
     114.                    register cache support).
     115. @num_reg_defaults_raw: Number of elements in reg_defaults_raw.
     116. @reg_format_endian: Endianness for formatted register addresses. If this is
     117.                     DEFAULT, the @reg_format_endian_default value from the
     118.                     regmap bus is used.
     119. @val_format_endian: Endianness for formatted register values. If this is
     120.                     DEFAULT, the @reg_format_endian_default value from the
     121.                     regmap bus is used.
     122.  123. @ranges: Array of configuration entries for virtual address ranges.
     124. @num_ranges: Number of range configuration entries.
     125. @use_hwlock: Indicate if a hardware spinlock should be used.
     126. @use_raw_spinlock: Indicate if a raw spinlock should be used.
     127. @hwlock_id: Specify the hardware spinlock id.
     128. @hwlock_mode: The hardware spinlock mode, should be HWLOCK_IRQSTATE,
     129. 	 HWLOCK_IRQ or 0.
     130. @can_sleep: Optional, specifies whether regmap operations can sleep.
     */
    struct regmap_config {
    	const char *name;
    
    	int reg_bits;
    	int reg_stride;
    	int reg_downshift;
    	unsigned int reg_base;
    	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 (*writeable_noinc_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);
    	int (*reg_update_bits)(void *context, unsigned int reg,
    			       unsigned int mask, 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 *wr_noinc_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_read;
    	bool use_single_write;
    	bool use_relaxed_mmio;
    	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;
    	bool use_raw_spinlock;
    	unsigned int hwlock_id;
    	unsigned int hwlock_mode;
    
    	bool can_sleep;
    };
    

    3. struct regmap_bus

    如下即为regmap_bus的定义,其主要提供如下几个方面的内容:

    1. Regmap bus的同步写接口;
    2. Regmap bus的异步写接口;
    3. Regmap bus的读接口;
    4. Regmap bus的读写flag;
    5. Regmap bus的寄存器、寄存器值的格式(大端、小端);
    6. Regmap bus异步写相关的缓存申请接口等。

    regmap_bus的定义也是比较简单,只需提供regmap_bus对应总线控制器的访问方法即可。

    /**
     * struct regmap_bus - Description of a hardware bus for the register map
     *                     infrastructure.
     *
     * @fast_io: Register IO is fast. Use a spinlock instead of a mutex
     *	     to perform locking. This field is ignored if custom lock/unlock
     *	     functions are used (see fields lock/unlock of
     *	     struct regmap_config).
     * @write: Write operation.
     * @gather_write: Write operation with split register/value, return -ENOTSUPP
     *                if not implemented  on a given device.
     * @async_write: Write operation which completes asynchronously, optional and
     *               must serialise with respect to non-async I/O.
     * @reg_write: Write a single register value to the given register address. This
     *             write operation has to complete when returning from the function.
     * @reg_update_bits: Update bits operation to be used against volatile
     *                   registers, intended for devices supporting some mechanism
     *                   for setting clearing bits without having to
     *                   read/modify/write.
     * @read: Read operation.  Data is returned in the buffer used to transmit
     *         data.
     * @reg_read: Read a single register value from a given register address.
     * @free_context: Free context.
     * @async_alloc: Allocate a regmap_async() structure.
     * @read_flag_mask: Mask to be set in the top byte of the register when doing
     *                  a read.
     * @reg_format_endian_default: Default endianness for formatted register
     *     addresses. Used when the regmap_config specifies DEFAULT. If this is
     *     DEFAULT, BIG is assumed.
     * @val_format_endian_default: Default endianness for formatted register
     *     values. Used when the regmap_config specifies DEFAULT. If this is
     *     DEFAULT, BIG is assumed.
     * @max_raw_read: Max raw read size that can be used on the bus.
     * @max_raw_write: Max raw write size that can be used on the bus.
     * @free_on_exit: kfree this on exit of regmap
     */
    struct regmap_bus {
    	bool fast_io;
    	regmap_hw_write write;
    	regmap_hw_gather_write gather_write;
    	regmap_hw_async_write async_write;
    	regmap_hw_reg_write reg_write;
    	regmap_hw_reg_update_bits reg_update_bits;
    	regmap_hw_read read;
    	regmap_hw_reg_read reg_read;
    	regmap_hw_free_context free_context;
    	regmap_hw_async_alloc async_alloc;
    	u8 read_flag_mask;
    	enum regmap_endian reg_format_endian_default;
    	enum regmap_endian val_format_endian_default;
    	size_t max_raw_read;
    	size_t max_raw_write;
    	bool free_on_exit;
    };
    

    参考链接如下:
    Linux regmap子系统分析之一 系统概述
    Linux regmap子系统分析之二 从数据结构分析系统实现
    其中关于 regmap 相关结构体之间调用关系参考如下:
    Linux regmap子系统分析之三 regmap bus实例分析

    四、regmap 使用

    1. regmap 初始化与释放

    目前 regmap 支持的物理总线有 i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq、spmi 和 w1 等,在 /include/linux/regmap.h 中提供了对各种总线 regmap 初始化的 api,以 I2C 与 SPI 两个常用接口为例,

    /**
     * devm_regmap_init_i2c() - Initialise managed register map
     *
     * @i2c: Device that will be interacted with
     * @config: Configuration for register map
     *
     * The return value will be an ERR_PTR() on error or a valid pointer
     * to a struct regmap.  The regmap will be automatically freed by the
     * device management code.
     */
    #define devm_regmap_init_i2c(i2c, config)				\
    	__regmap_lockdep_wrapper(__devm_regmap_init_i2c, #config,	\
    				i2c, config)
    
    /**
     * devm_regmap_init_spi() - Initialise register map
     *
     * @dev: Device that will be interacted with
     * @config: Configuration for register map
     *
     * The return value will be an ERR_PTR() on error or a valid pointer
     * to a struct regmap.  The map will be automatically freed by the
     * device management code.
     */
    #define devm_regmap_init_spi(dev, config)				\
    	__regmap_lockdep_wrapper(__devm_regmap_init_spi, #config,	\
    				dev, config)
    

    其中 i2c 为 i2c_client 对象指针,dev 为 spi_device 对象指针;
    config 为 regmap_config 结构体对象,是 regmap 注册时的重要结构体;
    返回值为 struct regmap 指针。

    void regmap_exit(struct regmap *map);
    

    2. regmap 读写接口

    regmap 读写接口屏蔽了 i2c 和 spi 读写的具体细节,提供统一接口,

    /**
     * regmap_write() - Write a value to a single register
     *
     * @map: Register map to write to
     * @reg: Register to write to
     * @val: Value to be written
     *
     * A value of zero will be returned on success, a negative errno will
     * be returned in error cases.
     */
    int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
    
    /**
     * regmap_read() - Read a value from a single register
     *
     * @map: Register map to read from
     * @reg: Register to be read from
     * @val: Pointer to store read value
     *
     * A value of zero will be returned on success, a negative errno will
     * be returned in error cases.
     */
    int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
    

    3. wm8960 驱动中 regmap 的使用

    /sound/soc/codec/wm8960.c 中使用 regmap 如下:

    struct wm8960_priv {
        ...
    	struct regmap *regmap;
    };
    
    /*
     * wm8960 register cache
     * We can't read the WM8960 register space when we are
     * using 2 wire for device control, so we cache them instead.
     */
    static const struct reg_default wm8960_reg_defaults[] = {
    	/* reg addr - reg default val */
    	{  0x0, 0x00a7 },
    	{  0x1, 0x00a7 },
    	...
    };
    
    static bool wm8960_volatile(struct device *dev, unsigned int reg)
    {
    	switch (reg) {
    	case WM8960_RESET:
    		return true;
    	default:
    		return false;
    	}
    }
    
    static const struct regmap_config wm8960_regmap = {
    	.reg_bits = 7,	//寄存器地址位数
    	.val_bits = 9,	//寄存器 val 位数
    	.max_register = WM8960_PLL4,	//寄存器最大地址
    
    	.reg_defaults = wm8960_reg_defaults, //上电时寄存器默认值,使用cache会使用
    	.num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults), //reg_defaults的元素个数
    	.cache_type = REGCACHE_RBTREE,	//cache方式,使用红黑树
    
    	.volatile_reg = wm8960_volatile,
    };
    
    static int wm8960_i2c_probe(struct i2c_client *i2c,
    			    const struct i2c_device_id *id)
    {
    	struct wm8960_priv *wm8960;
    	...
    	wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap);
    	if (IS_ERR(wm8960->regmap))
    		return PTR_ERR(wm8960->regmap);
    	...
    
    	/* Latch the update bits */
    	regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100);
    	regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100);
    	regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100);
    	regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100);
    	regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100);
    	regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100);
    	regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100);
    	regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100);
    	regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
    	regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);
    	...
    }
    

    4.ragmap中的cache使用

    /* An enum of all the supported cache types */
    enum regcache_type {
    	REGCACHE_NONE,	//不使用缓存
    	REGCACHE_RBTREE, //使用红黑树
    	REGCACHE_COMPRESSED, //使用LZO 压缩
    	REGCACHE_FLAT, //本质上是数组
    };
    
    • 经过 regmap cache,提高访问效率,对于写操作,待cache存在一定数据量或者超出时间后写入物理寄存器,但降低实时性。
    • 不经过 regmap cache,对于写操作,立即写入物理寄存器,实时性好。

    cache同步

    /**
     * regcache_sync - Sync the register cache with the hardware.
     *
     * @map: map to configure.
     *
     * Any registers that should not be synced should be marked as
     * volatile.  In general drivers can choose not to use the provided
     * syncing functionality if they so require.
     *
     * Return a negative value on failure, 0 on success.
     */
    int regcache_sync(struct regmap *map);
    
    /**
     * regcache_sync_region - Sync part  of the register cache with the hardware.
     *
     * @map: map to sync.
     * @min: first register to sync
     * @max: last register to sync
     *
     * Write all non-default register values in the specified region to
     * the hardware.
     *
     * Return a negative value on failure, 0 on success.
     */
    int regcache_sync_region(struct regmap *map, unsigned int min,
    			 unsigned int max);
    

    参考链接:regmap 使用

    展开全文
  • regmap是在 Linux 内核为减少慢速 I/O 驱动上的重复逻辑,提供一种通用的接口来操作底层硬件寄存器的模型框架,简化驱动开发,提高访问效率。


    1 前言

      Linux驱动开发中,对于一些外设型器件驱动,如ADC、DAC、EEPROM、Sensor,这里器件通常是以uart、i2c、spi、mipi为控制接口,通过配置器件寄存器来设置芯片工作模式、运行参数、校准值等等,并通过获取寄存器值来获得有效数据。


      普通的做法,我们是根据不同的控制总线接口来实现寄存器访问,这样的方式是需要根据总线类型来调整访问接口、数据结构,这样显得繁琐。比如,目前有个ADC器件,支持spi和i2c接口;在此之前采用的是spi接口;后面因cpu spi接口不够用,线需要改为i2c控制。这样,该ADC驱动程序得修改,从spi改为i2c驱动,虽然工作量不大,但是也得花费一定时间。那么大体工作量有:

    • spi_write/spi_rea接口修改为 i2c_transfer
    • spi片选(cs)修改为i2c从地址寻址
    • 数据结构修改, struct spi_message修改为struct rt_i2c_msg

      基于代码代码复用的原则之一,Linux在3.1内核后引入了regmap模型,将寄存器访问的共同逻辑抽象出来,只需初始化时指定总线类型、寄存器位宽等关键参数,即可通过regmap模型接口来操作器件寄存器。当然,regmap同样适用于操作cpu自身的寄存器。


    2 什么是regmap

      regmap是在 linux 内核为减少慢速 I/O 驱动上的重复逻辑,提供一种通用的接口来操作底层硬件寄存器的模型框架。此外,regmap在驱动和硬件寄存器之间增加了cache,减少底层低速 I/O 的操作次数,提高访问效率;当然实时性会有所降低。


    2.1 为什么要用regmap

      regmap的特性和优点决定了我们为什么要用regmap。

    • 统一寄存器操作接口
    • 提高代码重用性和驱动一致性
    • 减少底层I/O操作次数,提高访问效率
    • 简化驱动开发过程

    2.2 regmap 框架模型

      regmap整体上分为三层,从下到上分别为物理总线、regmap核心、regmap api。

    在这里插入图片描述

    regmap框架模型


    • 底层,对接的是具体物理总线,目前regmap框架支持i2c、spi、mmio、spmi、ac97总线
    • 核心层,regmap 核心实现
    • api,抽象通用接口

      对于使用regmap框架来说,可以不用关心regmap核心的实现过程,只需根据物理总线类型,配置好相关参数信息,即可调用regmap api访问芯片寄存器。


    3 怎样使用regmap

      使用regmap比较简单,使用前,只需根据外设属性配置总线类型、寄存器位宽、缓存类型、读写属性等参数;接着注册一个regmap实例;然后调用抽象访问接口访问寄存器。

    • 第一步,配置regmap信息
    • 第二步,注册regmap实例
    • 第三步,访问寄存器
    • 第四步,释放regmap实例

    3.1 配置信息

      配置信息,首先需了解配置信息数据结构,linux内核以 struct regmap_config描述该数据结构,位于kernel/include/linux/regmap.h中声明。我们将关键而且驱动工程师常配置的参数用中文注释出来。

    struct regmap_config {
    	const char *name;
    
    	int reg_bits;	/* 寄存器地址位宽,常见有8位、16位,必须设置 */
    	int reg_stride; /* 寄存器地址对齐位宽 */
    	int pad_bits;	/* 寄存器填充位宽 */
    	int val_bits;	/* 寄存器值位宽,常见有8位、16位,必须设置 */
    
    	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;	/* cache数据类型,支持三种:flat、rbtree、Izo */
    	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;	/* 寄存器地址大小端,大于8位时需设置 */
    	enum regmap_endian val_format_endian;	/* 寄存器值大小端,大于8位时需设置 */
    
    	const struct regmap_range_cfg *ranges;
    	unsigned int num_ranges;
    };
    

      目前linux内核支持三种cache数据类型,三种类型位于kernel/include/linux/regmap.h中定义:

    • flat,普通数据类型

    • rbtree,红黑树类型

    • lzo,压缩类型

    /* An enum of all the supported cache types */
    enum regcache_type {
    	REGCACHE_NONE,
    	REGCACHE_RBTREE,
    	REGCACHE_COMPRESSED,
    	REGCACHE_FLAT,
    };
    

    3.2 注册接口

      regmap为每一种物理接口提供了一个注册函数。

    regmap_init_i2c(struct i2c_client *i2c, struct regmap_config *config);   
    regmap_init_spi(struct spi_device *spi, strcut regmap_config *config);
    regmap_init_mmio(struct device *dev, struct regmap_config *config);   
    regmap_init_spmi_base(struct spmi_device *dev, strcut regmap_config *config);
    regmap_init_spmi_ext(struct spmi_device *dev, strcut regmap_config *config);
    regmap_init_ac97(struct snd_ac97 *ac97, strcut regmap_config *config);
    

    注:
    注册函数声明位于kernel/include/linux/regmap.h中,原型中linux内核通过宏定义实现,展开后即是上面函数声明。


    3.3 抽象访问接口

      配置和注册regmap实例后,我们就可以使用抽象接口来访问寄存器,摈弃之前那套繁琐的数据结构和函数api。接口比较通俗,根据函数名称和入口参数即可知道函数功能。接口分为两大类,设置类(与初始化配置信息不同)和访问类,访问类根据访问过程又分为两种:

    • 经过regmap cache,提高访问效率,对于写操作,待cache存在一定数据量或者超出时间后写入物理寄存器;但降低实时性

    • 不经过regmap cache,对于写操作,立即写入物理寄存器,实时性好;对于读操作,则经过cache,减少拷贝时间


    常用访问类api:

    int regmap_write(struct regmap *map, int reg, int val); /* 写单个寄存器 */ 
    int regmap_raw_write(struct regmap *map, int reg, void *val, size_t val_len); /* 单个寄存器写指定长度数据 */
    int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,size_t val_count); /* 写多个寄存器 */
    int regmap_multi_reg_write_bypassed(struct regmap *map, const struct reg_sequence *regs,int num_regs);/* 直接写入寄存器,不经过regmap cache */
    int regmap_raw_write_async(struct regmap *map, unsigned int reg,const void *val, size_t val_len);/* 写多个寄存器,并立即刷新cache写入 */
    int regmap_read(struct regmap *map, int reg, int *val); /* 读单个寄存器 */
    int regmap_raw_read(struct regmap *map, int reg, void *val, size_t val_len); /* 单个寄存器读指定长度数据 */  
    int regmap_bulk_read(struct regmap *map, int reg, void *val, size_t val_count); /* 读多个寄存器 */
    int regmap_update_bits(struct regmap *map, int reg, int mask, int val); 	/* 更新寄存器值指定bit */
    int regmap_write_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val);/* 写入寄存器值指定bit */
    

      更多的操作api参考regmap.h中的声明。


    3.4 释放接口

      在驱动注销函数里应调用regmap_exit释放已注册的regmap实例。

    void regmap_exit(struct regmap *map);
    

    4 实例

      以i2c为例,以伪代码访问寄存器比较传统方式和通过regmap访问方式。


    • 传统方式
    static int read_regs(struct i2c_client *client, uint8_t reg, uint8_t *pdata, int size)
    {
    	int ret = 0;
    	struct i2c_msg msg[2];
    	if(size == 0)
    	{
    		return 0;
    	}
    	msg[0].addr  = client->addr;  	 
        msg[0].buf   = &reg;               
        msg[0].len   = 1;                     
        msg[0].flags = 0; 
    	msg[1].addr  = client->addr;  	 
        msg[1].buf   = pdata;               
        msg[1].len   = size;                     
        msg[1].flags = I2C_M_RD; 
    
    	if(i2c_transfer(client->adapter, msg, 2) != 2)
    	{
    		ret =-1;
    	}
    
    	return ret;
    }
    

    • regmap方式
    /* 第一步配置信息 */
    static const struct regmap_config regmap_config = 
    {     
    	.reg_bits = 8,     
    	.val_bits = 8,       
    	.max_register = 255,     
    	.cache_type = REGCACHE_RBTREE,     
    	.volatile_reg = false,
    };   
    
    /* 第二步,注册regmap实例 */
    regmap = regmap_init_i2c(i2c_client, &regmap_config);  
    
    /* 第三步,访问操作 */
    static int read_regs(uint8_t reg, uint8_t *pdata, int size)
    {
    	 return regmap_raw_read(regmap, reg, pdata, size);
    }
    

      通过比较两者,很显然,regmap方式将i2c的数据结构、传输api隐藏,使用者无需关心i2c内部实现,简化驱动开发过程,提高代码的复用性。如果将该器件物理接口更换为spi,只需修改配置信息即可,寄存器访问过程无需更改。

    展开全文
  • regmap子系统regmap子系统框架介绍regmap子系统的作用regmap子系统框架regmap子系统数据结构与源代码i2c接口的codec驱动为例,说明regmap使用 regmap子系统框架介绍 regmap子系统的作用 随着linux内核的设备驱动...
  • @)减少底层I/O 操作次数,提高访问效率 3、Regmap驱动框架 /* @ Linux内核将regmap框架抽象为regmap结构体 @ 定义在文件drivers/base/regmap/internal.h 中 @ regmap 结构体 */ struct regmap { union { struct ...
  • Linux设备驱动使用regmap
  • 2.regmap子系统分析2.1 框架模型2.2 代码文件结构2.3 重要数据结构2.5 regmap通用操作接口2.6 使用流程3.rk808 PMIC 驱动regmap-i2c重要部分示例3.1 regmap_config 结构填充3.2 注册strcut regmap对象示例3.3 regmap...
  • Linux regmap机制(一)

    2022-03-07 15:28:14
    Linux regmap机制 在Linux下开发WDT驱动时候参考某源代码时候发现devm_regmap_init_mmio_clk()函数的使用,故深入探究一下。 什么是 Regmap Linux 下大部分设备的驱动开发都是操作其内部寄存器,比如 I2C/SPI 器件...
  • regmap子系统 在芯片驱动开发中,基本都是读写操作寄存器。 常用到的接口都是如SPI、I2C,对于一些硬件则可能会支持两种接口, 对于Linux的SPI、I2C总线,它们读写操作接口是不同,但是针对这些硬件而言无论使用哪...
  • regmap使用介绍【转】

    2022-07-11 21:29:27
    内核3.1引入一套新的API regmap,目的是提取出关于I2C SPI irq等相关注册、使能以及读写的公共部分,以提高代码的可重用性,并且使得在使用如上内核基础组件时变得更为简单易用。 0 基础结构 [cpp]view plaincopy ...
  • linux Regmap API 笔记

    2022-07-25 19:28:46
    linux Regmap
  • } 三、i2c_transfer()流程 1.kernel/drivers/base/regmap/regmap.c <1>.int regmap_write(struct regmap *map, unsigned int reg, unsigned int val){ ret = _regmap_write(map, reg, val); } <2>.int _regmap_...
  • 开发版芯片 imx6ull 驱动测量了系统调用read 10000次的时间验证了开启硬件浮点和不开启硬件浮点的运算...linux/regmap.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h&
  • 《 linux regmap子系统》

    2021-11-14 13:02:58
    最近在看高通音频相关的驱动,发现codec接口时spi接口,在驱动中找到对应spi接口驱动,发现和之前接触的codec rt5651-i2c驱动不一样,它采用的时regmap框架写的,就整理了一下简单的介绍,如下: 首先,要明确一点...
  • Linux Regmap分析

    千次阅读 2018-07-05 18:30:46
    - devm_regmap_init_mmio_clk - regmap_update_bits(dsi-&gt;regmap, DSI_PHY_TST_CTRL1, PHY_TESTEN, 0); - regmap_read(dsi-&gt;regmap, DSI_PHY_TST_CTRL1, &amp;data); - regmap_write(dsi-&...
  • Linux I2C设备regmap机制简析

    千次阅读 2019-03-17 22:08:48
    在Linu 3.1开始,Linux引入了regmap来同意管理内核的I2C, SPI等总线,将I2C, SPI驱动做了一次重构,把I/O读写的重复逻辑在regmap中实现。 用一个I2C设备为例,在3.1之前的I2C设备驱动,需要各自调用i2c_transfer来...
  • regmap快速读写

    2021-04-07 17:03:55
    regmap 机制是在 Linux 3.1 加入进来的特性。主要目的是减少慢速 I/O 驱动上的重复逻辑,提供一种通用的接口来操作底层硬件上的寄存器。其实这就是内核做的一次重构。regmap 除了能做到统一的 I/O 接口,还可以在...
  • 比如,之前如果要操作 i2c 设备的寄存器,我们要调用 i2c_transfer 接口,要操作 spi 设备的寄存器,就要调用 spi_write/spi_read 等接口,如果把它们抽象为 regmap 结构,那么只要调用 regmap_read/regmap_write 就...
  • 在前面一章我们分析了regmap子系统的数据结构,基本上熟悉了数据结构的关联,也就大概理解了regmap子系统的实现流程,本章我们简要介绍下regmap子系统中接口的调用过程,然后介绍下regmap bus的实现及其提供的regmap...
  • kernel 函数 devm_regmap_init_i2c kernel 版本:5.10 sound/soc/codecs/es8316.c es8316->regmap = devm_regmap_init_i2c(i2c_client, &es8316_regmap); if (IS_ERR(es8316->regmap)) return PTR_...
  • Linux regmap架构学习 一、内核3.1引入一套新的API regmap,目的是提取出关于I2C SPI irq等相关注册、使能以及读写的公共部分,以提高代码的可重用性,并且使得在使用如上内核基础组件时变得更为简单易用。主要...
  • 基于代码复用的原则,Linux 内核引入了 regmap 模型,regmap 将寄存器访问的共同逻辑抽 象出来,驱动开发人员不需要再去纠结使用 SPI 或者 I2C 接口 API 函数,统一使用 regmapAPI函数。这样的好处就是统一使用 ...
  • Linux内核regmap介绍

    千次阅读 2019-08-27 17:35:29
    Regmap介绍   内核3.1引入一套新的API regmap,目的是提取出关于I2C SPI irq等相关注册、使能以及读写的公共部分,以提高代码的可重用性,并且使得在使用如上内核基础组件时变得更为简单易用。 基础结构   ...
  • Linux regmap机制浅析

    2020-12-24 16:30:24
    kernel version:4.9.186 software platform:Qualcomm android 10 文章目录 一、regmap初始化 二、regmap读写接口 三、regmap释放 四、wm8988驱动中regmap的使用 五、ragmap中的cache使用 cache同步 regmap机制引入...
  • 设备驱动中的regmap

    千次阅读 2018-07-27 14:31:58
    regmap 机制是在 Linux 3.1 加入进来的特性。主要目的是减少慢速 I/O 驱动上的重复逻辑,提供一种通用的接口来操作底层硬件上的寄存器。其实这就是内核做的一次重构。regmap 除了能做到统一的 I/O 接口,还可以在...
  • 上一章我们简要分析了regmap子系统,本章我们将从regmap子系统的数据结构介绍regmap子系统的实现。 一、数据结构间的关联及说明 针对regmap子系统,我们首先要知道regmap子系统要解决的痛点是什么?我们知道...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,387
精华内容 554
关键字:

regmap