Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aa9b0667 authored by Swetha Chikkaboraiah's avatar Swetha Chikkaboraiah Committed by Gerrit - the friendly Code Review server
Browse files

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.

Change-Id: Id34e899845ea7320490dc6df5673ce3e77489982
Signed-off-by: default avatarSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Reviewed-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: default avatarGaurav Kohli <gkohli@codeaurora.org>
Tested-by: default avatarGaurav Kohli <gkohli@codeaurora.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Git-commit: ae0c2d725512f32a0d1a25f0cf2f07616d33a72e
Git-repo: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git


[schikk@codeaurora.org: Resolved merge conflicts and fixed
compilation issues]
Signed-off-by: default avatarSwetha Chikkaboraiah <schikk@codeaurora.org>
parent 9099432d
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -13,6 +13,16 @@ menuconfig NVMEM

if NVMEM

config NVMEM_SYSFS
	bool "/sys/bus/nvmem/devices/*/nvmem (sysfs interface)"
	depends on SYSFS
	default y
	help
	   Say Y here to add a sysfs interface for NVMEM.

	   This interface is mostly used by userspace applications to
	   read/write directly into nvmem.

config NVMEM_IMX_OCOTP
	tristate "i.MX6 On-Chip OTP Controller support"
	depends on SOC_IMX6 || COMPILE_TEST
+3 −0
Original line number Diff line number Diff line
@@ -5,6 +5,9 @@
obj-$(CONFIG_NVMEM)		+= nvmem_core.o
nvmem_core-y			:= core.o

obj-$(CONFIG_NVMEM_SYSFS)	+= nvmem_sysfs.o
nvmem_sysfs-y			:= nvmem-sysfs.o

# Devices
obj-$(CONFIG_NVMEM_IMX_OCOTP)	+= nvmem-imx-ocotp.o
nvmem-imx-ocotp-y		:= imx-ocotp.o
+3 −234
Original line number Diff line number Diff line
@@ -25,26 +25,7 @@
#include <linux/of.h>
#include <linux/slab.h>

struct nvmem_device {
	const char		*name;
	struct module		*owner;
	struct device		dev;
	int			stride;
	int			word_size;
	int			ncells;
	int			id;
	int			users;
	size_t			size;
	bool			read_only;
	int			flags;
	struct bin_attribute	eeprom;
	struct device		*base_dev;
	nvmem_reg_read_t	reg_read;
	nvmem_reg_write_t	reg_write;
	void *priv;
};

#define FLAG_COMPAT		BIT(0)
#include "nvmem.h"

struct nvmem_cell {
	const char		*name;
@@ -62,11 +43,6 @@ static DEFINE_IDA(nvmem_ida);
static LIST_HEAD(nvmem_cells);
static DEFINE_MUTEX(nvmem_cells_mutex);

#ifdef CONFIG_DEBUG_LOCK_ALLOC
static struct lock_class_key eeprom_lock_key;
#endif

#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
static int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
			  void *val, size_t bytes)
{
@@ -85,168 +61,6 @@ static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
	return -EINVAL;
}

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, 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 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_write(nvmem, 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,
};

static void nvmem_release(struct device *dev)
{
	struct nvmem_device *nvmem = to_nvmem_device(dev);
@@ -388,43 +202,6 @@ static int nvmem_add_cells(struct nvmem_device *nvmem,
	return rval;
}

/*
 * nvmem_setup_compat() - Create an additional binary entry in
 * drivers sys directory, to be backwards compatible with the older
 * drivers/misc/eeprom drivers.
 */
static int nvmem_setup_compat(struct nvmem_device *nvmem,
			      const struct nvmem_config *config)
{
	int rval;

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

/**
 * nvmem_register() - Register a nvmem device for given nvmem_config.
 * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
@@ -473,15 +250,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
	nvmem->read_only = of_property_read_bool(np, "read-only") |
			   config->read_only;

	if (config->root_only)
		nvmem->dev.groups = nvmem->read_only ?
			nvmem_ro_root_dev_groups :
			nvmem_rw_root_dev_groups;
	else
		nvmem->dev.groups = nvmem->read_only ?
			nvmem_ro_dev_groups :
			nvmem_rw_dev_groups;

	nvmem->dev.groups = nvmem_sysfs_get_groups(nvmem, config);
	device_initialize(&nvmem->dev);

	dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
@@ -491,7 +260,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
		goto err_put_device;

	if (config->compat) {
		rval = nvmem_setup_compat(nvmem, config);
		rval = nvmem_sysfs_setup_compat(nvmem, config);
		if (rval)
			goto err_device_del;
	}
+230 −0
Original line number Diff line number Diff line
// 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)
		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);
}

drivers/nvmem/nvmem.h

0 → 100644
+62 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef _DRIVERS_NVMEM_H
#define _DRIVERS_NVMEM_H

#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kref.h>
#include <linux/list.h>
#include <linux/nvmem-consumer.h>
#include <linux/nvmem-provider.h>

struct nvmem_device {
	const char              *name;
	struct module		*owner;
	struct device		dev;
	int			stride;
	int			word_size;
	int			ncells;
	int			id;
	int			users;
	size_t			size;
	bool			read_only;
	int			flags;
	struct bin_attribute	eeprom;
	struct device		*base_dev;
	nvmem_reg_read_t	reg_read;
	nvmem_reg_write_t	reg_write;
	void *priv;
};

#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
#define FLAG_COMPAT		BIT(0)

#ifdef CONFIG_NVMEM_SYSFS
const struct attribute_group **nvmem_sysfs_get_groups(
					struct nvmem_device *nvmem,
					const struct nvmem_config *config);
int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
			      const struct nvmem_config *config);
void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
			      const struct nvmem_config *config);
#else
static inline const struct attribute_group **nvmem_sysfs_get_groups(
					struct nvmem_device *nvmem,
					const struct nvmem_config *config)
{
	return NULL;
}

static inline int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
				      const struct nvmem_config *config)
{
	return -ENOSYS;
}
static inline void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
			      const struct nvmem_config *config)
{
}
#endif /* CONFIG_NVMEM_SYSFS */

#endif /* _DRIVERS_NVMEM_H */