summaryrefslogtreecommitdiff
path: root/drivers/power/power_supply_core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/power_supply_core.c')
-rw-r--r--drivers/power/power_supply_core.c100
1 files changed, 47 insertions, 53 deletions
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index 078afd61490d..6cb7fe5c022d 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -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);
@@ -234,7 +237,7 @@ static int power_supply_check_supplies(struct power_supply *psy)
return -ENOMEM;
}
- *psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * cnt,
+ *psy->supplied_from = devm_kzalloc(psy->dev, sizeof(char *) * (cnt - 1),
GFP_KERNEL);
if (!*psy->supplied_from) {
dev_err(psy->dev, "Couldn't allocate memory for supply list\n");
@@ -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;
}