Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[firefly-linux-kernel-4.4.55.git] / drivers / mfd / mfd-core.c
index f421586f29fb09b02644ee8b05aa6777fb353835..2676492447378e989b019d05e223b622afb2c79c 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/irqdomain.h>
 #include <linux/of.h>
+#include <linux/regulator/consumer.h>
 
 static struct device_type mfd_dev_type = {
        .name   = "mfd_device",
@@ -63,7 +64,8 @@ int mfd_cell_disable(struct platform_device *pdev)
 EXPORT_SYMBOL(mfd_cell_disable);
 
 static int mfd_platform_add_cell(struct platform_device *pdev,
-                                const struct mfd_cell *cell)
+                                const struct mfd_cell *cell,
+                                atomic_t *usage_count)
 {
        if (!cell)
                return 0;
@@ -72,11 +74,12 @@ static int mfd_platform_add_cell(struct platform_device *pdev,
        if (!pdev->mfd_cell)
                return -ENOMEM;
 
+       pdev->mfd_cell->usage_count = usage_count;
        return 0;
 }
 
 static int mfd_add_device(struct device *parent, int id,
-                         const struct mfd_cell *cell,
+                         const struct mfd_cell *cell, atomic_t *usage_count,
                          struct resource *mem_base,
                          int irq_base, struct irq_domain *domain)
 {
@@ -99,6 +102,13 @@ static int mfd_add_device(struct device *parent, int id,
        pdev->dev.dma_mask = parent->dma_mask;
        pdev->dev.dma_parms = parent->dma_parms;
 
+       ret = devm_regulator_bulk_register_supply_alias(
+                       &pdev->dev, cell->parent_supplies,
+                       parent, cell->parent_supplies,
+                       cell->num_parent_supplies);
+       if (ret < 0)
+               goto fail_res;
+
        if (parent->of_node && cell->of_compatible) {
                for_each_child_of_node(parent->of_node, np) {
                        if (of_device_is_compatible(np, cell->of_compatible)) {
@@ -112,12 +122,12 @@ static int mfd_add_device(struct device *parent, int id,
                ret = platform_device_add_data(pdev,
                                        cell->platform_data, cell->pdata_size);
                if (ret)
-                       goto fail_res;
+                       goto fail_alias;
        }
 
-       ret = mfd_platform_add_cell(pdev, cell);
+       ret = mfd_platform_add_cell(pdev, cell, usage_count);
        if (ret)
-               goto fail_res;
+               goto fail_alias;
 
        for (r = 0; r < cell->num_resources; r++) {
                res[r].name = cell->resources[r].name;
@@ -152,17 +162,17 @@ static int mfd_add_device(struct device *parent, int id,
                if (!cell->ignore_resource_conflicts) {
                        ret = acpi_check_resource_conflict(&res[r]);
                        if (ret)
-                               goto fail_res;
+                               goto fail_alias;
                }
        }
 
        ret = platform_device_add_resources(pdev, res, cell->num_resources);
        if (ret)
-               goto fail_res;
+               goto fail_alias;
 
        ret = platform_device_add(pdev);
        if (ret)
-               goto fail_res;
+               goto fail_alias;
 
        if (cell->pm_runtime_no_callbacks)
                pm_runtime_no_callbacks(&pdev->dev);
@@ -171,6 +181,10 @@ static int mfd_add_device(struct device *parent, int id,
 
        return 0;
 
+fail_alias:
+       devm_regulator_bulk_unregister_supply_alias(&pdev->dev,
+                                                   cell->parent_supplies,
+                                                   cell->num_parent_supplies);
 fail_res:
        kfree(res);
 fail_device:
@@ -180,12 +194,12 @@ fail_alloc:
 }
 
 int mfd_add_devices(struct device *parent, int id,
-                   struct mfd_cell *cells, int n_devs,
+                   const struct mfd_cell *cells, int n_devs,
                    struct resource *mem_base,
                    int irq_base, struct irq_domain *domain)
 {
        int i;
-       int ret = 0;
+       int ret;
        atomic_t *cnts;
 
        /* initialize reference counting for all cells */
@@ -195,16 +209,19 @@ int mfd_add_devices(struct device *parent, int id,
 
        for (i = 0; i < n_devs; i++) {
                atomic_set(&cnts[i], 0);
-               cells[i].usage_count = &cnts[i];
-               ret = mfd_add_device(parent, id, cells + i, mem_base,
+               ret = mfd_add_device(parent, id, cells + i, cnts + i, mem_base,
                                     irq_base, domain);
                if (ret)
-                       break;
+                       goto fail;
        }
 
-       if (ret)
-               mfd_remove_devices(parent);
+       return 0;
 
+fail:
+       if (i)
+               mfd_remove_devices(parent);
+       else
+               kfree(cnts);
        return ret;
 }
 EXPORT_SYMBOL(mfd_add_devices);
@@ -259,8 +276,8 @@ int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
        for (i = 0; i < n_clones; i++) {
                cell_entry.name = clones[i];
                /* don't give up if a single call fails; just report error */
-               if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0,
-                                  NULL))
+               if (mfd_add_device(pdev->dev.parent, -1, &cell_entry,
+                                  cell_entry.usage_count, NULL, 0, NULL))
                        dev_err(dev, "failed to create platform device '%s'\n",
                                        clones[i]);
        }