目录
1 描述
2 结构体
2.1 regmap
2.2 regmap_bus
2.3 regmap_config
3 regmap 操作函数
3.1 regmap 申请与初始化
3.1.1 regmap_init_i2c
3.1.2 regmap_init_spi
3.1.3 regmap_exit
3.2 regmap 设备访问 API 函数
3.2.1 regmap_read
3.2.2 regmap_write
4 示例
1 描述
Linux 内核引入了 regmap 模型,regmap 将寄存器访问的共同逻辑抽象出来,驱动开发人员不需要再去纠结使用 SPI 或者 I2C 接口 API 函数,统一使用 regmapAPI 函数。这样的好处就是统一使用 regmap,降低了代码冗余,提高了驱动的可以移植性。regmap 模型的重点在于:
通过 regmap 模型提供的统一接口函数来访问器件的寄存器,SOC 内部的寄存器也可以使用 regmap 接口函数来访问。
regmap 是 Linux 内核为了减少慢速 I/O 在驱动上的冗余开销,提供了一种通用的接口来操作硬件寄存器。另外,regmap 在驱动和硬件之间添加了 cache,降低了低速 I/O 的操作次数,提高了访问效率,缺点是实时性会降低。
regmap 框架结构如下图所示。
2 结构体
2.1 regmap
52 struct regmap {
53 union {
54 struct mutex mutex;
55 struct {
56 spinlock_t spinlock;
57 unsigned long spinlock_flags;
58 };
59 };
60 regmap_lock lock;
61 regmap_unlock unlock;
62 void *lock_arg; /* This is passed to lock/unlock functions */
63 gfp_t alloc_flags;
64
65 struct device *dev; /* Device we do I/O on */
66 void *work_buf; /* Scratch buffer used to format I/O */
67 struct regmap_format format; /* Buffer format */
68 const struct regmap_bus *bus;
69 void *bus_context;
70 const char *name;
71
72 bool async;
73 spinlock_t async_lock;
74 wait_queue_head_t async_waitq;
75 struct list_head async_list;
76 struct list_head async_free;
77 int async_ret;
78
79 #ifdef CONFIG_DEBUG_FS
80 bool debugfs_disable;
81 struct dentry *debugfs;
82 const char *debugfs_name;
83
84 unsigned int debugfs_reg_len;
85 unsigned int debugfs_val_len;
86 unsigned int debugfs_tot_len;
87
88 struct list_head debugfs_off_cache;
89 struct mutex cache_lock;
90 #endif
91
92 unsigned int max_register;
93 bool (*writeable_reg)(struct device *dev, unsigned int reg);
94 bool (*readable_reg)(struct device *dev, unsigned int reg);
95 bool (*volatile_reg)(struct device *dev, unsigned int reg);
96 bool (*precious_reg)(struct device *dev, unsigned int reg);
97 bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
98 const struct regmap_access_table *wr_table;
99 const struct regmap_access_table *rd_table;
100 const struct regmap_access_table *volatile_table;
101 const struct regmap_access_table *precious_table;
102 const struct regmap_access_table *rd_noinc_table;
103
104 int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
105 int (*reg_write)(void *context, unsigned int reg, unsigned int val);
106 int (*reg_update_bits)(void *context, unsigned int reg,
107 unsigned int mask, unsigned int val);
108
109 bool defer_caching;
110
111 unsigned long read_flag_mask;
112 unsigned long write_flag_mask;
113
114 /* number of bits to (left) shift the reg value when formatting*/
115 int reg_shift;
116 int reg_stride;
117 int reg_stride_order;
118
119 /* regcache specific members */
120 const struct regcache_ops *cache_ops;
121 enum regcache_type cache_type;
122
123 /* number of bytes in reg_defaults_raw */
124 unsigned int cache_size_raw;
125 /* number of bytes per word in reg_defaults_raw */
126 unsigned int cache_word_size;
127 /* number of entries in reg_defaults */
128 unsigned int num_reg_defaults;
129 /* number of entries in reg_defaults_raw */
130 unsigned int num_reg_defaults_raw;
131
132 /* if set, only the cache is modified not the HW */
133 bool cache_only;
134 /* if set, only the HW is modified not the cache */
135 bool cache_bypass;
136 /* if set, remember to free reg_defaults_raw */
137 bool cache_free;
138
139 struct reg_default *reg_defaults;
140 const void *reg_defaults_raw;
141 void *cache;
142 /* if set, the cache contains newer data than the HW */
143 bool cache_dirty;
144 /* if set, the HW registers are known to match map->reg_defaults */
145 bool no_sync_defaults;
146
147 struct reg_sequence *patch;
148 int patch_regs;
149
150 /* if set, converts bulk read to single read */
151 bool use_single_read;
152 /* if set, converts bulk read to single read */
153 bool use_single_write;
154 /* if set, the device supports multi write mode */
155 bool can_multi_write;
156
157 /* if set, raw reads/writes are limited to this size */
158 size_t max_raw_read;
159 size_t max_raw_write;
160
161 struct rb_root range_tree;
162 void *selector_work_buf; /* Scratch buffer used for selector */
163
164 struct hwspinlock *hwlock;
165 };
2.2 regmap_bus
regmap_bus 的结构体,它用于描述一种与寄存器映射(regmap)交互的硬件总线或接口的属性和功能。在嵌入式系统或硬件驱动开发中,寄存器映射是一种常见的技术,用于通过内存地址空间访问硬件设备的寄存器。这种方式使得软件可以像操作内存一样来读写硬件的寄存器,从而控制硬件设备。
regmap_bus结构定义了读写函数和默认的寄存器地址和寄存器值的大小端。
492 struct regmap_bus {
493 bool fast_io;
494 regmap_hw_write write;
495 regmap_hw_gather_write gather_write;
496 regmap_hw_async_write async_write;
497 regmap_hw_reg_write reg_write;
498 regmap_hw_reg_update_bits reg_update_bits;
499 regmap_hw_read read;
500 regmap_hw_reg_read reg_read;
501 regmap_hw_free_context free_context;
502 regmap_hw_async_alloc async_alloc;
503 u8 read_flag_mask;
504 enum regmap_endian reg_format_endian_default;
505 enum regmap_endian val_format_endian_default;
506 size_t max_raw_read;
507 size_t max_raw_write;
508 };
bool fast_io;:一个布尔值,指示是否使用快速I/O模式。这可能会影响读写操作的性能和方式。
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;:一个无符号8位整数,用作读操作时的标志掩码,可能用于控制读操作的某些方面。
enum regmap_endian reg_format_endian_default;:一个枚举类型,表示寄存器格式(即寄存器在硬件中的存储方式)的默认字节序(大端或小端)。
enum regmap_endian val_format_endian_default;:一个枚举类型,表示值格式(即写入或读取到寄存器的值的存储方式)的默认字节序。
size_t max_raw_read; 和 size_t max_raw_write;:分别表示在一次操作中能够读取或写入的最大原始数据量(以字节为单位)。这对于了解硬件的限制和优化数据传输效率很重要。
2.3 regmap_config
在regmap_config,定义了寄存器的各种信息,比如寄存器地址长度,寄存器值的长度,读写寄存器的地址范围的信息,寄存器地址和值的大小端以及缓冲方式。
343 struct regmap_config {
344 const char *name;
345
346 int reg_bits;
347 int reg_stride;
348 int pad_bits;
349 int val_bits;
350
351 bool (*writeable_reg)(struct device *dev, unsigned int reg);
352 bool (*readable_reg)(struct device *dev, unsigned int reg);
353 bool (*volatile_reg)(struct device *dev, unsigned int reg);
354 bool (*precious_reg)(struct device *dev, unsigned int reg);
355 bool (*readable_noinc_reg)(struct device *dev, unsigned int reg);
356
357 bool disable_locking;
358 regmap_lock lock;
359 regmap_unlock unlock;
360 void *lock_arg;
361
362 int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
363 int (*reg_write)(void *context, unsigned int reg, unsigned int val);
364
365 bool fast_io;
366
367 unsigned int max_register;
368 const struct regmap_access_table *wr_table;
369 const struct regmap_access_table *rd_table;
370 const struct regmap_access_table *volatile_table;
371 const struct regmap_access_table *precious_table;
372 const struct regmap_access_table *rd_noinc_table;
373 const struct reg_default *reg_defaults;
374 unsigned int num_reg_defaults;
375 enum regcache_type cache_type;
376 const void *reg_defaults_raw;
377 unsigned int num_reg_defaults_raw;
378
379 unsigned long read_flag_mask;
380 unsigned long write_flag_mask;
381 bool zero_flag_mask;
382
383 bool use_single_rw;
384 bool can_multi_write;
385
386 enum regmap_endian reg_format_endian;
387 enum regmap_endian val_format_endian;
388
389 const struct regmap_range_cfg *ranges;
390 unsigned int num_ranges;
391
392 bool use_hwlock;
393 unsigned int hwlock_id;
394 unsigned int hwlock_mode;
395 };
3 regmap 操作函数
3.1 regmap 申请与初始化
3.1.1 regmap_init_i2c
函数原型 | #define regmap_init_i2c(i2c, config) \ __regmap_lockdep_wrapper(__regmap_init_i2c, #config, \ i2c, config) | |
参数 | struct i2c_client *i2c | 这是指向 I2C 设备的指针,通常是一个 struct i2c_client 类型。 |
struct regmap_config *config | 这是一个配置结构体,包含有关寄存器映射的配置信息,比如寄存器地址、数据格式等。 | |
返回值 |
|
|
功能 | 用于初始化 I2C 设备的寄存器映射 |
当设备驱动配置好config以后,调用对应的regmap_init_i2c
281 struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
282 const struct regmap_config *config,
283 struct lock_class_key *lock_key,
284 const char *lock_name)
285 {
286 const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
287
288 if (IS_ERR(bus))
289 return ERR_CAST(bus);
290
291 return __regmap_init(&i2c->dev, bus, &i2c->dev, config,
292 lock_key, lock_name);
293 }
662 #define regmap_init_i2c(i2c, config) \
663 __regmap_lockdep_wrapper(__regmap_init_i2c, #config, \
664 i2c, config)
620 #ifdef CONFIG_LOCKDEP
621 #define __regmap_lockdep_wrapper(fn, name, ...) \
622 ( \
623 ({ \
624 static struct lock_class_key _key; \
625 fn(__VA_ARGS__, &_key, \
626 KBUILD_BASENAME ":" \
627 __stringify(__LINE__) ":" \
628 "(" name ")->lock"); \
629 }) \
630 )
631 #else
632 #define __regmap_lockdep_wrapper(fn, name, ...) fn(__VA_ARGS__, NULL, NULL)
633 #endif
281 struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
282 const struct regmap_config *config,
283 struct lock_class_key *lock_key,
284 const char *lock_name)
285 {
286 const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
287
288 if (IS_ERR(bus))
289 return ERR_CAST(bus);
290
291 return __regmap_init(&i2c->dev, bus, &i2c->dev, config,
292 lock_key, lock_name);
293 }
253 static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
254 const struct regmap_config *config)
255 {
256 if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
257 return ®map_i2c;
258 else if (config->val_bits == 8 && config->reg_bits == 8 &&
259 i2c_check_functionality(i2c->adapter,
260 I2C_FUNC_SMBUS_I2C_BLOCK))
261 return ®map_i2c_smbus_i2c_block;
262 else if (config->val_bits == 16 && config->reg_bits == 8 &&
263 i2c_check_functionality(i2c->adapter,
264 I2C_FUNC_SMBUS_WORD_DATA))
265 switch (regmap_get_val_endian(&i2c->dev, NULL, config)) {
266 case REGMAP_ENDIAN_LITTLE:
267 return ®map_smbus_word;
268 case REGMAP_ENDIAN_BIG:
269 return ®map_smbus_word_swapped;
270 default: /* everything else is not supported */
271 break;
272 }
273 else if (config->val_bits == 8 && config->reg_bits == 8 &&
274 i2c_check_functionality(i2c->adapter,
275 I2C_FUNC_SMBUS_BYTE_DATA))
276 return ®map_smbus_byte;
277
278 return ERR_PTR(-ENOTSUPP);
279 }
3.1.2 regmap_init_spi
701 #define regmap_init_spi(dev, config) \
702 __regmap_lockdep_wrapper(__regmap_init_spi, #config, \
703 dev, config)
3.1.3 regmap_exit
不管是什么接口,全部使用 regmap_exit 这个函数来释放 regmap。
1310 void regmap_exit(struct regmap *map)
1311 {
1312 struct regmap_async *async;
1313
1314 regcache_exit(map);
1315 regmap_debugfs_exit(map);
1316 regmap_range_exit(map);
1317 if (map->bus && map->bus->free_context)
1318 map->bus->free_context(map->bus_context);
1319 kfree(map->work_buf);
1320 while (!list_empty(&map->async_free)) {
1321 async = list_first_entry_or_null(&map->async_free,
1322 struct regmap_async,
1323 list);
1324 list_del(&async->list);
1325 kfree(async->work_buf);
1326 kfree(async);
1327 }
1328 if (map->hwlock)
1329 hwspin_lock_free(map->hwlock);
1330 kfree_const(map->name);
1331 kfree(map->patch);
1332 kfree(map);
1333 }
3.2 regmap 设备访问 API 函数
不管是 I2C 还是 SPI 等接口,还是 SOC 内部的寄存器,对于寄存器的操作就两种:读和写。regmap 提供了最核心的两个读写操作:regmap_read 和 regmap_write。这两个函数分别用来读/写寄存器。
3.2.1 regmap_read
函数原型 | int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) | |
参数 | struct regmap *map | regmap结构体指针 |
unsigned int reg | 读取的寄存器地址 | |
unsigned int *val | 存储读取的数据的指针 | |
返回值 | int | 成功:0 失败:负数 |
功能 | 从设备的寄存器中读取数据。regmap框架提供了一种统一的方式来访问设备的寄存器,无论这些寄存器是通过i2c、spi、内存映射还是其他方式访问的 |
2472 int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
2473 {
2474 int ret;
2475
2476 if (!IS_ALIGNED(reg, map->reg_stride))
2477 return -EINVAL;
2478
2479 map->lock(map->lock_arg);
2480
2481 ret = _regmap_read(map, reg, val);
2482
2483 map->unlock(map->lock_arg);
2484
2485 return ret;
2486 }
2428 static int _regmap_read(struct regmap *map, unsigned int reg,
2429 unsigned int *val)
2430 {
2431 int ret;
2432 void *context = _regmap_map_get_context(map);
2433
2434 if (!map->cache_bypass) {
2435 ret = regcache_read(map, reg, val);
2436 if (ret == 0)
2437 return 0;
2438 }
2439
2440 if (map->cache_only)
2441 return -EBUSY;
2442
2443 if (!regmap_readable(map, reg))
2444 return -EIO;
2445
2446 ret = map->reg_read(context, reg, val);
2447 if (ret == 0) {
2448 #ifdef LOG_DEVICE
2449 if (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
2450 dev_info(map->dev, "%x => %x\n", reg, *val);
2451 #endif
2452
2453 trace_regmap_reg_read(map, reg, *val);
2454
2455 if (!map->cache_bypass)
2456 regcache_write(map, reg, *val);
2457 }
2458
2459 return ret;
2460 }
3.2.2 regmap_write
函数原型 | int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) | |
参数 | struct regmap *map | regmap结构体指针 |
unsigned int reg | 写的寄存器地址 | |
unsigned int val | 写寄存器的值 | |
返回值 | int | 成功:0 失败:负数 |
功能 | 从设备的寄存器中写数据。regmap框架提供了一种统一的方式来访问设备的寄存器,无论这些寄存器是通过i2c、spi、内存映射还是其他方式访问的 |
1773 int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
1774 {
1775 int ret;
1776
1777 if (!IS_ALIGNED(reg, map->reg_stride))
1778 return -EINVAL;
1779
1780 map->lock(map->lock_arg);
1781
1782 ret = _regmap_write(map, reg, val);
1783
1784 map->unlock(map->lock_arg);
1785
1786 return ret;
1787 }
1734 int _regmap_write(struct regmap *map, unsigned int reg,
1735 unsigned int val)
1736 {
1737 int ret;
1738 void *context = _regmap_map_get_context(map);
1739
1740 if (!regmap_writeable(map, reg))
1741 return -EIO;
1742
1743 if (!map->cache_bypass && !map->defer_caching) {
1744 ret = regcache_write(map, reg, val);
1745 if (ret != 0)
1746 return ret;
1747 if (map->cache_only) {
1748 map->cache_dirty = true;
1749 return 0;
1750 }
1751 }
1752
1753 #ifdef LOG_DEVICE
1754 if (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
1755 dev_info(map->dev, "%x <= %x\n", reg, val);
1756 #endif
1757
1758 trace_regmap_reg_write(map, reg, val);
1759
1760 return map->reg_write(context, reg, val);
1761 }
4 示例
40 enum regcache_type {
41 REGCACHE_NONE,
42 REGCACHE_RBTREE,
43 REGCACHE_COMPRESSED,
44 REGCACHE_FLAT,
45 };
#define MCU_REG_MAX 100
static bool mcu_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case MCU_VID:
case MCU_PID:
case MCU_VERSION_MAJOR:
case MCU_VERSION_MINOR:
case MCU_POWER_LOSS:
case MCU_RTC_WAKE:
case MCU_WOL_WAKE:
case MCU_WDT_TIMOUT:
case MCU_WDT_CONTROL:
return true;
}
return false;
}
static const struct regmap_config mcu_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MCU_REG_MAX,
.cache_type = REGCACHE_NONE,
.volatile_reg = mcu_is_volatile_reg,
};
static int spirit_mcu_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
struct spirit_mcu *spirit_mcu;
struct device_node *np = client->dev.of_node;
spirit_mcu = devm_kzalloc(&client->dev, sizeof(struct spirit_mcu), GFP_KERNEL);
if (!spirit_mcu)
return -ENOMEM;
spirit_mcu->regmap = devm_regmap_init_i2c(client, &mcu_regmap_config);
if (IS_ERR(spirit_mcu->regmap)) {
dev_err(&client->dev, "regmap initialization failed\n");
return PTR_ERR(spirit_mcu->regmap);
}
spirit_mcu->userfeed = 0;
i2c_set_clientdata(client, spirit_mcu);
spirit_mcu->i2c = client;
spirit_mcu->np = np;
mcu_i2c_client = client;
ret = regmap_read(spirit_mcu->regmap, MCU_VID, &spirit_mcu->deviceInfo.vid);
if (ret) {
dev_err(&client->dev, "read 0x%x failed\n", MCU_VID);
//return ret;
}
return 0;
}
static const struct i2c_device_id spirit_mcu_id[] = {
{ "spirit_mcu", 0 },
{ }
};
static struct i2c_driver spirit_mcu_driver = {
.driver = {
.name = "spirit_mcu",
.owner = THIS_MODULE,
},
.probe = spirit_mcu_probe,
.id_table = spirit_mcu_id,
};
static int __init spirit_mcu_init(void)
{
return i2c_add_driver(&spirit_mcu_driver);
}
static void __exit spirit_mcu_exit(void)
{
unregister_reboot_notifier(&mcu_reboot_notifier);
i2c_del_driver(&spirit_mcu_driver);
}
MODULE_AUTHOR("neilnee@jwele.com.cn");
MODULE_DESCRIPTION("spirit mcu driver");
MODULE_LICENSE("GPL");
late_initcall(spirit_mcu_init);
module_exit(spirit_mcu_exit);