blob: 5628820b9595aefe52d47a31ee56a793de9208d2 [file] [log] [blame]
// 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 = S_IWUSR | S_IRUGO,
},
.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 = S_IRUGO,
},
.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 = 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,
};
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);
}