regulator: Fix for v7.4

One fix here, we had an issue with deferred hardware disables where if
 we were deferring while suspend was happening we would let the workqueue
 run which could result in us trying to access hardware after it or the
 buses used to access it were suspended.  We fix this by completing any
 work as we suspend the individual regulators.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQReagvpf3TrrA7BoYYv73Ows9A4iQUCauTPJwAKCRAv73Ows9A4
 ickkAP0StfBcdw1DyeDqAc3rK9y1qNE/X93ugNE+isHg3+kDEwEA0rB+UzRRODrs
 oR08dslt9aAOZpzj5c2alEIfnR4I7wY=
 =XIiE
 -----END PGP SIGNATURE-----

Merge tag 'regulator-fix-v7.4-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator

Pull regulator fix from Mark Brown:
 "One fix here, we had an issue with deferred hardware disables where
  if we were deferring while suspend was happening we would let the
  workqueue run which could result in us trying to access hardware after
  it or the buses used to access it were suspended.

  We fix this by completing any work as we suspend the individual
  regulators"

* tag 'regulator-fix-v7.4-merge-window' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator:
  regulator: irq: freeze deferred hardware access during suspend
This commit is contained in:
Linus Torvalds 2026-10-30 08:24:02 -10:00
commit 2228d64da8

View File

@ -15,6 +15,7 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/regulator/driver.h>
#include "internal.h"
@ -27,6 +28,7 @@ struct regulator_irq {
int irq;
int retry_cnt;
struct delayed_work isr_work;
struct workqueue_struct *wq;
};
/*
@ -145,12 +147,7 @@ static void regulator_notifier_isr_work(struct work_struct *work)
return;
reschedule:
if (!d->high_prio)
mod_delayed_work(system_dfl_wq, &h->isr_work,
msecs_to_jiffies(tmo));
else
mod_delayed_work(system_highpri_wq, &h->isr_work,
msecs_to_jiffies(tmo));
mod_delayed_work(h->wq, &h->isr_work, msecs_to_jiffies(tmo));
}
static irqreturn_t regulator_notifier_isr(int irq, void *data)
@ -248,13 +245,8 @@ static irqreturn_t regulator_notifier_isr(int irq, void *data)
}
if (d->irq_off_ms) {
if (!d->high_prio)
schedule_delayed_work(&h->isr_work,
msecs_to_jiffies(d->irq_off_ms));
else
mod_delayed_work(system_highpri_wq,
&h->isr_work,
msecs_to_jiffies(d->irq_off_ms));
mod_delayed_work(h->wq, &h->isr_work,
msecs_to_jiffies(d->irq_off_ms));
}
return IRQ_HANDLED;
@ -363,13 +355,25 @@ void *regulator_irq_helper(struct device *dev,
init_rdev_errors(h);
if (h->desc.irq_off_ms)
if (h->desc.irq_off_ms) {
if (h->desc.high_prio) {
h->wq = alloc_workqueue("regulator_irq_highpri",
WQ_FREEZABLE | WQ_HIGHPRI, 0);
if (!h->wq)
return ERR_PTR(-ENOMEM);
} else {
h->wq = system_freezable_wq;
}
INIT_DELAYED_WORK(&h->isr_work, regulator_notifier_isr_work);
}
ret = request_threaded_irq(h->irq, NULL, regulator_notifier_isr,
IRQF_ONESHOT | irq_flags, h->desc.name, h);
if (ret) {
dev_err(dev, "Failed to request IRQ %d\n", irq);
if (h->desc.irq_off_ms && h->desc.high_prio)
destroy_workqueue(h->wq);
return ERR_PTR(ret);
}
@ -393,8 +397,11 @@ void regulator_irq_helper_cancel(void **handle)
struct regulator_irq *h = *handle;
free_irq(h->irq, h);
if (h->desc.irq_off_ms)
if (h->desc.irq_off_ms) {
cancel_delayed_work_sync(&h->isr_work);
if (h->desc.high_prio)
destroy_workqueue(h->wq);
}
h = NULL;
}
@ -441,4 +448,3 @@ int regulator_irq_map_event_simple(int irq, struct regulator_irq_data *rid,
return 0;
}
EXPORT_SYMBOL_GPL(regulator_irq_map_event_simple);