android_kernel_motorola_sm6225/drivers/nvmem/nvmem-sysfs.c
Srinivas Kandagatla 434446e794 BACKPORT: 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.

Bug: 154188491
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>
(cherry picked from commit ae0c2d725512f32a0d1a25f0cf2f07616d33a72e)
Signed-off-by: Will McVicker <willmcvicker@google.com>
[willmcvicker: resolved trivial merge conflicts]
Change-Id: I648edb8d43fc7c2437c415b999c41657a6d11554
2020-04-16 12:19:27 -07:00

237 lines
5 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019, Linaro Limited
*/
#include "nvmem.h"
#ifdef CONFIG_DEBUG_LOCK_ALLOC
static struct lock_class_key eeprom_lock_key;
#endif
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,
};
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,
};
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,
};
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,
};
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);
}