diff options
Diffstat (limited to 'drivers/power/power_supply_core.c')
-rw-r--r-- | drivers/power/power_supply_core.c | 106 |
1 files changed, 97 insertions, 9 deletions
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c index 2ed4a4a6b3c5..869284c2e1e8 100644 --- a/drivers/power/power_supply_core.c +++ b/drivers/power/power_supply_core.c @@ -30,6 +30,8 @@ EXPORT_SYMBOL_GPL(power_supply_notifier); static struct device_type power_supply_dev_type; +#define POWER_SUPPLY_DEFERRED_REGISTER_TIME msecs_to_jiffies(10) + static bool __power_supply_is_supplied_by(struct power_supply *supplier, struct power_supply *supply) { @@ -121,6 +123,30 @@ void power_supply_changed(struct power_supply *psy) } EXPORT_SYMBOL_GPL(power_supply_changed); +/* + * Notify that power supply was registered after parent finished the probing. + * + * Often power supply is registered from driver's probe function. However + * calling power_supply_changed() directly from power_supply_register() + * would lead to execution of get_property() function provided by the driver + * too early - before the probe ends. + * + * Avoid that by waiting on parent's mutex. + */ +static void power_supply_deferred_register_work(struct work_struct *work) +{ + struct power_supply *psy = container_of(work, struct power_supply, + deferred_register_work.work); + + if (psy->dev.parent) + mutex_lock(&psy->dev.parent->mutex); + + power_supply_changed(psy); + + if (psy->dev.parent) + mutex_unlock(&psy->dev.parent->mutex); +} + #ifdef CONFIG_OF #include <linux/of.h> @@ -420,6 +446,45 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np, return psy; } EXPORT_SYMBOL_GPL(power_supply_get_by_phandle); + +static void devm_power_supply_put(struct device *dev, void *res) +{ + struct power_supply **psy = res; + + power_supply_put(*psy); +} + +/** + * devm_power_supply_get_by_phandle() - Resource managed version of + * power_supply_get_by_phandle() + * @dev: Pointer to device holding phandle property + * @phandle_name: Name of property holding a power supply phandle + * + * Return: On success returns a reference to a power supply with + * matching name equals to value under @property, NULL or ERR_PTR otherwise. + */ +struct power_supply *devm_power_supply_get_by_phandle(struct device *dev, + const char *property) +{ + struct power_supply **ptr, *psy; + + if (!dev->of_node) + return ERR_PTR(-ENODEV); + + ptr = devres_alloc(devm_power_supply_put, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + psy = power_supply_get_by_phandle(dev->of_node, property); + if (IS_ERR_OR_NULL(psy)) { + devres_free(ptr); + } else { + *ptr = psy; + devres_add(dev, ptr); + } + return psy; +} +EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle); #endif /* CONFIG_OF */ int power_supply_get_property(struct power_supply *psy, @@ -645,6 +710,10 @@ __power_supply_register(struct device *parent, struct power_supply *psy; int rc; + if (!parent) + pr_warn("%s: Expected proper parent device for '%s'\n", + __func__, desc->name); + psy = kzalloc(sizeof(*psy), GFP_KERNEL); if (!psy) return ERR_PTR(-ENOMEM); @@ -659,7 +728,6 @@ __power_supply_register(struct device *parent, dev->release = power_supply_dev_release; dev_set_drvdata(dev, psy); psy->desc = desc; - atomic_inc(&psy->use_cnt); if (cfg) { psy->drv_data = cfg->drv_data; psy->of_node = cfg->of_node; @@ -672,6 +740,8 @@ __power_supply_register(struct device *parent, goto dev_set_name_failed; INIT_WORK(&psy->changed_work, power_supply_changed_work); + INIT_DELAYED_WORK(&psy->deferred_register_work, + power_supply_deferred_register_work); rc = power_supply_check_supplies(psy); if (rc) { @@ -700,7 +770,20 @@ __power_supply_register(struct device *parent, if (rc) goto create_triggers_failed; - power_supply_changed(psy); + /* + * Update use_cnt after any uevents (most notably from device_add()). + * We are here still during driver's probe but + * the power_supply_uevent() calls back driver's get_property + * method so: + * 1. Driver did not assigned the returned struct power_supply, + * 2. Driver could not finish initialization (anything in its probe + * after calling power_supply_register()). + */ + atomic_inc(&psy->use_cnt); + + queue_delayed_work(system_power_efficient_wq, + &psy->deferred_register_work, + POWER_SUPPLY_DEFERRED_REGISTER_TIME); return psy; @@ -720,7 +803,8 @@ dev_set_name_failed: /** * power_supply_register() - Register new power supply - * @parent: Device to be a parent of power supply's device + * @parent: Device to be a parent of power supply's device, usually + * the device which probe function calls this * @desc: Description of power supply, must be valid through whole * lifetime of this power supply * @cfg: Run-time specific configuration accessed during registering, @@ -740,8 +824,9 @@ struct power_supply *__must_check power_supply_register(struct device *parent, EXPORT_SYMBOL_GPL(power_supply_register); /** - * power_supply_register() - Register new non-waking-source power supply - * @parent: Device to be a parent of power supply's device + * power_supply_register_no_ws() - Register new non-waking-source power supply + * @parent: Device to be a parent of power supply's device, usually + * the device which probe function calls this * @desc: Description of power supply, must be valid through whole * lifetime of this power supply * @cfg: Run-time specific configuration accessed during registering, @@ -769,8 +854,9 @@ static void devm_power_supply_release(struct device *dev, void *res) } /** - * power_supply_register() - Register managed power supply - * @parent: Device to be a parent of power supply's device + * devm_power_supply_register() - Register managed power supply + * @parent: Device to be a parent of power supply's device, usually + * the device which probe function calls this * @desc: Description of power supply, must be valid through whole * lifetime of this power supply * @cfg: Run-time specific configuration accessed during registering, @@ -804,8 +890,9 @@ devm_power_supply_register(struct device *parent, EXPORT_SYMBOL_GPL(devm_power_supply_register); /** - * power_supply_register() - Register managed non-waking-source power supply - * @parent: Device to be a parent of power supply's device + * devm_power_supply_register_no_ws() - Register managed non-waking-source power supply + * @parent: Device to be a parent of power supply's device, usually + * the device which probe function calls this * @desc: Description of power supply, must be valid through whole * lifetime of this power supply * @cfg: Run-time specific configuration accessed during registering, @@ -849,6 +936,7 @@ void power_supply_unregister(struct power_supply *psy) { WARN_ON(atomic_dec_return(&psy->use_cnt)); cancel_work_sync(&psy->changed_work); + cancel_delayed_work_sync(&psy->deferred_register_work); sysfs_remove_link(&psy->dev.kobj, "powers"); power_supply_remove_triggers(psy); psy_unregister_cooler(psy); |