linux驱动开发-regmap框架

一、regmap介绍

linux引入regmap是为了统一管理内核的i2c,spi等总线,将i2c、spi驱动做了一次重构,把I/O读写的重复逻辑在regmap中实现。只需要初始化时指定总线类型、寄存器位宽等关键参数,即可通过regmap模型接口来操作器件寄存器。

i2c、spi、mmio、irq等抽象出统一接口regmap_readregmap_writeregmap_update_bits等接口,从而提高代码的可重用性。regmap是在linux内核为减少慢速I/O驱动上的重复逻辑,提供的一种通用接口来操作底层硬件寄存器的模型框架。如下所示:

 框架主要分为三层:

底层物理总线:regmap对不同的物理总线进行封装,目前regmap支持的物理总线有i2c、i3c、spi、mmio、sccb等。

regmap核心层:用于实现regmap,链接API抽象层和物理层。

regmapAPI抽象层:向驱动编写人员提供API接口,驱动编写人员使用这些API接口来操作具体芯片设备,也是需要驱动编写人员重点掌握的。

二、使用regmap

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

2.1 配置regmap信息

配置信息首先需要了解配置信息的数据结构,linux内核将regmap框架抽象为regmap结构体,这个结构体定义在文件drivers/base/regmap/internal.h

struct regmap {
        union {
                struct mutex mutex;
                struct {
                        spinlock_t spinlock;
                        unsigned long spinlock_flags;
                };
        };
        regmap_lock lock;
        ......  
        struct rb_root range_tree;
        void *selector_work_buf; /* Scratch buffer used for selector */
};

要使用regmap,肯定要先给驱动分配一个具体的regmap结构体实例,regmap的初始化通过结构体regmap_config来完成。

regmap_config结构体就是用来初始化regmap的,这个结构体也定义在include/linux/regmap.h文件中,内容如下:

struct regmap_config {
        const char *name;
        reg_bits;                        //寄存器地址位数,必填字段。
        int reg_stride;
        int pad_bits;
        int val_bits;                        //寄存器值位数,必填字段 
        bool (*writeable_reg)(struct device *dev, unsigned int reg);
        ........ 
        unsigned int num_ranges;
};        

2.2 注册regmap信息  

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内核通过宏定义实现,展开后即是上面函数声明。

2.3 抽象访问接口

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

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

(2)不经过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); 

/* 直接写入寄存器,不经过regmap cache */
int regmap_multi_reg_write_bypassed(struct regmap *map, const struct reg_sequence *regs,int num_regs);

/* 写多个寄存器,并立即刷新cache写入 */
int regmap_raw_write_async(struct regmap *map, unsigned int reg,const void *val, size_t val_len);

/* 读单个寄存器 */
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);

/* 更新寄存器值指定bit */
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);

2.4 释放regemap接口

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

void regmap_exit(struct regmap *map);

猜你喜欢

转载自blog.csdn.net/Forever_change/article/details/134730899