162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * nvmem framework core.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
662306a36Sopenharmony_ci * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/export.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/idr.h>
1362306a36Sopenharmony_ci#include <linux/init.h>
1462306a36Sopenharmony_ci#include <linux/kref.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/nvmem-consumer.h>
1762306a36Sopenharmony_ci#include <linux/nvmem-provider.h>
1862306a36Sopenharmony_ci#include <linux/gpio/consumer.h>
1962306a36Sopenharmony_ci#include <linux/of.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct nvmem_device {
2362306a36Sopenharmony_ci	struct module		*owner;
2462306a36Sopenharmony_ci	struct device		dev;
2562306a36Sopenharmony_ci	int			stride;
2662306a36Sopenharmony_ci	int			word_size;
2762306a36Sopenharmony_ci	int			id;
2862306a36Sopenharmony_ci	struct kref		refcnt;
2962306a36Sopenharmony_ci	size_t			size;
3062306a36Sopenharmony_ci	bool			read_only;
3162306a36Sopenharmony_ci	bool			root_only;
3262306a36Sopenharmony_ci	int			flags;
3362306a36Sopenharmony_ci	enum nvmem_type		type;
3462306a36Sopenharmony_ci	struct bin_attribute	eeprom;
3562306a36Sopenharmony_ci	struct device		*base_dev;
3662306a36Sopenharmony_ci	struct list_head	cells;
3762306a36Sopenharmony_ci	const struct nvmem_keepout *keepout;
3862306a36Sopenharmony_ci	unsigned int		nkeepout;
3962306a36Sopenharmony_ci	nvmem_reg_read_t	reg_read;
4062306a36Sopenharmony_ci	nvmem_reg_write_t	reg_write;
4162306a36Sopenharmony_ci	struct gpio_desc	*wp_gpio;
4262306a36Sopenharmony_ci	struct nvmem_layout	*layout;
4362306a36Sopenharmony_ci	void *priv;
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#define FLAG_COMPAT		BIT(0)
4962306a36Sopenharmony_cistruct nvmem_cell_entry {
5062306a36Sopenharmony_ci	const char		*name;
5162306a36Sopenharmony_ci	int			offset;
5262306a36Sopenharmony_ci	size_t			raw_len;
5362306a36Sopenharmony_ci	int			bytes;
5462306a36Sopenharmony_ci	int			bit_offset;
5562306a36Sopenharmony_ci	int			nbits;
5662306a36Sopenharmony_ci	nvmem_cell_post_process_t read_post_process;
5762306a36Sopenharmony_ci	void			*priv;
5862306a36Sopenharmony_ci	struct device_node	*np;
5962306a36Sopenharmony_ci	struct nvmem_device	*nvmem;
6062306a36Sopenharmony_ci	struct list_head	node;
6162306a36Sopenharmony_ci};
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistruct nvmem_cell {
6462306a36Sopenharmony_ci	struct nvmem_cell_entry *entry;
6562306a36Sopenharmony_ci	const char		*id;
6662306a36Sopenharmony_ci	int			index;
6762306a36Sopenharmony_ci};
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic DEFINE_MUTEX(nvmem_mutex);
7062306a36Sopenharmony_cistatic DEFINE_IDA(nvmem_ida);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic DEFINE_MUTEX(nvmem_cell_mutex);
7362306a36Sopenharmony_cistatic LIST_HEAD(nvmem_cell_tables);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic DEFINE_MUTEX(nvmem_lookup_mutex);
7662306a36Sopenharmony_cistatic LIST_HEAD(nvmem_lookup_list);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nvmem_layout_lock);
8162306a36Sopenharmony_cistatic LIST_HEAD(nvmem_layouts);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int __nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
8462306a36Sopenharmony_ci			    void *val, size_t bytes)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	if (nvmem->reg_read)
8762306a36Sopenharmony_ci		return nvmem->reg_read(nvmem->priv, offset, val, bytes);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	return -EINVAL;
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic int __nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
9362306a36Sopenharmony_ci			     void *val, size_t bytes)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	int ret;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (nvmem->reg_write) {
9862306a36Sopenharmony_ci		gpiod_set_value_cansleep(nvmem->wp_gpio, 0);
9962306a36Sopenharmony_ci		ret = nvmem->reg_write(nvmem->priv, offset, val, bytes);
10062306a36Sopenharmony_ci		gpiod_set_value_cansleep(nvmem->wp_gpio, 1);
10162306a36Sopenharmony_ci		return ret;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return -EINVAL;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic int nvmem_access_with_keepouts(struct nvmem_device *nvmem,
10862306a36Sopenharmony_ci				      unsigned int offset, void *val,
10962306a36Sopenharmony_ci				      size_t bytes, int write)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	unsigned int end = offset + bytes;
11362306a36Sopenharmony_ci	unsigned int kend, ksize;
11462306a36Sopenharmony_ci	const struct nvmem_keepout *keepout = nvmem->keepout;
11562306a36Sopenharmony_ci	const struct nvmem_keepout *keepoutend = keepout + nvmem->nkeepout;
11662306a36Sopenharmony_ci	int rc;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/*
11962306a36Sopenharmony_ci	 * Skip all keepouts before the range being accessed.
12062306a36Sopenharmony_ci	 * Keepouts are sorted.
12162306a36Sopenharmony_ci	 */
12262306a36Sopenharmony_ci	while ((keepout < keepoutend) && (keepout->end <= offset))
12362306a36Sopenharmony_ci		keepout++;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	while ((offset < end) && (keepout < keepoutend)) {
12662306a36Sopenharmony_ci		/* Access the valid portion before the keepout. */
12762306a36Sopenharmony_ci		if (offset < keepout->start) {
12862306a36Sopenharmony_ci			kend = min(end, keepout->start);
12962306a36Sopenharmony_ci			ksize = kend - offset;
13062306a36Sopenharmony_ci			if (write)
13162306a36Sopenharmony_ci				rc = __nvmem_reg_write(nvmem, offset, val, ksize);
13262306a36Sopenharmony_ci			else
13362306a36Sopenharmony_ci				rc = __nvmem_reg_read(nvmem, offset, val, ksize);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci			if (rc)
13662306a36Sopenharmony_ci				return rc;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci			offset += ksize;
13962306a36Sopenharmony_ci			val += ksize;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		/*
14362306a36Sopenharmony_ci		 * Now we're aligned to the start of this keepout zone. Go
14462306a36Sopenharmony_ci		 * through it.
14562306a36Sopenharmony_ci		 */
14662306a36Sopenharmony_ci		kend = min(end, keepout->end);
14762306a36Sopenharmony_ci		ksize = kend - offset;
14862306a36Sopenharmony_ci		if (!write)
14962306a36Sopenharmony_ci			memset(val, keepout->value, ksize);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		val += ksize;
15262306a36Sopenharmony_ci		offset += ksize;
15362306a36Sopenharmony_ci		keepout++;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	/*
15762306a36Sopenharmony_ci	 * If we ran out of keepouts but there's still stuff to do, send it
15862306a36Sopenharmony_ci	 * down directly
15962306a36Sopenharmony_ci	 */
16062306a36Sopenharmony_ci	if (offset < end) {
16162306a36Sopenharmony_ci		ksize = end - offset;
16262306a36Sopenharmony_ci		if (write)
16362306a36Sopenharmony_ci			return __nvmem_reg_write(nvmem, offset, val, ksize);
16462306a36Sopenharmony_ci		else
16562306a36Sopenharmony_ci			return __nvmem_reg_read(nvmem, offset, val, ksize);
16662306a36Sopenharmony_ci	}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return 0;
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
17262306a36Sopenharmony_ci			  void *val, size_t bytes)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	if (!nvmem->nkeepout)
17562306a36Sopenharmony_ci		return __nvmem_reg_read(nvmem, offset, val, bytes);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return nvmem_access_with_keepouts(nvmem, offset, val, bytes, false);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
18162306a36Sopenharmony_ci			   void *val, size_t bytes)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	if (!nvmem->nkeepout)
18462306a36Sopenharmony_ci		return __nvmem_reg_write(nvmem, offset, val, bytes);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return nvmem_access_with_keepouts(nvmem, offset, val, bytes, true);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci#ifdef CONFIG_NVMEM_SYSFS
19062306a36Sopenharmony_cistatic const char * const nvmem_type_str[] = {
19162306a36Sopenharmony_ci	[NVMEM_TYPE_UNKNOWN] = "Unknown",
19262306a36Sopenharmony_ci	[NVMEM_TYPE_EEPROM] = "EEPROM",
19362306a36Sopenharmony_ci	[NVMEM_TYPE_OTP] = "OTP",
19462306a36Sopenharmony_ci	[NVMEM_TYPE_BATTERY_BACKED] = "Battery backed",
19562306a36Sopenharmony_ci	[NVMEM_TYPE_FRAM] = "FRAM",
19662306a36Sopenharmony_ci};
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_LOCK_ALLOC
19962306a36Sopenharmony_cistatic struct lock_class_key eeprom_lock_key;
20062306a36Sopenharmony_ci#endif
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic ssize_t type_show(struct device *dev,
20362306a36Sopenharmony_ci			 struct device_attribute *attr, char *buf)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct nvmem_device *nvmem = to_nvmem_device(dev);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return sprintf(buf, "%s\n", nvmem_type_str[nvmem->type]);
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(type);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic struct attribute *nvmem_attrs[] = {
21362306a36Sopenharmony_ci	&dev_attr_type.attr,
21462306a36Sopenharmony_ci	NULL,
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
21862306a36Sopenharmony_ci				   struct bin_attribute *attr, char *buf,
21962306a36Sopenharmony_ci				   loff_t pos, size_t count)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct device *dev;
22262306a36Sopenharmony_ci	struct nvmem_device *nvmem;
22362306a36Sopenharmony_ci	int rc;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (attr->private)
22662306a36Sopenharmony_ci		dev = attr->private;
22762306a36Sopenharmony_ci	else
22862306a36Sopenharmony_ci		dev = kobj_to_dev(kobj);
22962306a36Sopenharmony_ci	nvmem = to_nvmem_device(dev);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* Stop the user from reading */
23262306a36Sopenharmony_ci	if (pos >= nvmem->size)
23362306a36Sopenharmony_ci		return 0;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (!IS_ALIGNED(pos, nvmem->stride))
23662306a36Sopenharmony_ci		return -EINVAL;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	if (count < nvmem->word_size)
23962306a36Sopenharmony_ci		return -EINVAL;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (pos + count > nvmem->size)
24262306a36Sopenharmony_ci		count = nvmem->size - pos;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	count = round_down(count, nvmem->word_size);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (!nvmem->reg_read)
24762306a36Sopenharmony_ci		return -EPERM;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	rc = nvmem_reg_read(nvmem, pos, buf, count);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (rc)
25262306a36Sopenharmony_ci		return rc;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return count;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj,
25862306a36Sopenharmony_ci				    struct bin_attribute *attr, char *buf,
25962306a36Sopenharmony_ci				    loff_t pos, size_t count)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct device *dev;
26262306a36Sopenharmony_ci	struct nvmem_device *nvmem;
26362306a36Sopenharmony_ci	int rc;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (attr->private)
26662306a36Sopenharmony_ci		dev = attr->private;
26762306a36Sopenharmony_ci	else
26862306a36Sopenharmony_ci		dev = kobj_to_dev(kobj);
26962306a36Sopenharmony_ci	nvmem = to_nvmem_device(dev);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/* Stop the user from writing */
27262306a36Sopenharmony_ci	if (pos >= nvmem->size)
27362306a36Sopenharmony_ci		return -EFBIG;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (!IS_ALIGNED(pos, nvmem->stride))
27662306a36Sopenharmony_ci		return -EINVAL;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	if (count < nvmem->word_size)
27962306a36Sopenharmony_ci		return -EINVAL;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	if (pos + count > nvmem->size)
28262306a36Sopenharmony_ci		count = nvmem->size - pos;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	count = round_down(count, nvmem->word_size);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!nvmem->reg_write)
28762306a36Sopenharmony_ci		return -EPERM;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	rc = nvmem_reg_write(nvmem, pos, buf, count);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (rc)
29262306a36Sopenharmony_ci		return rc;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return count;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	umode_t mode = 0400;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (!nvmem->root_only)
30262306a36Sopenharmony_ci		mode |= 0044;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	if (!nvmem->read_only)
30562306a36Sopenharmony_ci		mode |= 0200;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (!nvmem->reg_write)
30862306a36Sopenharmony_ci		mode &= ~0200;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	if (!nvmem->reg_read)
31162306a36Sopenharmony_ci		mode &= ~0444;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	return mode;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic umode_t nvmem_bin_attr_is_visible(struct kobject *kobj,
31762306a36Sopenharmony_ci					 struct bin_attribute *attr, int i)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
32062306a36Sopenharmony_ci	struct nvmem_device *nvmem = to_nvmem_device(dev);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	attr->size = nvmem->size;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	return nvmem_bin_attr_get_umode(nvmem);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci/* default read/write permissions */
32862306a36Sopenharmony_cistatic struct bin_attribute bin_attr_rw_nvmem = {
32962306a36Sopenharmony_ci	.attr	= {
33062306a36Sopenharmony_ci		.name	= "nvmem",
33162306a36Sopenharmony_ci		.mode	= 0644,
33262306a36Sopenharmony_ci	},
33362306a36Sopenharmony_ci	.read	= bin_attr_nvmem_read,
33462306a36Sopenharmony_ci	.write	= bin_attr_nvmem_write,
33562306a36Sopenharmony_ci};
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic struct bin_attribute *nvmem_bin_attributes[] = {
33862306a36Sopenharmony_ci	&bin_attr_rw_nvmem,
33962306a36Sopenharmony_ci	NULL,
34062306a36Sopenharmony_ci};
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic const struct attribute_group nvmem_bin_group = {
34362306a36Sopenharmony_ci	.bin_attrs	= nvmem_bin_attributes,
34462306a36Sopenharmony_ci	.attrs		= nvmem_attrs,
34562306a36Sopenharmony_ci	.is_bin_visible = nvmem_bin_attr_is_visible,
34662306a36Sopenharmony_ci};
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic const struct attribute_group *nvmem_dev_groups[] = {
34962306a36Sopenharmony_ci	&nvmem_bin_group,
35062306a36Sopenharmony_ci	NULL,
35162306a36Sopenharmony_ci};
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_cistatic struct bin_attribute bin_attr_nvmem_eeprom_compat = {
35462306a36Sopenharmony_ci	.attr	= {
35562306a36Sopenharmony_ci		.name	= "eeprom",
35662306a36Sopenharmony_ci	},
35762306a36Sopenharmony_ci	.read	= bin_attr_nvmem_read,
35862306a36Sopenharmony_ci	.write	= bin_attr_nvmem_write,
35962306a36Sopenharmony_ci};
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci/*
36262306a36Sopenharmony_ci * nvmem_setup_compat() - Create an additional binary entry in
36362306a36Sopenharmony_ci * drivers sys directory, to be backwards compatible with the older
36462306a36Sopenharmony_ci * drivers/misc/eeprom drivers.
36562306a36Sopenharmony_ci */
36662306a36Sopenharmony_cistatic int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
36762306a36Sopenharmony_ci				    const struct nvmem_config *config)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	int rval;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (!config->compat)
37262306a36Sopenharmony_ci		return 0;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	if (!config->base_dev)
37562306a36Sopenharmony_ci		return -EINVAL;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (config->type == NVMEM_TYPE_FRAM)
37862306a36Sopenharmony_ci		bin_attr_nvmem_eeprom_compat.attr.name = "fram";
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	nvmem->eeprom = bin_attr_nvmem_eeprom_compat;
38162306a36Sopenharmony_ci	nvmem->eeprom.attr.mode = nvmem_bin_attr_get_umode(nvmem);
38262306a36Sopenharmony_ci	nvmem->eeprom.size = nvmem->size;
38362306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_LOCK_ALLOC
38462306a36Sopenharmony_ci	nvmem->eeprom.attr.key = &eeprom_lock_key;
38562306a36Sopenharmony_ci#endif
38662306a36Sopenharmony_ci	nvmem->eeprom.private = &nvmem->dev;
38762306a36Sopenharmony_ci	nvmem->base_dev = config->base_dev;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	rval = device_create_bin_file(nvmem->base_dev, &nvmem->eeprom);
39062306a36Sopenharmony_ci	if (rval) {
39162306a36Sopenharmony_ci		dev_err(&nvmem->dev,
39262306a36Sopenharmony_ci			"Failed to create eeprom binary file %d\n", rval);
39362306a36Sopenharmony_ci		return rval;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	nvmem->flags |= FLAG_COMPAT;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return 0;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
40262306a36Sopenharmony_ci			      const struct nvmem_config *config)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	if (config->compat)
40562306a36Sopenharmony_ci		device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci#else /* CONFIG_NVMEM_SYSFS */
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
41162306a36Sopenharmony_ci				    const struct nvmem_config *config)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	return -ENOSYS;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_cistatic void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
41662306a36Sopenharmony_ci				      const struct nvmem_config *config)
41762306a36Sopenharmony_ci{
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci#endif /* CONFIG_NVMEM_SYSFS */
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic void nvmem_release(struct device *dev)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	struct nvmem_device *nvmem = to_nvmem_device(dev);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	ida_free(&nvmem_ida, nvmem->id);
42762306a36Sopenharmony_ci	gpiod_put(nvmem->wp_gpio);
42862306a36Sopenharmony_ci	kfree(nvmem);
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic const struct device_type nvmem_provider_type = {
43262306a36Sopenharmony_ci	.release	= nvmem_release,
43362306a36Sopenharmony_ci};
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic struct bus_type nvmem_bus_type = {
43662306a36Sopenharmony_ci	.name		= "nvmem",
43762306a36Sopenharmony_ci};
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic void nvmem_cell_entry_drop(struct nvmem_cell_entry *cell)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell);
44262306a36Sopenharmony_ci	mutex_lock(&nvmem_mutex);
44362306a36Sopenharmony_ci	list_del(&cell->node);
44462306a36Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
44562306a36Sopenharmony_ci	of_node_put(cell->np);
44662306a36Sopenharmony_ci	kfree_const(cell->name);
44762306a36Sopenharmony_ci	kfree(cell);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct nvmem_cell_entry *cell, *p;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	list_for_each_entry_safe(cell, p, &nvmem->cells, node)
45562306a36Sopenharmony_ci		nvmem_cell_entry_drop(cell);
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic void nvmem_cell_entry_add(struct nvmem_cell_entry *cell)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	mutex_lock(&nvmem_mutex);
46162306a36Sopenharmony_ci	list_add_tail(&cell->node, &cell->nvmem->cells);
46262306a36Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
46362306a36Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell);
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int nvmem_cell_info_to_nvmem_cell_entry_nodup(struct nvmem_device *nvmem,
46762306a36Sopenharmony_ci						     const struct nvmem_cell_info *info,
46862306a36Sopenharmony_ci						     struct nvmem_cell_entry *cell)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	cell->nvmem = nvmem;
47162306a36Sopenharmony_ci	cell->offset = info->offset;
47262306a36Sopenharmony_ci	cell->raw_len = info->raw_len ?: info->bytes;
47362306a36Sopenharmony_ci	cell->bytes = info->bytes;
47462306a36Sopenharmony_ci	cell->name = info->name;
47562306a36Sopenharmony_ci	cell->read_post_process = info->read_post_process;
47662306a36Sopenharmony_ci	cell->priv = info->priv;
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	cell->bit_offset = info->bit_offset;
47962306a36Sopenharmony_ci	cell->nbits = info->nbits;
48062306a36Sopenharmony_ci	cell->np = info->np;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (cell->nbits)
48362306a36Sopenharmony_ci		cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset,
48462306a36Sopenharmony_ci					   BITS_PER_BYTE);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
48762306a36Sopenharmony_ci		dev_err(&nvmem->dev,
48862306a36Sopenharmony_ci			"cell %s unaligned to nvmem stride %d\n",
48962306a36Sopenharmony_ci			cell->name ?: "<unknown>", nvmem->stride);
49062306a36Sopenharmony_ci		return -EINVAL;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	return 0;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_cistatic int nvmem_cell_info_to_nvmem_cell_entry(struct nvmem_device *nvmem,
49762306a36Sopenharmony_ci					       const struct nvmem_cell_info *info,
49862306a36Sopenharmony_ci					       struct nvmem_cell_entry *cell)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	int err;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	err = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, cell);
50362306a36Sopenharmony_ci	if (err)
50462306a36Sopenharmony_ci		return err;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	cell->name = kstrdup_const(info->name, GFP_KERNEL);
50762306a36Sopenharmony_ci	if (!cell->name)
50862306a36Sopenharmony_ci		return -ENOMEM;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return 0;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci/**
51462306a36Sopenharmony_ci * nvmem_add_one_cell() - Add one cell information to an nvmem device
51562306a36Sopenharmony_ci *
51662306a36Sopenharmony_ci * @nvmem: nvmem device to add cells to.
51762306a36Sopenharmony_ci * @info: nvmem cell info to add to the device
51862306a36Sopenharmony_ci *
51962306a36Sopenharmony_ci * Return: 0 or negative error code on failure.
52062306a36Sopenharmony_ci */
52162306a36Sopenharmony_ciint nvmem_add_one_cell(struct nvmem_device *nvmem,
52262306a36Sopenharmony_ci		       const struct nvmem_cell_info *info)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct nvmem_cell_entry *cell;
52562306a36Sopenharmony_ci	int rval;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	cell = kzalloc(sizeof(*cell), GFP_KERNEL);
52862306a36Sopenharmony_ci	if (!cell)
52962306a36Sopenharmony_ci		return -ENOMEM;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, info, cell);
53262306a36Sopenharmony_ci	if (rval) {
53362306a36Sopenharmony_ci		kfree(cell);
53462306a36Sopenharmony_ci		return rval;
53562306a36Sopenharmony_ci	}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	nvmem_cell_entry_add(cell);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	return 0;
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_add_one_cell);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci/**
54462306a36Sopenharmony_ci * nvmem_add_cells() - Add cell information to an nvmem device
54562306a36Sopenharmony_ci *
54662306a36Sopenharmony_ci * @nvmem: nvmem device to add cells to.
54762306a36Sopenharmony_ci * @info: nvmem cell info to add to the device
54862306a36Sopenharmony_ci * @ncells: number of cells in info
54962306a36Sopenharmony_ci *
55062306a36Sopenharmony_ci * Return: 0 or negative error code on failure.
55162306a36Sopenharmony_ci */
55262306a36Sopenharmony_cistatic int nvmem_add_cells(struct nvmem_device *nvmem,
55362306a36Sopenharmony_ci		    const struct nvmem_cell_info *info,
55462306a36Sopenharmony_ci		    int ncells)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	int i, rval;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	for (i = 0; i < ncells; i++) {
55962306a36Sopenharmony_ci		rval = nvmem_add_one_cell(nvmem, &info[i]);
56062306a36Sopenharmony_ci		if (rval)
56162306a36Sopenharmony_ci			return rval;
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	return 0;
56562306a36Sopenharmony_ci}
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci/**
56862306a36Sopenharmony_ci * nvmem_register_notifier() - Register a notifier block for nvmem events.
56962306a36Sopenharmony_ci *
57062306a36Sopenharmony_ci * @nb: notifier block to be called on nvmem events.
57162306a36Sopenharmony_ci *
57262306a36Sopenharmony_ci * Return: 0 on success, negative error number on failure.
57362306a36Sopenharmony_ci */
57462306a36Sopenharmony_ciint nvmem_register_notifier(struct notifier_block *nb)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	return blocking_notifier_chain_register(&nvmem_notifier, nb);
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_register_notifier);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci/**
58162306a36Sopenharmony_ci * nvmem_unregister_notifier() - Unregister a notifier block for nvmem events.
58262306a36Sopenharmony_ci *
58362306a36Sopenharmony_ci * @nb: notifier block to be unregistered.
58462306a36Sopenharmony_ci *
58562306a36Sopenharmony_ci * Return: 0 on success, negative error number on failure.
58662306a36Sopenharmony_ci */
58762306a36Sopenharmony_ciint nvmem_unregister_notifier(struct notifier_block *nb)
58862306a36Sopenharmony_ci{
58962306a36Sopenharmony_ci	return blocking_notifier_chain_unregister(&nvmem_notifier, nb);
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_unregister_notifier);
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_cistatic int nvmem_add_cells_from_table(struct nvmem_device *nvmem)
59462306a36Sopenharmony_ci{
59562306a36Sopenharmony_ci	const struct nvmem_cell_info *info;
59662306a36Sopenharmony_ci	struct nvmem_cell_table *table;
59762306a36Sopenharmony_ci	struct nvmem_cell_entry *cell;
59862306a36Sopenharmony_ci	int rval = 0, i;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	mutex_lock(&nvmem_cell_mutex);
60162306a36Sopenharmony_ci	list_for_each_entry(table, &nvmem_cell_tables, node) {
60262306a36Sopenharmony_ci		if (strcmp(nvmem_dev_name(nvmem), table->nvmem_name) == 0) {
60362306a36Sopenharmony_ci			for (i = 0; i < table->ncells; i++) {
60462306a36Sopenharmony_ci				info = &table->cells[i];
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci				cell = kzalloc(sizeof(*cell), GFP_KERNEL);
60762306a36Sopenharmony_ci				if (!cell) {
60862306a36Sopenharmony_ci					rval = -ENOMEM;
60962306a36Sopenharmony_ci					goto out;
61062306a36Sopenharmony_ci				}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci				rval = nvmem_cell_info_to_nvmem_cell_entry(nvmem, info, cell);
61362306a36Sopenharmony_ci				if (rval) {
61462306a36Sopenharmony_ci					kfree(cell);
61562306a36Sopenharmony_ci					goto out;
61662306a36Sopenharmony_ci				}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci				nvmem_cell_entry_add(cell);
61962306a36Sopenharmony_ci			}
62062306a36Sopenharmony_ci		}
62162306a36Sopenharmony_ci	}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ciout:
62462306a36Sopenharmony_ci	mutex_unlock(&nvmem_cell_mutex);
62562306a36Sopenharmony_ci	return rval;
62662306a36Sopenharmony_ci}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cistatic struct nvmem_cell_entry *
62962306a36Sopenharmony_cinvmem_find_cell_entry_by_name(struct nvmem_device *nvmem, const char *cell_id)
63062306a36Sopenharmony_ci{
63162306a36Sopenharmony_ci	struct nvmem_cell_entry *iter, *cell = NULL;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	mutex_lock(&nvmem_mutex);
63462306a36Sopenharmony_ci	list_for_each_entry(iter, &nvmem->cells, node) {
63562306a36Sopenharmony_ci		if (strcmp(cell_id, iter->name) == 0) {
63662306a36Sopenharmony_ci			cell = iter;
63762306a36Sopenharmony_ci			break;
63862306a36Sopenharmony_ci		}
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	return cell;
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_cistatic int nvmem_validate_keepouts(struct nvmem_device *nvmem)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	unsigned int cur = 0;
64862306a36Sopenharmony_ci	const struct nvmem_keepout *keepout = nvmem->keepout;
64962306a36Sopenharmony_ci	const struct nvmem_keepout *keepoutend = keepout + nvmem->nkeepout;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	while (keepout < keepoutend) {
65262306a36Sopenharmony_ci		/* Ensure keepouts are sorted and don't overlap. */
65362306a36Sopenharmony_ci		if (keepout->start < cur) {
65462306a36Sopenharmony_ci			dev_err(&nvmem->dev,
65562306a36Sopenharmony_ci				"Keepout regions aren't sorted or overlap.\n");
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci			return -ERANGE;
65862306a36Sopenharmony_ci		}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci		if (keepout->end < keepout->start) {
66162306a36Sopenharmony_ci			dev_err(&nvmem->dev,
66262306a36Sopenharmony_ci				"Invalid keepout region.\n");
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci			return -EINVAL;
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci		/*
66862306a36Sopenharmony_ci		 * Validate keepouts (and holes between) don't violate
66962306a36Sopenharmony_ci		 * word_size constraints.
67062306a36Sopenharmony_ci		 */
67162306a36Sopenharmony_ci		if ((keepout->end - keepout->start < nvmem->word_size) ||
67262306a36Sopenharmony_ci		    ((keepout->start != cur) &&
67362306a36Sopenharmony_ci		     (keepout->start - cur < nvmem->word_size))) {
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci			dev_err(&nvmem->dev,
67662306a36Sopenharmony_ci				"Keepout regions violate word_size constraints.\n");
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci			return -ERANGE;
67962306a36Sopenharmony_ci		}
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci		/* Validate keepouts don't violate stride (alignment). */
68262306a36Sopenharmony_ci		if (!IS_ALIGNED(keepout->start, nvmem->stride) ||
68362306a36Sopenharmony_ci		    !IS_ALIGNED(keepout->end, nvmem->stride)) {
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci			dev_err(&nvmem->dev,
68662306a36Sopenharmony_ci				"Keepout regions violate stride.\n");
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci			return -EINVAL;
68962306a36Sopenharmony_ci		}
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci		cur = keepout->end;
69262306a36Sopenharmony_ci		keepout++;
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic int nvmem_add_cells_from_dt(struct nvmem_device *nvmem, struct device_node *np)
69962306a36Sopenharmony_ci{
70062306a36Sopenharmony_ci	struct nvmem_layout *layout = nvmem->layout;
70162306a36Sopenharmony_ci	struct device *dev = &nvmem->dev;
70262306a36Sopenharmony_ci	struct device_node *child;
70362306a36Sopenharmony_ci	const __be32 *addr;
70462306a36Sopenharmony_ci	int len, ret;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	for_each_child_of_node(np, child) {
70762306a36Sopenharmony_ci		struct nvmem_cell_info info = {0};
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		addr = of_get_property(child, "reg", &len);
71062306a36Sopenharmony_ci		if (!addr)
71162306a36Sopenharmony_ci			continue;
71262306a36Sopenharmony_ci		if (len < 2 * sizeof(u32)) {
71362306a36Sopenharmony_ci			dev_err(dev, "nvmem: invalid reg on %pOF\n", child);
71462306a36Sopenharmony_ci			of_node_put(child);
71562306a36Sopenharmony_ci			return -EINVAL;
71662306a36Sopenharmony_ci		}
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci		info.offset = be32_to_cpup(addr++);
71962306a36Sopenharmony_ci		info.bytes = be32_to_cpup(addr);
72062306a36Sopenharmony_ci		info.name = kasprintf(GFP_KERNEL, "%pOFn", child);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci		addr = of_get_property(child, "bits", &len);
72362306a36Sopenharmony_ci		if (addr && len == (2 * sizeof(u32))) {
72462306a36Sopenharmony_ci			info.bit_offset = be32_to_cpup(addr++);
72562306a36Sopenharmony_ci			info.nbits = be32_to_cpup(addr);
72662306a36Sopenharmony_ci		}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci		info.np = of_node_get(child);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		if (layout && layout->fixup_cell_info)
73162306a36Sopenharmony_ci			layout->fixup_cell_info(nvmem, layout, &info);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci		ret = nvmem_add_one_cell(nvmem, &info);
73462306a36Sopenharmony_ci		kfree(info.name);
73562306a36Sopenharmony_ci		if (ret) {
73662306a36Sopenharmony_ci			of_node_put(child);
73762306a36Sopenharmony_ci			return ret;
73862306a36Sopenharmony_ci		}
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	return 0;
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistatic int nvmem_add_cells_from_legacy_of(struct nvmem_device *nvmem)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	return nvmem_add_cells_from_dt(nvmem, nvmem->dev.of_node);
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic int nvmem_add_cells_from_fixed_layout(struct nvmem_device *nvmem)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct device_node *layout_np;
75262306a36Sopenharmony_ci	int err = 0;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	layout_np = of_nvmem_layout_get_container(nvmem);
75562306a36Sopenharmony_ci	if (!layout_np)
75662306a36Sopenharmony_ci		return 0;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (of_device_is_compatible(layout_np, "fixed-layout"))
75962306a36Sopenharmony_ci		err = nvmem_add_cells_from_dt(nvmem, layout_np);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	of_node_put(layout_np);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	return err;
76462306a36Sopenharmony_ci}
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ciint __nvmem_layout_register(struct nvmem_layout *layout, struct module *owner)
76762306a36Sopenharmony_ci{
76862306a36Sopenharmony_ci	layout->owner = owner;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	spin_lock(&nvmem_layout_lock);
77162306a36Sopenharmony_ci	list_add(&layout->node, &nvmem_layouts);
77262306a36Sopenharmony_ci	spin_unlock(&nvmem_layout_lock);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_ADD, layout);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	return 0;
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__nvmem_layout_register);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_civoid nvmem_layout_unregister(struct nvmem_layout *layout)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_LAYOUT_REMOVE, layout);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	spin_lock(&nvmem_layout_lock);
78562306a36Sopenharmony_ci	list_del(&layout->node);
78662306a36Sopenharmony_ci	spin_unlock(&nvmem_layout_lock);
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_layout_unregister);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic struct nvmem_layout *nvmem_layout_get(struct nvmem_device *nvmem)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	struct device_node *layout_np;
79362306a36Sopenharmony_ci	struct nvmem_layout *l, *layout = ERR_PTR(-EPROBE_DEFER);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	layout_np = of_nvmem_layout_get_container(nvmem);
79662306a36Sopenharmony_ci	if (!layout_np)
79762306a36Sopenharmony_ci		return NULL;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* Fixed layouts don't have a matching driver */
80062306a36Sopenharmony_ci	if (of_device_is_compatible(layout_np, "fixed-layout")) {
80162306a36Sopenharmony_ci		of_node_put(layout_np);
80262306a36Sopenharmony_ci		return NULL;
80362306a36Sopenharmony_ci	}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/*
80662306a36Sopenharmony_ci	 * In case the nvmem device was built-in while the layout was built as a
80762306a36Sopenharmony_ci	 * module, we shall manually request the layout driver loading otherwise
80862306a36Sopenharmony_ci	 * we'll never have any match.
80962306a36Sopenharmony_ci	 */
81062306a36Sopenharmony_ci	of_request_module(layout_np);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	spin_lock(&nvmem_layout_lock);
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	list_for_each_entry(l, &nvmem_layouts, node) {
81562306a36Sopenharmony_ci		if (of_match_node(l->of_match_table, layout_np)) {
81662306a36Sopenharmony_ci			if (try_module_get(l->owner))
81762306a36Sopenharmony_ci				layout = l;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci			break;
82062306a36Sopenharmony_ci		}
82162306a36Sopenharmony_ci	}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	spin_unlock(&nvmem_layout_lock);
82462306a36Sopenharmony_ci	of_node_put(layout_np);
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	return layout;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic void nvmem_layout_put(struct nvmem_layout *layout)
83062306a36Sopenharmony_ci{
83162306a36Sopenharmony_ci	if (layout)
83262306a36Sopenharmony_ci		module_put(layout->owner);
83362306a36Sopenharmony_ci}
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_cistatic int nvmem_add_cells_from_layout(struct nvmem_device *nvmem)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	struct nvmem_layout *layout = nvmem->layout;
83862306a36Sopenharmony_ci	int ret;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	if (layout && layout->add_cells) {
84162306a36Sopenharmony_ci		ret = layout->add_cells(&nvmem->dev, nvmem, layout);
84262306a36Sopenharmony_ci		if (ret)
84362306a36Sopenharmony_ci			return ret;
84462306a36Sopenharmony_ci	}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	return 0;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
85062306a36Sopenharmony_ci/**
85162306a36Sopenharmony_ci * of_nvmem_layout_get_container() - Get OF node to layout container.
85262306a36Sopenharmony_ci *
85362306a36Sopenharmony_ci * @nvmem: nvmem device.
85462306a36Sopenharmony_ci *
85562306a36Sopenharmony_ci * Return: a node pointer with refcount incremented or NULL if no
85662306a36Sopenharmony_ci * container exists. Use of_node_put() on it when done.
85762306a36Sopenharmony_ci */
85862306a36Sopenharmony_cistruct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
85962306a36Sopenharmony_ci{
86062306a36Sopenharmony_ci	return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
86162306a36Sopenharmony_ci}
86262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
86362306a36Sopenharmony_ci#endif
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ciconst void *nvmem_layout_get_match_data(struct nvmem_device *nvmem,
86662306a36Sopenharmony_ci					struct nvmem_layout *layout)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct device_node __maybe_unused *layout_np;
86962306a36Sopenharmony_ci	const struct of_device_id *match;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci	layout_np = of_nvmem_layout_get_container(nvmem);
87262306a36Sopenharmony_ci	match = of_match_node(layout->of_match_table, layout_np);
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	return match ? match->data : NULL;
87562306a36Sopenharmony_ci}
87662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_layout_get_match_data);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci/**
87962306a36Sopenharmony_ci * nvmem_register() - Register a nvmem device for given nvmem_config.
88062306a36Sopenharmony_ci * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
88162306a36Sopenharmony_ci *
88262306a36Sopenharmony_ci * @config: nvmem device configuration with which nvmem device is created.
88362306a36Sopenharmony_ci *
88462306a36Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
88562306a36Sopenharmony_ci * on success.
88662306a36Sopenharmony_ci */
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistruct nvmem_device *nvmem_register(const struct nvmem_config *config)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct nvmem_device *nvmem;
89162306a36Sopenharmony_ci	int rval;
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	if (!config->dev)
89462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	if (!config->reg_read && !config->reg_write)
89762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	nvmem = kzalloc(sizeof(*nvmem), GFP_KERNEL);
90062306a36Sopenharmony_ci	if (!nvmem)
90162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	rval = ida_alloc(&nvmem_ida, GFP_KERNEL);
90462306a36Sopenharmony_ci	if (rval < 0) {
90562306a36Sopenharmony_ci		kfree(nvmem);
90662306a36Sopenharmony_ci		return ERR_PTR(rval);
90762306a36Sopenharmony_ci	}
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	nvmem->id = rval;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	nvmem->dev.type = &nvmem_provider_type;
91262306a36Sopenharmony_ci	nvmem->dev.bus = &nvmem_bus_type;
91362306a36Sopenharmony_ci	nvmem->dev.parent = config->dev;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	device_initialize(&nvmem->dev);
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (!config->ignore_wp)
91862306a36Sopenharmony_ci		nvmem->wp_gpio = gpiod_get_optional(config->dev, "wp",
91962306a36Sopenharmony_ci						    GPIOD_OUT_HIGH);
92062306a36Sopenharmony_ci	if (IS_ERR(nvmem->wp_gpio)) {
92162306a36Sopenharmony_ci		rval = PTR_ERR(nvmem->wp_gpio);
92262306a36Sopenharmony_ci		nvmem->wp_gpio = NULL;
92362306a36Sopenharmony_ci		goto err_put_device;
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	kref_init(&nvmem->refcnt);
92762306a36Sopenharmony_ci	INIT_LIST_HEAD(&nvmem->cells);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	nvmem->owner = config->owner;
93062306a36Sopenharmony_ci	if (!nvmem->owner && config->dev->driver)
93162306a36Sopenharmony_ci		nvmem->owner = config->dev->driver->owner;
93262306a36Sopenharmony_ci	nvmem->stride = config->stride ?: 1;
93362306a36Sopenharmony_ci	nvmem->word_size = config->word_size ?: 1;
93462306a36Sopenharmony_ci	nvmem->size = config->size;
93562306a36Sopenharmony_ci	nvmem->root_only = config->root_only;
93662306a36Sopenharmony_ci	nvmem->priv = config->priv;
93762306a36Sopenharmony_ci	nvmem->type = config->type;
93862306a36Sopenharmony_ci	nvmem->reg_read = config->reg_read;
93962306a36Sopenharmony_ci	nvmem->reg_write = config->reg_write;
94062306a36Sopenharmony_ci	nvmem->keepout = config->keepout;
94162306a36Sopenharmony_ci	nvmem->nkeepout = config->nkeepout;
94262306a36Sopenharmony_ci	if (config->of_node)
94362306a36Sopenharmony_ci		nvmem->dev.of_node = config->of_node;
94462306a36Sopenharmony_ci	else if (!config->no_of_node)
94562306a36Sopenharmony_ci		nvmem->dev.of_node = config->dev->of_node;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	switch (config->id) {
94862306a36Sopenharmony_ci	case NVMEM_DEVID_NONE:
94962306a36Sopenharmony_ci		rval = dev_set_name(&nvmem->dev, "%s", config->name);
95062306a36Sopenharmony_ci		break;
95162306a36Sopenharmony_ci	case NVMEM_DEVID_AUTO:
95262306a36Sopenharmony_ci		rval = dev_set_name(&nvmem->dev, "%s%d", config->name, nvmem->id);
95362306a36Sopenharmony_ci		break;
95462306a36Sopenharmony_ci	default:
95562306a36Sopenharmony_ci		rval = dev_set_name(&nvmem->dev, "%s%d",
95662306a36Sopenharmony_ci			     config->name ? : "nvmem",
95762306a36Sopenharmony_ci			     config->name ? config->id : nvmem->id);
95862306a36Sopenharmony_ci		break;
95962306a36Sopenharmony_ci	}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci	if (rval)
96262306a36Sopenharmony_ci		goto err_put_device;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	nvmem->read_only = device_property_present(config->dev, "read-only") ||
96562306a36Sopenharmony_ci			   config->read_only || !nvmem->reg_write;
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci#ifdef CONFIG_NVMEM_SYSFS
96862306a36Sopenharmony_ci	nvmem->dev.groups = nvmem_dev_groups;
96962306a36Sopenharmony_ci#endif
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	if (nvmem->nkeepout) {
97262306a36Sopenharmony_ci		rval = nvmem_validate_keepouts(nvmem);
97362306a36Sopenharmony_ci		if (rval)
97462306a36Sopenharmony_ci			goto err_put_device;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (config->compat) {
97862306a36Sopenharmony_ci		rval = nvmem_sysfs_setup_compat(nvmem, config);
97962306a36Sopenharmony_ci		if (rval)
98062306a36Sopenharmony_ci			goto err_put_device;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	/*
98462306a36Sopenharmony_ci	 * If the driver supplied a layout by config->layout, the module
98562306a36Sopenharmony_ci	 * pointer will be NULL and nvmem_layout_put() will be a noop.
98662306a36Sopenharmony_ci	 */
98762306a36Sopenharmony_ci	nvmem->layout = config->layout ?: nvmem_layout_get(nvmem);
98862306a36Sopenharmony_ci	if (IS_ERR(nvmem->layout)) {
98962306a36Sopenharmony_ci		rval = PTR_ERR(nvmem->layout);
99062306a36Sopenharmony_ci		nvmem->layout = NULL;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		if (rval == -EPROBE_DEFER)
99362306a36Sopenharmony_ci			goto err_teardown_compat;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	if (config->cells) {
99762306a36Sopenharmony_ci		rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
99862306a36Sopenharmony_ci		if (rval)
99962306a36Sopenharmony_ci			goto err_remove_cells;
100062306a36Sopenharmony_ci	}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci	rval = nvmem_add_cells_from_table(nvmem);
100362306a36Sopenharmony_ci	if (rval)
100462306a36Sopenharmony_ci		goto err_remove_cells;
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	rval = nvmem_add_cells_from_legacy_of(nvmem);
100762306a36Sopenharmony_ci	if (rval)
100862306a36Sopenharmony_ci		goto err_remove_cells;
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	rval = nvmem_add_cells_from_fixed_layout(nvmem);
101162306a36Sopenharmony_ci	if (rval)
101262306a36Sopenharmony_ci		goto err_remove_cells;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	rval = nvmem_add_cells_from_layout(nvmem);
101562306a36Sopenharmony_ci	if (rval)
101662306a36Sopenharmony_ci		goto err_remove_cells;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	rval = device_add(&nvmem->dev);
102162306a36Sopenharmony_ci	if (rval)
102262306a36Sopenharmony_ci		goto err_remove_cells;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	return nvmem;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cierr_remove_cells:
102962306a36Sopenharmony_ci	nvmem_device_remove_all_cells(nvmem);
103062306a36Sopenharmony_ci	nvmem_layout_put(nvmem->layout);
103162306a36Sopenharmony_cierr_teardown_compat:
103262306a36Sopenharmony_ci	if (config->compat)
103362306a36Sopenharmony_ci		nvmem_sysfs_remove_compat(nvmem, config);
103462306a36Sopenharmony_cierr_put_device:
103562306a36Sopenharmony_ci	put_device(&nvmem->dev);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	return ERR_PTR(rval);
103862306a36Sopenharmony_ci}
103962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_register);
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_cistatic void nvmem_device_release(struct kref *kref)
104262306a36Sopenharmony_ci{
104362306a36Sopenharmony_ci	struct nvmem_device *nvmem;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	nvmem = container_of(kref, struct nvmem_device, refcnt);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem);
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (nvmem->flags & FLAG_COMPAT)
105062306a36Sopenharmony_ci		device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	nvmem_device_remove_all_cells(nvmem);
105362306a36Sopenharmony_ci	nvmem_layout_put(nvmem->layout);
105462306a36Sopenharmony_ci	device_unregister(&nvmem->dev);
105562306a36Sopenharmony_ci}
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci/**
105862306a36Sopenharmony_ci * nvmem_unregister() - Unregister previously registered nvmem device
105962306a36Sopenharmony_ci *
106062306a36Sopenharmony_ci * @nvmem: Pointer to previously registered nvmem device.
106162306a36Sopenharmony_ci */
106262306a36Sopenharmony_civoid nvmem_unregister(struct nvmem_device *nvmem)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	if (nvmem)
106562306a36Sopenharmony_ci		kref_put(&nvmem->refcnt, nvmem_device_release);
106662306a36Sopenharmony_ci}
106762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_unregister);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_cistatic void devm_nvmem_unregister(void *nvmem)
107062306a36Sopenharmony_ci{
107162306a36Sopenharmony_ci	nvmem_unregister(nvmem);
107262306a36Sopenharmony_ci}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci/**
107562306a36Sopenharmony_ci * devm_nvmem_register() - Register a managed nvmem device for given
107662306a36Sopenharmony_ci * nvmem_config.
107762306a36Sopenharmony_ci * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
107862306a36Sopenharmony_ci *
107962306a36Sopenharmony_ci * @dev: Device that uses the nvmem device.
108062306a36Sopenharmony_ci * @config: nvmem device configuration with which nvmem device is created.
108162306a36Sopenharmony_ci *
108262306a36Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
108362306a36Sopenharmony_ci * on success.
108462306a36Sopenharmony_ci */
108562306a36Sopenharmony_cistruct nvmem_device *devm_nvmem_register(struct device *dev,
108662306a36Sopenharmony_ci					 const struct nvmem_config *config)
108762306a36Sopenharmony_ci{
108862306a36Sopenharmony_ci	struct nvmem_device *nvmem;
108962306a36Sopenharmony_ci	int ret;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	nvmem = nvmem_register(config);
109262306a36Sopenharmony_ci	if (IS_ERR(nvmem))
109362306a36Sopenharmony_ci		return nvmem;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	ret = devm_add_action_or_reset(dev, devm_nvmem_unregister, nvmem);
109662306a36Sopenharmony_ci	if (ret)
109762306a36Sopenharmony_ci		return ERR_PTR(ret);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	return nvmem;
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_nvmem_register);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cistatic struct nvmem_device *__nvmem_device_get(void *data,
110462306a36Sopenharmony_ci			int (*match)(struct device *dev, const void *data))
110562306a36Sopenharmony_ci{
110662306a36Sopenharmony_ci	struct nvmem_device *nvmem = NULL;
110762306a36Sopenharmony_ci	struct device *dev;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	mutex_lock(&nvmem_mutex);
111062306a36Sopenharmony_ci	dev = bus_find_device(&nvmem_bus_type, NULL, data, match);
111162306a36Sopenharmony_ci	if (dev)
111262306a36Sopenharmony_ci		nvmem = to_nvmem_device(dev);
111362306a36Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
111462306a36Sopenharmony_ci	if (!nvmem)
111562306a36Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	if (!try_module_get(nvmem->owner)) {
111862306a36Sopenharmony_ci		dev_err(&nvmem->dev,
111962306a36Sopenharmony_ci			"could not increase module refcount for cell %s\n",
112062306a36Sopenharmony_ci			nvmem_dev_name(nvmem));
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci		put_device(&nvmem->dev);
112362306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
112462306a36Sopenharmony_ci	}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	kref_get(&nvmem->refcnt);
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	return nvmem;
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic void __nvmem_device_put(struct nvmem_device *nvmem)
113262306a36Sopenharmony_ci{
113362306a36Sopenharmony_ci	put_device(&nvmem->dev);
113462306a36Sopenharmony_ci	module_put(nvmem->owner);
113562306a36Sopenharmony_ci	kref_put(&nvmem->refcnt, nvmem_device_release);
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
113962306a36Sopenharmony_ci/**
114062306a36Sopenharmony_ci * of_nvmem_device_get() - Get nvmem device from a given id
114162306a36Sopenharmony_ci *
114262306a36Sopenharmony_ci * @np: Device tree node that uses the nvmem device.
114362306a36Sopenharmony_ci * @id: nvmem name from nvmem-names property.
114462306a36Sopenharmony_ci *
114562306a36Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
114662306a36Sopenharmony_ci * on success.
114762306a36Sopenharmony_ci */
114862306a36Sopenharmony_cistruct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id)
114962306a36Sopenharmony_ci{
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	struct device_node *nvmem_np;
115262306a36Sopenharmony_ci	struct nvmem_device *nvmem;
115362306a36Sopenharmony_ci	int index = 0;
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	if (id)
115662306a36Sopenharmony_ci		index = of_property_match_string(np, "nvmem-names", id);
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	nvmem_np = of_parse_phandle(np, "nvmem", index);
115962306a36Sopenharmony_ci	if (!nvmem_np)
116062306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
116362306a36Sopenharmony_ci	of_node_put(nvmem_np);
116462306a36Sopenharmony_ci	return nvmem;
116562306a36Sopenharmony_ci}
116662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_nvmem_device_get);
116762306a36Sopenharmony_ci#endif
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci/**
117062306a36Sopenharmony_ci * nvmem_device_get() - Get nvmem device from a given id
117162306a36Sopenharmony_ci *
117262306a36Sopenharmony_ci * @dev: Device that uses the nvmem device.
117362306a36Sopenharmony_ci * @dev_name: name of the requested nvmem device.
117462306a36Sopenharmony_ci *
117562306a36Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
117662306a36Sopenharmony_ci * on success.
117762306a36Sopenharmony_ci */
117862306a36Sopenharmony_cistruct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name)
117962306a36Sopenharmony_ci{
118062306a36Sopenharmony_ci	if (dev->of_node) { /* try dt first */
118162306a36Sopenharmony_ci		struct nvmem_device *nvmem;
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_ci		nvmem = of_nvmem_device_get(dev->of_node, dev_name);
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci		if (!IS_ERR(nvmem) || PTR_ERR(nvmem) == -EPROBE_DEFER)
118662306a36Sopenharmony_ci			return nvmem;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	}
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci	return __nvmem_device_get((void *)dev_name, device_match_name);
119162306a36Sopenharmony_ci}
119262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_get);
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci/**
119562306a36Sopenharmony_ci * nvmem_device_find() - Find nvmem device with matching function
119662306a36Sopenharmony_ci *
119762306a36Sopenharmony_ci * @data: Data to pass to match function
119862306a36Sopenharmony_ci * @match: Callback function to check device
119962306a36Sopenharmony_ci *
120062306a36Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
120162306a36Sopenharmony_ci * on success.
120262306a36Sopenharmony_ci */
120362306a36Sopenharmony_cistruct nvmem_device *nvmem_device_find(void *data,
120462306a36Sopenharmony_ci			int (*match)(struct device *dev, const void *data))
120562306a36Sopenharmony_ci{
120662306a36Sopenharmony_ci	return __nvmem_device_get(data, match);
120762306a36Sopenharmony_ci}
120862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_find);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic int devm_nvmem_device_match(struct device *dev, void *res, void *data)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	struct nvmem_device **nvmem = res;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	if (WARN_ON(!nvmem || !*nvmem))
121562306a36Sopenharmony_ci		return 0;
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci	return *nvmem == data;
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic void devm_nvmem_device_release(struct device *dev, void *res)
122162306a36Sopenharmony_ci{
122262306a36Sopenharmony_ci	nvmem_device_put(*(struct nvmem_device **)res);
122362306a36Sopenharmony_ci}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci/**
122662306a36Sopenharmony_ci * devm_nvmem_device_put() - put alredy got nvmem device
122762306a36Sopenharmony_ci *
122862306a36Sopenharmony_ci * @dev: Device that uses the nvmem device.
122962306a36Sopenharmony_ci * @nvmem: pointer to nvmem device allocated by devm_nvmem_cell_get(),
123062306a36Sopenharmony_ci * that needs to be released.
123162306a36Sopenharmony_ci */
123262306a36Sopenharmony_civoid devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem)
123362306a36Sopenharmony_ci{
123462306a36Sopenharmony_ci	int ret;
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	ret = devres_release(dev, devm_nvmem_device_release,
123762306a36Sopenharmony_ci			     devm_nvmem_device_match, nvmem);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci	WARN_ON(ret);
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_nvmem_device_put);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci/**
124462306a36Sopenharmony_ci * nvmem_device_put() - put alredy got nvmem device
124562306a36Sopenharmony_ci *
124662306a36Sopenharmony_ci * @nvmem: pointer to nvmem device that needs to be released.
124762306a36Sopenharmony_ci */
124862306a36Sopenharmony_civoid nvmem_device_put(struct nvmem_device *nvmem)
124962306a36Sopenharmony_ci{
125062306a36Sopenharmony_ci	__nvmem_device_put(nvmem);
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_put);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci/**
125562306a36Sopenharmony_ci * devm_nvmem_device_get() - Get nvmem cell of device form a given id
125662306a36Sopenharmony_ci *
125762306a36Sopenharmony_ci * @dev: Device that requests the nvmem device.
125862306a36Sopenharmony_ci * @id: name id for the requested nvmem device.
125962306a36Sopenharmony_ci *
126062306a36Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_cell
126162306a36Sopenharmony_ci * on success.  The nvmem_cell will be freed by the automatically once the
126262306a36Sopenharmony_ci * device is freed.
126362306a36Sopenharmony_ci */
126462306a36Sopenharmony_cistruct nvmem_device *devm_nvmem_device_get(struct device *dev, const char *id)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	struct nvmem_device **ptr, *nvmem;
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	ptr = devres_alloc(devm_nvmem_device_release, sizeof(*ptr), GFP_KERNEL);
126962306a36Sopenharmony_ci	if (!ptr)
127062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	nvmem = nvmem_device_get(dev, id);
127362306a36Sopenharmony_ci	if (!IS_ERR(nvmem)) {
127462306a36Sopenharmony_ci		*ptr = nvmem;
127562306a36Sopenharmony_ci		devres_add(dev, ptr);
127662306a36Sopenharmony_ci	} else {
127762306a36Sopenharmony_ci		devres_free(ptr);
127862306a36Sopenharmony_ci	}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	return nvmem;
128162306a36Sopenharmony_ci}
128262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_nvmem_device_get);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_cistatic struct nvmem_cell *nvmem_create_cell(struct nvmem_cell_entry *entry,
128562306a36Sopenharmony_ci					    const char *id, int index)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	struct nvmem_cell *cell;
128862306a36Sopenharmony_ci	const char *name = NULL;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	cell = kzalloc(sizeof(*cell), GFP_KERNEL);
129162306a36Sopenharmony_ci	if (!cell)
129262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	if (id) {
129562306a36Sopenharmony_ci		name = kstrdup_const(id, GFP_KERNEL);
129662306a36Sopenharmony_ci		if (!name) {
129762306a36Sopenharmony_ci			kfree(cell);
129862306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
129962306a36Sopenharmony_ci		}
130062306a36Sopenharmony_ci	}
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	cell->id = name;
130362306a36Sopenharmony_ci	cell->entry = entry;
130462306a36Sopenharmony_ci	cell->index = index;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	return cell;
130762306a36Sopenharmony_ci}
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_cistatic struct nvmem_cell *
131062306a36Sopenharmony_cinvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci	struct nvmem_cell_entry *cell_entry;
131362306a36Sopenharmony_ci	struct nvmem_cell *cell = ERR_PTR(-ENOENT);
131462306a36Sopenharmony_ci	struct nvmem_cell_lookup *lookup;
131562306a36Sopenharmony_ci	struct nvmem_device *nvmem;
131662306a36Sopenharmony_ci	const char *dev_id;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	if (!dev)
131962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	dev_id = dev_name(dev);
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	mutex_lock(&nvmem_lookup_mutex);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	list_for_each_entry(lookup, &nvmem_lookup_list, node) {
132662306a36Sopenharmony_ci		if ((strcmp(lookup->dev_id, dev_id) == 0) &&
132762306a36Sopenharmony_ci		    (strcmp(lookup->con_id, con_id) == 0)) {
132862306a36Sopenharmony_ci			/* This is the right entry. */
132962306a36Sopenharmony_ci			nvmem = __nvmem_device_get((void *)lookup->nvmem_name,
133062306a36Sopenharmony_ci						   device_match_name);
133162306a36Sopenharmony_ci			if (IS_ERR(nvmem)) {
133262306a36Sopenharmony_ci				/* Provider may not be registered yet. */
133362306a36Sopenharmony_ci				cell = ERR_CAST(nvmem);
133462306a36Sopenharmony_ci				break;
133562306a36Sopenharmony_ci			}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci			cell_entry = nvmem_find_cell_entry_by_name(nvmem,
133862306a36Sopenharmony_ci								   lookup->cell_name);
133962306a36Sopenharmony_ci			if (!cell_entry) {
134062306a36Sopenharmony_ci				__nvmem_device_put(nvmem);
134162306a36Sopenharmony_ci				cell = ERR_PTR(-ENOENT);
134262306a36Sopenharmony_ci			} else {
134362306a36Sopenharmony_ci				cell = nvmem_create_cell(cell_entry, con_id, 0);
134462306a36Sopenharmony_ci				if (IS_ERR(cell))
134562306a36Sopenharmony_ci					__nvmem_device_put(nvmem);
134662306a36Sopenharmony_ci			}
134762306a36Sopenharmony_ci			break;
134862306a36Sopenharmony_ci		}
134962306a36Sopenharmony_ci	}
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	mutex_unlock(&nvmem_lookup_mutex);
135262306a36Sopenharmony_ci	return cell;
135362306a36Sopenharmony_ci}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
135662306a36Sopenharmony_cistatic struct nvmem_cell_entry *
135762306a36Sopenharmony_cinvmem_find_cell_entry_by_node(struct nvmem_device *nvmem, struct device_node *np)
135862306a36Sopenharmony_ci{
135962306a36Sopenharmony_ci	struct nvmem_cell_entry *iter, *cell = NULL;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	mutex_lock(&nvmem_mutex);
136262306a36Sopenharmony_ci	list_for_each_entry(iter, &nvmem->cells, node) {
136362306a36Sopenharmony_ci		if (np == iter->np) {
136462306a36Sopenharmony_ci			cell = iter;
136562306a36Sopenharmony_ci			break;
136662306a36Sopenharmony_ci		}
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	return cell;
137162306a36Sopenharmony_ci}
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci/**
137462306a36Sopenharmony_ci * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
137562306a36Sopenharmony_ci *
137662306a36Sopenharmony_ci * @np: Device tree node that uses the nvmem cell.
137762306a36Sopenharmony_ci * @id: nvmem cell name from nvmem-cell-names property, or NULL
137862306a36Sopenharmony_ci *      for the cell at index 0 (the lone cell with no accompanying
137962306a36Sopenharmony_ci *      nvmem-cell-names property).
138062306a36Sopenharmony_ci *
138162306a36Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer
138262306a36Sopenharmony_ci * to a struct nvmem_cell.  The nvmem_cell will be freed by the
138362306a36Sopenharmony_ci * nvmem_cell_put().
138462306a36Sopenharmony_ci */
138562306a36Sopenharmony_cistruct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
138662306a36Sopenharmony_ci{
138762306a36Sopenharmony_ci	struct device_node *cell_np, *nvmem_np;
138862306a36Sopenharmony_ci	struct nvmem_device *nvmem;
138962306a36Sopenharmony_ci	struct nvmem_cell_entry *cell_entry;
139062306a36Sopenharmony_ci	struct nvmem_cell *cell;
139162306a36Sopenharmony_ci	struct of_phandle_args cell_spec;
139262306a36Sopenharmony_ci	int index = 0;
139362306a36Sopenharmony_ci	int cell_index = 0;
139462306a36Sopenharmony_ci	int ret;
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_ci	/* if cell name exists, find index to the name */
139762306a36Sopenharmony_ci	if (id)
139862306a36Sopenharmony_ci		index = of_property_match_string(np, "nvmem-cell-names", id);
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	ret = of_parse_phandle_with_optional_args(np, "nvmem-cells",
140162306a36Sopenharmony_ci						  "#nvmem-cell-cells",
140262306a36Sopenharmony_ci						  index, &cell_spec);
140362306a36Sopenharmony_ci	if (ret)
140462306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_ci	if (cell_spec.args_count > 1)
140762306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	cell_np = cell_spec.np;
141062306a36Sopenharmony_ci	if (cell_spec.args_count)
141162306a36Sopenharmony_ci		cell_index = cell_spec.args[0];
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_ci	nvmem_np = of_get_parent(cell_np);
141462306a36Sopenharmony_ci	if (!nvmem_np) {
141562306a36Sopenharmony_ci		of_node_put(cell_np);
141662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
141762306a36Sopenharmony_ci	}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	/* nvmem layouts produce cells within the nvmem-layout container */
142062306a36Sopenharmony_ci	if (of_node_name_eq(nvmem_np, "nvmem-layout")) {
142162306a36Sopenharmony_ci		nvmem_np = of_get_next_parent(nvmem_np);
142262306a36Sopenharmony_ci		if (!nvmem_np) {
142362306a36Sopenharmony_ci			of_node_put(cell_np);
142462306a36Sopenharmony_ci			return ERR_PTR(-EINVAL);
142562306a36Sopenharmony_ci		}
142662306a36Sopenharmony_ci	}
142762306a36Sopenharmony_ci
142862306a36Sopenharmony_ci	nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
142962306a36Sopenharmony_ci	of_node_put(nvmem_np);
143062306a36Sopenharmony_ci	if (IS_ERR(nvmem)) {
143162306a36Sopenharmony_ci		of_node_put(cell_np);
143262306a36Sopenharmony_ci		return ERR_CAST(nvmem);
143362306a36Sopenharmony_ci	}
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
143662306a36Sopenharmony_ci	of_node_put(cell_np);
143762306a36Sopenharmony_ci	if (!cell_entry) {
143862306a36Sopenharmony_ci		__nvmem_device_put(nvmem);
143962306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
144062306a36Sopenharmony_ci	}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	cell = nvmem_create_cell(cell_entry, id, cell_index);
144362306a36Sopenharmony_ci	if (IS_ERR(cell))
144462306a36Sopenharmony_ci		__nvmem_device_put(nvmem);
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	return cell;
144762306a36Sopenharmony_ci}
144862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_nvmem_cell_get);
144962306a36Sopenharmony_ci#endif
145062306a36Sopenharmony_ci
145162306a36Sopenharmony_ci/**
145262306a36Sopenharmony_ci * nvmem_cell_get() - Get nvmem cell of device form a given cell name
145362306a36Sopenharmony_ci *
145462306a36Sopenharmony_ci * @dev: Device that requests the nvmem cell.
145562306a36Sopenharmony_ci * @id: nvmem cell name to get (this corresponds with the name from the
145662306a36Sopenharmony_ci *      nvmem-cell-names property for DT systems and with the con_id from
145762306a36Sopenharmony_ci *      the lookup entry for non-DT systems).
145862306a36Sopenharmony_ci *
145962306a36Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer
146062306a36Sopenharmony_ci * to a struct nvmem_cell.  The nvmem_cell will be freed by the
146162306a36Sopenharmony_ci * nvmem_cell_put().
146262306a36Sopenharmony_ci */
146362306a36Sopenharmony_cistruct nvmem_cell *nvmem_cell_get(struct device *dev, const char *id)
146462306a36Sopenharmony_ci{
146562306a36Sopenharmony_ci	struct nvmem_cell *cell;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	if (dev->of_node) { /* try dt first */
146862306a36Sopenharmony_ci		cell = of_nvmem_cell_get(dev->of_node, id);
146962306a36Sopenharmony_ci		if (!IS_ERR(cell) || PTR_ERR(cell) == -EPROBE_DEFER)
147062306a36Sopenharmony_ci			return cell;
147162306a36Sopenharmony_ci	}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_ci	/* NULL cell id only allowed for device tree; invalid otherwise */
147462306a36Sopenharmony_ci	if (!id)
147562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
147662306a36Sopenharmony_ci
147762306a36Sopenharmony_ci	return nvmem_cell_get_from_lookup(dev, id);
147862306a36Sopenharmony_ci}
147962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_get);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cistatic void devm_nvmem_cell_release(struct device *dev, void *res)
148262306a36Sopenharmony_ci{
148362306a36Sopenharmony_ci	nvmem_cell_put(*(struct nvmem_cell **)res);
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci/**
148762306a36Sopenharmony_ci * devm_nvmem_cell_get() - Get nvmem cell of device form a given id
148862306a36Sopenharmony_ci *
148962306a36Sopenharmony_ci * @dev: Device that requests the nvmem cell.
149062306a36Sopenharmony_ci * @id: nvmem cell name id to get.
149162306a36Sopenharmony_ci *
149262306a36Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer
149362306a36Sopenharmony_ci * to a struct nvmem_cell.  The nvmem_cell will be freed by the
149462306a36Sopenharmony_ci * automatically once the device is freed.
149562306a36Sopenharmony_ci */
149662306a36Sopenharmony_cistruct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *id)
149762306a36Sopenharmony_ci{
149862306a36Sopenharmony_ci	struct nvmem_cell **ptr, *cell;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	ptr = devres_alloc(devm_nvmem_cell_release, sizeof(*ptr), GFP_KERNEL);
150162306a36Sopenharmony_ci	if (!ptr)
150262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	cell = nvmem_cell_get(dev, id);
150562306a36Sopenharmony_ci	if (!IS_ERR(cell)) {
150662306a36Sopenharmony_ci		*ptr = cell;
150762306a36Sopenharmony_ci		devres_add(dev, ptr);
150862306a36Sopenharmony_ci	} else {
150962306a36Sopenharmony_ci		devres_free(ptr);
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	return cell;
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_nvmem_cell_get);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_cistatic int devm_nvmem_cell_match(struct device *dev, void *res, void *data)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	struct nvmem_cell **c = res;
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	if (WARN_ON(!c || !*c))
152162306a36Sopenharmony_ci		return 0;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	return *c == data;
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci/**
152762306a36Sopenharmony_ci * devm_nvmem_cell_put() - Release previously allocated nvmem cell
152862306a36Sopenharmony_ci * from devm_nvmem_cell_get.
152962306a36Sopenharmony_ci *
153062306a36Sopenharmony_ci * @dev: Device that requests the nvmem cell.
153162306a36Sopenharmony_ci * @cell: Previously allocated nvmem cell by devm_nvmem_cell_get().
153262306a36Sopenharmony_ci */
153362306a36Sopenharmony_civoid devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell)
153462306a36Sopenharmony_ci{
153562306a36Sopenharmony_ci	int ret;
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci	ret = devres_release(dev, devm_nvmem_cell_release,
153862306a36Sopenharmony_ci				devm_nvmem_cell_match, cell);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	WARN_ON(ret);
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ciEXPORT_SYMBOL(devm_nvmem_cell_put);
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci/**
154562306a36Sopenharmony_ci * nvmem_cell_put() - Release previously allocated nvmem cell.
154662306a36Sopenharmony_ci *
154762306a36Sopenharmony_ci * @cell: Previously allocated nvmem cell by nvmem_cell_get().
154862306a36Sopenharmony_ci */
154962306a36Sopenharmony_civoid nvmem_cell_put(struct nvmem_cell *cell)
155062306a36Sopenharmony_ci{
155162306a36Sopenharmony_ci	struct nvmem_device *nvmem = cell->entry->nvmem;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	if (cell->id)
155462306a36Sopenharmony_ci		kfree_const(cell->id);
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	kfree(cell);
155762306a36Sopenharmony_ci	__nvmem_device_put(nvmem);
155862306a36Sopenharmony_ci}
155962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_put);
156062306a36Sopenharmony_ci
156162306a36Sopenharmony_cistatic void nvmem_shift_read_buffer_in_place(struct nvmem_cell_entry *cell, void *buf)
156262306a36Sopenharmony_ci{
156362306a36Sopenharmony_ci	u8 *p, *b;
156462306a36Sopenharmony_ci	int i, extra, bit_offset = cell->bit_offset;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	p = b = buf;
156762306a36Sopenharmony_ci	if (bit_offset) {
156862306a36Sopenharmony_ci		/* First shift */
156962306a36Sopenharmony_ci		*b++ >>= bit_offset;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci		/* setup rest of the bytes if any */
157262306a36Sopenharmony_ci		for (i = 1; i < cell->bytes; i++) {
157362306a36Sopenharmony_ci			/* Get bits from next byte and shift them towards msb */
157462306a36Sopenharmony_ci			*p |= *b << (BITS_PER_BYTE - bit_offset);
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci			p = b;
157762306a36Sopenharmony_ci			*b++ >>= bit_offset;
157862306a36Sopenharmony_ci		}
157962306a36Sopenharmony_ci	} else {
158062306a36Sopenharmony_ci		/* point to the msb */
158162306a36Sopenharmony_ci		p += cell->bytes - 1;
158262306a36Sopenharmony_ci	}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci	/* result fits in less bytes */
158562306a36Sopenharmony_ci	extra = cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE);
158662306a36Sopenharmony_ci	while (--extra >= 0)
158762306a36Sopenharmony_ci		*p-- = 0;
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	/* clear msb bits if any leftover in the last byte */
159062306a36Sopenharmony_ci	if (cell->nbits % BITS_PER_BYTE)
159162306a36Sopenharmony_ci		*p &= GENMASK((cell->nbits % BITS_PER_BYTE) - 1, 0);
159262306a36Sopenharmony_ci}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic int __nvmem_cell_read(struct nvmem_device *nvmem,
159562306a36Sopenharmony_ci			     struct nvmem_cell_entry *cell,
159662306a36Sopenharmony_ci			     void *buf, size_t *len, const char *id, int index)
159762306a36Sopenharmony_ci{
159862306a36Sopenharmony_ci	int rc;
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->raw_len);
160162306a36Sopenharmony_ci
160262306a36Sopenharmony_ci	if (rc)
160362306a36Sopenharmony_ci		return rc;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	/* shift bits in-place */
160662306a36Sopenharmony_ci	if (cell->bit_offset || cell->nbits)
160762306a36Sopenharmony_ci		nvmem_shift_read_buffer_in_place(cell, buf);
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci	if (cell->read_post_process) {
161062306a36Sopenharmony_ci		rc = cell->read_post_process(cell->priv, id, index,
161162306a36Sopenharmony_ci					     cell->offset, buf, cell->raw_len);
161262306a36Sopenharmony_ci		if (rc)
161362306a36Sopenharmony_ci			return rc;
161462306a36Sopenharmony_ci	}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	if (len)
161762306a36Sopenharmony_ci		*len = cell->bytes;
161862306a36Sopenharmony_ci
161962306a36Sopenharmony_ci	return 0;
162062306a36Sopenharmony_ci}
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci/**
162362306a36Sopenharmony_ci * nvmem_cell_read() - Read a given nvmem cell
162462306a36Sopenharmony_ci *
162562306a36Sopenharmony_ci * @cell: nvmem cell to be read.
162662306a36Sopenharmony_ci * @len: pointer to length of cell which will be populated on successful read;
162762306a36Sopenharmony_ci *	 can be NULL.
162862306a36Sopenharmony_ci *
162962306a36Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a buffer on success. The
163062306a36Sopenharmony_ci * buffer should be freed by the consumer with a kfree().
163162306a36Sopenharmony_ci */
163262306a36Sopenharmony_civoid *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
163362306a36Sopenharmony_ci{
163462306a36Sopenharmony_ci	struct nvmem_cell_entry *entry = cell->entry;
163562306a36Sopenharmony_ci	struct nvmem_device *nvmem = entry->nvmem;
163662306a36Sopenharmony_ci	u8 *buf;
163762306a36Sopenharmony_ci	int rc;
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	if (!nvmem)
164062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	buf = kzalloc(max_t(size_t, entry->raw_len, entry->bytes), GFP_KERNEL);
164362306a36Sopenharmony_ci	if (!buf)
164462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	rc = __nvmem_cell_read(nvmem, cell->entry, buf, len, cell->id, cell->index);
164762306a36Sopenharmony_ci	if (rc) {
164862306a36Sopenharmony_ci		kfree(buf);
164962306a36Sopenharmony_ci		return ERR_PTR(rc);
165062306a36Sopenharmony_ci	}
165162306a36Sopenharmony_ci
165262306a36Sopenharmony_ci	return buf;
165362306a36Sopenharmony_ci}
165462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read);
165562306a36Sopenharmony_ci
165662306a36Sopenharmony_cistatic void *nvmem_cell_prepare_write_buffer(struct nvmem_cell_entry *cell,
165762306a36Sopenharmony_ci					     u8 *_buf, int len)
165862306a36Sopenharmony_ci{
165962306a36Sopenharmony_ci	struct nvmem_device *nvmem = cell->nvmem;
166062306a36Sopenharmony_ci	int i, rc, nbits, bit_offset = cell->bit_offset;
166162306a36Sopenharmony_ci	u8 v, *p, *buf, *b, pbyte, pbits;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	nbits = cell->nbits;
166462306a36Sopenharmony_ci	buf = kzalloc(cell->bytes, GFP_KERNEL);
166562306a36Sopenharmony_ci	if (!buf)
166662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	memcpy(buf, _buf, len);
166962306a36Sopenharmony_ci	p = b = buf;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	if (bit_offset) {
167262306a36Sopenharmony_ci		pbyte = *b;
167362306a36Sopenharmony_ci		*b <<= bit_offset;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci		/* setup the first byte with lsb bits from nvmem */
167662306a36Sopenharmony_ci		rc = nvmem_reg_read(nvmem, cell->offset, &v, 1);
167762306a36Sopenharmony_ci		if (rc)
167862306a36Sopenharmony_ci			goto err;
167962306a36Sopenharmony_ci		*b++ |= GENMASK(bit_offset - 1, 0) & v;
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci		/* setup rest of the byte if any */
168262306a36Sopenharmony_ci		for (i = 1; i < cell->bytes; i++) {
168362306a36Sopenharmony_ci			/* Get last byte bits and shift them towards lsb */
168462306a36Sopenharmony_ci			pbits = pbyte >> (BITS_PER_BYTE - 1 - bit_offset);
168562306a36Sopenharmony_ci			pbyte = *b;
168662306a36Sopenharmony_ci			p = b;
168762306a36Sopenharmony_ci			*b <<= bit_offset;
168862306a36Sopenharmony_ci			*b++ |= pbits;
168962306a36Sopenharmony_ci		}
169062306a36Sopenharmony_ci	}
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci	/* if it's not end on byte boundary */
169362306a36Sopenharmony_ci	if ((nbits + bit_offset) % BITS_PER_BYTE) {
169462306a36Sopenharmony_ci		/* setup the last byte with msb bits from nvmem */
169562306a36Sopenharmony_ci		rc = nvmem_reg_read(nvmem,
169662306a36Sopenharmony_ci				    cell->offset + cell->bytes - 1, &v, 1);
169762306a36Sopenharmony_ci		if (rc)
169862306a36Sopenharmony_ci			goto err;
169962306a36Sopenharmony_ci		*p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci	}
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci	return buf;
170462306a36Sopenharmony_cierr:
170562306a36Sopenharmony_ci	kfree(buf);
170662306a36Sopenharmony_ci	return ERR_PTR(rc);
170762306a36Sopenharmony_ci}
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_cistatic int __nvmem_cell_entry_write(struct nvmem_cell_entry *cell, void *buf, size_t len)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	struct nvmem_device *nvmem = cell->nvmem;
171262306a36Sopenharmony_ci	int rc;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	if (!nvmem || nvmem->read_only ||
171562306a36Sopenharmony_ci	    (cell->bit_offset == 0 && len != cell->bytes))
171662306a36Sopenharmony_ci		return -EINVAL;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci	/*
171962306a36Sopenharmony_ci	 * Any cells which have a read_post_process hook are read-only because
172062306a36Sopenharmony_ci	 * we cannot reverse the operation and it might affect other cells,
172162306a36Sopenharmony_ci	 * too.
172262306a36Sopenharmony_ci	 */
172362306a36Sopenharmony_ci	if (cell->read_post_process)
172462306a36Sopenharmony_ci		return -EINVAL;
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	if (cell->bit_offset || cell->nbits) {
172762306a36Sopenharmony_ci		buf = nvmem_cell_prepare_write_buffer(cell, buf, len);
172862306a36Sopenharmony_ci		if (IS_ERR(buf))
172962306a36Sopenharmony_ci			return PTR_ERR(buf);
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	rc = nvmem_reg_write(nvmem, cell->offset, buf, cell->bytes);
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	/* free the tmp buffer */
173562306a36Sopenharmony_ci	if (cell->bit_offset || cell->nbits)
173662306a36Sopenharmony_ci		kfree(buf);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	if (rc)
173962306a36Sopenharmony_ci		return rc;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	return len;
174262306a36Sopenharmony_ci}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci/**
174562306a36Sopenharmony_ci * nvmem_cell_write() - Write to a given nvmem cell
174662306a36Sopenharmony_ci *
174762306a36Sopenharmony_ci * @cell: nvmem cell to be written.
174862306a36Sopenharmony_ci * @buf: Buffer to be written.
174962306a36Sopenharmony_ci * @len: length of buffer to be written to nvmem cell.
175062306a36Sopenharmony_ci *
175162306a36Sopenharmony_ci * Return: length of bytes written or negative on failure.
175262306a36Sopenharmony_ci */
175362306a36Sopenharmony_ciint nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
175462306a36Sopenharmony_ci{
175562306a36Sopenharmony_ci	return __nvmem_cell_entry_write(cell->entry, buf, len);
175662306a36Sopenharmony_ci}
175762306a36Sopenharmony_ci
175862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_write);
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_cistatic int nvmem_cell_read_common(struct device *dev, const char *cell_id,
176162306a36Sopenharmony_ci				  void *val, size_t count)
176262306a36Sopenharmony_ci{
176362306a36Sopenharmony_ci	struct nvmem_cell *cell;
176462306a36Sopenharmony_ci	void *buf;
176562306a36Sopenharmony_ci	size_t len;
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci	cell = nvmem_cell_get(dev, cell_id);
176862306a36Sopenharmony_ci	if (IS_ERR(cell))
176962306a36Sopenharmony_ci		return PTR_ERR(cell);
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	buf = nvmem_cell_read(cell, &len);
177262306a36Sopenharmony_ci	if (IS_ERR(buf)) {
177362306a36Sopenharmony_ci		nvmem_cell_put(cell);
177462306a36Sopenharmony_ci		return PTR_ERR(buf);
177562306a36Sopenharmony_ci	}
177662306a36Sopenharmony_ci	if (len != count) {
177762306a36Sopenharmony_ci		kfree(buf);
177862306a36Sopenharmony_ci		nvmem_cell_put(cell);
177962306a36Sopenharmony_ci		return -EINVAL;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci	memcpy(val, buf, count);
178262306a36Sopenharmony_ci	kfree(buf);
178362306a36Sopenharmony_ci	nvmem_cell_put(cell);
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	return 0;
178662306a36Sopenharmony_ci}
178762306a36Sopenharmony_ci
178862306a36Sopenharmony_ci/**
178962306a36Sopenharmony_ci * nvmem_cell_read_u8() - Read a cell value as a u8
179062306a36Sopenharmony_ci *
179162306a36Sopenharmony_ci * @dev: Device that requests the nvmem cell.
179262306a36Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
179362306a36Sopenharmony_ci * @val: pointer to output value.
179462306a36Sopenharmony_ci *
179562306a36Sopenharmony_ci * Return: 0 on success or negative errno.
179662306a36Sopenharmony_ci */
179762306a36Sopenharmony_ciint nvmem_cell_read_u8(struct device *dev, const char *cell_id, u8 *val)
179862306a36Sopenharmony_ci{
179962306a36Sopenharmony_ci	return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
180062306a36Sopenharmony_ci}
180162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_u8);
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_ci/**
180462306a36Sopenharmony_ci * nvmem_cell_read_u16() - Read a cell value as a u16
180562306a36Sopenharmony_ci *
180662306a36Sopenharmony_ci * @dev: Device that requests the nvmem cell.
180762306a36Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
180862306a36Sopenharmony_ci * @val: pointer to output value.
180962306a36Sopenharmony_ci *
181062306a36Sopenharmony_ci * Return: 0 on success or negative errno.
181162306a36Sopenharmony_ci */
181262306a36Sopenharmony_ciint nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val)
181362306a36Sopenharmony_ci{
181462306a36Sopenharmony_ci	return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
181562306a36Sopenharmony_ci}
181662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_u16);
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci/**
181962306a36Sopenharmony_ci * nvmem_cell_read_u32() - Read a cell value as a u32
182062306a36Sopenharmony_ci *
182162306a36Sopenharmony_ci * @dev: Device that requests the nvmem cell.
182262306a36Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
182362306a36Sopenharmony_ci * @val: pointer to output value.
182462306a36Sopenharmony_ci *
182562306a36Sopenharmony_ci * Return: 0 on success or negative errno.
182662306a36Sopenharmony_ci */
182762306a36Sopenharmony_ciint nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val)
182862306a36Sopenharmony_ci{
182962306a36Sopenharmony_ci	return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_u32);
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci/**
183462306a36Sopenharmony_ci * nvmem_cell_read_u64() - Read a cell value as a u64
183562306a36Sopenharmony_ci *
183662306a36Sopenharmony_ci * @dev: Device that requests the nvmem cell.
183762306a36Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
183862306a36Sopenharmony_ci * @val: pointer to output value.
183962306a36Sopenharmony_ci *
184062306a36Sopenharmony_ci * Return: 0 on success or negative errno.
184162306a36Sopenharmony_ci */
184262306a36Sopenharmony_ciint nvmem_cell_read_u64(struct device *dev, const char *cell_id, u64 *val)
184362306a36Sopenharmony_ci{
184462306a36Sopenharmony_ci	return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
184562306a36Sopenharmony_ci}
184662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_u64);
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_cistatic const void *nvmem_cell_read_variable_common(struct device *dev,
184962306a36Sopenharmony_ci						   const char *cell_id,
185062306a36Sopenharmony_ci						   size_t max_len, size_t *len)
185162306a36Sopenharmony_ci{
185262306a36Sopenharmony_ci	struct nvmem_cell *cell;
185362306a36Sopenharmony_ci	int nbits;
185462306a36Sopenharmony_ci	void *buf;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	cell = nvmem_cell_get(dev, cell_id);
185762306a36Sopenharmony_ci	if (IS_ERR(cell))
185862306a36Sopenharmony_ci		return cell;
185962306a36Sopenharmony_ci
186062306a36Sopenharmony_ci	nbits = cell->entry->nbits;
186162306a36Sopenharmony_ci	buf = nvmem_cell_read(cell, len);
186262306a36Sopenharmony_ci	nvmem_cell_put(cell);
186362306a36Sopenharmony_ci	if (IS_ERR(buf))
186462306a36Sopenharmony_ci		return buf;
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	/*
186762306a36Sopenharmony_ci	 * If nbits is set then nvmem_cell_read() can significantly exaggerate
186862306a36Sopenharmony_ci	 * the length of the real data. Throw away the extra junk.
186962306a36Sopenharmony_ci	 */
187062306a36Sopenharmony_ci	if (nbits)
187162306a36Sopenharmony_ci		*len = DIV_ROUND_UP(nbits, 8);
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_ci	if (*len > max_len) {
187462306a36Sopenharmony_ci		kfree(buf);
187562306a36Sopenharmony_ci		return ERR_PTR(-ERANGE);
187662306a36Sopenharmony_ci	}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	return buf;
187962306a36Sopenharmony_ci}
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci/**
188262306a36Sopenharmony_ci * nvmem_cell_read_variable_le_u32() - Read up to 32-bits of data as a little endian number.
188362306a36Sopenharmony_ci *
188462306a36Sopenharmony_ci * @dev: Device that requests the nvmem cell.
188562306a36Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
188662306a36Sopenharmony_ci * @val: pointer to output value.
188762306a36Sopenharmony_ci *
188862306a36Sopenharmony_ci * Return: 0 on success or negative errno.
188962306a36Sopenharmony_ci */
189062306a36Sopenharmony_ciint nvmem_cell_read_variable_le_u32(struct device *dev, const char *cell_id,
189162306a36Sopenharmony_ci				    u32 *val)
189262306a36Sopenharmony_ci{
189362306a36Sopenharmony_ci	size_t len;
189462306a36Sopenharmony_ci	const u8 *buf;
189562306a36Sopenharmony_ci	int i;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len);
189862306a36Sopenharmony_ci	if (IS_ERR(buf))
189962306a36Sopenharmony_ci		return PTR_ERR(buf);
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	/* Copy w/ implicit endian conversion */
190262306a36Sopenharmony_ci	*val = 0;
190362306a36Sopenharmony_ci	for (i = 0; i < len; i++)
190462306a36Sopenharmony_ci		*val |= buf[i] << (8 * i);
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	kfree(buf);
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci	return 0;
190962306a36Sopenharmony_ci}
191062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u32);
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci/**
191362306a36Sopenharmony_ci * nvmem_cell_read_variable_le_u64() - Read up to 64-bits of data as a little endian number.
191462306a36Sopenharmony_ci *
191562306a36Sopenharmony_ci * @dev: Device that requests the nvmem cell.
191662306a36Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
191762306a36Sopenharmony_ci * @val: pointer to output value.
191862306a36Sopenharmony_ci *
191962306a36Sopenharmony_ci * Return: 0 on success or negative errno.
192062306a36Sopenharmony_ci */
192162306a36Sopenharmony_ciint nvmem_cell_read_variable_le_u64(struct device *dev, const char *cell_id,
192262306a36Sopenharmony_ci				    u64 *val)
192362306a36Sopenharmony_ci{
192462306a36Sopenharmony_ci	size_t len;
192562306a36Sopenharmony_ci	const u8 *buf;
192662306a36Sopenharmony_ci	int i;
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci	buf = nvmem_cell_read_variable_common(dev, cell_id, sizeof(*val), &len);
192962306a36Sopenharmony_ci	if (IS_ERR(buf))
193062306a36Sopenharmony_ci		return PTR_ERR(buf);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	/* Copy w/ implicit endian conversion */
193362306a36Sopenharmony_ci	*val = 0;
193462306a36Sopenharmony_ci	for (i = 0; i < len; i++)
193562306a36Sopenharmony_ci		*val |= (uint64_t)buf[i] << (8 * i);
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	kfree(buf);
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci	return 0;
194062306a36Sopenharmony_ci}
194162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_variable_le_u64);
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci/**
194462306a36Sopenharmony_ci * nvmem_device_cell_read() - Read a given nvmem device and cell
194562306a36Sopenharmony_ci *
194662306a36Sopenharmony_ci * @nvmem: nvmem device to read from.
194762306a36Sopenharmony_ci * @info: nvmem cell info to be read.
194862306a36Sopenharmony_ci * @buf: buffer pointer which will be populated on successful read.
194962306a36Sopenharmony_ci *
195062306a36Sopenharmony_ci * Return: length of successful bytes read on success and negative
195162306a36Sopenharmony_ci * error code on error.
195262306a36Sopenharmony_ci */
195362306a36Sopenharmony_cissize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
195462306a36Sopenharmony_ci			   struct nvmem_cell_info *info, void *buf)
195562306a36Sopenharmony_ci{
195662306a36Sopenharmony_ci	struct nvmem_cell_entry cell;
195762306a36Sopenharmony_ci	int rc;
195862306a36Sopenharmony_ci	ssize_t len;
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	if (!nvmem)
196162306a36Sopenharmony_ci		return -EINVAL;
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	rc = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, &cell);
196462306a36Sopenharmony_ci	if (rc)
196562306a36Sopenharmony_ci		return rc;
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	rc = __nvmem_cell_read(nvmem, &cell, buf, &len, NULL, 0);
196862306a36Sopenharmony_ci	if (rc)
196962306a36Sopenharmony_ci		return rc;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	return len;
197262306a36Sopenharmony_ci}
197362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_cell_read);
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci/**
197662306a36Sopenharmony_ci * nvmem_device_cell_write() - Write cell to a given nvmem device
197762306a36Sopenharmony_ci *
197862306a36Sopenharmony_ci * @nvmem: nvmem device to be written to.
197962306a36Sopenharmony_ci * @info: nvmem cell info to be written.
198062306a36Sopenharmony_ci * @buf: buffer to be written to cell.
198162306a36Sopenharmony_ci *
198262306a36Sopenharmony_ci * Return: length of bytes written or negative error code on failure.
198362306a36Sopenharmony_ci */
198462306a36Sopenharmony_ciint nvmem_device_cell_write(struct nvmem_device *nvmem,
198562306a36Sopenharmony_ci			    struct nvmem_cell_info *info, void *buf)
198662306a36Sopenharmony_ci{
198762306a36Sopenharmony_ci	struct nvmem_cell_entry cell;
198862306a36Sopenharmony_ci	int rc;
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_ci	if (!nvmem)
199162306a36Sopenharmony_ci		return -EINVAL;
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	rc = nvmem_cell_info_to_nvmem_cell_entry_nodup(nvmem, info, &cell);
199462306a36Sopenharmony_ci	if (rc)
199562306a36Sopenharmony_ci		return rc;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	return __nvmem_cell_entry_write(&cell, buf, cell.bytes);
199862306a36Sopenharmony_ci}
199962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_cell_write);
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_ci/**
200262306a36Sopenharmony_ci * nvmem_device_read() - Read from a given nvmem device
200362306a36Sopenharmony_ci *
200462306a36Sopenharmony_ci * @nvmem: nvmem device to read from.
200562306a36Sopenharmony_ci * @offset: offset in nvmem device.
200662306a36Sopenharmony_ci * @bytes: number of bytes to read.
200762306a36Sopenharmony_ci * @buf: buffer pointer which will be populated on successful read.
200862306a36Sopenharmony_ci *
200962306a36Sopenharmony_ci * Return: length of successful bytes read on success and negative
201062306a36Sopenharmony_ci * error code on error.
201162306a36Sopenharmony_ci */
201262306a36Sopenharmony_ciint nvmem_device_read(struct nvmem_device *nvmem,
201362306a36Sopenharmony_ci		      unsigned int offset,
201462306a36Sopenharmony_ci		      size_t bytes, void *buf)
201562306a36Sopenharmony_ci{
201662306a36Sopenharmony_ci	int rc;
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	if (!nvmem)
201962306a36Sopenharmony_ci		return -EINVAL;
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	rc = nvmem_reg_read(nvmem, offset, buf, bytes);
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	if (rc)
202462306a36Sopenharmony_ci		return rc;
202562306a36Sopenharmony_ci
202662306a36Sopenharmony_ci	return bytes;
202762306a36Sopenharmony_ci}
202862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_read);
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci/**
203162306a36Sopenharmony_ci * nvmem_device_write() - Write cell to a given nvmem device
203262306a36Sopenharmony_ci *
203362306a36Sopenharmony_ci * @nvmem: nvmem device to be written to.
203462306a36Sopenharmony_ci * @offset: offset in nvmem device.
203562306a36Sopenharmony_ci * @bytes: number of bytes to write.
203662306a36Sopenharmony_ci * @buf: buffer to be written.
203762306a36Sopenharmony_ci *
203862306a36Sopenharmony_ci * Return: length of bytes written or negative error code on failure.
203962306a36Sopenharmony_ci */
204062306a36Sopenharmony_ciint nvmem_device_write(struct nvmem_device *nvmem,
204162306a36Sopenharmony_ci		       unsigned int offset,
204262306a36Sopenharmony_ci		       size_t bytes, void *buf)
204362306a36Sopenharmony_ci{
204462306a36Sopenharmony_ci	int rc;
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	if (!nvmem)
204762306a36Sopenharmony_ci		return -EINVAL;
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_ci	rc = nvmem_reg_write(nvmem, offset, buf, bytes);
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	if (rc)
205262306a36Sopenharmony_ci		return rc;
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	return bytes;
205662306a36Sopenharmony_ci}
205762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_write);
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci/**
206062306a36Sopenharmony_ci * nvmem_add_cell_table() - register a table of cell info entries
206162306a36Sopenharmony_ci *
206262306a36Sopenharmony_ci * @table: table of cell info entries
206362306a36Sopenharmony_ci */
206462306a36Sopenharmony_civoid nvmem_add_cell_table(struct nvmem_cell_table *table)
206562306a36Sopenharmony_ci{
206662306a36Sopenharmony_ci	mutex_lock(&nvmem_cell_mutex);
206762306a36Sopenharmony_ci	list_add_tail(&table->node, &nvmem_cell_tables);
206862306a36Sopenharmony_ci	mutex_unlock(&nvmem_cell_mutex);
206962306a36Sopenharmony_ci}
207062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_add_cell_table);
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci/**
207362306a36Sopenharmony_ci * nvmem_del_cell_table() - remove a previously registered cell info table
207462306a36Sopenharmony_ci *
207562306a36Sopenharmony_ci * @table: table of cell info entries
207662306a36Sopenharmony_ci */
207762306a36Sopenharmony_civoid nvmem_del_cell_table(struct nvmem_cell_table *table)
207862306a36Sopenharmony_ci{
207962306a36Sopenharmony_ci	mutex_lock(&nvmem_cell_mutex);
208062306a36Sopenharmony_ci	list_del(&table->node);
208162306a36Sopenharmony_ci	mutex_unlock(&nvmem_cell_mutex);
208262306a36Sopenharmony_ci}
208362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_del_cell_table);
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci/**
208662306a36Sopenharmony_ci * nvmem_add_cell_lookups() - register a list of cell lookup entries
208762306a36Sopenharmony_ci *
208862306a36Sopenharmony_ci * @entries: array of cell lookup entries
208962306a36Sopenharmony_ci * @nentries: number of cell lookup entries in the array
209062306a36Sopenharmony_ci */
209162306a36Sopenharmony_civoid nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
209262306a36Sopenharmony_ci{
209362306a36Sopenharmony_ci	int i;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ci	mutex_lock(&nvmem_lookup_mutex);
209662306a36Sopenharmony_ci	for (i = 0; i < nentries; i++)
209762306a36Sopenharmony_ci		list_add_tail(&entries[i].node, &nvmem_lookup_list);
209862306a36Sopenharmony_ci	mutex_unlock(&nvmem_lookup_mutex);
209962306a36Sopenharmony_ci}
210062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_add_cell_lookups);
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci/**
210362306a36Sopenharmony_ci * nvmem_del_cell_lookups() - remove a list of previously added cell lookup
210462306a36Sopenharmony_ci *                            entries
210562306a36Sopenharmony_ci *
210662306a36Sopenharmony_ci * @entries: array of cell lookup entries
210762306a36Sopenharmony_ci * @nentries: number of cell lookup entries in the array
210862306a36Sopenharmony_ci */
210962306a36Sopenharmony_civoid nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
211062306a36Sopenharmony_ci{
211162306a36Sopenharmony_ci	int i;
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	mutex_lock(&nvmem_lookup_mutex);
211462306a36Sopenharmony_ci	for (i = 0; i < nentries; i++)
211562306a36Sopenharmony_ci		list_del(&entries[i].node);
211662306a36Sopenharmony_ci	mutex_unlock(&nvmem_lookup_mutex);
211762306a36Sopenharmony_ci}
211862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_del_cell_lookups);
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci/**
212162306a36Sopenharmony_ci * nvmem_dev_name() - Get the name of a given nvmem device.
212262306a36Sopenharmony_ci *
212362306a36Sopenharmony_ci * @nvmem: nvmem device.
212462306a36Sopenharmony_ci *
212562306a36Sopenharmony_ci * Return: name of the nvmem device.
212662306a36Sopenharmony_ci */
212762306a36Sopenharmony_ciconst char *nvmem_dev_name(struct nvmem_device *nvmem)
212862306a36Sopenharmony_ci{
212962306a36Sopenharmony_ci	return dev_name(&nvmem->dev);
213062306a36Sopenharmony_ci}
213162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_dev_name);
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_cistatic int __init nvmem_init(void)
213462306a36Sopenharmony_ci{
213562306a36Sopenharmony_ci	return bus_register(&nvmem_bus_type);
213662306a36Sopenharmony_ci}
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_cistatic void __exit nvmem_exit(void)
213962306a36Sopenharmony_ci{
214062306a36Sopenharmony_ci	bus_unregister(&nvmem_bus_type);
214162306a36Sopenharmony_ci}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_cisubsys_initcall(nvmem_init);
214462306a36Sopenharmony_cimodule_exit(nvmem_exit);
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ciMODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
214762306a36Sopenharmony_ciMODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
214862306a36Sopenharmony_ciMODULE_DESCRIPTION("nvmem Driver Core");
2149