thermal: rockchip: add temperature dump when panic
[firefly-linux-kernel-4.4.55.git] / drivers / nvmem / core.c
index 6fd4e5a5ef4a495bbd412ee33b931f4fb3a8a24f..bb4ea123547f169b02044d6cc15f867b188f6f5c 100644 (file)
 #include <linux/nvmem-consumer.h>
 #include <linux/nvmem-provider.h>
 #include <linux/of.h>
-#include <linux/regmap.h>
 #include <linux/slab.h>
 
 struct nvmem_device {
        const char              *name;
-       struct regmap           *regmap;
        struct module           *owner;
        struct device           dev;
        int                     stride;
@@ -38,8 +36,16 @@ struct nvmem_device {
        int                     users;
        size_t                  size;
        bool                    read_only;
+       int                     flags;
+       struct bin_attribute    eeprom;
+       struct device           *base_dev;
+       nvmem_reg_read_t        reg_read;
+       nvmem_reg_write_t       reg_write;
+       void *priv;
 };
 
+#define FLAG_COMPAT            BIT(0)
+
 struct nvmem_cell {
        const char              *name;
        int                     offset;
@@ -56,26 +62,56 @@ static DEFINE_IDA(nvmem_ida);
 static LIST_HEAD(nvmem_cells);
 static DEFINE_MUTEX(nvmem_cells_mutex);
 
+#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)
+{
+       if (nvmem->reg_read)
+               return nvmem->reg_read(nvmem->priv, offset, val, bytes);
+
+       return -EINVAL;
+}
+
+static int nvmem_reg_write(struct nvmem_device *nvmem, unsigned int offset,
+                          void *val, size_t bytes)
+{
+       if (nvmem->reg_write)
+               return nvmem->reg_write(nvmem->priv, offset, val, bytes);
+
+       return -EINVAL;
+}
 
 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 = container_of(kobj, struct device, kobj);
-       struct nvmem_device *nvmem = to_nvmem_device(dev);
+       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 = regmap_raw_read(nvmem->regmap, pos, buf, count);
+       rc = nvmem_reg_read(nvmem, pos, buf, count);
 
        if (IS_ERR_VALUE(rc))
                return rc;
@@ -87,20 +123,29 @@ 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 = container_of(kobj, struct device, kobj);
-       struct nvmem_device *nvmem = to_nvmem_device(dev);
+       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 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 = regmap_raw_write(nvmem->regmap, pos, buf, count);
+       rc = nvmem_reg_write(nvmem, pos, buf, count);
 
        if (IS_ERR_VALUE(rc))
                return rc;
@@ -155,6 +200,53 @@ static const struct attribute_group *nvmem_ro_dev_groups[] = {
        NULL,
 };
 
+/* default read/write permissions, root only */
+static struct bin_attribute bin_attr_rw_root_nvmem = {
+       .attr   = {
+               .name   = "nvmem",
+               .mode   = S_IWUSR | S_IRUSR,
+       },
+       .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,
+};
+
+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   = S_IRUSR,
+       },
+       .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,
+};
+
+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);
@@ -288,12 +380,51 @@ static int nvmem_add_cells(struct nvmem_device *nvmem,
 
        return 0;
 err:
-       while (--i)
+       while (i--)
                nvmem_cell_drop(cells[i]);
 
+       kfree(cells);
+
        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() - Register a nvmem device for given nvmem_config.
  * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem
@@ -308,18 +439,11 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
 {
        struct nvmem_device *nvmem;
        struct device_node *np;
-       struct regmap *rm;
        int rval;
 
        if (!config->dev)
                return ERR_PTR(-EINVAL);
 
-       rm = dev_get_regmap(config->dev, NULL);
-       if (!rm) {
-               dev_err(config->dev, "Regmap not found\n");
-               return ERR_PTR(-EINVAL);
-       }
-
        nvmem = kzalloc(sizeof(*nvmem), GFP_KERNEL);
        if (!nvmem)
                return ERR_PTR(-ENOMEM);
@@ -331,14 +455,16 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
        }
 
        nvmem->id = rval;
-       nvmem->regmap = rm;
        nvmem->owner = config->owner;
-       nvmem->stride = regmap_get_reg_stride(rm);
-       nvmem->word_size = regmap_get_val_bytes(rm);
-       nvmem->size = regmap_get_max_register(rm) + nvmem->stride;
+       nvmem->stride = config->stride;
+       nvmem->word_size = config->word_size;
+       nvmem->size = config->size;
        nvmem->dev.type = &nvmem_provider_type;
        nvmem->dev.bus = &nvmem_bus_type;
        nvmem->dev.parent = config->dev;
+       nvmem->priv = config->priv;
+       nvmem->reg_read = config->reg_read;
+       nvmem->reg_write = config->reg_write;
        np = config->dev->of_node;
        nvmem->dev.of_node = np;
        dev_set_name(&nvmem->dev, "%s%d",
@@ -347,24 +473,37 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
        nvmem->read_only = of_property_read_bool(np, "read-only") |
                           config->read_only;
 
-       nvmem->dev.groups = nvmem->read_only ? nvmem_ro_dev_groups :
-                                              nvmem_rw_dev_groups;
+       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;
 
        device_initialize(&nvmem->dev);
 
        dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name);
 
        rval = device_add(&nvmem->dev);
-       if (rval) {
-               ida_simple_remove(&nvmem_ida, nvmem->id);
-               kfree(nvmem);
-               return ERR_PTR(rval);
+       if (rval)
+               goto out;
+
+       if (config->compat) {
+               rval = nvmem_setup_compat(nvmem, config);
+               if (rval)
+                       goto out;
        }
 
        if (config->cells)
                nvmem_add_cells(nvmem, config);
 
        return nvmem;
+out:
+       ida_simple_remove(&nvmem_ida, nvmem->id);
+       kfree(nvmem);
+       return ERR_PTR(rval);
 }
 EXPORT_SYMBOL_GPL(nvmem_register);
 
@@ -384,6 +523,9 @@ int nvmem_unregister(struct nvmem_device *nvmem)
        }
        mutex_unlock(&nvmem_mutex);
 
