Due to using devm_regulator_bulk_get() the LDO regulator which may
have been registered is still referenced when ldo_regulator_remove()
tries to unregister it. This leads to the following warning from the
regulator subsystem (and makes it impossible to unload the
snd-soc-sgtl5000 module):
WARNING: CPU: 0 PID: 1297 at drivers/regulator/core.c:3600 regulator_unregister+0x78/0xd4()
Modules linked in: snd_soc_imx_sgtl5000(-) snd_soc_fsl_ssi imx_pcm_dma imx_pcm_fiq snd_soc_imx_audmux snd_soc_sgtl5000 regmap_i2c snd_soc_core snd_compress snd_pcm_dmaengine snd_pcm snd_timer snd flexcan can_dev usbmisc_imx mmc_block ci_hdrc_imx ci_hdrc udc_core ehci_hcd imx_keypad matrix_keymap sdhci_esdhc_imx sdhci_pltfm sdhci mmc_core spidev spi_imx spi_bitbang gpio_keys [last unloaded: tsc2007]
CPU: 0 PID: 1297 Comm: modprobe Not tainted 3.16.0-rc2-next-
20140627-dt+ #1
Backtrace:
[<
80011324>] (dump_backtrace) from [<
8001169c>] (show_stack+0x18/0x1c)
r6:
804c5563 r5:
00000009 r4:
00000000 r3:
00400100
[<
80011684>] (show_stack) from [<
803b7cc0>] (dump_stack+0x20/0x28)
[<
803b7ca0>] (dump_stack) from [<
8001cc70>] (warn_slowpath_common+0x6c/0x8c)
[<
8001cc04>] (warn_slowpath_common) from [<
8001ccb4>] (warn_slowpath_null+0x24/0x2c)
r8:
9e6c93c0 r7:
00000000 r6:
00000000 r5:
9e6c9798 r4:
9d820980
[<
8001cc90>] (warn_slowpath_null) from [<
802472bc>] (regulator_unregister+0x78/0xd4)
[<
80247244>] (regulator_unregister) from [<
7f176720>] (ldo_regulator_remove+0x28/0x30 [snd_soc_sgtl5000])
r4:
9e7497c0 r3:
9d871918
[<
7f1766f8>] (ldo_regulator_remove [snd_soc_sgtl5000]) from [<
7f17675c>] (sgtl5000_remove+0x34/0x3c [snd_soc_sgtl5000])
[<
7f176728>] (sgtl5000_remove [snd_soc_sgtl5000]) from [<
7f13e090>] (soc_remove_codec+0x24/0x94 [snd_soc_core])
r5:
9d8234ac r4:
9e7497c0
Use regulator_bulk_get() and explicitly free the regulators to prevent
this.
Also set sgtl5000->ldo to NULL when the regulator has been
unregistered to prevent possible derefence of the stale pointer.
return 0;
regulator_unregister(ldo->dev);
+ sgtl5000->ldo = NULL;
return 0;
}
return ret;
}
- ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+ /*
+ * We cannot use devm_regulator_bulk_get() here, because
+ * we need to be able to drop the reference to the internal
+ * VDDD regulator before unregistering it in ldo_regulator_remove().
+ */
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
if (ret)
goto err_ldo_remove;
ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
if (ret)
- goto err_ldo_remove;
+ goto err_regulator_free;
/* wait for all power rails bring up */
udelay(10);
return 0;
+err_regulator_free:
+ regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
err_ldo_remove:
if (!external_vddd)
ldo_regulator_remove(codec);
return ret;
-
}
static int sgtl5000_probe(struct snd_soc_codec *codec)
err:
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
+ regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
ldo_regulator_remove(codec);
return ret;
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
+ regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
ldo_regulator_remove(codec);
return 0;