本帖最后由 robe.zhang 于 2021-7-9 22:58 编辑
【ALINX AXU2CGB试用】atmel at24 驱动源码框架分析
Atmel at24 eeprom 驱动程序最终是注册了mvmem实现的所有功能
struct at24_data 数据结构中包括 nvmem 成员变量,还包括一个struct at24_client 类型的client[] 数组。两个疑问: 为什么不是 struct i2c_client 类型,而是 structat24_client 为什么不是 client,而是 client 数组 为什么不是 struct i2c_client 类型,而是 structat24_client,这个疑问打开 struct at24_client数据结构就有了答案,struct at24_client 数据结构封装了struct i2c_client 数据结构,同时还封装了一个 structregmap 数据结构 新的疑问:regmap 干什么用? 为什么不是 client,而是 client 数组:初始化的时候确实只有一个 client,如下截图: 但是有的芯片是多地址芯片:每个地址对应一个 struct i2c_client, 就会有多个client 了 其余的多个 client 全部在 at24_make_dummy_client函数中初始化,并在第 528 行赋值给 client 数据,同时赋值了一个 regmap 结构体变量 Atmel at24 注册nvmem,当作 nvmem 来看待;它同时继承了 struct i2c_client 数据结构和 regmap 数据结构,同时具有 i2c 功能和 regmap 功能。
这是 nvmem 注册驱动的过程:从源码中仅仅提炼了核心代码: 最终创建了设备文件,可以通过设备文件来访问。 访问设备文件时候,调用了 nvmem->reg_read/write 接口,此接口来自at24_read / at24_write 可以追踪: 追踪 at24_read / at24_write 函数可知,不用 regmap 时候,at24 的读写直接调用 map->bus->read /write 完成;使用 regmap 时候,分两步,一次是cache 读写,一次是 map->bus->read /write. map->bus->read /write 函数是 bus 中的两个方法,bus 是在 regmap_get_i2c_bus函数中获取到的regmap_i2c,此方法退化为 i2c 核心层实现的传输函数i2c_master_send、i2c_transfer 等,就接上 i2c 驱动源码核心框架部分了 regmap 是个啥东西,自己可以去网上搜,笔者换个角度去看regmap,从源码角度去看 regmap 是个啥东西。 - _regmap_read(map, reg + regmap_get_offset(map, i), &ival); # 2
- regcache_read(map, reg, val);
- map->reg_read(context, reg, val);
- _regmap_write(map, reg + regmap_get_offset(map, i), ival); # 1
- regcache_write(map, reg, val);
- map->reg_write(context, reg, val);
复制代码
这是 regmap 读写的函数的实现,先 regcache 读写,然后在调用 map->reg_read /reg_write,本质是在 i2c 总线到 eeprom 器件之间加了一层 cache,类似与 cpu 和 内存之间的加了cache 一样。Regcache_read / Regcache_write 是如何实现的:先看他的初始化过程,初始化在__regmap_init 中regcache_init 函数中: Cache 初始化时候直接赋值: map->cache_ops = cache_types;
cache 初始化时候,有三种类型可选,一种是 rbtree,索引效率高,一种是 lzo 压缩格式,一种是 flat 平坦型。 - static const struct regcache_ops *cache_types[] = {
- ®cache_rbtree_ops,
- #if IS_ENABLED(CONFIG_REGCACHE_COMPRESSED)
- ®cache_lzo_ops,
- #endif
- ®cache_flat_ops,
- };
复制代码详细看 regcache_flat_ops,,读写函数直接就是赋值,读cache把 cache 中的数据赋值给变量,写 cache 把数据赋值给 cache,相当简单快捷 - struct regcache_ops regcache_flat_ops = {
- .type = REGCACHE_FLAT,
- .name = "flat",
- .init = regcache_flat_init,
- .exit = regcache_flat_exit,
- .read = regcache_flat_read, # *value = cache[index];
- .write = regcache_flat_write, # cache[index] = value;
- };
复制代码赋值后调用 init 初始化cahce,init 函数如下,创建 regmap 的 cache,然后先填充初值
Regcache_read / Regcache_write 读写函数调用了map->cache_ops->write / read 函数,最终就是两个赋值完成的 *value = cache[index]; 和 cache[index] =value; Regcache_read / Regcache_write 读写之后照样调用map->reg_read / write 读写,map->reg_read/ write 来自 devm_regmap_init_i2c 中的 __devm_regmap_init_i2c 中的 __devm_regmap_init中的 __regmap_init 函数中赋值的,其实就是 at24_read/ at24_write,又接住上面了。 Regcache_read / Regcache_write 都是被 at24_read / at24_write 调用的.
总结:At24 eeprom 驱动,注册 nvmem 驱动,通过sysfs 文件系统访问 eeprom实现读写,读写最终还是通过 i2c 子系统实现的读写,读写过程中引入了 regmap 层类似 cache 功能。
看完 atmel at24 驱动,有个相似的 atmel at25 驱动,扫一眼看懂 从源码获取到两个信息 1,同样是注册nvmem,2,spi 总线。 就看这么多,能猜出来,atmel at25 和 atmel at24 驱动所有的几乎都一摸一样,仅仅的区别是一个 i2c 总线,一个 spi 总线,仅此而已。笔者没看代码,猜的哈,应该没大错,如果有告诉我
|