18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * nvmem framework core.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
68c2ecf20Sopenharmony_ci * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/export.h>
118c2ecf20Sopenharmony_ci#include <linux/fs.h>
128c2ecf20Sopenharmony_ci#include <linux/idr.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/kref.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/nvmem-consumer.h>
178c2ecf20Sopenharmony_ci#include <linux/nvmem-provider.h>
188c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
198c2ecf20Sopenharmony_ci#include <linux/of.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct nvmem_device {
238c2ecf20Sopenharmony_ci	struct module		*owner;
248c2ecf20Sopenharmony_ci	struct device		dev;
258c2ecf20Sopenharmony_ci	int			stride;
268c2ecf20Sopenharmony_ci	int			word_size;
278c2ecf20Sopenharmony_ci	int			id;
288c2ecf20Sopenharmony_ci	struct kref		refcnt;
298c2ecf20Sopenharmony_ci	size_t			size;
308c2ecf20Sopenharmony_ci	bool			read_only;
318c2ecf20Sopenharmony_ci	bool			root_only;
328c2ecf20Sopenharmony_ci	int			flags;
338c2ecf20Sopenharmony_ci	enum nvmem_type		type;
348c2ecf20Sopenharmony_ci	struct bin_attribute	eeprom;
358c2ecf20Sopenharmony_ci	struct device		*base_dev;
368c2ecf20Sopenharmony_ci	struct list_head	cells;
378c2ecf20Sopenharmony_ci	nvmem_reg_read_t	reg_read;
388c2ecf20Sopenharmony_ci	nvmem_reg_write_t	reg_write;
398c2ecf20Sopenharmony_ci	struct gpio_desc	*wp_gpio;
408c2ecf20Sopenharmony_ci	void *priv;
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#define FLAG_COMPAT		BIT(0)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct nvmem_cell {
488c2ecf20Sopenharmony_ci	const char		*name;
498c2ecf20Sopenharmony_ci	int			offset;
508c2ecf20Sopenharmony_ci	int			bytes;
518c2ecf20Sopenharmony_ci	int			bit_offset;
528c2ecf20Sopenharmony_ci	int			nbits;
538c2ecf20Sopenharmony_ci	struct device_node	*np;
548c2ecf20Sopenharmony_ci	struct nvmem_device	*nvmem;
558c2ecf20Sopenharmony_ci	struct list_head	node;
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(nvmem_mutex);
598c2ecf20Sopenharmony_cistatic DEFINE_IDA(nvmem_ida);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(nvmem_cell_mutex);
628c2ecf20Sopenharmony_cistatic LIST_HEAD(nvmem_cell_tables);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(nvmem_lookup_mutex);
658c2ecf20Sopenharmony_cistatic LIST_HEAD(nvmem_lookup_list);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
708c2ecf20Sopenharmony_ci			  void *val, size_t bytes)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	if (nvmem->reg_read)
738c2ecf20Sopenharmony_ci		return nvmem->reg_read(nvmem->priv, offset, val, bytes);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return -EINVAL;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
798c2ecf20Sopenharmony_ci			   void *val, size_t bytes)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	int ret;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (nvmem->reg_write) {
848c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(nvmem->wp_gpio, 0);
858c2ecf20Sopenharmony_ci		ret = nvmem->reg_write(nvmem->priv, offset, val, bytes);
868c2ecf20Sopenharmony_ci		gpiod_set_value_cansleep(nvmem->wp_gpio, 1);
878c2ecf20Sopenharmony_ci		return ret;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return -EINVAL;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#ifdef CONFIG_NVMEM_SYSFS
948c2ecf20Sopenharmony_cistatic const char * const nvmem_type_str[] = {
958c2ecf20Sopenharmony_ci	[NVMEM_TYPE_UNKNOWN] = "Unknown",
968c2ecf20Sopenharmony_ci	[NVMEM_TYPE_EEPROM] = "EEPROM",
978c2ecf20Sopenharmony_ci	[NVMEM_TYPE_OTP] = "OTP",
988c2ecf20Sopenharmony_ci	[NVMEM_TYPE_BATTERY_BACKED] = "Battery backed",
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_LOCK_ALLOC
1028c2ecf20Sopenharmony_cistatic struct lock_class_key eeprom_lock_key;
1038c2ecf20Sopenharmony_ci#endif
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic ssize_t type_show(struct device *dev,
1068c2ecf20Sopenharmony_ci			 struct device_attribute *attr, char *buf)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem = to_nvmem_device(dev);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", nvmem_type_str[nvmem->type]);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(type);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic struct attribute *nvmem_attrs[] = {
1168c2ecf20Sopenharmony_ci	&dev_attr_type.attr,
1178c2ecf20Sopenharmony_ci	NULL,
1188c2ecf20Sopenharmony_ci};
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
1218c2ecf20Sopenharmony_ci				   struct bin_attribute *attr, char *buf,
1228c2ecf20Sopenharmony_ci				   loff_t pos, size_t count)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct device *dev;
1258c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem;
1268c2ecf20Sopenharmony_ci	int rc;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (attr->private)
1298c2ecf20Sopenharmony_ci		dev = attr->private;
1308c2ecf20Sopenharmony_ci	else
1318c2ecf20Sopenharmony_ci		dev = kobj_to_dev(kobj);
1328c2ecf20Sopenharmony_ci	nvmem = to_nvmem_device(dev);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/* Stop the user from reading */
1358c2ecf20Sopenharmony_ci	if (pos >= nvmem->size)
1368c2ecf20Sopenharmony_ci		return 0;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (!IS_ALIGNED(pos, nvmem->stride))
1398c2ecf20Sopenharmony_ci		return -EINVAL;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (count < nvmem->word_size)
1428c2ecf20Sopenharmony_ci		return -EINVAL;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (pos + count > nvmem->size)
1458c2ecf20Sopenharmony_ci		count = nvmem->size - pos;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	count = round_down(count, nvmem->word_size);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	if (!nvmem->reg_read)
1508c2ecf20Sopenharmony_ci		return -EPERM;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	rc = nvmem_reg_read(nvmem, pos, buf, count);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (rc)
1558c2ecf20Sopenharmony_ci		return rc;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return count;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj,
1618c2ecf20Sopenharmony_ci				    struct bin_attribute *attr, char *buf,
1628c2ecf20Sopenharmony_ci				    loff_t pos, size_t count)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct device *dev;
1658c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem;
1668c2ecf20Sopenharmony_ci	int rc;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	if (attr->private)
1698c2ecf20Sopenharmony_ci		dev = attr->private;
1708c2ecf20Sopenharmony_ci	else
1718c2ecf20Sopenharmony_ci		dev = kobj_to_dev(kobj);
1728c2ecf20Sopenharmony_ci	nvmem = to_nvmem_device(dev);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* Stop the user from writing */
1758c2ecf20Sopenharmony_ci	if (pos >= nvmem->size)
1768c2ecf20Sopenharmony_ci		return -EFBIG;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (!IS_ALIGNED(pos, nvmem->stride))
1798c2ecf20Sopenharmony_ci		return -EINVAL;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (count < nvmem->word_size)
1828c2ecf20Sopenharmony_ci		return -EINVAL;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	if (pos + count > nvmem->size)
1858c2ecf20Sopenharmony_ci		count = nvmem->size - pos;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	count = round_down(count, nvmem->word_size);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (!nvmem->reg_write)
1908c2ecf20Sopenharmony_ci		return -EPERM;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	rc = nvmem_reg_write(nvmem, pos, buf, count);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	if (rc)
1958c2ecf20Sopenharmony_ci		return rc;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return count;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic umode_t nvmem_bin_attr_get_umode(struct nvmem_device *nvmem)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	umode_t mode = 0400;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (!nvmem->root_only)
2058c2ecf20Sopenharmony_ci		mode |= 0044;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (!nvmem->read_only)
2088c2ecf20Sopenharmony_ci		mode |= 0200;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (!nvmem->reg_write)
2118c2ecf20Sopenharmony_ci		mode &= ~0200;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (!nvmem->reg_read)
2148c2ecf20Sopenharmony_ci		mode &= ~0444;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return mode;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic umode_t nvmem_bin_attr_is_visible(struct kobject *kobj,
2208c2ecf20Sopenharmony_ci					 struct bin_attribute *attr, int i)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
2238c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem = to_nvmem_device(dev);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	attr->size = nvmem->size;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return nvmem_bin_attr_get_umode(nvmem);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci/* default read/write permissions */
2318c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_rw_nvmem = {
2328c2ecf20Sopenharmony_ci	.attr	= {
2338c2ecf20Sopenharmony_ci		.name	= "nvmem",
2348c2ecf20Sopenharmony_ci		.mode	= 0644,
2358c2ecf20Sopenharmony_ci	},
2368c2ecf20Sopenharmony_ci	.read	= bin_attr_nvmem_read,
2378c2ecf20Sopenharmony_ci	.write	= bin_attr_nvmem_write,
2388c2ecf20Sopenharmony_ci};
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic struct bin_attribute *nvmem_bin_attributes[] = {
2418c2ecf20Sopenharmony_ci	&bin_attr_rw_nvmem,
2428c2ecf20Sopenharmony_ci	NULL,
2438c2ecf20Sopenharmony_ci};
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_cistatic const struct attribute_group nvmem_bin_group = {
2468c2ecf20Sopenharmony_ci	.bin_attrs	= nvmem_bin_attributes,
2478c2ecf20Sopenharmony_ci	.attrs		= nvmem_attrs,
2488c2ecf20Sopenharmony_ci	.is_bin_visible = nvmem_bin_attr_is_visible,
2498c2ecf20Sopenharmony_ci};
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic const struct attribute_group *nvmem_dev_groups[] = {
2528c2ecf20Sopenharmony_ci	&nvmem_bin_group,
2538c2ecf20Sopenharmony_ci	NULL,
2548c2ecf20Sopenharmony_ci};
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_cistatic struct bin_attribute bin_attr_nvmem_eeprom_compat = {
2578c2ecf20Sopenharmony_ci	.attr	= {
2588c2ecf20Sopenharmony_ci		.name	= "eeprom",
2598c2ecf20Sopenharmony_ci	},
2608c2ecf20Sopenharmony_ci	.read	= bin_attr_nvmem_read,
2618c2ecf20Sopenharmony_ci	.write	= bin_attr_nvmem_write,
2628c2ecf20Sopenharmony_ci};
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci/*
2658c2ecf20Sopenharmony_ci * nvmem_setup_compat() - Create an additional binary entry in
2668c2ecf20Sopenharmony_ci * drivers sys directory, to be backwards compatible with the older
2678c2ecf20Sopenharmony_ci * drivers/misc/eeprom drivers.
2688c2ecf20Sopenharmony_ci */
2698c2ecf20Sopenharmony_cistatic int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
2708c2ecf20Sopenharmony_ci				    const struct nvmem_config *config)
2718c2ecf20Sopenharmony_ci{
2728c2ecf20Sopenharmony_ci	int rval;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (!config->compat)
2758c2ecf20Sopenharmony_ci		return 0;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (!config->base_dev)
2788c2ecf20Sopenharmony_ci		return -EINVAL;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	nvmem->eeprom = bin_attr_nvmem_eeprom_compat;
2818c2ecf20Sopenharmony_ci	nvmem->eeprom.attr.mode = nvmem_bin_attr_get_umode(nvmem);
2828c2ecf20Sopenharmony_ci	nvmem->eeprom.size = nvmem->size;
2838c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_LOCK_ALLOC
2848c2ecf20Sopenharmony_ci	nvmem->eeprom.attr.key = &eeprom_lock_key;
2858c2ecf20Sopenharmony_ci#endif
2868c2ecf20Sopenharmony_ci	nvmem->eeprom.private = &nvmem->dev;
2878c2ecf20Sopenharmony_ci	nvmem->base_dev = config->base_dev;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	rval = device_create_bin_file(nvmem->base_dev, &nvmem->eeprom);
2908c2ecf20Sopenharmony_ci	if (rval) {
2918c2ecf20Sopenharmony_ci		dev_err(&nvmem->dev,
2928c2ecf20Sopenharmony_ci			"Failed to create eeprom binary file %d\n", rval);
2938c2ecf20Sopenharmony_ci		return rval;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	nvmem->flags |= FLAG_COMPAT;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	return 0;
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
3028c2ecf20Sopenharmony_ci			      const struct nvmem_config *config)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	if (config->compat)
3058c2ecf20Sopenharmony_ci		device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
3068c2ecf20Sopenharmony_ci}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci#else /* CONFIG_NVMEM_SYSFS */
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_cistatic int nvmem_sysfs_setup_compat(struct nvmem_device *nvmem,
3118c2ecf20Sopenharmony_ci				    const struct nvmem_config *config)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	return -ENOSYS;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_cistatic void nvmem_sysfs_remove_compat(struct nvmem_device *nvmem,
3168c2ecf20Sopenharmony_ci				      const struct nvmem_config *config)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci#endif /* CONFIG_NVMEM_SYSFS */
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void nvmem_release(struct device *dev)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem = to_nvmem_device(dev);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	ida_free(&nvmem_ida, nvmem->id);
3278c2ecf20Sopenharmony_ci	gpiod_put(nvmem->wp_gpio);
3288c2ecf20Sopenharmony_ci	kfree(nvmem);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic const struct device_type nvmem_provider_type = {
3328c2ecf20Sopenharmony_ci	.release	= nvmem_release,
3338c2ecf20Sopenharmony_ci};
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic struct bus_type nvmem_bus_type = {
3368c2ecf20Sopenharmony_ci	.name		= "nvmem",
3378c2ecf20Sopenharmony_ci};
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_cistatic void nvmem_cell_drop(struct nvmem_cell *cell)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_REMOVE, cell);
3428c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_mutex);
3438c2ecf20Sopenharmony_ci	list_del(&cell->node);
3448c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
3458c2ecf20Sopenharmony_ci	of_node_put(cell->np);
3468c2ecf20Sopenharmony_ci	kfree_const(cell->name);
3478c2ecf20Sopenharmony_ci	kfree(cell);
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct nvmem_cell *cell, *p;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cell, p, &nvmem->cells, node)
3558c2ecf20Sopenharmony_ci		nvmem_cell_drop(cell);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic void nvmem_cell_add(struct nvmem_cell *cell)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_mutex);
3618c2ecf20Sopenharmony_ci	list_add_tail(&cell->node, &cell->nvmem->cells);
3628c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
3638c2ecf20Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_CELL_ADD, cell);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic int nvmem_cell_info_to_nvmem_cell_nodup(struct nvmem_device *nvmem,
3678c2ecf20Sopenharmony_ci					const struct nvmem_cell_info *info,
3688c2ecf20Sopenharmony_ci					struct nvmem_cell *cell)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	cell->nvmem = nvmem;
3718c2ecf20Sopenharmony_ci	cell->offset = info->offset;
3728c2ecf20Sopenharmony_ci	cell->bytes = info->bytes;
3738c2ecf20Sopenharmony_ci	cell->name = info->name;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	cell->bit_offset = info->bit_offset;
3768c2ecf20Sopenharmony_ci	cell->nbits = info->nbits;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (cell->nbits)
3798c2ecf20Sopenharmony_ci		cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset,
3808c2ecf20Sopenharmony_ci					   BITS_PER_BYTE);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
3838c2ecf20Sopenharmony_ci		dev_err(&nvmem->dev,
3848c2ecf20Sopenharmony_ci			"cell %s unaligned to nvmem stride %d\n",
3858c2ecf20Sopenharmony_ci			cell->name ?: "<unknown>", nvmem->stride);
3868c2ecf20Sopenharmony_ci		return -EINVAL;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	return 0;
3908c2ecf20Sopenharmony_ci}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_cistatic int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem,
3938c2ecf20Sopenharmony_ci				const struct nvmem_cell_info *info,
3948c2ecf20Sopenharmony_ci				struct nvmem_cell *cell)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	int err;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	err = nvmem_cell_info_to_nvmem_cell_nodup(nvmem, info, cell);
3998c2ecf20Sopenharmony_ci	if (err)
4008c2ecf20Sopenharmony_ci		return err;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	cell->name = kstrdup_const(info->name, GFP_KERNEL);
4038c2ecf20Sopenharmony_ci	if (!cell->name)
4048c2ecf20Sopenharmony_ci		return -ENOMEM;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	return 0;
4078c2ecf20Sopenharmony_ci}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci/**
4108c2ecf20Sopenharmony_ci * nvmem_add_cells() - Add cell information to an nvmem device
4118c2ecf20Sopenharmony_ci *
4128c2ecf20Sopenharmony_ci * @nvmem: nvmem device to add cells to.
4138c2ecf20Sopenharmony_ci * @info: nvmem cell info to add to the device
4148c2ecf20Sopenharmony_ci * @ncells: number of cells in info
4158c2ecf20Sopenharmony_ci *
4168c2ecf20Sopenharmony_ci * Return: 0 or negative error code on failure.
4178c2ecf20Sopenharmony_ci */
4188c2ecf20Sopenharmony_cistatic int nvmem_add_cells(struct nvmem_device *nvmem,
4198c2ecf20Sopenharmony_ci		    const struct nvmem_cell_info *info,
4208c2ecf20Sopenharmony_ci		    int ncells)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	struct nvmem_cell **cells;
4238c2ecf20Sopenharmony_ci	int i, rval;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	cells = kcalloc(ncells, sizeof(*cells), GFP_KERNEL);
4268c2ecf20Sopenharmony_ci	if (!cells)
4278c2ecf20Sopenharmony_ci		return -ENOMEM;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	for (i = 0; i < ncells; i++) {
4308c2ecf20Sopenharmony_ci		cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL);
4318c2ecf20Sopenharmony_ci		if (!cells[i]) {
4328c2ecf20Sopenharmony_ci			rval = -ENOMEM;
4338c2ecf20Sopenharmony_ci			goto err;
4348c2ecf20Sopenharmony_ci		}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci		rval = nvmem_cell_info_to_nvmem_cell(nvmem, &info[i], cells[i]);
4378c2ecf20Sopenharmony_ci		if (rval) {
4388c2ecf20Sopenharmony_ci			kfree(cells[i]);
4398c2ecf20Sopenharmony_ci			goto err;
4408c2ecf20Sopenharmony_ci		}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci		nvmem_cell_add(cells[i]);
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	/* remove tmp array */
4468c2ecf20Sopenharmony_ci	kfree(cells);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return 0;
4498c2ecf20Sopenharmony_cierr:
4508c2ecf20Sopenharmony_ci	while (i--)
4518c2ecf20Sopenharmony_ci		nvmem_cell_drop(cells[i]);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	kfree(cells);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	return rval;
4568c2ecf20Sopenharmony_ci}
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci/**
4598c2ecf20Sopenharmony_ci * nvmem_register_notifier() - Register a notifier block for nvmem events.
4608c2ecf20Sopenharmony_ci *
4618c2ecf20Sopenharmony_ci * @nb: notifier block to be called on nvmem events.
4628c2ecf20Sopenharmony_ci *
4638c2ecf20Sopenharmony_ci * Return: 0 on success, negative error number on failure.
4648c2ecf20Sopenharmony_ci */
4658c2ecf20Sopenharmony_ciint nvmem_register_notifier(struct notifier_block *nb)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	return blocking_notifier_chain_register(&nvmem_notifier, nb);
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_register_notifier);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/**
4728c2ecf20Sopenharmony_ci * nvmem_unregister_notifier() - Unregister a notifier block for nvmem events.
4738c2ecf20Sopenharmony_ci *
4748c2ecf20Sopenharmony_ci * @nb: notifier block to be unregistered.
4758c2ecf20Sopenharmony_ci *
4768c2ecf20Sopenharmony_ci * Return: 0 on success, negative error number on failure.
4778c2ecf20Sopenharmony_ci */
4788c2ecf20Sopenharmony_ciint nvmem_unregister_notifier(struct notifier_block *nb)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	return blocking_notifier_chain_unregister(&nvmem_notifier, nb);
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_unregister_notifier);
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic int nvmem_add_cells_from_table(struct nvmem_device *nvmem)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	const struct nvmem_cell_info *info;
4878c2ecf20Sopenharmony_ci	struct nvmem_cell_table *table;
4888c2ecf20Sopenharmony_ci	struct nvmem_cell *cell;
4898c2ecf20Sopenharmony_ci	int rval = 0, i;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_cell_mutex);
4928c2ecf20Sopenharmony_ci	list_for_each_entry(table, &nvmem_cell_tables, node) {
4938c2ecf20Sopenharmony_ci		if (strcmp(nvmem_dev_name(nvmem), table->nvmem_name) == 0) {
4948c2ecf20Sopenharmony_ci			for (i = 0; i < table->ncells; i++) {
4958c2ecf20Sopenharmony_ci				info = &table->cells[i];
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci				cell = kzalloc(sizeof(*cell), GFP_KERNEL);
4988c2ecf20Sopenharmony_ci				if (!cell) {
4998c2ecf20Sopenharmony_ci					rval = -ENOMEM;
5008c2ecf20Sopenharmony_ci					goto out;
5018c2ecf20Sopenharmony_ci				}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci				rval = nvmem_cell_info_to_nvmem_cell(nvmem,
5048c2ecf20Sopenharmony_ci								     info,
5058c2ecf20Sopenharmony_ci								     cell);
5068c2ecf20Sopenharmony_ci				if (rval) {
5078c2ecf20Sopenharmony_ci					kfree(cell);
5088c2ecf20Sopenharmony_ci					goto out;
5098c2ecf20Sopenharmony_ci				}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci				nvmem_cell_add(cell);
5128c2ecf20Sopenharmony_ci			}
5138c2ecf20Sopenharmony_ci		}
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ciout:
5178c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_cell_mutex);
5188c2ecf20Sopenharmony_ci	return rval;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic struct nvmem_cell *
5228c2ecf20Sopenharmony_cinvmem_find_cell_by_name(struct nvmem_device *nvmem, const char *cell_id)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	struct nvmem_cell *iter, *cell = NULL;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_mutex);
5278c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &nvmem->cells, node) {
5288c2ecf20Sopenharmony_ci		if (strcmp(cell_id, iter->name) == 0) {
5298c2ecf20Sopenharmony_ci			cell = iter;
5308c2ecf20Sopenharmony_ci			break;
5318c2ecf20Sopenharmony_ci		}
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	return cell;
5368c2ecf20Sopenharmony_ci}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_cistatic int nvmem_add_cells_from_of(struct nvmem_device *nvmem)
5398c2ecf20Sopenharmony_ci{
5408c2ecf20Sopenharmony_ci	struct device_node *parent, *child;
5418c2ecf20Sopenharmony_ci	struct device *dev = &nvmem->dev;
5428c2ecf20Sopenharmony_ci	struct nvmem_cell *cell;
5438c2ecf20Sopenharmony_ci	const __be32 *addr;
5448c2ecf20Sopenharmony_ci	int len;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	parent = dev->of_node;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	for_each_child_of_node(parent, child) {
5498c2ecf20Sopenharmony_ci		addr = of_get_property(child, "reg", &len);
5508c2ecf20Sopenharmony_ci		if (!addr)
5518c2ecf20Sopenharmony_ci			continue;
5528c2ecf20Sopenharmony_ci		if (len < 2 * sizeof(u32)) {
5538c2ecf20Sopenharmony_ci			dev_err(dev, "nvmem: invalid reg on %pOF\n", child);
5548c2ecf20Sopenharmony_ci			of_node_put(child);
5558c2ecf20Sopenharmony_ci			return -EINVAL;
5568c2ecf20Sopenharmony_ci		}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci		cell = kzalloc(sizeof(*cell), GFP_KERNEL);
5598c2ecf20Sopenharmony_ci		if (!cell) {
5608c2ecf20Sopenharmony_ci			of_node_put(child);
5618c2ecf20Sopenharmony_ci			return -ENOMEM;
5628c2ecf20Sopenharmony_ci		}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci		cell->nvmem = nvmem;
5658c2ecf20Sopenharmony_ci		cell->offset = be32_to_cpup(addr++);
5668c2ecf20Sopenharmony_ci		cell->bytes = be32_to_cpup(addr);
5678c2ecf20Sopenharmony_ci		cell->name = kasprintf(GFP_KERNEL, "%pOFn", child);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		addr = of_get_property(child, "bits", &len);
5708c2ecf20Sopenharmony_ci		if (addr && len == (2 * sizeof(u32))) {
5718c2ecf20Sopenharmony_ci			cell->bit_offset = be32_to_cpup(addr++);
5728c2ecf20Sopenharmony_ci			cell->nbits = be32_to_cpup(addr);
5738c2ecf20Sopenharmony_ci		}
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		if (cell->nbits)
5768c2ecf20Sopenharmony_ci			cell->bytes = DIV_ROUND_UP(
5778c2ecf20Sopenharmony_ci					cell->nbits + cell->bit_offset,
5788c2ecf20Sopenharmony_ci					BITS_PER_BYTE);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci		if (!IS_ALIGNED(cell->offset, nvmem->stride)) {
5818c2ecf20Sopenharmony_ci			dev_err(dev, "cell %s unaligned to nvmem stride %d\n",
5828c2ecf20Sopenharmony_ci				cell->name, nvmem->stride);
5838c2ecf20Sopenharmony_ci			/* Cells already added will be freed later. */
5848c2ecf20Sopenharmony_ci			kfree_const(cell->name);
5858c2ecf20Sopenharmony_ci			kfree(cell);
5868c2ecf20Sopenharmony_ci			of_node_put(child);
5878c2ecf20Sopenharmony_ci			return -EINVAL;
5888c2ecf20Sopenharmony_ci		}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci		cell->np = of_node_get(child);
5918c2ecf20Sopenharmony_ci		nvmem_cell_add(cell);
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	return 0;
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci/**
5988c2ecf20Sopenharmony_ci * nvmem_register() - Register a nvmem device for given nvmem_config.
5998c2ecf20Sopenharmony_ci * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
6008c2ecf20Sopenharmony_ci *
6018c2ecf20Sopenharmony_ci * @config: nvmem device configuration with which nvmem device is created.
6028c2ecf20Sopenharmony_ci *
6038c2ecf20Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
6048c2ecf20Sopenharmony_ci * on success.
6058c2ecf20Sopenharmony_ci */
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistruct nvmem_device *nvmem_register(const struct nvmem_config *config)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem;
6108c2ecf20Sopenharmony_ci	int rval;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (!config->dev)
6138c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (!config->reg_read && !config->reg_write)
6168c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	nvmem = kzalloc(sizeof(*nvmem), GFP_KERNEL);
6198c2ecf20Sopenharmony_ci	if (!nvmem)
6208c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	rval  = ida_alloc(&nvmem_ida, GFP_KERNEL);
6238c2ecf20Sopenharmony_ci	if (rval < 0) {
6248c2ecf20Sopenharmony_ci		kfree(nvmem);
6258c2ecf20Sopenharmony_ci		return ERR_PTR(rval);
6268c2ecf20Sopenharmony_ci	}
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	nvmem->id = rval;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	nvmem->dev.type = &nvmem_provider_type;
6318c2ecf20Sopenharmony_ci	nvmem->dev.bus = &nvmem_bus_type;
6328c2ecf20Sopenharmony_ci	nvmem->dev.parent = config->dev;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	device_initialize(&nvmem->dev);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (!config->ignore_wp)
6378c2ecf20Sopenharmony_ci		nvmem->wp_gpio = gpiod_get_optional(config->dev, "wp",
6388c2ecf20Sopenharmony_ci						    GPIOD_OUT_HIGH);
6398c2ecf20Sopenharmony_ci	if (IS_ERR(nvmem->wp_gpio)) {
6408c2ecf20Sopenharmony_ci		rval = PTR_ERR(nvmem->wp_gpio);
6418c2ecf20Sopenharmony_ci		nvmem->wp_gpio = NULL;
6428c2ecf20Sopenharmony_ci		goto err_put_device;
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	kref_init(&nvmem->refcnt);
6468c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&nvmem->cells);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	nvmem->owner = config->owner;
6498c2ecf20Sopenharmony_ci	if (!nvmem->owner && config->dev->driver)
6508c2ecf20Sopenharmony_ci		nvmem->owner = config->dev->driver->owner;
6518c2ecf20Sopenharmony_ci	nvmem->stride = config->stride ?: 1;
6528c2ecf20Sopenharmony_ci	nvmem->word_size = config->word_size ?: 1;
6538c2ecf20Sopenharmony_ci	nvmem->size = config->size;
6548c2ecf20Sopenharmony_ci	nvmem->root_only = config->root_only;
6558c2ecf20Sopenharmony_ci	nvmem->priv = config->priv;
6568c2ecf20Sopenharmony_ci	nvmem->type = config->type;
6578c2ecf20Sopenharmony_ci	nvmem->reg_read = config->reg_read;
6588c2ecf20Sopenharmony_ci	nvmem->reg_write = config->reg_write;
6598c2ecf20Sopenharmony_ci	if (!config->no_of_node)
6608c2ecf20Sopenharmony_ci		nvmem->dev.of_node = config->dev->of_node;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	switch (config->id) {
6638c2ecf20Sopenharmony_ci	case NVMEM_DEVID_NONE:
6648c2ecf20Sopenharmony_ci		rval = dev_set_name(&nvmem->dev, "%s", config->name);
6658c2ecf20Sopenharmony_ci		break;
6668c2ecf20Sopenharmony_ci	case NVMEM_DEVID_AUTO:
6678c2ecf20Sopenharmony_ci		rval = dev_set_name(&nvmem->dev, "%s%d", config->name, nvmem->id);
6688c2ecf20Sopenharmony_ci		break;
6698c2ecf20Sopenharmony_ci	default:
6708c2ecf20Sopenharmony_ci		rval = dev_set_name(&nvmem->dev, "%s%d",
6718c2ecf20Sopenharmony_ci			     config->name ? : "nvmem",
6728c2ecf20Sopenharmony_ci			     config->name ? config->id : nvmem->id);
6738c2ecf20Sopenharmony_ci		break;
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	if (rval)
6778c2ecf20Sopenharmony_ci		goto err_put_device;
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci	nvmem->read_only = device_property_present(config->dev, "read-only") ||
6808c2ecf20Sopenharmony_ci			   config->read_only || !nvmem->reg_write;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci#ifdef CONFIG_NVMEM_SYSFS
6838c2ecf20Sopenharmony_ci	nvmem->dev.groups = nvmem_dev_groups;
6848c2ecf20Sopenharmony_ci#endif
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if (config->compat) {
6878c2ecf20Sopenharmony_ci		rval = nvmem_sysfs_setup_compat(nvmem, config);
6888c2ecf20Sopenharmony_ci		if (rval)
6898c2ecf20Sopenharmony_ci			goto err_put_device;
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (config->cells) {
6938c2ecf20Sopenharmony_ci		rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
6948c2ecf20Sopenharmony_ci		if (rval)
6958c2ecf20Sopenharmony_ci			goto err_remove_cells;
6968c2ecf20Sopenharmony_ci	}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	rval = nvmem_add_cells_from_table(nvmem);
6998c2ecf20Sopenharmony_ci	if (rval)
7008c2ecf20Sopenharmony_ci		goto err_remove_cells;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	rval = nvmem_add_cells_from_of(nvmem);
7038c2ecf20Sopenharmony_ci	if (rval)
7048c2ecf20Sopenharmony_ci		goto err_remove_cells;
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci	rval = device_add(&nvmem->dev);
7098c2ecf20Sopenharmony_ci	if (rval)
7108c2ecf20Sopenharmony_ci		goto err_remove_cells;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_ADD, nvmem);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	return nvmem;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cierr_remove_cells:
7178c2ecf20Sopenharmony_ci	nvmem_device_remove_all_cells(nvmem);
7188c2ecf20Sopenharmony_ci	if (config->compat)
7198c2ecf20Sopenharmony_ci		nvmem_sysfs_remove_compat(nvmem, config);
7208c2ecf20Sopenharmony_cierr_put_device:
7218c2ecf20Sopenharmony_ci	put_device(&nvmem->dev);
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	return ERR_PTR(rval);
7248c2ecf20Sopenharmony_ci}
7258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_register);
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_cistatic void nvmem_device_release(struct kref *kref)
7288c2ecf20Sopenharmony_ci{
7298c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	nvmem = container_of(kref, struct nvmem_device, refcnt);
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	blocking_notifier_call_chain(&nvmem_notifier, NVMEM_REMOVE, nvmem);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci	if (nvmem->flags & FLAG_COMPAT)
7368c2ecf20Sopenharmony_ci		device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	nvmem_device_remove_all_cells(nvmem);
7398c2ecf20Sopenharmony_ci	device_unregister(&nvmem->dev);
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci/**
7438c2ecf20Sopenharmony_ci * nvmem_unregister() - Unregister previously registered nvmem device
7448c2ecf20Sopenharmony_ci *
7458c2ecf20Sopenharmony_ci * @nvmem: Pointer to previously registered nvmem device.
7468c2ecf20Sopenharmony_ci */
7478c2ecf20Sopenharmony_civoid nvmem_unregister(struct nvmem_device *nvmem)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	kref_put(&nvmem->refcnt, nvmem_device_release);
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_unregister);
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_cistatic void devm_nvmem_release(struct device *dev, void *res)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	nvmem_unregister(*(struct nvmem_device **)res);
7568c2ecf20Sopenharmony_ci}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci/**
7598c2ecf20Sopenharmony_ci * devm_nvmem_register() - Register a managed nvmem device for given
7608c2ecf20Sopenharmony_ci * nvmem_config.
7618c2ecf20Sopenharmony_ci * Also creates a binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
7628c2ecf20Sopenharmony_ci *
7638c2ecf20Sopenharmony_ci * @dev: Device that uses the nvmem device.
7648c2ecf20Sopenharmony_ci * @config: nvmem device configuration with which nvmem device is created.
7658c2ecf20Sopenharmony_ci *
7668c2ecf20Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device
7678c2ecf20Sopenharmony_ci * on success.
7688c2ecf20Sopenharmony_ci */
7698c2ecf20Sopenharmony_cistruct nvmem_device *devm_nvmem_register(struct device *dev,
7708c2ecf20Sopenharmony_ci					 const struct nvmem_config *config)
7718c2ecf20Sopenharmony_ci{
7728c2ecf20Sopenharmony_ci	struct nvmem_device **ptr, *nvmem;
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	ptr = devres_alloc(devm_nvmem_release, sizeof(*ptr), GFP_KERNEL);
7758c2ecf20Sopenharmony_ci	if (!ptr)
7768c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	nvmem = nvmem_register(config);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	if (!IS_ERR(nvmem)) {
7818c2ecf20Sopenharmony_ci		*ptr = nvmem;
7828c2ecf20Sopenharmony_ci		devres_add(dev, ptr);
7838c2ecf20Sopenharmony_ci	} else {
7848c2ecf20Sopenharmony_ci		devres_free(ptr);
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	return nvmem;
7888c2ecf20Sopenharmony_ci}
7898c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_nvmem_register);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_cistatic int devm_nvmem_match(struct device *dev, void *res, void *data)
7928c2ecf20Sopenharmony_ci{
7938c2ecf20Sopenharmony_ci	struct nvmem_device **r = res;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	return *r == data;
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci/**
7998c2ecf20Sopenharmony_ci * devm_nvmem_unregister() - Unregister previously registered managed nvmem
8008c2ecf20Sopenharmony_ci * device.
8018c2ecf20Sopenharmony_ci *
8028c2ecf20Sopenharmony_ci * @dev: Device that uses the nvmem device.
8038c2ecf20Sopenharmony_ci * @nvmem: Pointer to previously registered nvmem device.
8048c2ecf20Sopenharmony_ci *
8058c2ecf20Sopenharmony_ci * Return: Will be negative on error or zero on success.
8068c2ecf20Sopenharmony_ci */
8078c2ecf20Sopenharmony_ciint devm_nvmem_unregister(struct device *dev, struct nvmem_device *nvmem)
8088c2ecf20Sopenharmony_ci{
8098c2ecf20Sopenharmony_ci	return devres_release(dev, devm_nvmem_release, devm_nvmem_match, nvmem);
8108c2ecf20Sopenharmony_ci}
8118c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_nvmem_unregister);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_cistatic struct nvmem_device *__nvmem_device_get(void *data,
8148c2ecf20Sopenharmony_ci			int (*match)(struct device *dev, const void *data))
8158c2ecf20Sopenharmony_ci{
8168c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem = NULL;
8178c2ecf20Sopenharmony_ci	struct device *dev;
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_mutex);
8208c2ecf20Sopenharmony_ci	dev = bus_find_device(&nvmem_bus_type, NULL, data, match);
8218c2ecf20Sopenharmony_ci	if (dev)
8228c2ecf20Sopenharmony_ci		nvmem = to_nvmem_device(dev);
8238c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
8248c2ecf20Sopenharmony_ci	if (!nvmem)
8258c2ecf20Sopenharmony_ci		return ERR_PTR(-EPROBE_DEFER);
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	if (!try_module_get(nvmem->owner)) {
8288c2ecf20Sopenharmony_ci		dev_err(&nvmem->dev,
8298c2ecf20Sopenharmony_ci			"could not increase module refcount for cell %s\n",
8308c2ecf20Sopenharmony_ci			nvmem_dev_name(nvmem));
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci		put_device(&nvmem->dev);
8338c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	kref_get(&nvmem->refcnt);
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	return nvmem;
8398c2ecf20Sopenharmony_ci}
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_cistatic void __nvmem_device_put(struct nvmem_device *nvmem)
8428c2ecf20Sopenharmony_ci{
8438c2ecf20Sopenharmony_ci	put_device(&nvmem->dev);
8448c2ecf20Sopenharmony_ci	module_put(nvmem->owner);
8458c2ecf20Sopenharmony_ci	kref_put(&nvmem->refcnt, nvmem_device_release);
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
8498c2ecf20Sopenharmony_ci/**
8508c2ecf20Sopenharmony_ci * of_nvmem_device_get() - Get nvmem device from a given id
8518c2ecf20Sopenharmony_ci *
8528c2ecf20Sopenharmony_ci * @np: Device tree node that uses the nvmem device.
8538c2ecf20Sopenharmony_ci * @id: nvmem name from nvmem-names property.
8548c2ecf20Sopenharmony_ci *
8558c2ecf20Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
8568c2ecf20Sopenharmony_ci * on success.
8578c2ecf20Sopenharmony_ci */
8588c2ecf20Sopenharmony_cistruct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id)
8598c2ecf20Sopenharmony_ci{
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	struct device_node *nvmem_np;
8628c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem;
8638c2ecf20Sopenharmony_ci	int index = 0;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	if (id)
8668c2ecf20Sopenharmony_ci		index = of_property_match_string(np, "nvmem-names", id);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	nvmem_np = of_parse_phandle(np, "nvmem", index);
8698c2ecf20Sopenharmony_ci	if (!nvmem_np)
8708c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
8738c2ecf20Sopenharmony_ci	of_node_put(nvmem_np);
8748c2ecf20Sopenharmony_ci	return nvmem;
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_nvmem_device_get);
8778c2ecf20Sopenharmony_ci#endif
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci/**
8808c2ecf20Sopenharmony_ci * nvmem_device_get() - Get nvmem device from a given id
8818c2ecf20Sopenharmony_ci *
8828c2ecf20Sopenharmony_ci * @dev: Device that uses the nvmem device.
8838c2ecf20Sopenharmony_ci * @dev_name: name of the requested nvmem device.
8848c2ecf20Sopenharmony_ci *
8858c2ecf20Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
8868c2ecf20Sopenharmony_ci * on success.
8878c2ecf20Sopenharmony_ci */
8888c2ecf20Sopenharmony_cistruct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	if (dev->of_node) { /* try dt first */
8918c2ecf20Sopenharmony_ci		struct nvmem_device *nvmem;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci		nvmem = of_nvmem_device_get(dev->of_node, dev_name);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci		if (!IS_ERR(nvmem) || PTR_ERR(nvmem) == -EPROBE_DEFER)
8968c2ecf20Sopenharmony_ci			return nvmem;
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	}
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	return __nvmem_device_get((void *)dev_name, device_match_name);
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_get);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci/**
9058c2ecf20Sopenharmony_ci * nvmem_device_find() - Find nvmem device with matching function
9068c2ecf20Sopenharmony_ci *
9078c2ecf20Sopenharmony_ci * @data: Data to pass to match function
9088c2ecf20Sopenharmony_ci * @match: Callback function to check device
9098c2ecf20Sopenharmony_ci *
9108c2ecf20Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
9118c2ecf20Sopenharmony_ci * on success.
9128c2ecf20Sopenharmony_ci */
9138c2ecf20Sopenharmony_cistruct nvmem_device *nvmem_device_find(void *data,
9148c2ecf20Sopenharmony_ci			int (*match)(struct device *dev, const void *data))
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	return __nvmem_device_get(data, match);
9178c2ecf20Sopenharmony_ci}
9188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_find);
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_cistatic int devm_nvmem_device_match(struct device *dev, void *res, void *data)
9218c2ecf20Sopenharmony_ci{
9228c2ecf20Sopenharmony_ci	struct nvmem_device **nvmem = res;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	if (WARN_ON(!nvmem || !*nvmem))
9258c2ecf20Sopenharmony_ci		return 0;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	return *nvmem == data;
9288c2ecf20Sopenharmony_ci}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_cistatic void devm_nvmem_device_release(struct device *dev, void *res)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	nvmem_device_put(*(struct nvmem_device **)res);
9338c2ecf20Sopenharmony_ci}
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci/**
9368c2ecf20Sopenharmony_ci * devm_nvmem_device_put() - put alredy got nvmem device
9378c2ecf20Sopenharmony_ci *
9388c2ecf20Sopenharmony_ci * @dev: Device that uses the nvmem device.
9398c2ecf20Sopenharmony_ci * @nvmem: pointer to nvmem device allocated by devm_nvmem_cell_get(),
9408c2ecf20Sopenharmony_ci * that needs to be released.
9418c2ecf20Sopenharmony_ci */
9428c2ecf20Sopenharmony_civoid devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem)
9438c2ecf20Sopenharmony_ci{
9448c2ecf20Sopenharmony_ci	int ret;
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci	ret = devres_release(dev, devm_nvmem_device_release,
9478c2ecf20Sopenharmony_ci			     devm_nvmem_device_match, nvmem);
9488c2ecf20Sopenharmony_ci
9498c2ecf20Sopenharmony_ci	WARN_ON(ret);
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_nvmem_device_put);
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci/**
9548c2ecf20Sopenharmony_ci * nvmem_device_put() - put alredy got nvmem device
9558c2ecf20Sopenharmony_ci *
9568c2ecf20Sopenharmony_ci * @nvmem: pointer to nvmem device that needs to be released.
9578c2ecf20Sopenharmony_ci */
9588c2ecf20Sopenharmony_civoid nvmem_device_put(struct nvmem_device *nvmem)
9598c2ecf20Sopenharmony_ci{
9608c2ecf20Sopenharmony_ci	__nvmem_device_put(nvmem);
9618c2ecf20Sopenharmony_ci}
9628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_put);
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci/**
9658c2ecf20Sopenharmony_ci * devm_nvmem_device_get() - Get nvmem cell of device form a given id
9668c2ecf20Sopenharmony_ci *
9678c2ecf20Sopenharmony_ci * @dev: Device that requests the nvmem device.
9688c2ecf20Sopenharmony_ci * @id: name id for the requested nvmem device.
9698c2ecf20Sopenharmony_ci *
9708c2ecf20Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_cell
9718c2ecf20Sopenharmony_ci * on success.  The nvmem_cell will be freed by the automatically once the
9728c2ecf20Sopenharmony_ci * device is freed.
9738c2ecf20Sopenharmony_ci */
9748c2ecf20Sopenharmony_cistruct nvmem_device *devm_nvmem_device_get(struct device *dev, const char *id)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	struct nvmem_device **ptr, *nvmem;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	ptr = devres_alloc(devm_nvmem_device_release, sizeof(*ptr), GFP_KERNEL);
9798c2ecf20Sopenharmony_ci	if (!ptr)
9808c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	nvmem = nvmem_device_get(dev, id);
9838c2ecf20Sopenharmony_ci	if (!IS_ERR(nvmem)) {
9848c2ecf20Sopenharmony_ci		*ptr = nvmem;
9858c2ecf20Sopenharmony_ci		devres_add(dev, ptr);
9868c2ecf20Sopenharmony_ci	} else {
9878c2ecf20Sopenharmony_ci		devres_free(ptr);
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	return nvmem;
9918c2ecf20Sopenharmony_ci}
9928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_nvmem_device_get);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_cistatic struct nvmem_cell *
9958c2ecf20Sopenharmony_cinvmem_cell_get_from_lookup(struct device *dev, const char *con_id)
9968c2ecf20Sopenharmony_ci{
9978c2ecf20Sopenharmony_ci	struct nvmem_cell *cell = ERR_PTR(-ENOENT);
9988c2ecf20Sopenharmony_ci	struct nvmem_cell_lookup *lookup;
9998c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem;
10008c2ecf20Sopenharmony_ci	const char *dev_id;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	if (!dev)
10038c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	dev_id = dev_name(dev);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_lookup_mutex);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	list_for_each_entry(lookup, &nvmem_lookup_list, node) {
10108c2ecf20Sopenharmony_ci		if ((strcmp(lookup->dev_id, dev_id) == 0) &&
10118c2ecf20Sopenharmony_ci		    (strcmp(lookup->con_id, con_id) == 0)) {
10128c2ecf20Sopenharmony_ci			/* This is the right entry. */
10138c2ecf20Sopenharmony_ci			nvmem = __nvmem_device_get((void *)lookup->nvmem_name,
10148c2ecf20Sopenharmony_ci						   device_match_name);
10158c2ecf20Sopenharmony_ci			if (IS_ERR(nvmem)) {
10168c2ecf20Sopenharmony_ci				/* Provider may not be registered yet. */
10178c2ecf20Sopenharmony_ci				cell = ERR_CAST(nvmem);
10188c2ecf20Sopenharmony_ci				break;
10198c2ecf20Sopenharmony_ci			}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci			cell = nvmem_find_cell_by_name(nvmem,
10228c2ecf20Sopenharmony_ci						       lookup->cell_name);
10238c2ecf20Sopenharmony_ci			if (!cell) {
10248c2ecf20Sopenharmony_ci				__nvmem_device_put(nvmem);
10258c2ecf20Sopenharmony_ci				cell = ERR_PTR(-ENOENT);
10268c2ecf20Sopenharmony_ci			}
10278c2ecf20Sopenharmony_ci			break;
10288c2ecf20Sopenharmony_ci		}
10298c2ecf20Sopenharmony_ci	}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_lookup_mutex);
10328c2ecf20Sopenharmony_ci	return cell;
10338c2ecf20Sopenharmony_ci}
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_OF)
10368c2ecf20Sopenharmony_cistatic struct nvmem_cell *
10378c2ecf20Sopenharmony_cinvmem_find_cell_by_node(struct nvmem_device *nvmem, struct device_node *np)
10388c2ecf20Sopenharmony_ci{
10398c2ecf20Sopenharmony_ci	struct nvmem_cell *iter, *cell = NULL;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_mutex);
10428c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &nvmem->cells, node) {
10438c2ecf20Sopenharmony_ci		if (np == iter->np) {
10448c2ecf20Sopenharmony_ci			cell = iter;
10458c2ecf20Sopenharmony_ci			break;
10468c2ecf20Sopenharmony_ci		}
10478c2ecf20Sopenharmony_ci	}
10488c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_mutex);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	return cell;
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci/**
10548c2ecf20Sopenharmony_ci * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id
10558c2ecf20Sopenharmony_ci *
10568c2ecf20Sopenharmony_ci * @np: Device tree node that uses the nvmem cell.
10578c2ecf20Sopenharmony_ci * @id: nvmem cell name from nvmem-cell-names property, or NULL
10588c2ecf20Sopenharmony_ci *      for the cell at index 0 (the lone cell with no accompanying
10598c2ecf20Sopenharmony_ci *      nvmem-cell-names property).
10608c2ecf20Sopenharmony_ci *
10618c2ecf20Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer
10628c2ecf20Sopenharmony_ci * to a struct nvmem_cell.  The nvmem_cell will be freed by the
10638c2ecf20Sopenharmony_ci * nvmem_cell_put().
10648c2ecf20Sopenharmony_ci */
10658c2ecf20Sopenharmony_cistruct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
10668c2ecf20Sopenharmony_ci{
10678c2ecf20Sopenharmony_ci	struct device_node *cell_np, *nvmem_np;
10688c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem;
10698c2ecf20Sopenharmony_ci	struct nvmem_cell *cell;
10708c2ecf20Sopenharmony_ci	int index = 0;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	/* if cell name exists, find index to the name */
10738c2ecf20Sopenharmony_ci	if (id)
10748c2ecf20Sopenharmony_ci		index = of_property_match_string(np, "nvmem-cell-names", id);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	cell_np = of_parse_phandle(np, "nvmem-cells", index);
10778c2ecf20Sopenharmony_ci	if (!cell_np)
10788c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci	nvmem_np = of_get_next_parent(cell_np);
10818c2ecf20Sopenharmony_ci	if (!nvmem_np)
10828c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	nvmem = __nvmem_device_get(nvmem_np, device_match_of_node);
10858c2ecf20Sopenharmony_ci	of_node_put(nvmem_np);
10868c2ecf20Sopenharmony_ci	if (IS_ERR(nvmem))
10878c2ecf20Sopenharmony_ci		return ERR_CAST(nvmem);
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_ci	cell = nvmem_find_cell_by_node(nvmem, cell_np);
10908c2ecf20Sopenharmony_ci	if (!cell) {
10918c2ecf20Sopenharmony_ci		__nvmem_device_put(nvmem);
10928c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOENT);
10938c2ecf20Sopenharmony_ci	}
10948c2ecf20Sopenharmony_ci
10958c2ecf20Sopenharmony_ci	return cell;
10968c2ecf20Sopenharmony_ci}
10978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(of_nvmem_cell_get);
10988c2ecf20Sopenharmony_ci#endif
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci/**
11018c2ecf20Sopenharmony_ci * nvmem_cell_get() - Get nvmem cell of device form a given cell name
11028c2ecf20Sopenharmony_ci *
11038c2ecf20Sopenharmony_ci * @dev: Device that requests the nvmem cell.
11048c2ecf20Sopenharmony_ci * @id: nvmem cell name to get (this corresponds with the name from the
11058c2ecf20Sopenharmony_ci *      nvmem-cell-names property for DT systems and with the con_id from
11068c2ecf20Sopenharmony_ci *      the lookup entry for non-DT systems).
11078c2ecf20Sopenharmony_ci *
11088c2ecf20Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer
11098c2ecf20Sopenharmony_ci * to a struct nvmem_cell.  The nvmem_cell will be freed by the
11108c2ecf20Sopenharmony_ci * nvmem_cell_put().
11118c2ecf20Sopenharmony_ci */
11128c2ecf20Sopenharmony_cistruct nvmem_cell *nvmem_cell_get(struct device *dev, const char *id)
11138c2ecf20Sopenharmony_ci{
11148c2ecf20Sopenharmony_ci	struct nvmem_cell *cell;
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci	if (dev->of_node) { /* try dt first */
11178c2ecf20Sopenharmony_ci		cell = of_nvmem_cell_get(dev->of_node, id);
11188c2ecf20Sopenharmony_ci		if (!IS_ERR(cell) || PTR_ERR(cell) == -EPROBE_DEFER)
11198c2ecf20Sopenharmony_ci			return cell;
11208c2ecf20Sopenharmony_ci	}
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci	/* NULL cell id only allowed for device tree; invalid otherwise */
11238c2ecf20Sopenharmony_ci	if (!id)
11248c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
11258c2ecf20Sopenharmony_ci
11268c2ecf20Sopenharmony_ci	return nvmem_cell_get_from_lookup(dev, id);
11278c2ecf20Sopenharmony_ci}
11288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_get);
11298c2ecf20Sopenharmony_ci
11308c2ecf20Sopenharmony_cistatic void devm_nvmem_cell_release(struct device *dev, void *res)
11318c2ecf20Sopenharmony_ci{
11328c2ecf20Sopenharmony_ci	nvmem_cell_put(*(struct nvmem_cell **)res);
11338c2ecf20Sopenharmony_ci}
11348c2ecf20Sopenharmony_ci
11358c2ecf20Sopenharmony_ci/**
11368c2ecf20Sopenharmony_ci * devm_nvmem_cell_get() - Get nvmem cell of device form a given id
11378c2ecf20Sopenharmony_ci *
11388c2ecf20Sopenharmony_ci * @dev: Device that requests the nvmem cell.
11398c2ecf20Sopenharmony_ci * @id: nvmem cell name id to get.
11408c2ecf20Sopenharmony_ci *
11418c2ecf20Sopenharmony_ci * Return: Will be an ERR_PTR() on error or a valid pointer
11428c2ecf20Sopenharmony_ci * to a struct nvmem_cell.  The nvmem_cell will be freed by the
11438c2ecf20Sopenharmony_ci * automatically once the device is freed.
11448c2ecf20Sopenharmony_ci */
11458c2ecf20Sopenharmony_cistruct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *id)
11468c2ecf20Sopenharmony_ci{
11478c2ecf20Sopenharmony_ci	struct nvmem_cell **ptr, *cell;
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	ptr = devres_alloc(devm_nvmem_cell_release, sizeof(*ptr), GFP_KERNEL);
11508c2ecf20Sopenharmony_ci	if (!ptr)
11518c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci	cell = nvmem_cell_get(dev, id);
11548c2ecf20Sopenharmony_ci	if (!IS_ERR(cell)) {
11558c2ecf20Sopenharmony_ci		*ptr = cell;
11568c2ecf20Sopenharmony_ci		devres_add(dev, ptr);
11578c2ecf20Sopenharmony_ci	} else {
11588c2ecf20Sopenharmony_ci		devres_free(ptr);
11598c2ecf20Sopenharmony_ci	}
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci	return cell;
11628c2ecf20Sopenharmony_ci}
11638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_nvmem_cell_get);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_cistatic int devm_nvmem_cell_match(struct device *dev, void *res, void *data)
11668c2ecf20Sopenharmony_ci{
11678c2ecf20Sopenharmony_ci	struct nvmem_cell **c = res;
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	if (WARN_ON(!c || !*c))
11708c2ecf20Sopenharmony_ci		return 0;
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_ci	return *c == data;
11738c2ecf20Sopenharmony_ci}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci/**
11768c2ecf20Sopenharmony_ci * devm_nvmem_cell_put() - Release previously allocated nvmem cell
11778c2ecf20Sopenharmony_ci * from devm_nvmem_cell_get.
11788c2ecf20Sopenharmony_ci *
11798c2ecf20Sopenharmony_ci * @dev: Device that requests the nvmem cell.
11808c2ecf20Sopenharmony_ci * @cell: Previously allocated nvmem cell by devm_nvmem_cell_get().
11818c2ecf20Sopenharmony_ci */
11828c2ecf20Sopenharmony_civoid devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell)
11838c2ecf20Sopenharmony_ci{
11848c2ecf20Sopenharmony_ci	int ret;
11858c2ecf20Sopenharmony_ci
11868c2ecf20Sopenharmony_ci	ret = devres_release(dev, devm_nvmem_cell_release,
11878c2ecf20Sopenharmony_ci				devm_nvmem_cell_match, cell);
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	WARN_ON(ret);
11908c2ecf20Sopenharmony_ci}
11918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(devm_nvmem_cell_put);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci/**
11948c2ecf20Sopenharmony_ci * nvmem_cell_put() - Release previously allocated nvmem cell.
11958c2ecf20Sopenharmony_ci *
11968c2ecf20Sopenharmony_ci * @cell: Previously allocated nvmem cell by nvmem_cell_get().
11978c2ecf20Sopenharmony_ci */
11988c2ecf20Sopenharmony_civoid nvmem_cell_put(struct nvmem_cell *cell)
11998c2ecf20Sopenharmony_ci{
12008c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem = cell->nvmem;
12018c2ecf20Sopenharmony_ci
12028c2ecf20Sopenharmony_ci	__nvmem_device_put(nvmem);
12038c2ecf20Sopenharmony_ci}
12048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_put);
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_cistatic void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
12078c2ecf20Sopenharmony_ci{
12088c2ecf20Sopenharmony_ci	u8 *p, *b;
12098c2ecf20Sopenharmony_ci	int i, extra, bit_offset = cell->bit_offset;
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	p = b = buf;
12128c2ecf20Sopenharmony_ci	if (bit_offset) {
12138c2ecf20Sopenharmony_ci		/* First shift */
12148c2ecf20Sopenharmony_ci		*b++ >>= bit_offset;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci		/* setup rest of the bytes if any */
12178c2ecf20Sopenharmony_ci		for (i = 1; i < cell->bytes; i++) {
12188c2ecf20Sopenharmony_ci			/* Get bits from next byte and shift them towards msb */
12198c2ecf20Sopenharmony_ci			*p |= *b << (BITS_PER_BYTE - bit_offset);
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci			p = b;
12228c2ecf20Sopenharmony_ci			*b++ >>= bit_offset;
12238c2ecf20Sopenharmony_ci		}
12248c2ecf20Sopenharmony_ci	} else {
12258c2ecf20Sopenharmony_ci		/* point to the msb */
12268c2ecf20Sopenharmony_ci		p += cell->bytes - 1;
12278c2ecf20Sopenharmony_ci	}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_ci	/* result fits in less bytes */
12308c2ecf20Sopenharmony_ci	extra = cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE);
12318c2ecf20Sopenharmony_ci	while (--extra >= 0)
12328c2ecf20Sopenharmony_ci		*p-- = 0;
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	/* clear msb bits if any leftover in the last byte */
12358c2ecf20Sopenharmony_ci	if (cell->nbits % BITS_PER_BYTE)
12368c2ecf20Sopenharmony_ci		*p &= GENMASK((cell->nbits % BITS_PER_BYTE) - 1, 0);
12378c2ecf20Sopenharmony_ci}
12388c2ecf20Sopenharmony_ci
12398c2ecf20Sopenharmony_cistatic int __nvmem_cell_read(struct nvmem_device *nvmem,
12408c2ecf20Sopenharmony_ci		      struct nvmem_cell *cell,
12418c2ecf20Sopenharmony_ci		      void *buf, size_t *len)
12428c2ecf20Sopenharmony_ci{
12438c2ecf20Sopenharmony_ci	int rc;
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci	rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes);
12468c2ecf20Sopenharmony_ci
12478c2ecf20Sopenharmony_ci	if (rc)
12488c2ecf20Sopenharmony_ci		return rc;
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	/* shift bits in-place */
12518c2ecf20Sopenharmony_ci	if (cell->bit_offset || cell->nbits)
12528c2ecf20Sopenharmony_ci		nvmem_shift_read_buffer_in_place(cell, buf);
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	if (len)
12558c2ecf20Sopenharmony_ci		*len = cell->bytes;
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	return 0;
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci/**
12618c2ecf20Sopenharmony_ci * nvmem_cell_read() - Read a given nvmem cell
12628c2ecf20Sopenharmony_ci *
12638c2ecf20Sopenharmony_ci * @cell: nvmem cell to be read.
12648c2ecf20Sopenharmony_ci * @len: pointer to length of cell which will be populated on successful read;
12658c2ecf20Sopenharmony_ci *	 can be NULL.
12668c2ecf20Sopenharmony_ci *
12678c2ecf20Sopenharmony_ci * Return: ERR_PTR() on error or a valid pointer to a buffer on success. The
12688c2ecf20Sopenharmony_ci * buffer should be freed by the consumer with a kfree().
12698c2ecf20Sopenharmony_ci */
12708c2ecf20Sopenharmony_civoid *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
12718c2ecf20Sopenharmony_ci{
12728c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem = cell->nvmem;
12738c2ecf20Sopenharmony_ci	u8 *buf;
12748c2ecf20Sopenharmony_ci	int rc;
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	if (!nvmem)
12778c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
12788c2ecf20Sopenharmony_ci
12798c2ecf20Sopenharmony_ci	buf = kzalloc(cell->bytes, GFP_KERNEL);
12808c2ecf20Sopenharmony_ci	if (!buf)
12818c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	rc = __nvmem_cell_read(nvmem, cell, buf, len);
12848c2ecf20Sopenharmony_ci	if (rc) {
12858c2ecf20Sopenharmony_ci		kfree(buf);
12868c2ecf20Sopenharmony_ci		return ERR_PTR(rc);
12878c2ecf20Sopenharmony_ci	}
12888c2ecf20Sopenharmony_ci
12898c2ecf20Sopenharmony_ci	return buf;
12908c2ecf20Sopenharmony_ci}
12918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read);
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_cistatic void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
12948c2ecf20Sopenharmony_ci					     u8 *_buf, int len)
12958c2ecf20Sopenharmony_ci{
12968c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem = cell->nvmem;
12978c2ecf20Sopenharmony_ci	int i, rc, nbits, bit_offset = cell->bit_offset;
12988c2ecf20Sopenharmony_ci	u8 v, *p, *buf, *b, pbyte, pbits;
12998c2ecf20Sopenharmony_ci
13008c2ecf20Sopenharmony_ci	nbits = cell->nbits;
13018c2ecf20Sopenharmony_ci	buf = kzalloc(cell->bytes, GFP_KERNEL);
13028c2ecf20Sopenharmony_ci	if (!buf)
13038c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	memcpy(buf, _buf, len);
13068c2ecf20Sopenharmony_ci	p = b = buf;
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci	if (bit_offset) {
13098c2ecf20Sopenharmony_ci		pbyte = *b;
13108c2ecf20Sopenharmony_ci		*b <<= bit_offset;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci		/* setup the first byte with lsb bits from nvmem */
13138c2ecf20Sopenharmony_ci		rc = nvmem_reg_read(nvmem, cell->offset, &v, 1);
13148c2ecf20Sopenharmony_ci		if (rc)
13158c2ecf20Sopenharmony_ci			goto err;
13168c2ecf20Sopenharmony_ci		*b++ |= GENMASK(bit_offset - 1, 0) & v;
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci		/* setup rest of the byte if any */
13198c2ecf20Sopenharmony_ci		for (i = 1; i < cell->bytes; i++) {
13208c2ecf20Sopenharmony_ci			/* Get last byte bits and shift them towards lsb */
13218c2ecf20Sopenharmony_ci			pbits = pbyte >> (BITS_PER_BYTE - 1 - bit_offset);
13228c2ecf20Sopenharmony_ci			pbyte = *b;
13238c2ecf20Sopenharmony_ci			p = b;
13248c2ecf20Sopenharmony_ci			*b <<= bit_offset;
13258c2ecf20Sopenharmony_ci			*b++ |= pbits;
13268c2ecf20Sopenharmony_ci		}
13278c2ecf20Sopenharmony_ci	}
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci	/* if it's not end on byte boundary */
13308c2ecf20Sopenharmony_ci	if ((nbits + bit_offset) % BITS_PER_BYTE) {
13318c2ecf20Sopenharmony_ci		/* setup the last byte with msb bits from nvmem */
13328c2ecf20Sopenharmony_ci		rc = nvmem_reg_read(nvmem,
13338c2ecf20Sopenharmony_ci				    cell->offset + cell->bytes - 1, &v, 1);
13348c2ecf20Sopenharmony_ci		if (rc)
13358c2ecf20Sopenharmony_ci			goto err;
13368c2ecf20Sopenharmony_ci		*p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	return buf;
13418c2ecf20Sopenharmony_cierr:
13428c2ecf20Sopenharmony_ci	kfree(buf);
13438c2ecf20Sopenharmony_ci	return ERR_PTR(rc);
13448c2ecf20Sopenharmony_ci}
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci/**
13478c2ecf20Sopenharmony_ci * nvmem_cell_write() - Write to a given nvmem cell
13488c2ecf20Sopenharmony_ci *
13498c2ecf20Sopenharmony_ci * @cell: nvmem cell to be written.
13508c2ecf20Sopenharmony_ci * @buf: Buffer to be written.
13518c2ecf20Sopenharmony_ci * @len: length of buffer to be written to nvmem cell.
13528c2ecf20Sopenharmony_ci *
13538c2ecf20Sopenharmony_ci * Return: length of bytes written or negative on failure.
13548c2ecf20Sopenharmony_ci */
13558c2ecf20Sopenharmony_ciint nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
13568c2ecf20Sopenharmony_ci{
13578c2ecf20Sopenharmony_ci	struct nvmem_device *nvmem = cell->nvmem;
13588c2ecf20Sopenharmony_ci	int rc;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	if (!nvmem || nvmem->read_only ||
13618c2ecf20Sopenharmony_ci	    (cell->bit_offset == 0 && len != cell->bytes))
13628c2ecf20Sopenharmony_ci		return -EINVAL;
13638c2ecf20Sopenharmony_ci
13648c2ecf20Sopenharmony_ci	if (cell->bit_offset || cell->nbits) {
13658c2ecf20Sopenharmony_ci		buf = nvmem_cell_prepare_write_buffer(cell, buf, len);
13668c2ecf20Sopenharmony_ci		if (IS_ERR(buf))
13678c2ecf20Sopenharmony_ci			return PTR_ERR(buf);
13688c2ecf20Sopenharmony_ci	}
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	rc = nvmem_reg_write(nvmem, cell->offset, buf, cell->bytes);
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	/* free the tmp buffer */
13738c2ecf20Sopenharmony_ci	if (cell->bit_offset || cell->nbits)
13748c2ecf20Sopenharmony_ci		kfree(buf);
13758c2ecf20Sopenharmony_ci
13768c2ecf20Sopenharmony_ci	if (rc)
13778c2ecf20Sopenharmony_ci		return rc;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	return len;
13808c2ecf20Sopenharmony_ci}
13818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_write);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_cistatic int nvmem_cell_read_common(struct device *dev, const char *cell_id,
13848c2ecf20Sopenharmony_ci				  void *val, size_t count)
13858c2ecf20Sopenharmony_ci{
13868c2ecf20Sopenharmony_ci	struct nvmem_cell *cell;
13878c2ecf20Sopenharmony_ci	void *buf;
13888c2ecf20Sopenharmony_ci	size_t len;
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	cell = nvmem_cell_get(dev, cell_id);
13918c2ecf20Sopenharmony_ci	if (IS_ERR(cell))
13928c2ecf20Sopenharmony_ci		return PTR_ERR(cell);
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci	buf = nvmem_cell_read(cell, &len);
13958c2ecf20Sopenharmony_ci	if (IS_ERR(buf)) {
13968c2ecf20Sopenharmony_ci		nvmem_cell_put(cell);
13978c2ecf20Sopenharmony_ci		return PTR_ERR(buf);
13988c2ecf20Sopenharmony_ci	}
13998c2ecf20Sopenharmony_ci	if (len != count) {
14008c2ecf20Sopenharmony_ci		kfree(buf);
14018c2ecf20Sopenharmony_ci		nvmem_cell_put(cell);
14028c2ecf20Sopenharmony_ci		return -EINVAL;
14038c2ecf20Sopenharmony_ci	}
14048c2ecf20Sopenharmony_ci	memcpy(val, buf, count);
14058c2ecf20Sopenharmony_ci	kfree(buf);
14068c2ecf20Sopenharmony_ci	nvmem_cell_put(cell);
14078c2ecf20Sopenharmony_ci
14088c2ecf20Sopenharmony_ci	return 0;
14098c2ecf20Sopenharmony_ci}
14108c2ecf20Sopenharmony_ci
14118c2ecf20Sopenharmony_ci/**
14128c2ecf20Sopenharmony_ci * nvmem_cell_read_u8() - Read a cell value as a u8
14138c2ecf20Sopenharmony_ci *
14148c2ecf20Sopenharmony_ci * @dev: Device that requests the nvmem cell.
14158c2ecf20Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
14168c2ecf20Sopenharmony_ci * @val: pointer to output value.
14178c2ecf20Sopenharmony_ci *
14188c2ecf20Sopenharmony_ci * Return: 0 on success or negative errno.
14198c2ecf20Sopenharmony_ci */
14208c2ecf20Sopenharmony_ciint nvmem_cell_read_u8(struct device *dev, const char *cell_id, u8 *val)
14218c2ecf20Sopenharmony_ci{
14228c2ecf20Sopenharmony_ci	return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
14238c2ecf20Sopenharmony_ci}
14248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_u8);
14258c2ecf20Sopenharmony_ci
14268c2ecf20Sopenharmony_ci/**
14278c2ecf20Sopenharmony_ci * nvmem_cell_read_u16() - Read a cell value as a u16
14288c2ecf20Sopenharmony_ci *
14298c2ecf20Sopenharmony_ci * @dev: Device that requests the nvmem cell.
14308c2ecf20Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
14318c2ecf20Sopenharmony_ci * @val: pointer to output value.
14328c2ecf20Sopenharmony_ci *
14338c2ecf20Sopenharmony_ci * Return: 0 on success or negative errno.
14348c2ecf20Sopenharmony_ci */
14358c2ecf20Sopenharmony_ciint nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val)
14368c2ecf20Sopenharmony_ci{
14378c2ecf20Sopenharmony_ci	return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
14388c2ecf20Sopenharmony_ci}
14398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_u16);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci/**
14428c2ecf20Sopenharmony_ci * nvmem_cell_read_u32() - Read a cell value as a u32
14438c2ecf20Sopenharmony_ci *
14448c2ecf20Sopenharmony_ci * @dev: Device that requests the nvmem cell.
14458c2ecf20Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
14468c2ecf20Sopenharmony_ci * @val: pointer to output value.
14478c2ecf20Sopenharmony_ci *
14488c2ecf20Sopenharmony_ci * Return: 0 on success or negative errno.
14498c2ecf20Sopenharmony_ci */
14508c2ecf20Sopenharmony_ciint nvmem_cell_read_u32(struct device *dev, const char *cell_id, u32 *val)
14518c2ecf20Sopenharmony_ci{
14528c2ecf20Sopenharmony_ci	return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
14538c2ecf20Sopenharmony_ci}
14548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_u32);
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci/**
14578c2ecf20Sopenharmony_ci * nvmem_cell_read_u64() - Read a cell value as a u64
14588c2ecf20Sopenharmony_ci *
14598c2ecf20Sopenharmony_ci * @dev: Device that requests the nvmem cell.
14608c2ecf20Sopenharmony_ci * @cell_id: Name of nvmem cell to read.
14618c2ecf20Sopenharmony_ci * @val: pointer to output value.
14628c2ecf20Sopenharmony_ci *
14638c2ecf20Sopenharmony_ci * Return: 0 on success or negative errno.
14648c2ecf20Sopenharmony_ci */
14658c2ecf20Sopenharmony_ciint nvmem_cell_read_u64(struct device *dev, const char *cell_id, u64 *val)
14668c2ecf20Sopenharmony_ci{
14678c2ecf20Sopenharmony_ci	return nvmem_cell_read_common(dev, cell_id, val, sizeof(*val));
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_cell_read_u64);
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci/**
14728c2ecf20Sopenharmony_ci * nvmem_device_cell_read() - Read a given nvmem device and cell
14738c2ecf20Sopenharmony_ci *
14748c2ecf20Sopenharmony_ci * @nvmem: nvmem device to read from.
14758c2ecf20Sopenharmony_ci * @info: nvmem cell info to be read.
14768c2ecf20Sopenharmony_ci * @buf: buffer pointer which will be populated on successful read.
14778c2ecf20Sopenharmony_ci *
14788c2ecf20Sopenharmony_ci * Return: length of successful bytes read on success and negative
14798c2ecf20Sopenharmony_ci * error code on error.
14808c2ecf20Sopenharmony_ci */
14818c2ecf20Sopenharmony_cissize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
14828c2ecf20Sopenharmony_ci			   struct nvmem_cell_info *info, void *buf)
14838c2ecf20Sopenharmony_ci{
14848c2ecf20Sopenharmony_ci	struct nvmem_cell cell;
14858c2ecf20Sopenharmony_ci	int rc;
14868c2ecf20Sopenharmony_ci	ssize_t len;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci	if (!nvmem)
14898c2ecf20Sopenharmony_ci		return -EINVAL;
14908c2ecf20Sopenharmony_ci
14918c2ecf20Sopenharmony_ci	rc = nvmem_cell_info_to_nvmem_cell_nodup(nvmem, info, &cell);
14928c2ecf20Sopenharmony_ci	if (rc)
14938c2ecf20Sopenharmony_ci		return rc;
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_ci	rc = __nvmem_cell_read(nvmem, &cell, buf, &len);
14968c2ecf20Sopenharmony_ci	if (rc)
14978c2ecf20Sopenharmony_ci		return rc;
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	return len;
15008c2ecf20Sopenharmony_ci}
15018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_cell_read);
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci/**
15048c2ecf20Sopenharmony_ci * nvmem_device_cell_write() - Write cell to a given nvmem device
15058c2ecf20Sopenharmony_ci *
15068c2ecf20Sopenharmony_ci * @nvmem: nvmem device to be written to.
15078c2ecf20Sopenharmony_ci * @info: nvmem cell info to be written.
15088c2ecf20Sopenharmony_ci * @buf: buffer to be written to cell.
15098c2ecf20Sopenharmony_ci *
15108c2ecf20Sopenharmony_ci * Return: length of bytes written or negative error code on failure.
15118c2ecf20Sopenharmony_ci */
15128c2ecf20Sopenharmony_ciint nvmem_device_cell_write(struct nvmem_device *nvmem,
15138c2ecf20Sopenharmony_ci			    struct nvmem_cell_info *info, void *buf)
15148c2ecf20Sopenharmony_ci{
15158c2ecf20Sopenharmony_ci	struct nvmem_cell cell;
15168c2ecf20Sopenharmony_ci	int rc;
15178c2ecf20Sopenharmony_ci
15188c2ecf20Sopenharmony_ci	if (!nvmem)
15198c2ecf20Sopenharmony_ci		return -EINVAL;
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci	rc = nvmem_cell_info_to_nvmem_cell_nodup(nvmem, info, &cell);
15228c2ecf20Sopenharmony_ci	if (rc)
15238c2ecf20Sopenharmony_ci		return rc;
15248c2ecf20Sopenharmony_ci
15258c2ecf20Sopenharmony_ci	return nvmem_cell_write(&cell, buf, cell.bytes);
15268c2ecf20Sopenharmony_ci}
15278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_cell_write);
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci/**
15308c2ecf20Sopenharmony_ci * nvmem_device_read() - Read from a given nvmem device
15318c2ecf20Sopenharmony_ci *
15328c2ecf20Sopenharmony_ci * @nvmem: nvmem device to read from.
15338c2ecf20Sopenharmony_ci * @offset: offset in nvmem device.
15348c2ecf20Sopenharmony_ci * @bytes: number of bytes to read.
15358c2ecf20Sopenharmony_ci * @buf: buffer pointer which will be populated on successful read.
15368c2ecf20Sopenharmony_ci *
15378c2ecf20Sopenharmony_ci * Return: length of successful bytes read on success and negative
15388c2ecf20Sopenharmony_ci * error code on error.
15398c2ecf20Sopenharmony_ci */
15408c2ecf20Sopenharmony_ciint nvmem_device_read(struct nvmem_device *nvmem,
15418c2ecf20Sopenharmony_ci		      unsigned int offset,
15428c2ecf20Sopenharmony_ci		      size_t bytes, void *buf)
15438c2ecf20Sopenharmony_ci{
15448c2ecf20Sopenharmony_ci	int rc;
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci	if (!nvmem)
15478c2ecf20Sopenharmony_ci		return -EINVAL;
15488c2ecf20Sopenharmony_ci
15498c2ecf20Sopenharmony_ci	rc = nvmem_reg_read(nvmem, offset, buf, bytes);
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci	if (rc)
15528c2ecf20Sopenharmony_ci		return rc;
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	return bytes;
15558c2ecf20Sopenharmony_ci}
15568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_read);
15578c2ecf20Sopenharmony_ci
15588c2ecf20Sopenharmony_ci/**
15598c2ecf20Sopenharmony_ci * nvmem_device_write() - Write cell to a given nvmem device
15608c2ecf20Sopenharmony_ci *
15618c2ecf20Sopenharmony_ci * @nvmem: nvmem device to be written to.
15628c2ecf20Sopenharmony_ci * @offset: offset in nvmem device.
15638c2ecf20Sopenharmony_ci * @bytes: number of bytes to write.
15648c2ecf20Sopenharmony_ci * @buf: buffer to be written.
15658c2ecf20Sopenharmony_ci *
15668c2ecf20Sopenharmony_ci * Return: length of bytes written or negative error code on failure.
15678c2ecf20Sopenharmony_ci */
15688c2ecf20Sopenharmony_ciint nvmem_device_write(struct nvmem_device *nvmem,
15698c2ecf20Sopenharmony_ci		       unsigned int offset,
15708c2ecf20Sopenharmony_ci		       size_t bytes, void *buf)
15718c2ecf20Sopenharmony_ci{
15728c2ecf20Sopenharmony_ci	int rc;
15738c2ecf20Sopenharmony_ci
15748c2ecf20Sopenharmony_ci	if (!nvmem)
15758c2ecf20Sopenharmony_ci		return -EINVAL;
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	rc = nvmem_reg_write(nvmem, offset, buf, bytes);
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci	if (rc)
15808c2ecf20Sopenharmony_ci		return rc;
15818c2ecf20Sopenharmony_ci
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ci	return bytes;
15848c2ecf20Sopenharmony_ci}
15858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_device_write);
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci/**
15888c2ecf20Sopenharmony_ci * nvmem_add_cell_table() - register a table of cell info entries
15898c2ecf20Sopenharmony_ci *
15908c2ecf20Sopenharmony_ci * @table: table of cell info entries
15918c2ecf20Sopenharmony_ci */
15928c2ecf20Sopenharmony_civoid nvmem_add_cell_table(struct nvmem_cell_table *table)
15938c2ecf20Sopenharmony_ci{
15948c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_cell_mutex);
15958c2ecf20Sopenharmony_ci	list_add_tail(&table->node, &nvmem_cell_tables);
15968c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_cell_mutex);
15978c2ecf20Sopenharmony_ci}
15988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_add_cell_table);
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_ci/**
16018c2ecf20Sopenharmony_ci * nvmem_del_cell_table() - remove a previously registered cell info table
16028c2ecf20Sopenharmony_ci *
16038c2ecf20Sopenharmony_ci * @table: table of cell info entries
16048c2ecf20Sopenharmony_ci */
16058c2ecf20Sopenharmony_civoid nvmem_del_cell_table(struct nvmem_cell_table *table)
16068c2ecf20Sopenharmony_ci{
16078c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_cell_mutex);
16088c2ecf20Sopenharmony_ci	list_del(&table->node);
16098c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_cell_mutex);
16108c2ecf20Sopenharmony_ci}
16118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_del_cell_table);
16128c2ecf20Sopenharmony_ci
16138c2ecf20Sopenharmony_ci/**
16148c2ecf20Sopenharmony_ci * nvmem_add_cell_lookups() - register a list of cell lookup entries
16158c2ecf20Sopenharmony_ci *
16168c2ecf20Sopenharmony_ci * @entries: array of cell lookup entries
16178c2ecf20Sopenharmony_ci * @nentries: number of cell lookup entries in the array
16188c2ecf20Sopenharmony_ci */
16198c2ecf20Sopenharmony_civoid nvmem_add_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
16208c2ecf20Sopenharmony_ci{
16218c2ecf20Sopenharmony_ci	int i;
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_lookup_mutex);
16248c2ecf20Sopenharmony_ci	for (i = 0; i < nentries; i++)
16258c2ecf20Sopenharmony_ci		list_add_tail(&entries[i].node, &nvmem_lookup_list);
16268c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_lookup_mutex);
16278c2ecf20Sopenharmony_ci}
16288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_add_cell_lookups);
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_ci/**
16318c2ecf20Sopenharmony_ci * nvmem_del_cell_lookups() - remove a list of previously added cell lookup
16328c2ecf20Sopenharmony_ci *                            entries
16338c2ecf20Sopenharmony_ci *
16348c2ecf20Sopenharmony_ci * @entries: array of cell lookup entries
16358c2ecf20Sopenharmony_ci * @nentries: number of cell lookup entries in the array
16368c2ecf20Sopenharmony_ci */
16378c2ecf20Sopenharmony_civoid nvmem_del_cell_lookups(struct nvmem_cell_lookup *entries, size_t nentries)
16388c2ecf20Sopenharmony_ci{
16398c2ecf20Sopenharmony_ci	int i;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	mutex_lock(&nvmem_lookup_mutex);
16428c2ecf20Sopenharmony_ci	for (i = 0; i < nentries; i++)
16438c2ecf20Sopenharmony_ci		list_del(&entries[i].node);
16448c2ecf20Sopenharmony_ci	mutex_unlock(&nvmem_lookup_mutex);
16458c2ecf20Sopenharmony_ci}
16468c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_del_cell_lookups);
16478c2ecf20Sopenharmony_ci
16488c2ecf20Sopenharmony_ci/**
16498c2ecf20Sopenharmony_ci * nvmem_dev_name() - Get the name of a given nvmem device.
16508c2ecf20Sopenharmony_ci *
16518c2ecf20Sopenharmony_ci * @nvmem: nvmem device.
16528c2ecf20Sopenharmony_ci *
16538c2ecf20Sopenharmony_ci * Return: name of the nvmem device.
16548c2ecf20Sopenharmony_ci */
16558c2ecf20Sopenharmony_ciconst char *nvmem_dev_name(struct nvmem_device *nvmem)
16568c2ecf20Sopenharmony_ci{
16578c2ecf20Sopenharmony_ci	return dev_name(&nvmem->dev);
16588c2ecf20Sopenharmony_ci}
16598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(nvmem_dev_name);
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_cistatic int __init nvmem_init(void)
16628c2ecf20Sopenharmony_ci{
16638c2ecf20Sopenharmony_ci	return bus_register(&nvmem_bus_type);
16648c2ecf20Sopenharmony_ci}
16658c2ecf20Sopenharmony_ci
16668c2ecf20Sopenharmony_cistatic void __exit nvmem_exit(void)
16678c2ecf20Sopenharmony_ci{
16688c2ecf20Sopenharmony_ci	bus_unregister(&nvmem_bus_type);
16698c2ecf20Sopenharmony_ci}
16708c2ecf20Sopenharmony_ci
16718c2ecf20Sopenharmony_cisubsys_initcall(nvmem_init);
16728c2ecf20Sopenharmony_cimodule_exit(nvmem_exit);
16738c2ecf20Sopenharmony_ci
16748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
16758c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com");
16768c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("nvmem Driver Core");
16778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1678