]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
ASoC: sgtl5000: fix regulator registering/removal
authorLothar Waßmann <LW@KARO-electronics.de>
Thu, 17 Jul 2014 13:06:11 +0000 (15:06 +0200)
committerLothar Waßmann <LW@KARO-electronics.de>
Thu, 17 Jul 2014 13:06:11 +0000 (15:06 +0200)
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.

sound/soc/codecs/sgtl5000.c

index 9f126d625d8ace2325042d40d4d1e680d516bd99..7449438095f9458ffc48ffd797ef11e0afc89ead 100644 (file)
@@ -881,6 +881,7 @@ static int ldo_regulator_remove(struct snd_soc_codec *codec)
                return 0;
 
        regulator_unregister(ldo->dev);
+       sgtl5000->ldo = NULL;
 
        return 0;
 }
@@ -1265,7 +1266,12 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
                        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;
@@ -1273,18 +1279,20 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
        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)
@@ -1349,6 +1357,8 @@ 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;
@@ -1362,6 +1372,8 @@ static int sgtl5000_remove(struct snd_soc_codec *codec)
 
        regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
                                                sgtl5000->supplies);
+       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+                                sgtl5000->supplies);
        ldo_regulator_remove(codec);
 
        return 0;