Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mpe/linux
[firefly-linux-kernel-4.4.55.git] / drivers / power / power_supply_core.c
index 10f0b57f00d90abf7447dea6e6dfb533340c0b57..6cb7fe5c022d48ca237e31eb89e78f8f70665776 100644 (file)
@@ -58,7 +58,7 @@ static bool __power_supply_is_supplied_by(struct power_supply *supplier,
 
 static int __power_supply_changed_work(struct device *dev, void *data)
 {
-       struct power_supply *psy = (struct power_supply *)data;
+       struct power_supply *psy = data;
        struct power_supply *pst = dev_get_drvdata(dev);
 
        if (__power_supply_is_supplied_by(psy, pst)) {
@@ -78,7 +78,14 @@ static void power_supply_changed_work(struct work_struct *work)
        dev_dbg(psy->dev, "%s\n", __func__);
 
        spin_lock_irqsave(&psy->changed_lock, flags);
-       if (psy->changed) {
+       /*
+        * Check 'changed' here to avoid issues due to race between
+        * power_supply_changed() and this routine. In worst case
+        * power_supply_changed() can be called again just before we take above
+        * lock. During the first call of this routine we will mark 'changed' as
+        * false and it will stay false for the next call as well.
+        */
+       if (likely(psy->changed)) {
                psy->changed = false;
                spin_unlock_irqrestore(&psy->changed_lock, flags);
                class_for_each_device(power_supply_class, NULL, psy,
@@ -89,12 +96,13 @@ static void power_supply_changed_work(struct work_struct *work)
                kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
                spin_lock_irqsave(&psy->changed_lock, flags);
        }
+
        /*
-        * Dependent power supplies (e.g. battery) may have changed state
-        * as a result of this event, so poll again and hold the
-        * wakeup_source until all events are processed.
+        * Hold the wakeup_source until all events are processed.
+        * power_supply_changed() might have called again and have set 'changed'
+        * to true.
         */
-       if (!psy->changed)
+       if (likely(!psy->changed))
                pm_relax(psy->dev);
        spin_unlock_irqrestore(&psy->changed_lock, flags);
 }
@@ -119,7 +127,7 @@ EXPORT_SYMBOL_GPL(power_supply_changed);
 static int __power_supply_populate_supplied_from(struct device *dev,
                                                 void *data)
 {
-       struct power_supply *psy = (struct power_supply *)data;
+       struct power_supply *psy = data;
        struct power_supply *epsy = dev_get_drvdata(dev);
        struct device_node *np;
        int i = 0;
@@ -127,7 +135,7 @@ static int __power_supply_populate_supplied_from(struct device *dev,
        do {
                np = of_parse_phandle(psy->of_node, "power-supplies", i++);
                if (!np)
-                       continue;
+                       break;
 
                if (np == epsy->of_node) {
                        dev_info(psy->dev, "%s: Found supply : %s\n",
@@ -158,12 +166,12 @@ static int power_supply_populate_supplied_from(struct power_supply *psy)
 static int  __power_supply_find_supply_from_node(struct device *dev,
                                                 void *data)
 {
-       struct device_node *np = (struct device_node *)data;
+       struct device_node *np = data;
        struct power_supply *epsy = dev_get_drvdata(dev);
 
-       /* return error breaks out of class_for_each_device loop */
+       /* returning non-zero breaks out of class_for_each_device loop */
        if (epsy->of_node == np)
-               return -EINVAL;
+               return 1;
 
        return 0;
 }
@@ -171,30 +179,21 @@ static int  __power_supply_find_supply_from_node(struct device *dev,
 static int power_supply_find_supply_from_node(struct device_node *supply_node)
 {
        int error;
-       struct device *dev;
-       struct class_dev_iter iter;
 
        /*
-        * Use iterator to see if any other device is registered.
-        * This is required since class_for_each_device returns 0
-        * if there are no devices registered.
-        */
-       class_dev_iter_init(&iter, power_supply_class, NULL, NULL);
-       dev = class_dev_iter_next(&iter);
-
-       if (!dev)
-               return -EPROBE_DEFER;
-
-       /*
-        * We have to treat the return value as inverted, because if
-        * we return error on not found, then it won't continue looking.
-        * So we trick it by returning error on success to stop looking
-        * once the matching device is found.
+        * class_for_each_device() either returns its own errors or values
+        * returned by __power_supply_find_supply_from_node().
+        *
+        * __power_supply_find_supply_from_node() will return 0 (no match)
+        * or 1 (match).
+        *
+        * We return 0 if class_for_each_device() returned 1, -EPROBE_DEFER if
+        * it returned 0, or error as returned by it.
         */
        error = class_for_each_device(power_supply_class, NULL, supply_node,
                                       __power_supply_find_supply_from_node);
 
-       return error ? 0 : -EPROBE_DEFER;
+       return error ? (error == 1 ? 0 : error) : -EPROBE_DEFER;
 }
 
 static int power_supply_check_supplies(struct power_supply *psy)
@@ -215,17 +214,21 @@ static int power_supply_check_supplies(struct power_supply *psy)
 
                np = of_parse_phandle(psy->of_node, "power-supplies", cnt++);
                if (!np)
-                       continue;
+                       break;
 
                ret = power_supply_find_supply_from_node(np);
+               of_node_put(np);
+
                if (ret) {
-                       dev_dbg(psy->dev, "Failed to find supply, defer!\n");
-                       of_node_put(np);
-                       return -EPROBE_DEFER;
+                       dev_dbg(psy->dev, "Failed to find supply!\n");
+                       return ret;
                }
-               of_node_put(np);
        } while (np);
 
+       /* Missing valid "power-supplies" entries */
+       if (cnt == 1)
+               return 0;
+
        /* All supplies found, allocate char ** array for filling */
        psy->supplied_from = devm_kzalloc(psy->dev, sizeof(psy->supplied_from),
                                          GFP_KERNEL);
@@ -253,14 +256,12 @@ static inline int power_supply_check_supplies(struct power_supply *psy)
 static int __power_supply_am_i_supplied(struct device *dev, void *data)
 {
        union power_supply_propval ret = {0,};
-       struct power_supply *psy = (struct power_supply *)data;
+       struct power_supply *psy = data;
        struct power_supply *epsy = dev_get_drvdata(dev);
 
        if (__power_supply_is_supplied_by(epsy, psy))
-               if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret)) {
-                       if (ret.intval)
-                               return ret.intval;
-               }
+               if (!epsy->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, &ret))
+                       return ret.intval;
 
        return 0;
 }
@@ -285,12 +286,10 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data)
        unsigned int *count = data;
 
        (*count)++;
-       if (psy->type != POWER_SUPPLY_TYPE_BATTERY) {
-               if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
-                       return 0;
-               if (ret.intval)
+       if (psy->type != POWER_SUPPLY_TYPE_BATTERY)
+               if (!psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
                        return ret.intval;
-       }
+
        return 0;
 }
 
@@ -423,9 +422,7 @@ static int psy_register_thermal(struct power_supply *psy)
                if (psy->properties[i] == POWER_SUPPLY_PROP_TEMP) {
                        psy->tzd = thermal_zone_device_register(psy->name, 0, 0,
                                        psy, &psy_tzd_ops, NULL, 0, 0);
-                       if (IS_ERR(psy->tzd))
-                               return PTR_ERR(psy->tzd);
-                       break;
+                       return PTR_ERR_OR_ZERO(psy->tzd);
                }
        }
        return 0;
@@ -503,9 +500,7 @@ static int psy_register_cooler(struct power_supply *psy)
                        psy->tcd = thermal_cooling_device_register(
                                                        (char *)psy->name,
                                                        psy, &psy_tcd_ops);
-                       if (IS_ERR(psy->tcd))
-                               return PTR_ERR(psy->tcd);
-                       break;
+                       return PTR_ERR_OR_ZERO(psy->tcd);
                }
        }
        return 0;
@@ -591,7 +586,7 @@ static int __power_supply_register(struct device *parent,
 
        power_supply_changed(psy);
 
-       goto success;
+       return 0;
 
 create_triggers_failed:
        psy_unregister_cooler(psy);
@@ -604,7 +599,6 @@ wakeup_init_failed:
 check_supplies_failed:
 dev_set_name_failed:
        put_device(dev);
-success:
        return rc;
 }