diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index f8f392592193..cfcc05dfe28d 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -3054,6 +3054,20 @@ static void ab8500_fg_unbind(struct device *dev, struct device *master, flush_workqueue(di->fg_wq); } +/* Disable, not cancel: works stay disabled so nothing can re-arm them. */ +static void ab8500_fg_destroy_workqueue(void *data) +{ + struct ab8500_fg *di = data; + + disable_work_sync(&di->fg_acc_cur_work); + disable_work_sync(&di->fg_work); + disable_delayed_work_sync(&di->fg_reinit_work); + disable_delayed_work_sync(&di->fg_low_bat_work); + disable_delayed_work_sync(&di->fg_check_hw_failure_work); + disable_delayed_work_sync(&di->fg_periodic_work); + destroy_workqueue(di->fg_wq); +} + static const struct component_ops ab8500_fg_component_ops = { .bind = ab8500_fg_bind, .unbind = ab8500_fg_unbind, @@ -3155,6 +3169,11 @@ static int ab8500_fg_probe(struct platform_device *pdev) return PTR_ERR(di->fg_psy); } + /* Registered after fg_psy, before the IRQs: devm frees IRQ -> workqueue -> fg_psy. */ + ret = devm_add_action_or_reset(dev, ab8500_fg_destroy_workqueue, di); + if (ret) + return ret; + di->fg_samples = SEC_TO_SAMPLE(di->bm->fg_params->init_timer); /* @@ -3167,20 +3186,16 @@ static int ab8500_fg_probe(struct platform_device *pdev) /* Register primary interrupt handlers */ for (i = 0; i < ARRAY_SIZE(ab8500_fg_irq); i++) { irq = platform_get_irq_byname(pdev, ab8500_fg_irq[i].name); - if (irq < 0) { - destroy_workqueue(di->fg_wq); + if (irq < 0) return irq; - } ret = devm_request_threaded_irq(dev, irq, NULL, ab8500_fg_irq[i].isr, IRQF_SHARED | IRQF_NO_SUSPEND | IRQF_ONESHOT, ab8500_fg_irq[i].name, di); - if (ret != 0) { - destroy_workqueue(di->fg_wq); + if (ret != 0) return ret; - } dev_dbg(dev, "Requested %s IRQ %d: %d\n", ab8500_fg_irq[i].name, irq, ret); } @@ -3194,7 +3209,6 @@ static int ab8500_fg_probe(struct platform_device *pdev) ret = ab8500_fg_sysfs_init(di); if (ret) { dev_err(dev, "failed to create sysfs entry\n"); - destroy_workqueue(di->fg_wq); return ret; } @@ -3202,7 +3216,6 @@ static int ab8500_fg_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "failed to create FG psy\n"); ab8500_fg_sysfs_exit(di); - destroy_workqueue(di->fg_wq); return ret; } @@ -3222,7 +3235,6 @@ static void ab8500_fg_remove(struct platform_device *pdev) { struct ab8500_fg *di = platform_get_drvdata(pdev); - destroy_workqueue(di->fg_wq); component_del(&pdev->dev, &ab8500_fg_component_ops); list_del(&di->node); ab8500_fg_sysfs_exit(di);