diff options
Diffstat (limited to 'drivers/nvmem')
-rw-r--r-- | drivers/nvmem/Kconfig | 4 | ||||
-rw-r--r-- | drivers/nvmem/core.c | 141 | ||||
-rw-r--r-- | drivers/nvmem/imx-ocotp.c | 2 | ||||
-rw-r--r-- | drivers/nvmem/mtk-efuse.c | 23 |
4 files changed, 158 insertions, 12 deletions
diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig index 5bd18cc1a69c..ca52952d850f 100644 --- a/drivers/nvmem/Kconfig +++ b/drivers/nvmem/Kconfig @@ -37,6 +37,7 @@ config NVMEM_LPC18XX_EEPROM config NVMEM_MXS_OCOTP tristate "Freescale MXS On-Chip OTP Memory Support" depends on ARCH_MXS || COMPILE_TEST + depends on HAS_IOMEM help If you say Y here, you will get readonly access to the One Time Programmable memory pages that are stored @@ -59,6 +60,7 @@ config MTK_EFUSE config QCOM_QFPROM tristate "QCOM QFPROM Support" depends on ARCH_QCOM || COMPILE_TEST + depends on HAS_IOMEM select REGMAP_MMIO help Say y here to enable QFPROM support. The QFPROM provides access @@ -70,6 +72,7 @@ config QCOM_QFPROM config ROCKCHIP_EFUSE tristate "Rockchip eFuse Support" depends on ARCH_ROCKCHIP || COMPILE_TEST + depends on HAS_IOMEM help This is a simple drive to dump specified values of Rockchip SoC from eFuse, such as cpu-leakage. @@ -91,6 +94,7 @@ config NVMEM_SUNXI_SID config NVMEM_VF610_OCOTP tristate "VF610 SoC OCOTP support" depends on SOC_VF610 || COMPILE_TEST + depends on HAS_IOMEM help This is a driver for the 'OCOTP' peripheral available on Vybrid devices like VF5xx and VF6xx. diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index de14fae6f7f6..0de3d878c439 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -38,8 +38,13 @@ struct nvmem_device { int users; size_t size; bool read_only; + int flags; + struct bin_attribute eeprom; + struct device *base_dev; }; +#define FLAG_COMPAT BIT(0) + struct nvmem_cell { const char *name; int offset; @@ -56,16 +61,26 @@ static DEFINE_IDA(nvmem_ida); static LIST_HEAD(nvmem_cells); static DEFINE_MUTEX(nvmem_cells_mutex); +#ifdef CONFIG_DEBUG_LOCK_ALLOC +static struct lock_class_key eeprom_lock_key; +#endif + #define to_nvmem_device(d) container_of(d, struct nvmem_device, dev) static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { - struct device *dev = container_of(kobj, struct device, kobj); - struct nvmem_device *nvmem = to_nvmem_device(dev); + struct device *dev; + struct nvmem_device *nvmem; int rc; + if (attr->private) + dev = attr->private; + else + dev = container_of(kobj, struct device, kobj); + nvmem = to_nvmem_device(dev); + /* Stop the user from reading */ if (pos >= nvmem->size) return 0; @@ -90,10 +105,16 @@ static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t pos, size_t count) { - struct device *dev = container_of(kobj, struct device, kobj); - struct nvmem_device *nvmem = to_nvmem_device(dev); + struct device *dev; + struct nvmem_device *nvmem; int rc; + if (attr->private) + dev = attr->private; + else + dev = container_of(kobj, struct device, kobj); + nvmem = to_nvmem_device(dev); + /* Stop the user from writing */ if (pos >= nvmem->size) return 0; @@ -161,6 +182,53 @@ static const struct attribute_group *nvmem_ro_dev_groups[] = { NULL, }; +/* default read/write permissions, root only */ +static struct bin_attribute bin_attr_rw_root_nvmem = { + .attr = { + .name = "nvmem", + .mode = S_IWUSR | S_IRUSR, + }, + .read = bin_attr_nvmem_read, + .write = bin_attr_nvmem_write, +}; + +static struct bin_attribute *nvmem_bin_rw_root_attributes[] = { + &bin_attr_rw_root_nvmem, + NULL, +}; + +static const struct attribute_group nvmem_bin_rw_root_group = { + .bin_attrs = nvmem_bin_rw_root_attributes, +}; + +static const struct attribute_group *nvmem_rw_root_dev_groups[] = { + &nvmem_bin_rw_root_group, + NULL, +}; + +/* read only permission, root only */ +static struct bin_attribute bin_attr_ro_root_nvmem = { + .attr = { + .name = "nvmem", + .mode = S_IRUSR, + }, + .read = bin_attr_nvmem_read, +}; + +static struct bin_attribute *nvmem_bin_ro_root_attributes[] = { + &bin_attr_ro_root_nvmem, + NULL, +}; + +static const struct attribute_group nvmem_bin_ro_root_group = { + .bin_attrs = nvmem_bin_ro_root_attributes, +}; + +static const struct attribute_group *nvmem_ro_root_dev_groups[] = { + &nvmem_bin_ro_root_group, + NULL, +}; + static void nvmem_release(struct device *dev) { struct nvmem_device *nvmem = to_nvmem_device(dev); @@ -302,6 +370,43 @@ err: return rval; } +/* + * nvmem_setup_compat() - Create an additional binary entry in + * drivers sys directory, to be backwards compatible with the older + * drivers/misc/eeprom drivers. + */ +static int nvmem_setup_compat(struct nvmem_device *nvmem, + const struct nvmem_config *config) +{ + int rval; + + if (!config->base_dev) + return -EINVAL; + + if (nvmem->read_only) + nvmem->eeprom = bin_attr_ro_root_nvmem; + else + nvmem->eeprom = bin_attr_rw_root_nvmem; + nvmem->eeprom.attr.name = "eeprom"; + nvmem->eeprom.size = nvmem->size; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + nvmem->eeprom.attr.key = &eeprom_lock_key; +#endif + nvmem->eeprom.private = &nvmem->dev; + nvmem->base_dev = config->base_dev; + + rval = device_create_bin_file(nvmem->base_dev, &nvmem->eeprom); + if (rval) { + dev_err(&nvmem->dev, + "Failed to create eeprom binary file %d\n", rval); + return rval; + } + + nvmem->flags |= FLAG_COMPAT; + + return 0; +} + /** * nvmem_register() - Register a nvmem device for given nvmem_config. * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem @@ -355,24 +460,37 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config) nvmem->read_only = of_property_read_bool(np, "read-only") | config->read_only; - nvmem->dev.groups = nvmem->read_only ? nvmem_ro_dev_groups : - nvmem_rw_dev_groups; + 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) { - ida_simple_remove(&nvmem_ida, nvmem->id); - kfree(nvmem); - return ERR_PTR(rval); + 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); } EXPORT_SYMBOL_GPL(nvmem_register); @@ -392,6 +510,9 @@ int nvmem_unregister(struct nvmem_device *nvmem) } mutex_unlock(&nvmem_mutex); + if (nvmem->flags & FLAG_COMPAT) + device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); + nvmem_device_remove_all_cells(nvmem); device_del(&nvmem->dev); diff --git a/drivers/nvmem/imx-ocotp.c b/drivers/nvmem/imx-ocotp.c index b7971d410b60..d7796eb5421f 100644 --- a/drivers/nvmem/imx-ocotp.c +++ b/drivers/nvmem/imx-ocotp.c @@ -51,7 +51,7 @@ static int imx_ocotp_read(void *context, const void *reg, size_t reg_size, val += 4; } - return (i - index) * 4; + return 0; } static int imx_ocotp_write(void *context, const void *data, size_t count) diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index 7b35f5b630cd..9c49369beea5 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c @@ -83,7 +83,28 @@ static struct platform_driver mtk_efuse_driver = { .of_match_table = mtk_efuse_of_match, }, }; -module_platform_driver(mtk_efuse_driver); + +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; +} + +static void __exit mtk_efuse_exit(void) +{ + return platform_driver_unregister(&mtk_efuse_driver); +} + +subsys_initcall(mtk_efuse_init); +module_exit(mtk_efuse_exit); + MODULE_AUTHOR("Andrew-CT Chen <andrew-ct.chen@mediatek.com>"); MODULE_DESCRIPTION("Mediatek EFUSE driver"); MODULE_LICENSE("GPL v2"); |