nvmem是针对非易失性,例如eeprom, efuses等的一个抽象层,nvmem分为consumer和provide,其在kernel中的位置为drivers/nvmem
其中provide 最终要的就是调用nvmem_register,其一般的模板如下:
static struct nvmem_config econfig = {
.name = "qfprom",
.owner = THIS_MODULE,
};
static int qfprom_probe(struct platform_device *pdev)
{
...
econfig.dev = &pdev->dev;
nvmem = nvmem_register(&econfig);
...
}
而consumer 可以用下面这些API 使用provide提供的设备例如eeprom, efuses等
struct nvmem_device *nvmem_device_get(struct device *dev, const char *name);
struct nvmem_device *devm_nvmem_device_get(struct device *dev,
const char *name);
void nvmem_device_put(struct nvmem_device *nvmem);
int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset,
size_t bytes, void *buf);
int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset,
size_t bytes, void *buf);
int nvmem_device_cell_read(struct nvmem_device *nvmem,
struct nvmem_cell_info *info, void *buf);
int nvmem_device_cell_write(struct nvmem_device *nvmem,
struct nvmem_cell_info *info, void *buf);
obj-$(CONFIG_NVMEM) += nvmem_core.o
nvmem_core-y := core.o
# Devices
obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
nvmem-imx-ocotp-y := imx-ocotp.o
obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o
nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o
obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o
nvmem-mxs-ocotp-y := mxs-ocotp.o
obj-$(CONFIG_MTK_EFUSE) += nvmem_mtk-efuse.o
nvmem_mtk-efuse-y := mtk-efuse.o
obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o
nvmem_qfprom-y := qfprom.o
obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o
nvmem_rockchip_efuse-y := rockchip-efuse.o
obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
nvmem_sunxi_sid-y := sunxi_sid.o
obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
nvmem-vf610-ocotp-y := vf610-ocotp.o
obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o
nvmem_meson_efuse-y := meson-efuse.o
从drivers/nvmem里面的makefile中可以看到核心文件就一个core.c 这个文件通过provide和consumer 使用的API。这里以mtk-efuse.c为例
static int mtk_reg_read(void *context,
unsigned int reg, void *_val, size_t bytes)
{
void __iomem *base = context;
u32 *val = _val;
int i = 0, words = bytes / 4;
while (words--)
*val++ = readl(base + reg + (i++ * 4));
return 0;
}
static int mtk_reg_write(void *context,
unsigned int reg, void *_val, size_t bytes)
{
void __iomem *base = context;
u32 *val = _val;
int i = 0, words = bytes / 4;
while (words--)
writel(*val++, base + reg + (i++ * 4));
return 0;
}
static int mtk_efuse_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct nvmem_device *nvmem;
struct nvmem_config *econfig;
void __iomem *base;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
// 申请一个nvmem_config结构
econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL);
if (!econfig)
return -ENOMEM;
//给nvmem_config *econfig 赋值
econfig->stride = 4;
econfig->word_size = 4;
econfig->reg_read = mtk_reg_read;
econfig->reg_write = mtk_reg_write;
econfig->size = resource_size(res);
econfig->priv = base;
econfig->dev = dev;
econfig->owner = THIS_MODULE;
//调用提供provide的注册函数
nvmem = nvmem_register(econfig);
if (IS_ERR(nvmem))
return PTR_ERR(nvmem);
platform_set_drvdata(pdev, nvmem);
return 0;
}
static int mtk_efuse_remove(struct platform_device *pdev)
{
struct nvmem_device *nvmem = platform_get_drvdata(pdev);
return nvmem_unregister(nvmem);
}
static const struct of_device_id mtk_efuse_of_match[] = {
{ .compatible = "mediatek,mt8173-efuse",},
{ .compatible = "mediatek,efuse",},
{/* sentinel */},
};
MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
static struct platform_driver mtk_efuse_driver = {
.probe = mtk_efuse_probe,
.remove = mtk_efuse_remove,
.driver = {
.name = "mediatek,efuse",
.of_match_table = mtk_efuse_of_match,
},
};
static int __init mtk_efuse_init(void)
{
int ret;
ret = platform_driver_register(&mtk_efuse_driver);
if (ret) {
pr_err("Failed to register efuse driver\n");
return ret;
}
return 0;
}
subsys_initcall(mtk_efuse_init);
可以看到mtk-efuse.c 是按照provide的模板写的,其最重要在其probe函数中调用nvmem_register
struct nvmem_device *nvmem_register(const struct nvmem_config *config)
{
struct nvmem_device *nvmem;
struct device_node *np;
int rval;
if (!config->dev)
return ERR_PTR(-EINVAL);
nvmem = kzalloc(sizeof(*nvmem), GFP_KERNEL);
if (!nvmem)
return ERR_PTR(-ENOMEM);
rval = ida_simple_get(&nvmem_ida, 0, 0, GFP_KERNEL);
if (rval < 0) {
kfree(nvmem);
return ERR_PTR(rval);
}
nvmem->id = rval;
nvmem->owner = config->owner;
nvmem->stride = config->stride;
nvmem->word_size = config->word_size;
nvmem->size = config->size;
nvmem->dev.type = &nvmem_provider_type;
nvmem->dev.bus = &nvmem_bus_type;
nvmem->dev.parent = config->dev;
nvmem->priv = config->priv;
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
np = config->dev->of_node;
nvmem->dev.of_node = np;
dev_set_name(&nvmem->dev, "%s%d",
config->name ? : "nvmem", config->id);
nvmem->read_only = of_property_read_bool(np, "read-only") |
config->read_only;
if (config->root_only)
nvmem->dev.groups = nvmem->read_only ?
nvmem_ro_root_dev_groups :
nvmem_rw_root_dev_groups;
else
nvmem->dev.groups = nvmem->read_only ?
nvmem_ro_dev_groups :
nvmem_rw_dev_groups;
device_initialize(&nvmem->dev);
dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
rval = device_add(&nvmem->dev);
if (rval)
goto out;
if (config->compat) {
rval = nvmem_setup_compat(nvmem, config);
if (rval)
goto out;
}
if (config->cells)
nvmem_add_cells(nvmem, config);
return nvmem;
out:
ida_simple_remove(&nvmem_ida, nvmem->id);
kfree(nvmem);
return ERR_PTR(rval);
}
在nvmem_register 中有申请一个nvmem_device *nvmem; 最终要的就是
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
这样当consumer调用nvmem_device_write 来写/读 provide的时候
int nvmem_device_write(struct nvmem_device *nvmem,
unsigned int offset,
size_t bytes, void *buf)
{
int rc;
if (!nvmem)
return -EINVAL;
rc = nvmem_reg_write(nvmem, offset, buf, bytes);
if (rc)
return rc;
return bytes;
}
static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
void *val, size_t bytes)
{
if (nvmem->reg_write)
return nvmem->reg_write(nvmem->priv, offset, val, bytes);
return -EINVAL;
}
可见在nvmem_reg_write 中最终调用nvmem->reg_write。而nvmem->reg_write 是在nvmem_register 中赋值的
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
对应本例就是
econfig->reg_read = mtk_reg_read;
econfig->reg_write = mtk_reg_write;
对应user space就可以直接读写/sys/bus/nvmem/devices/*/nvmem
Userspace can read/write the raw NVMEM file located at
/sys/bus/nvmem/devices/*/nvmem
ex:
hexdump /sys/bus/nvmem/devices/qfprom0/nvmem
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c00
0000000 0000 0000 0000 0000 0000 0000 0000 0000
...
*
0001000
其中provide 最终要的就是调用nvmem_register,其一般的模板如下:
static struct nvmem_config econfig = {
.name = "qfprom",
.owner = THIS_MODULE,
};
static int qfprom_probe(struct platform_device *pdev)
{
...
econfig.dev = &pdev->dev;
nvmem = nvmem_register(&econfig);
...
}
而consumer 可以用下面这些API 使用provide提供的设备例如eeprom, efuses等
struct nvmem_device *nvmem_device_get(struct device *dev, const char *name);
struct nvmem_device *devm_nvmem_device_get(struct device *dev,
const char *name);
void nvmem_device_put(struct nvmem_device *nvmem);
int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset,
size_t bytes, void *buf);
int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset,
size_t bytes, void *buf);
int nvmem_device_cell_read(struct nvmem_device *nvmem,
struct nvmem_cell_info *info, void *buf);
int nvmem_device_cell_write(struct nvmem_device *nvmem,
struct nvmem_cell_info *info, void *buf);
obj-$(CONFIG_NVMEM) += nvmem_core.o
nvmem_core-y := core.o
# Devices
obj-$(CONFIG_NVMEM_IMX_OCOTP) += nvmem-imx-ocotp.o
nvmem-imx-ocotp-y := imx-ocotp.o
obj-$(CONFIG_NVMEM_LPC18XX_EEPROM) += nvmem_lpc18xx_eeprom.o
nvmem_lpc18xx_eeprom-y := lpc18xx_eeprom.o
obj-$(CONFIG_NVMEM_MXS_OCOTP) += nvmem-mxs-ocotp.o
nvmem-mxs-ocotp-y := mxs-ocotp.o
obj-$(CONFIG_MTK_EFUSE) += nvmem_mtk-efuse.o
nvmem_mtk-efuse-y := mtk-efuse.o
obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o
nvmem_qfprom-y := qfprom.o
obj-$(CONFIG_ROCKCHIP_EFUSE) += nvmem_rockchip_efuse.o
nvmem_rockchip_efuse-y := rockchip-efuse.o
obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o
nvmem_sunxi_sid-y := sunxi_sid.o
obj-$(CONFIG_NVMEM_VF610_OCOTP) += nvmem-vf610-ocotp.o
nvmem-vf610-ocotp-y := vf610-ocotp.o
obj-$(CONFIG_MESON_EFUSE) += nvmem_meson_efuse.o
nvmem_meson_efuse-y := meson-efuse.o
从drivers/nvmem里面的makefile中可以看到核心文件就一个core.c 这个文件通过provide和consumer 使用的API。这里以mtk-efuse.c为例
static int mtk_reg_read(void *context,
unsigned int reg, void *_val, size_t bytes)
{
void __iomem *base = context;
u32 *val = _val;
int i = 0, words = bytes / 4;
while (words--)
*val++ = readl(base + reg + (i++ * 4));
return 0;
}
static int mtk_reg_write(void *context,
unsigned int reg, void *_val, size_t bytes)
{
void __iomem *base = context;
u32 *val = _val;
int i = 0, words = bytes / 4;
while (words--)
writel(*val++, base + reg + (i++ * 4));
return 0;
}
static int mtk_efuse_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
struct nvmem_device *nvmem;
struct nvmem_config *econfig;
void __iomem *base;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(dev, res);
if (IS_ERR(base))
return PTR_ERR(base);
// 申请一个nvmem_config结构
econfig = devm_kzalloc(dev, sizeof(*econfig), GFP_KERNEL);
if (!econfig)
return -ENOMEM;
//给nvmem_config *econfig 赋值
econfig->stride = 4;
econfig->word_size = 4;
econfig->reg_read = mtk_reg_read;
econfig->reg_write = mtk_reg_write;
econfig->size = resource_size(res);
econfig->priv = base;
econfig->dev = dev;
econfig->owner = THIS_MODULE;
//调用提供provide的注册函数
nvmem = nvmem_register(econfig);
if (IS_ERR(nvmem))
return PTR_ERR(nvmem);
platform_set_drvdata(pdev, nvmem);
return 0;
}
static int mtk_efuse_remove(struct platform_device *pdev)
{
struct nvmem_device *nvmem = platform_get_drvdata(pdev);
return nvmem_unregister(nvmem);
}
static const struct of_device_id mtk_efuse_of_match[] = {
{ .compatible = "mediatek,mt8173-efuse",},
{ .compatible = "mediatek,efuse",},
{/* sentinel */},
};
MODULE_DEVICE_TABLE(of, mtk_efuse_of_match);
static struct platform_driver mtk_efuse_driver = {
.probe = mtk_efuse_probe,
.remove = mtk_efuse_remove,
.driver = {
.name = "mediatek,efuse",
.of_match_table = mtk_efuse_of_match,
},
};
static int __init mtk_efuse_init(void)
{
int ret;
ret = platform_driver_register(&mtk_efuse_driver);
if (ret) {
pr_err("Failed to register efuse driver\n");
return ret;
}
return 0;
}
subsys_initcall(mtk_efuse_init);
可以看到mtk-efuse.c 是按照provide的模板写的,其最重要在其probe函数中调用nvmem_register
struct nvmem_device *nvmem_register(const struct nvmem_config *config)
{
struct nvmem_device *nvmem;
struct device_node *np;
int rval;
if (!config->dev)
return ERR_PTR(-EINVAL);
nvmem = kzalloc(sizeof(*nvmem), GFP_KERNEL);
if (!nvmem)
return ERR_PTR(-ENOMEM);
rval = ida_simple_get(&nvmem_ida, 0, 0, GFP_KERNEL);
if (rval < 0) {
kfree(nvmem);
return ERR_PTR(rval);
}
nvmem->id = rval;
nvmem->owner = config->owner;
nvmem->stride = config->stride;
nvmem->word_size = config->word_size;
nvmem->size = config->size;
nvmem->dev.type = &nvmem_provider_type;
nvmem->dev.bus = &nvmem_bus_type;
nvmem->dev.parent = config->dev;
nvmem->priv = config->priv;
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
np = config->dev->of_node;
nvmem->dev.of_node = np;
dev_set_name(&nvmem->dev, "%s%d",
config->name ? : "nvmem", config->id);
nvmem->read_only = of_property_read_bool(np, "read-only") |
config->read_only;
if (config->root_only)
nvmem->dev.groups = nvmem->read_only ?
nvmem_ro_root_dev_groups :
nvmem_rw_root_dev_groups;
else
nvmem->dev.groups = nvmem->read_only ?
nvmem_ro_dev_groups :
nvmem_rw_dev_groups;
device_initialize(&nvmem->dev);
dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
rval = device_add(&nvmem->dev);
if (rval)
goto out;
if (config->compat) {
rval = nvmem_setup_compat(nvmem, config);
if (rval)
goto out;
}
if (config->cells)
nvmem_add_cells(nvmem, config);
return nvmem;
out:
ida_simple_remove(&nvmem_ida, nvmem->id);
kfree(nvmem);
return ERR_PTR(rval);
}
在nvmem_register 中有申请一个nvmem_device *nvmem; 最终要的就是
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
这样当consumer调用nvmem_device_write 来写/读 provide的时候
int nvmem_device_write(struct nvmem_device *nvmem,
unsigned int offset,
size_t bytes, void *buf)
{
int rc;
if (!nvmem)
return -EINVAL;
rc = nvmem_reg_write(nvmem, offset, buf, bytes);
if (rc)
return rc;
return bytes;
}
static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
void *val, size_t bytes)
{
if (nvmem->reg_write)
return nvmem->reg_write(nvmem->priv, offset, val, bytes);
return -EINVAL;
}
可见在nvmem_reg_write 中最终调用nvmem->reg_write。而nvmem->reg_write 是在nvmem_register 中赋值的
nvmem->reg_read = config->reg_read;
nvmem->reg_write = config->reg_write;
对应本例就是
econfig->reg_read = mtk_reg_read;
econfig->reg_write = mtk_reg_write;
对应user space就可以直接读写/sys/bus/nvmem/devices/*/nvmem
Userspace can read/write the raw NVMEM file located at
/sys/bus/nvmem/devices/*/nvmem
ex:
hexdump /sys/bus/nvmem/devices/qfprom0/nvmem
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c00
0000000 0000 0000 0000 0000 0000 0000 0000 0000
...
*
0001000