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