Merge branch 'for-5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/wq
[linux-2.6-block.git] / drivers / nvmem / core.c
index f24008b66826902f43b2abdaeb5703e9a6deb201..c7892c3da91f8f2e69ad4f4fd1bca5c79afce8bc 100644 (file)
 #include <linux/nvmem-provider.h>
 #include <linux/of.h>
 #include <linux/slab.h>
-
-struct nvmem_device {
-       struct module           *owner;
-       struct device           dev;
-       int                     stride;
-       int                     word_size;
-       int                     id;
-       struct kref             refcnt;
-       size_t                  size;
-       bool                    read_only;
-       int                     flags;
-       enum nvmem_type         type;
-       struct bin_attribute    eeprom;
-       struct device           *base_dev;
-       struct list_head        cells;
-       nvmem_reg_read_t        reg_read;
-       nvmem_reg_write_t       reg_write;
-       void *priv;
-};
-
-#define FLAG_COMPAT            BIT(0)
+#include "nvmem.h"
 
 struct nvmem_cell {
        const char              *name;
@@ -61,18 +41,7 @@ static LIST_HEAD(nvmem_lookup_list);
 
 static BLOCKING_NOTIFIER_HEAD(nvmem_notifier);
 
-static const char * const nvmem_type_str[] = {
-       [NVMEM_TYPE_UNKNOWN] = "Unknown",
-       [NVMEM_TYPE_EEPROM] = "EEPROM",
-       [NVMEM_TYPE_OTP] = "OTP",
-       [NVMEM_TYPE_BATTERY_BACKED] = "Battery backed",
-};
 
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-static struct lock_class_key eeprom_lock_key;
-#endif
-
-#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev)
 static int nvmem_reg_read(struct nvmem_device *nvmem, unsigned int offset,
                          void *val, size_t bytes)
 {
@@ -91,187 +60,6 @@ static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
        return -EINVAL;
 }
 
-static ssize_t type_show(struct device *dev,
-                        struct device_attribute *attr, char *buf)
-{
-       struct nvmem_device *nvmem = to_nvmem_device(dev);
-
-       return sprintf(buf, "%s\n", nvmem_type_str[nvmem->type]);
-}
-
-static DEVICE_ATTR_RO(type);
-
-static struct attribute *nvmem_attrs[] = {
-       &dev_attr_type.attr,
-       NULL,
-};
-
-static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj,
-                                   struct bin_attribute *attr,
-                                   char *buf, loff_t pos, size_t count)
-{
-       struct device *dev;
-       struct nvmem_device *nvmem;
-       int rc;
-
-       if (attr->private)
-               dev = attr->private;
-       else
-               dev = container_of(kobj, struct device, kobj);
-       nvmem = to_nvmem_device(dev);
-
-       /* Stop the user from reading */
-       if (pos >= nvmem->size)
-               return 0;
-
-       if (count < nvmem->word_size)
-               return -EINVAL;
-
-       if (pos + count > nvmem->size)
-               count = nvmem->size - pos;
-
-       count = round_down(count, nvmem->word_size);
-
-       rc = nvmem_reg_read(nvmem, pos, buf, count);
-
-       if (rc)
-               return rc;
-
-       return count;
-}
-
-static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj,
-                                    struct bin_attribute *attr,
-                                    char *buf, loff_t pos, size_t count)
-{
-       struct device *dev;
-       struct nvmem_device *nvmem;
-       int rc;
-
-       if (attr->private)
-               dev = attr->private;
-       else
-               dev = container_of(kobj, struct device, kobj);
-       nvmem = to_nvmem_device(dev);
-
-       /* Stop the user from writing */
-       if (pos >= nvmem->size)
-               return -EFBIG;
-
-       if (count < nvmem->word_size)
-               return -EINVAL;
-
-       if (pos + count > nvmem->size)
-               count = nvmem->size - pos;
-
-       count = round_down(count, nvmem->word_size);
-
-       rc = nvmem_reg_write(nvmem, pos, buf, count);
-
-       if (rc)
-               return rc;
-
-       return count;
-}
-
-/* default read/write permissions */
-static struct bin_attribute bin_attr_rw_nvmem = {
-       .attr   = {
-               .name   = "nvmem",
-               .mode   = 0644,
-       },
-       .read   = bin_attr_nvmem_read,
-       .write  = bin_attr_nvmem_write,
-};
-
-static struct bin_attribute *nvmem_bin_rw_attributes[] = {
-       &bin_attr_rw_nvmem,
-       NULL,
-};
-
-static const struct attribute_group nvmem_bin_rw_group = {
-       .bin_attrs      = nvmem_bin_rw_attributes,
-       .attrs          = nvmem_attrs,
-};
-
-static const struct attribute_group *nvmem_rw_dev_groups[] = {
-       &nvmem_bin_rw_group,
-       NULL,
-};
-
-/* read only permission */
-static struct bin_attribute bin_attr_ro_nvmem = {
-       .attr   = {
-               .name   = "nvmem",
-               .mode   = 0444,
-       },
-       .read   = bin_attr_nvmem_read,
-};
-
-static struct bin_attribute *nvmem_bin_ro_attributes[] = {
-       &bin_attr_ro_nvmem,
-       NULL,
-};
-
-static const struct attribute_group nvmem_bin_ro_group = {
-       .bin_attrs      = nvmem_bin_ro_attributes,
-       .attrs          = nvmem_attrs,
-};
-
-static const struct attribute_group *nvmem_ro_dev_groups[] = {
-       &nvmem_bin_ro_group,
-       NULL,
-};
-
-/* default read/write permissions, root only */
-static struct bin_attribute bin_attr_rw_root_nvmem = {
-       .attr   = {
-               .name   = "nvmem",
-               .mode   = 0600,
-       },
-       .read   = bin_attr_nvmem_read,
-       .write  = bin_attr_nvmem_write,
-};
-
-static struct bin_attribute *nvmem_bin_rw_root_attributes[] = {
-       &bin_attr_rw_root_nvmem,
-       NULL,
-};
-
-static const struct attribute_group nvmem_bin_rw_root_group = {
-       .bin_attrs      = nvmem_bin_rw_root_attributes,
-       .attrs          = nvmem_attrs,
-};
-
-static const struct attribute_group *nvmem_rw_root_dev_groups[] = {
-       &nvmem_bin_rw_root_group,
-       NULL,
-};
-
-/* read only permission, root only */
-static struct bin_attribute bin_attr_ro_root_nvmem = {
-       .attr   = {
-               .name   = "nvmem",
-               .mode   = 0400,
-       },
-       .read   = bin_attr_nvmem_read,
-};
-
-static struct bin_attribute *nvmem_bin_ro_root_attributes[] = {
-       &bin_attr_ro_root_nvmem,
-       NULL,
-};
-
-static const struct attribute_group nvmem_bin_ro_root_group = {
-       .bin_attrs      = nvmem_bin_ro_root_attributes,
-       .attrs          = nvmem_attrs,
-};
-
-static const struct attribute_group *nvmem_ro_root_dev_groups[] = {
-       &nvmem_bin_ro_root_group,
-       NULL,
-};
-
 static void nvmem_release(struct device *dev)
 {
        struct nvmem_device *nvmem = to_nvmem_device(dev);
@@ -422,43 +210,6 @@ err:
        return rval;
 }
 
