// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019, Linaro Limited */ #include "nvmem.h" static const char * const nvmem_type_str[] = { [NVMEM_TYPE_UNKNOWN] = "Unknown", [NVMEM_TYPE_EEPROM] = "EEPROM", [NVMEM_TYPE_OTP] = "OTP", [NVMEM_TYPE_BATTERY_BACKED] = "Battery backed", }; #ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key eeprom_lock_key; #endif static ssize_t type_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nvmem_device *nvmem = to_nvmem_device(dev); return sprintf(buf, "%s\n", nvmem_type_str[nvmem->type]); } static DEVICE_ATTR_RO(type); static struct attribute *nvmem_attrs[] = { &dev_attr_type.attr, NULL, }; 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; 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; if (count < nvmem->word_size) return -EINVAL; if (pos + count > nvmem->size) count = nvmem->size - pos; count = round_down(count, nvmem->word_size); rc = nvmem->reg_read(nvmem->priv, pos, buf, count); if (rc) return rc; return count; } 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; 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 -EFBIG; if (count < nvmem->word_size) return -EINVAL; if (pos + count > nvmem->size) count = nvmem->size - pos; count = round_down(count, nvmem->word_size); rc = nvmem->reg_write(nvmem->priv, pos, buf, count); if (rc) return rc; return count; } /* default read/write permissions */ static struct bin_attribute bin_attr_rw_nvmem = { .attr = { .name = "nvmem", .mode = 0644, }, .read = bin_attr_nvmem_read, .write = bin_attr_nvmem_write, }; static struct bin_attribute *nvmem_bin_rw_attributes[] = { &bin_attr_rw_nvmem, NULL, }; static const struct attribute_group nvmem_bin_rw_group = { .bin_attrs = nvmem_bin_rw_attributes, .attrs = nvmem_attrs, }; static const struct attribute_group *nvmem_rw_dev_groups[] = { &nvmem_bin_rw_group, NULL, }; /* read only permission */ static struct bin_attribute bin_attr_ro_nvmem = { .attr = { .name = "nvmem", .mode = 0444, }, .read = bin_attr_nvmem_read, }; static struct bin_attribute *nvmem_bin_ro_attributes[] = { &bin_attr_ro_nvmem, NULL, }; static const struct attribute_group nvmem_bin_ro_group = { .bin_attrs = nvmem_bin_ro_attributes, .attrs = nvmem_attrs, }; static const struct attribute_group *nvmem_ro_dev_groups[] = { &nvmem_bin_ro_group, NULL, }; /* default read/write permissions, root only */ static struct bin_attribute bin_attr_rw_root_nvmem = { .attr = { .name = "nvmem", .mode = 0600, }, .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, .attrs = nvmem_attrs, }; 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 = 0400, }, .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, .attrs = nvmem_attrs, }; static const struct attribute_group *nvmem_ro_root_dev_groups[] = { &nvmem_bin_ro_root_group, NULL, }; const struct attribute_group **nvmem_sysfs_get_groups( struct nvmem_device *nvmem, const struct nvmem_config *config) { if (config->root_only) return nvmem->read_only ? nvmem_ro_root_dev_groups : nvmem_rw_root_dev_groups; return nvmem->read_only ? nvmem_ro_dev_groups : nvmem_rw_dev_groups; } /* * nvmem_setup_compat() - Create an additional binary entry in * drivers sys directory, to be backwards compatible with the older * drivers/misc/eeprom drivers. */ int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem, const struct nvmem_config *config) { int rval; if (!config->compat) return 0; if (!config->base_dev) return -EINVAL; if (nvmem->read_only) { if (config->root_only) nvmem->eeprom = bin_attr_ro_root_nvmem; else nvmem->eeprom = bin_attr_ro_nvmem; } else { if (config->root_only) nvmem->eeprom = bin_attr_rw_root_nvmem; else nvmem->eeprom = bin_attr_rw_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; } void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem, const struct nvmem_config *config) { if (config->compat) device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom); }