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:
@@ -394,7 +394,12 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
|||||||
goto err;
|
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);
|
wm8994->supplies);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
|
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);
|
ret = regulator_bulk_enable(wm8994->num_supplies, wm8994->supplies);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret);
|
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);
|
ret = wm8994_reg_read(wm8994, WM8994_SOFTWARE_RESET);
|
||||||
@@ -595,6 +600,8 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
|
|||||||
err_enable:
|
err_enable:
|
||||||
regulator_bulk_disable(wm8994->num_supplies,
|
regulator_bulk_disable(wm8994->num_supplies,
|
||||||
wm8994->supplies);
|
wm8994->supplies);
|
||||||
|
err_regulator_free:
|
||||||
|
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
|
||||||
err:
|
err:
|
||||||
mfd_remove_devices(wm8994->dev);
|
mfd_remove_devices(wm8994->dev);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -605,6 +612,7 @@ static void wm8994_device_exit(struct wm8994 *wm8994)
|
|||||||
pm_runtime_disable(wm8994->dev);
|
pm_runtime_disable(wm8994->dev);
|
||||||
wm8994_irq_exit(wm8994);
|
wm8994_irq_exit(wm8994);
|
||||||
regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies);
|
regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies);
|
||||||
|
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
|
||||||
mfd_remove_devices(wm8994->dev);
|
mfd_remove_devices(wm8994->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user