| From 4706985a046a0b9a5760f9575771302208b8fe3b Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sun, 7 Mar 2021 16:17:57 +0100 |
| Subject: extcon: arizona: Fix various races on driver unbind |
| |
| From: Hans de Goede <hdegoede@redhat.com> |
| |
| [ Upstream commit e5b499f6fb17bc95a813e85d0796522280203806 ] |
| |
| We must free/disable all interrupts and cancel all pending works |
| before doing further cleanup. |
| |
| Before this commit arizona_extcon_remove() was doing several |
| register writes to shut things down before disabling the IRQs |
| and it was cancelling only 1 of the 3 different works used. |
| |
| Move all the register-writes shutting things down to after |
| the disabling of the IRQs and add the 2 missing |
| cancel_delayed_work_sync() calls. |
| |
| This fixes various possible races on driver unbind. One of which |
| would always trigger on devices using the mic-clamp feature for |
| jack detection. The ARIZONA_MICD_CLAMP_MODE_MASK update was |
| done before disabling the IRQs, causing: |
| 1. arizona_jackdet() to run |
| 2. detect a jack being inserted (clamp disabled means jack inserted) |
| 3. call arizona_start_mic() which: |
| 3.1 Enables the MICVDD regulator |
| 3.2 takes a pm_runtime_reference |
| |
| And this was all happening after the ARIZONA_MICD_ENA bit clearing, |
| which would undo 3.1 and 3.2 because the ARIZONA_MICD_CLAMP_MODE_MASK |
| update was being done after the ARIZONA_MICD_ENA bit clearing. |
| |
| So this means that arizona_extcon_remove() would exit with |
| 1. MICVDD enabled and 2. The pm_runtime_reference being unbalanced. |
| |
| MICVDD still being enabled caused the following oops when the |
| regulator is released by the devm framework: |
| |
| [ 2850.745757] ------------[ cut here ]------------ |
| [ 2850.745827] WARNING: CPU: 2 PID: 2098 at drivers/regulator/core.c:2123 _regulator_put.part.0+0x19f/0x1b0 |
| [ 2850.745835] Modules linked in: extcon_arizona ... |
| ... |
| [ 2850.746909] Call Trace: |
| [ 2850.746932] regulator_put+0x2d/0x40 |
| [ 2850.746946] release_nodes+0x22a/0x260 |
| [ 2850.746984] __device_release_driver+0x190/0x240 |
| [ 2850.747002] driver_detach+0xd4/0x120 |
| ... |
| [ 2850.747337] ---[ end trace f455dfd7abd9781f ]--- |
| |
| Note this oops is just one of various theoretically possible races caused |
| by the wrong ordering inside arizona_extcon_remove(), this fixes the |
| ordering fixing all possible races, including the reported oops. |
| |
| Signed-off-by: Hans de Goede <hdegoede@redhat.com> |
| Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> |
| Acked-by: Charles Keepax <ckeepax@opensource.cirrus.com> |
| Tested-by: Charles Keepax <ckeepax@opensource.cirrus.com> |
| Acked-by: Chanwoo Choi <cw00.choi@samsung.com> |
| Signed-off-by: Lee Jones <lee.jones@linaro.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/extcon/extcon-arizona.c | 40 +++++++++++++++++---------------- |
| 1 file changed, 21 insertions(+), 19 deletions(-) |
| |
| diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c |
| index f7ef247de46a..76aacbac5869 100644 |
| --- a/drivers/extcon/extcon-arizona.c |
| +++ b/drivers/extcon/extcon-arizona.c |
| @@ -1760,25 +1760,6 @@ static int arizona_extcon_remove(struct platform_device *pdev) |
| bool change; |
| int ret; |
| |
| - ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, |
| - ARIZONA_MICD_ENA, 0, |
| - &change); |
| - if (ret < 0) { |
| - dev_err(&pdev->dev, "Failed to disable micd on remove: %d\n", |
| - ret); |
| - } else if (change) { |
| - regulator_disable(info->micvdd); |
| - pm_runtime_put(info->dev); |
| - } |
| - |
| - gpiod_put(info->micd_pol_gpio); |
| - |
| - pm_runtime_disable(&pdev->dev); |
| - |
| - regmap_update_bits(arizona->regmap, |
| - ARIZONA_MICD_CLAMP_CONTROL, |
| - ARIZONA_MICD_CLAMP_MODE_MASK, 0); |
| - |
| if (info->micd_clamp) { |
| jack_irq_rise = ARIZONA_IRQ_MICD_CLAMP_RISE; |
| jack_irq_fall = ARIZONA_IRQ_MICD_CLAMP_FALL; |
| @@ -1794,10 +1775,31 @@ static int arizona_extcon_remove(struct platform_device *pdev) |
| arizona_free_irq(arizona, jack_irq_rise, info); |
| arizona_free_irq(arizona, jack_irq_fall, info); |
| cancel_delayed_work_sync(&info->hpdet_work); |
| + cancel_delayed_work_sync(&info->micd_detect_work); |
| + cancel_delayed_work_sync(&info->micd_timeout_work); |
| + |
| + ret = regmap_update_bits_check(arizona->regmap, ARIZONA_MIC_DETECT_1, |
| + ARIZONA_MICD_ENA, 0, |
| + &change); |
| + if (ret < 0) { |
| + dev_err(&pdev->dev, "Failed to disable micd on remove: %d\n", |
| + ret); |
| + } else if (change) { |
| + regulator_disable(info->micvdd); |
| + pm_runtime_put(info->dev); |
| + } |
| + |
| + regmap_update_bits(arizona->regmap, |
| + ARIZONA_MICD_CLAMP_CONTROL, |
| + ARIZONA_MICD_CLAMP_MODE_MASK, 0); |
| regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE, |
| ARIZONA_JD1_ENA, 0); |
| arizona_clk32k_disable(arizona); |
| |
| + gpiod_put(info->micd_pol_gpio); |
| + |
| + pm_runtime_disable(&pdev->dev); |
| + |
| return 0; |
| } |
| |
| -- |
| 2.30.2 |
| |