linux/drivers/nvmem/nvmem-sysfs.c
Srinivas Kandagatla ae0c2d7255 nvmem: core: add NVMEM_SYSFS Kconfig
Many nvmem providers are not very keen on having default sysfs
nvmem entry, as most of the usecases for them are inside kernel
itself. And in some cases read/writes to some areas in nvmem are
restricted and trapped at secure monitor level, so accessing them
from userspace would result in board reboots.

This patch adds new NVMEM_SYSFS Kconfig to make binary sysfs entry
an optional one. This provision will give more flexibility to users.
This patch also moves existing sysfs code to a new file so that its
not compiled in when its not really required.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Gaurav Kohli <gkohli@codeaurora.org>
Tested-by: Gaurav Kohli <gkohli@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-04-25 19:44:33 +02:00

257 lines
5.4 KiB
C

// 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)
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;
}
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);
}