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>
This commit is contained in:
parent
8ca9edc837
commit
202b56890a
|
@ -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 @@ static int wm8994_device_init(struct wm8994 *wm8994, int 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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user