支持作者新书《Yocto项目实战教程:高效定制嵌入式Linux系统》,点击京东购买
Linux nvmem 子系统全面解析:综合场景、内核机制、实战分析
一、nvmem 是什么?
nvmem
是 Linux 内核中提供的一套给 EEPROM/非易逝存储器 (如 OTP/EFUSE/小宽带 SPI Flash) 综合读写与管理的子系统。
该子系统被应用在不少需要少量压缩信息存储的场景,包括:
- 网卡 MAC 地址
- 工厂生产时写入的二进制数据
- 产品编号/序列号
- 音频/摄像头/光线设备的标志/校准值
过去这类需每个驱动单独写通信代码,有重复、难以处理同一设备多个使用者等缺点。nvmem
子系统就是为了解决这些问题而出现。
二、核心概念
1. Cell 、Provider 、Consumer
-
nvmem provider: 提供数据给其他模块或用户端,如 at24 这种 EEPROM 驱动就是 provider
-
nvmem cell: 分割成的一段有意义数据,如 MAC 地址的地址段(经常在 EEPROM 中的 0x00~0x05 地址)
-
nvmem consumer: 使用这段数据的设备/驱动,如网卡驱动读取 MAC
2. 设备树模型
nvmem@50 {
compatible = "atmel,24c02";
reg = <0x50>;
#address-cells = <1>;
#size-cells = <1>;
mac_addr: mac@0 {
reg = <0x0 0x6>;
};
};
ethernet@30be0000 {
nvmem-cells = <&mac_addr>;
nvmem-cell-names = "mac-address";
};
三、驱动端:注册 nvmem 设备
1. EEPROM 为例
#include <linux/nvmem-provider.h>
static const struct nvmem_config eeprom_nvmem_cfg = {
.name = "eeprom-2-0050",
.dev = &client->dev,
.size = size,
.word_size = 1,
.stride = 1,
.read_only = true,
.reg_read = at24_reg_read,
};
at24->nvmem = devm_nvmem_register(&client->dev, &eeprom_nvmem_cfg);
2. 必填配置项
项 | 说明 |
---|---|
name | 设备名 |
dev | 对应 struct device |
size | 数据大小 |
reg_read | 读取函数 |
reg_write | 写入函数(非必) |
四、消费端:获取 nvmem 数据
1. nvmem_cell_get()
struct nvmem_cell *cell;
cell = nvmem_cell_get(dev, "mac-address");
2. nvmem_cell_read()
size_t len;
void *data;
data = nvmem_cell_read(cell, &len);
if (!IS_ERR(data))
memcpy(mac, data, ETH_ALEN);
五、用户端读取
cat /sys/bus/nvmem/devices/eeprom-2-0050/nvmem | hexdump -C
如果设备树中配置了 MAC 地址 cell:
cat /sys/firmware/devicetree/base/ethernet@30be0000/nvmem-cell-names
六、实战分析:at24.c 中如何集成 nvmem
at24->nvmem = devm_nvmem_register(dev, &nvmem_config);
if (IS_ERR(at24->nvmem)) {
pm_runtime_disable(dev);
if (!pm_runtime_status_suspended(dev))
regulator_disable(at24->vcc_reg);
return PTR_ERR(at24->nvmem);
}
这段代码在 at24_probe() 最后执行,如果注册失败会引发该设备 probe fail。
常见错误如下:
[ 1.853129] at24: probe of 2-0050 failed with error -16
通常是 nvmem 名称冲突/注册重复/配置错误。
七、面试问题 & 可口说化答案
问题 1:你怎么说明 nvmem 子系统?
nvmem 是 Linux 内核中对非易逝存储器进行抽象和分组管理的一套框架。它分为 provider 和 consumer,支持设备树创建 cell,可被驱动或用户端通用读取。经常用于 MAC 地址、压缩值、历史历练信息等存储设备。
问题 2:如何从 EEPROM 读 MAC 地址?
通过 nvmem 子系统:
- EEPROM 作为 provider,在设备树中配置 cell
- Ethernet 驱动作为 consumer,配置
nvmem-cells
- 驱动中调用
nvmem_cell_get()
+nvmem_cell_read()
读取 MAC
八、总结
nvmem
子系统是非易逝存储器管理的标准化接口,读/写分离,简化了多驱动共用同一段数据的约束。- 在 Yocto/嵌入式项目中,推荐使用 Descriptor GPIO + nvmem 配置模型,让终端产品更可读懂、可控、可优化。