+       if (nvmem->flags & FLAG_COMPAT)
+               device_remove_bin_file(nvmem->base_dev, &nvmem->eeprom);
+
        nvmem_device_remove_all_cells(nvmem);
        device_del(&nvmem->dev);
 
@@ -819,7 +961,7 @@ static int __nvmem_cell_read(struct nvmem_device *nvmem,
 {
        int rc;
 
-       rc = regmap_raw_read(nvmem->regmap, cell->offset, buf, cell->bytes);
+       rc = nvmem_reg_read(nvmem, cell->offset, buf, cell->bytes);
 
        if (IS_ERR_VALUE(rc))
                return rc;
@@ -848,7 +990,7 @@ void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len)
        u8 *buf;
        int rc;
 
-       if (!nvmem || !nvmem->regmap)
+       if (!nvmem)
                return ERR_PTR(-EINVAL);
 
        buf = kzalloc(cell->bytes, GFP_KERNEL);
@@ -885,7 +1027,7 @@ static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
                *b <<= bit_offset;
 
                /* setup the first byte with lsb bits from nvmem */
-               rc = regmap_raw_read(nvmem->regmap, cell->offset, &v, 1);
+               rc = nvmem_reg_read(nvmem, cell->offset, &v, 1);
                *b++ |= GENMASK(bit_offset - 1, 0) & v;
 
                /* setup rest of the byte if any */
@@ -902,7 +1044,7 @@ static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell,
        /* if it's not end on byte boundary */
        if ((nbits + bit_offset) % BITS_PER_BYTE) {
                /* setup the last byte with msb bits from nvmem */
-               rc = regmap_raw_read(nvmem->regmap,
+               rc = nvmem_reg_read(nvmem,
                                    cell->offset + cell->bytes - 1, &v, 1);
                *p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v;
 
@@ -925,7 +1067,7 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
        struct nvmem_device *nvmem = cell->nvmem;
        int rc;
 
-       if (!nvmem || !nvmem->regmap || nvmem->read_only ||
+       if (!nvmem || nvmem->read_only ||
            (cell->bit_offset == 0 && len != cell->bytes))
                return -EINVAL;
 
@@ -935,7 +1077,7 @@ int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len)
                        return PTR_ERR(buf);
        }
 
-       rc = regmap_raw_write(nvmem->regmap, cell->offset, buf, cell->bytes);
+       rc = nvmem_reg_write(nvmem, cell->offset, buf, cell->bytes);
 
        /* free the tmp buffer */
        if (cell->bit_offset || cell->nbits)
@@ -965,7 +1107,7 @@ ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem,
        int rc;
        ssize_t len;
 
-       if (!nvmem || !nvmem->regmap)
+       if (!nvmem)
                return -EINVAL;
 
        rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
@@ -995,7 +1137,7 @@ int nvmem_device_cell_write(struct nvmem_device *nvmem,
        struct nvmem_cell cell;
        int rc;
 
-       if (!nvmem || !nvmem->regmap)
+       if (!nvmem)
                return -EINVAL;
 
        rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell);
@@ -1023,10 +1165,10 @@ int nvmem_device_read(struct nvmem_device *nvmem,
 {
        int rc;
 
-       if (!nvmem || !nvmem->regmap)
+       if (!nvmem)
                return -EINVAL;
 
-       rc = regmap_raw_read(nvmem->regmap, offset, buf, bytes);
+       rc = nvmem_reg_read(nvmem, offset, buf, bytes);
 
        if (IS_ERR_VALUE(rc))
                return rc;
@@ -1051,10 +1193,10 @@ int nvmem_device_write(struct nvmem_device *nvmem,
 {
        int rc;
 
-       if (!nvmem || !nvmem->regmap)
+       if (!nvmem)
                return -EINVAL;
 
-       rc = regmap_raw_write(nvmem->regmap, offset, buf, bytes);
+       rc = nvmem_reg_write(nvmem, offset, buf, bytes);
 
        if (IS_ERR_VALUE(rc))
                return rc;