forked from mirrors/linux
		
	The compatibility "eeprom" attribute is currently root-only no
matter what the configuration says. The "nvmem" attribute does
respect the setting of the root_only configuration bit, so do the
same for "eeprom".
Signed-off-by: Jean Delvare <jdelvare@suse.de>
Fixes: b6c217ab9b ("nvmem: Add backwards compatibility support for older EEPROM drivers.")
Reviewed-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Link: https://lore.kernel.org/r/20190728184255.563332e6@endymion
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
		
	
			
		
			
				
	
	
		
			263 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			263 lines
		
	
	
	
		
			5.6 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) {
 | 
						|
		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);
 | 
						|
}
 |