-/*
- * nvmem_setup_compat() - Create an additional binary entry in
- * drivers sys directory, to be backwards compatible with the older
- * drivers/misc/eeprom drivers.
- */
-static int nvmem_setup_compat(struct nvmem_device *nvmem,
-                             const struct nvmem_config *config)
-{
-       int rval;
-
-       if (!config->base_dev)
-               return -EINVAL;
-
-       if (nvmem->read_only)
-               nvmem->eeprom = bin_attr_ro_root_nvmem;
-       else
-               nvmem->eeprom = bin_attr_rw_root_nvmem;
-       nvmem->eeprom.attr.name = "eeprom";
-       nvmem->eeprom.size = nvmem->size;
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-       nvmem->eeprom.attr.key = &eeprom_lock_key;
-#endif
-       nvmem->eeprom.private = &nvmem->dev;
-       nvmem->base_dev = config->base_dev;
-
-       rval = device_create_bin_file(nvmem->base_dev, &nvmem->eeprom);
-       if (rval) {
-               dev_err(&nvmem->dev,
-                       "Failed to create eeprom binary file %d\n", rval);
-               return rval;
-       }
-
-       nvmem->flags |= FLAG_COMPAT;
-
-       return 0;
-}
-
 /**
  * nvmem_register_notifier() - Register a notifier block for nvmem events.
  *
@@ -651,14 +402,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
        nvmem->read_only = device_property_present(config->dev, "read-only") ||
                           config->read_only || !nvmem->reg_write;
 
-       if (config->root_only)
-               nvmem->dev.groups = nvmem->read_only ?
-                       nvmem_ro_root_dev_groups :
-                       nvmem_rw_root_dev_groups;
-       else
-               nvmem->dev.groups = nvmem->read_only ?
-                       nvmem_ro_dev_groups :
-                       nvmem_rw_dev_groups;
+       nvmem->dev.groups = nvmem_sysfs_get_groups(nvmem, config);
 
        device_initialize(&nvmem->dev);
 
@@ -669,7 +413,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
                goto err_put_device;
 
        if (config->compat) {
-               rval = nvmem_setup_compat(nvmem, config);
+               rval = nvmem_sysfs_setup_compat(nvmem, config);
                if (rval)
                        goto err_device_del;
        }
@@ -696,7 +440,7 @@ err_remove_cells:
        nvmem_device_remove_all_cells(nvmem);
 err_teardown_compat:
        if (config->compat)
-               device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+               nvmem_sysfs_remove_compat(nvmem, config);
 err_device_del:
        device_del(&nvmem->dev);
 err_put_device:
@@ -1166,7 +910,7 @@ EXPORT_SYMBOL_GPL(nvmem_cell_put);
 static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
 {
        u8 *p, *b;
-       int i, bit_offset = cell->bit_offset;
+       int i, extra, bit_offset = cell->bit_offset;
 
        p = b = buf;
        if (bit_offset) {
@@ -1181,11 +925,16 @@ static void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, void *buf)
                        p = b;
                        *b++ >>= bit_offset;
                }
-
-               /* result fits in less bytes */
-               if (cell->bytes != DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE))
-                       *p-- = 0;
+       } else {
+               /* point to the msb */
+               p += cell->bytes - 1;
        }
