]> Pileus Git - ~andy/linux/blobdiff - drivers/regulator/core.c
Merge branch 'drm-fixes' of git://people.freedesktop.org/~airlied/linux
[~andy/linux] / drivers / regulator / core.c
index 938398f3e869c57814fc130b9133ac435118214e..ca86f39a0fdc824fba463f0eb30157ce933bbe9c 100644 (file)
@@ -25,6 +25,8 @@
 #include <linux/mutex.h>
 #include <linux/suspend.h>
 #include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/regulator/of_regulator.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
@@ -132,6 +134,33 @@ static struct regulator *get_device_regulator(struct device *dev)
        return NULL;
 }
 
+/**
+ * of_get_regulator - get a regulator device node based on supply name
+ * @dev: Device pointer for the consumer (of regulator) device
+ * @supply: regulator supply name
+ *
+ * Extract the regulator device node corresponding to the supply name.
+ * retruns the device node corresponding to the regulator if found, else
+ * returns NULL.
+ */
+static struct device_node *of_get_regulator(struct device *dev, const char *supply)
+{
+       struct device_node *regnode = NULL;
+       char prop_name[32]; /* 32 is max size of property name */
+
+       dev_dbg(dev, "Looking up %s-supply from device tree\n", supply);
+
+       snprintf(prop_name, 32, "%s-supply", supply);
+       regnode = of_parse_phandle(dev->of_node, prop_name, 0);
+
+       if (!regnode) {
+               dev_warn(dev, "%s property in node %s references invalid phandle",
+                               prop_name, dev->of_node->full_name);
+               return NULL;
+       }
+       return regnode;
+}
+
 /* Platform voltage constraint check */
 static int regulator_check_voltage(struct regulator_dev *rdev,
                                   int *min_uV, int *max_uV)
