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