+
+       /* result fits in less bytes */
+       extra = cell->bytes - DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE);
+       while (--extra >= 0)
+               *p-- = 0;
+
        /* clear msb bits if any leftover in the last byte */
        *p &= GENMASK((cell->nbits%BITS_PER_BYTE) - 1, 0);
 }
@@ -1334,6 +1083,43 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
 }
 EXPORT_SYMBOL_GPL(nvmem_cell_write);
 
+/**
+ * nvmem_cell_read_u16() - Read a cell value as an u16
+ *
+ * @dev: Device that requests the nvmem cell.
+ * @cell_id: Name of nvmem cell to read.
+ * @val: pointer to output value.
+ *
+ * Return: 0 on success or negative errno.
+ */
+int nvmem_cell_read_u16(struct device *dev, const char *cell_id, u16 *val)
+{
+       struct nvmem_cell *cell;
+       void *buf;
+       size_t len;
+
+       cell = nvmem_cell_get(dev, cell_id);
+       if (IS_ERR(cell))
+               return PTR_ERR(cell);
+
+       buf = nvmem_cell_read(cell, &len);
+       if (IS_ERR(buf)) {
+               nvmem_cell_put(cell);
+               return PTR_ERR(buf);
+       }
+       if (len != sizeof(*val)) {
+               kfree(buf);
+               nvmem_cell_put(cell);
+               return -EINVAL;
+       }
+       memcpy(val, buf, sizeof(*val));
+       kfree(buf);
+       nvmem_cell_put(cell);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nvmem_cell_read_u16);
+
 /**
  * nvmem_cell_read_u32() - Read a cell value as an u32
  *