fuzidage
专注嵌入式、linux驱动 、arm裸机研究

导航

 

1 regmap的架构

regmap是为了方便操作寄存器而设计的,它将所有模块的寄存器(包括soc上模块的寄存器和外围设备的寄存器等)
抽象出来,用一套统一接口来操作寄存器,统一操作 i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq等。

image

image

regmap 框架分为三层:

①、底层物理总线: regmap 支持的物理总线有 i2c、i3c、spi、mmio、sccb、sdw、slimbus、irq、spmi 和 w1

②、regmap 核心层:用于实现 regmap核心。

③、regmapAPI 抽象层,regmap 向驱动编写人员提供的 API 接口。

2 数据结构

2.1 regmap 结构体

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;
	...
	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);
	......
	struct rb_root range_tree;
	void *selector_work_buf; /* Scratch buffer used for selector */
};  // drivers/base/regmap/internal.h

regmap 的初始化通过结构体 regmap_config来完成。

2.2 regmap_config

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);
	//是否不能读回调函数,当寄存器不能读,就返回tree
    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;//快速 I/O,使用 spinlock 替代 mutex 来提升锁性能。
	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;//寄存器模式值,此结构体有两个成员变量:reg 是寄存器地址,def 是默认值。
	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;
};

3 API

3.1 Regmap 申请与初始化

regmap 支持多种物理总线,比如 I2C 和 SPI,我们需要根据所使用的接口来选 择合适的 regmap 初始化函数。

struct regmap * regmap_init_spi(struct spi_device *spi, const struct regmap_config *config);
struct regmap * regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config);//返回一个regmap结构体指针
void regmap_exit(struct regmap *map);//不管是什么物理接口,退出都用regmap_exit

3.2 regmap 设备访问

int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);

regmap_readregmap_write 的基础上还衍生出了其他一些 regmap 的 API 函数:

/* regmap_update_bits函数,看名字就知道,此函数用来修改寄存器指定的 bit。
* mask:掩码,需要更新的位必须在掩码中设置为 1。
* val:需要更新的位值。
* 比如要将寄存器的 bit1 和 bit2 置 1,那么 mask 应该设置为 0X00000011,此时 val 的 bit1
和 bit2 应该设置为 1,也就是 0Xxxxxxx11。如果要清除寄存器的 bit4 和 bit7,那么 mask 应该
设置为 0X10010000,val 的 bit4 和 bit7 设置为 0,也就是 0X0xx0xxxx。
*/

int regmap_update_bits (struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val);

//读取写入多个寄存器的值
//map:要操作的 regmap。
//reg:要读写的第一个寄存器。
//val:要读写的寄存器数据缓冲区。
//val_count:要读写的寄存器数量
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_count);
int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count)

3.3 regmap_config 掩码设置

结构体regmap_config里面有三个关于掩码的成员变量:read_flag_mask write_flag_mask.

Linux下SPI子系统驱动

image

其中读操作要将寄存器的地址 bit7 置 1,表示这是 一个读操作。当我们使用regmap的时候就不需要手动将寄存器地址的 bit7 置 1,在初始化 regmap_config 的时候直接将read_flag_mask设置为 0X80 即可,这样通过regmap读取 SPI 内部寄存器的时候就会将寄存器地址与read_flag_mask进行或运算,结果就是将 bit7 置 1。

static struct regmap_bus regmap_spi = {
	.write = regmap_spi_write,
	.gather_write = regmap_spi_gather_write,
	.async_write = regmap_spi_async_write,
	.async_alloc = regmap_spi_async_alloc,
	.read = regmap_spi_read,
	.read_flag_mask = 0x80,
	.reg_format_endian_default = REGMAP_ENDIAN_BIG,
	.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
...
struct regmap *regmap_init_spi(struct spi_device *spi, const struct regmap_config *config)
{
	return regmap_init(&spi->dev, &regmap_spi, &spi->dev, config);
}// regmap-spi.c 

regmap_init 函数中找到如下所示内容:

if (config->read_flag_mask || config->write_flag_mask) {
	map->read_flag_mask = config->read_flag_mask;
	map->write_flag_mask = config->write_flag_mask;
} else if (bus) {
	map->read_flag_mask = bus->read_flag_mask;
}

可以看到初始化时用regmap_config 中的读写掩码来初始化regmap_bus中的掩码。由于 regmap_spi 默认将 read_flag_mask 设置为 0X80,假如你所使用的 SPI 设备不需要读掩码,在初始 化regmap_config的时候一定要将 read_flag_mask 设置为 0X00。

4 Regmap操作SPI-ICM20608实验

Linux下SPI子系统驱动

image

之前构造了icm20608_dev利用spi子系统框架通信。框架还是保持不变,添加regmap和regmap_config参数。

4.1 初始化 regmap

image

image

4.2 regmap 访问icm20608

以前的读写寄存器操作要构造spi_message,spi_transfer:

image

image

现在直接套用reg_map函数:

image

image

读多组寄存器也是一样,调用regmap_bulk_read

image

5 Regmap操作I2C-ap3216c实验

image

Linux I2C子系统驱动

5.1 初始化 regmap

image

5.2 regmap 访问ap3216c

image

image

这里初始化open fd时,要用到regmap_write进行i2c从设备的寄存器写入初始化。然后调用read,调用regmap_read进行读操作,这里每次都只读取一个byte,不需要用到regmap_bulk_read.

posted on 2024-07-12 17:50  fuzidage  阅读(15)  评论(0编辑  收藏  举报