diff options
author | Viresh Kumar <viresh.kumar@linaro.org> | 2016-10-27 13:20:18 +0300 |
---|---|---|
committer | Lee Jones <lee.jones@linaro.org> | 2016-11-29 11:21:27 +0300 |
commit | 202b56890aead063f90b6995554d4602dbd7752e (patch) | |
tree | 84a0ffb4694f3d3a11706d3afdfccb88e72c82ca /drivers/mfd/wm8994-core.c | |
parent | 8ca9edc837932469b81b8b47ea43a074b6add970 (diff) | |
download | linux-202b56890aead063f90b6995554d4602dbd7752e.tar.xz |
mfd: wm8994-core: Don't use managed regulator bulk get API
The kernel WARNs and then crashes today if wm8994_device_init() fails
after calling devm_regulator_bulk_get().
That happens because there are multiple devices involved here and the
order in which managed resources are freed isn't correct.
The regulators are added as children of wm8994->dev. Whereas,
devm_regulator_bulk_get() receives wm8994->dev as the device, though it
gets the same regulators which were added as children of wm8994->dev
earlier.
During failures, the children are removed first and the core eventually
calls regulator_unregister() for them. As regulator_put() was never done
for them (opposite of devm_regulator_bulk_get()), the kernel WARNs at
WARN_ON(rdev->open_count);
And eventually it crashes from debugfs_remove_recursive().
Fix the kernel warnings and crashes by using regulator_bulk_get()
instead of devm_regulator_bulk_get() and explicitly freeing the supplies
in exit paths.
Tested on Exynos 5250, dual core ARM A15 machine.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Acked-by: Charles Keepax <ckeepax@opensource.wolfsonmicro.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers/mfd/wm8994-core.c')
-rw-r--r-- | drivers/mfd/wm8994-core.c | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 95e6bc55adbb..953d0790ffd5 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -393,8 +393,13 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) BUG(); goto err; } - - ret = devm_regulator_bulk_get(wm8994->dev, wm8994->num_supplies, + + /* + * Can't use devres helper here as some of the supplies are provided by + * wm8994->dev's children (regulators) and those regulators are + * unregistered by the devres core before the supplies are freed. + */ + ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret); @@ -404,7 +409,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) ret = regulator_bulk_enable(wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret); - goto err; + goto err_regulator_free; } ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET); @@ -595,6 +600,8 @@ err_irq: err_enable: regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); +err_regulator_free: + regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); err: mfd_remove_devices(wm8994->dev); return ret; @@ -605,6 +612,7 @@ static void wm8994_device_exit(struct wm8994 *wm8994) pm_runtime_disable(wm8994->dev); wm8994_irq_exit(wm8994); regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); + regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); mfd_remove_devices(wm8994->dev); } |