@@ -883,8 +912,12 @@ static int set_machine_constraints(struct regulator_dev *rdev,
        int ret = 0;
        struct regulator_ops *ops = rdev->desc->ops;
 
-       rdev->constraints = kmemdup(constraints, sizeof(*constraints),
-                                   GFP_KERNEL);
+       if (constraints)
+               rdev->constraints = kmemdup(constraints, sizeof(*constraints),
+                                           GFP_KERNEL);
+       else
+               rdev->constraints = kzalloc(sizeof(*constraints),
+                                           GFP_KERNEL);
        if (!rdev->constraints)
                return -ENOMEM;
 
@@ -893,7 +926,7 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                goto out;
 
        /* do we need to setup our suspend state */
-       if (constraints->initial_state) {
+       if (rdev->constraints->initial_state) {
                ret = suspend_prepare(rdev, rdev->constraints->initial_state);
                if (ret < 0) {
                        rdev_err(rdev, "failed to set suspend state\n");
@@ -901,7 +934,7 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                }
        }
 
-       if (constraints->initial_mode) {
+       if (rdev->constraints->initial_mode) {
                if (!ops->set_mode) {
                        rdev_err(rdev, "no set_mode operation\n");
                        ret = -EINVAL;
@@ -952,9 +985,8 @@ static int set_supply(struct regulator_dev *rdev,
        rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev));
 
        rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY");
-       if (IS_ERR(rdev->supply)) {
-               err = PTR_ERR(rdev->supply);
-               rdev->supply = NULL;
+       if (rdev->supply == NULL) {
+               err = -ENOMEM;
                return err;
        }
 
@@ -1148,6 +1180,30 @@ static int _regulator_get_enable_time(struct regulator_dev *rdev)
        return rdev->desc->ops->enable_time(rdev);
 }
 
+static struct regulator_dev *regulator_dev_lookup(struct device *dev,
+                                                        const char *supply)
+{
+       struct regulator_dev *r;
+       struct device_node *node;
+
+       /* first do a dt based lookup */
+       if (dev && dev->of_node) {
+               node = of_get_regulator(dev, supply);
+               if (node)
+                       list_for_each_entry(r, &regulator_list, list)
+                               if (r->dev.parent &&
+                                       node == r->dev.of_node)
+                                       return r;
+       }
+
+       /* if not found, try doing it non-dt way */
+       list_for_each_entry(r, &regulator_list, list)
+               if (strcmp(rdev_get_name(r), supply) == 0)
+                       return r;
+
+       return NULL;
+}
+
 /* Internal regulator request function */
 static struct regulator *_regulator_get(struct device *dev, const char *id,
                                        int exclusive)
@@ -1168,6 +1224,10 @@ static struct regulator *_regulator_get(struct device *dev, const char *id,
 
        mutex_lock(&regulator_list_mutex);
 
+       rdev = regulator_dev_lookup(dev, id);
+       if (rdev)
+               goto found;
+
        list_for_each_entry(map, &regulator_map_list, list) {
                /* If the mapping has a device set up it must match */
                if (map->dev_name &&
@@ -1221,6 +1281,7 @@ found:
        if (regulator == NULL) {
                regulator = ERR_PTR(-ENOMEM);
                module_put(rdev->owner);
+               goto out;
        }
 
        rdev->open_count++;
@@ -1726,6 +1787,7 @@ int regulator_is_supported_voltage(struct regulator *regulator,
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(regulator_is_supported_voltage);
 
 static int _regulator_do_set_voltage(struct regulator_dev *rdev,
                                     int min_uV, int max_uV)
@@ -2428,6 +2490,43 @@ err:
 }
 EXPORT_SYMBOL_GPL(regulator_bulk_disable);
 
+/**
+ * regulator_bulk_force_disable - force disable multiple regulator consumers
+ *
+ * @num_consumers: Number of consumers
+ * @consumers:     Consumer data; clients are stored here.
+ * @return         0 on success, an errno on failure
+ *
+ * This convenience API allows consumers to forcibly disable multiple regulator
+ * clients in a single API call.
+ * NOTE: This should be used for situations when device damage will
+ * likely occur if the regulators are not disabled (e.g. over temp).
+ * Although regulator_force_disable function call for some consumers can
+ * return error numbers, the function is called for all consumers.
+ */
+int regulator_bulk_force_disable(int num_consumers,
+                          struct regulator_bulk_data *consumers)
+{
+       int i;
+       int ret;
+
+       for (i = 0; i < num_consumers; i++)
+               consumers[i].ret =
+                           regulator_force_disable(consumers[i].consumer);
+
+       for (i = 0; i < num_consumers; i++) {
+               if (consumers[i].ret != 0) {
+                       ret = consumers[i].ret;
+                       goto out;
+               }
+       }
+
+       return 0;
+out:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_bulk_force_disable);
+
 /**
  * regulator_bulk_free - free multiple regulator consumers
  *
@@ -2503,7 +2602,8 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
        int                     status = 0;
 
        /* some attributes need specific methods to be displayed */
-       if (ops->get_voltage || ops->get_voltage_sel) {
+       if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
+           (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0)) {
                status = device_create_file(dev, &dev_attr_microvolts);
                if (status < 0)
                        return status;
@@ -2637,11 +2737,13 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
  */
 struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        struct device *dev, const struct regulator_init_data *init_data,
-       void *driver_data)
+       void *driver_data, struct device_node *of_node)
 {
+       const struct regulation_constraints *constraints = NULL;
        static atomic_t regulator_no = ATOMIC_INIT(0);
        struct regulator_dev *rdev;
        int ret, i;
+       const char *supply = NULL;
 
        if (regulator_desc == NULL)
                return ERR_PTR(-EINVAL);
@@ -2653,9 +2755,6 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
            regulator_desc->type != REGULATOR_CURRENT)
                return ERR_PTR(-EINVAL);
 
-       if (!init_data)
-               return ERR_PTR(-EINVAL);
-
        /* Only one of each should be implemented */
        WARN_ON(regulator_desc->ops->get_voltage &&
                regulator_desc->ops->get_voltage_sel);
@@ -2688,7 +2787,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);
 
        /* preform any regulator specific init */
-       if (init_data->regulator_init) {
+       if (init_data && init_data->regulator_init) {
                ret = init_data->regulator_init(rdev->reg_data);
                if (ret < 0)
                        goto clean;
@@ -2696,6 +2795,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
 
        /* register with sysfs */
        rdev->dev.class = &regulator_class;
+       rdev->dev.of_node = of_node;
        rdev->dev.parent = dev;
        dev_set_name(&rdev->dev, "regulator.%d",
                     atomic_inc_return(&regulator_no) - 1);
@@ -2708,7 +2808,10 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        dev_set_drvdata(&rdev->dev, rdev);
 
        /* set regulator constraints */
-       ret = set_machine_constraints(rdev, &init_data->constraints);
+       if (init_data)
+               constraints = &init_data->constraints;
+
+       ret = set_machine_constraints(rdev, constraints);
        if (ret < 0)
                goto scrub;
 
@@ -2717,21 +2820,18 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        if (ret < 0)
                goto scrub;
 
-       if (init_data->supply_regulator) {
+       if (init_data && init_data->supply_regulator)
+               supply = init_data->supply_regulator;
+       else if (regulator_desc->supply_name)
+               supply = regulator_desc->supply_name;
+
+       if (supply) {
                struct regulator_dev *r;
-               int found = 0;
 
-               list_for_each_entry(r, &regulator_list, list) {
-                       if (strcmp(rdev_get_name(r),
-                                  init_data->supply_regulator) == 0) {
-                               found = 1;
-                               break;
-                       }
-               }
+               r = regulator_dev_lookup(dev, supply);
 
-               if (!found) {
-                       dev_err(dev, "Failed to find supply %s\n",
-                               init_data->supply_regulator);
+               if (!r) {
+                       dev_err(dev, "Failed to find supply %s\n", supply);
                        ret = -ENODEV;
                        goto scrub;
                }
@@ -2739,18 +2839,28 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
                ret = set_supply(rdev, r);
                if (ret < 0)
                        goto scrub;
+
+               /* Enable supply if rail is enabled */
+               if (rdev->desc->ops->is_enabled &&
+                               rdev->desc->ops->is_enabled(rdev)) {
+                       ret = regulator_enable(rdev->supply);
+                       if (ret < 0)
+                               goto scrub;
+               }
        }
 
        /* add consumers devices */
-       for (i = 0; i < init_data->num_consumer_supplies; i++) {
-               ret = set_consumer_device_supply(rdev,
-                       init_data->consumer_supplies[i].dev,
-                       init_data->consumer_supplies[i].dev_name,
-                       init_data->consumer_supplies[i].supply);
-               if (ret < 0) {
-                       dev_err(dev, "Failed to set supply %s\n",
+       if (init_data) {
+               for (i = 0; i < init_data->num_consumer_supplies; i++) {
+                       ret = set_consumer_device_supply(rdev,
+                               init_data->consumer_supplies[i].dev,
+                               init_data->consumer_supplies[i].dev_name,
                                init_data->consumer_supplies[i].supply);
-                       goto unset_supplies;
+                       if (ret < 0) {
+                               dev_err(dev, "Failed to set supply %s\n",
+                                       init_data->consumer_supplies[i].supply);
+                               goto unset_supplies;
+                       }
                }
        }