Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Yuho Choi <dbgh9129@gmail.com>
To: Lee Jones <lee@kernel.org>,
	Matthias Brugger <matthias.bgg@gmail.com>,
	AngeloGioacchino Del Regno
	<angelogioacchino.delregno@collabora.com>
Cc: linux-kernel@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-mediatek@lists.infradead.org,
	Yuho Choi <dbgh9129@gmail.com>
Subject: [PATCH v2] mfd: mt6397-irq: Fix PM notifier and IRQ domain teardown
Date: Fri, 19 Jun 2026 12:56:49 -0400	[thread overview]
Message-ID: <20260619165649.1067266-1-dbgh9129@gmail.com> (raw)

mt6397_irq_init() registers a PM notifier and creates an irq_domain. Both
objects point back to the devm-managed mt6397_chip.

The notifier was not unregistered on probe failure or unbind, so a later PM
transition could dereference freed chip state. The irq_domain had the
opposite lifetime problem: it could be removed while the devm-managed
parent IRQ was still active, but it was leaked on successful unbind.

Manage both lifetimes with devm actions. Register the domain cleanup before
requesting the parent IRQ, so devres frees the parent IRQ before removing
child mappings and the irq_domain. This keeps the domain alive while the
parent IRQ handler can still run.

Also serialize PM notifier mask writes with irqlock. While suspend is in
progress, keep wake_mask programmed in hardware even if a concurrent IRQ
sync updates irq_masks_cur in software.

Fixes: a4872e80ce7d ("mfd: mt6397: Extract IRQ related code from core driver")
Fixes: 4e2e7cfec13a ("mfd: mt6397: Modify suspend/resume behavior")
Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
---
Changes in v2:
- Use devm actions for PM notifier and irq_domain cleanup.
- Remove direct irq_domain_remove() calls from failure paths.
- Serialize PM notifier mask writes with irqlock.
---
 drivers/mfd/mt6397-core.c       |  4 +-
 drivers/mfd/mt6397-irq.c        | 85 +++++++++++++++++++++++----------
 include/linux/mfd/mt6397/core.h |  1 +
 3 files changed, 63 insertions(+), 27 deletions(-)

diff --git a/drivers/mfd/mt6397-core.c b/drivers/mfd/mt6397-core.c
index 1bdacda9a933..ff1e393202ee 100644
--- a/drivers/mfd/mt6397-core.c
+++ b/drivers/mfd/mt6397-core.c
@@ -389,10 +389,8 @@ static int mt6397_probe(struct platform_device *pdev)
 	ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE,
 				   pmic_core->cells, pmic_core->cell_size,
 				   NULL, 0, pmic->irq_domain);
-	if (ret) {
-		irq_domain_remove(pmic->irq_domain);
+	if (ret)
 		dev_err(&pdev->dev, "failed to add child devices: %d\n", ret);
-	}
 
 	return ret;
 }
diff --git a/drivers/mfd/mt6397-irq.c b/drivers/mfd/mt6397-irq.c
index 5d2e5459f744..4be74d8325cd 100644
--- a/drivers/mfd/mt6397-irq.c
+++ b/drivers/mfd/mt6397-irq.c
@@ -25,17 +25,23 @@ static void mt6397_irq_lock(struct irq_data *data)
 	mutex_lock(&mt6397->irqlock);
 }
 
+static void mt6397_irq_write_masks(struct mt6397_chip *mt6397,
+				   const u16 *masks)
+{
+	regmap_write(mt6397->regmap, mt6397->int_con[0], masks[0]);
+	regmap_write(mt6397->regmap, mt6397->int_con[1], masks[1]);
+	if (mt6397->int_con[2])
+		regmap_write(mt6397->regmap, mt6397->int_con[2], masks[2]);
+}
+
 static void mt6397_irq_sync_unlock(struct irq_data *data)
 {
 	struct mt6397_chip *mt6397 = irq_data_get_irq_chip_data(data);
+	const u16 *masks;
 
-	regmap_write(mt6397->regmap, mt6397->int_con[0],
-		     mt6397->irq_masks_cur[0]);
-	regmap_write(mt6397->regmap, mt6397->int_con[1],
-		     mt6397->irq_masks_cur[1]);
-	if (mt6397->int_con[2])
-		regmap_write(mt6397->regmap, mt6397->int_con[2],
-			     mt6397->irq_masks_cur[2]);
+	masks = mt6397->irq_suspended ? mt6397->wake_mask :
+					mt6397->irq_masks_cur;
+	mt6397_irq_write_masks(mt6397, masks);
 
 	mutex_unlock(&mt6397->irqlock);
 }
@@ -141,24 +147,20 @@ static int mt6397_irq_pm_notifier(struct notifier_block *notifier,
 
 	switch (pm_event) {
 	case PM_SUSPEND_PREPARE:
-		regmap_write(chip->regmap,
-			     chip->int_con[0], chip->wake_mask[0]);
-		regmap_write(chip->regmap,
-			     chip->int_con[1], chip->wake_mask[1]);
-		if (chip->int_con[2])
-			regmap_write(chip->regmap,
-				     chip->int_con[2], chip->wake_mask[2]);
+		mutex_lock(&chip->irqlock);
+		chip->irq_suspended = true;
+		mt6397_irq_write_masks(chip, chip->wake_mask);
+		mutex_unlock(&chip->irqlock);
+
 		enable_irq_wake(chip->irq);
 		break;
 
 	case PM_POST_SUSPEND:
-		regmap_write(chip->regmap,
-			     chip->int_con[0], chip->irq_masks_cur[0]);
-		regmap_write(chip->regmap,
-			     chip->int_con[1], chip->irq_masks_cur[1]);
-		if (chip->int_con[2])
-			regmap_write(chip->regmap,
-				     chip->int_con[2], chip->irq_masks_cur[2]);
+		mutex_lock(&chip->irqlock);
+		chip->irq_suspended = false;
+		mt6397_irq_write_masks(chip, chip->irq_masks_cur);
+		mutex_unlock(&chip->irqlock);
+
 		disable_irq_wake(chip->irq);
 		break;
 
@@ -169,6 +171,29 @@ static int mt6397_irq_pm_notifier(struct notifier_block *notifier,
 	return NOTIFY_DONE;
 }
 
+static void mt6397_irq_pm_notifier_unregister(void *data)
+{
+	struct mt6397_chip *chip = data;
+
+	unregister_pm_notifier(&chip->pm_nb);
+}
+
+static void mt6397_irq_domain_remove(void *data)
+{
+	struct mt6397_chip *chip = data;
+	unsigned int hwirq;
+	unsigned int virq;
+
+	for (hwirq = 0; hwirq < MT6397_IRQ_NR; hwirq++) {
+		virq = irq_find_mapping(chip->irq_domain, hwirq);
+		if (virq)
+			irq_dispose_mapping(virq);
+	}
+
+	irq_domain_remove(chip->irq_domain);
+	chip->irq_domain = NULL;
+}
+
 int mt6397_irq_init(struct mt6397_chip *chip)
 {
 	int ret;
@@ -223,16 +248,28 @@ int mt6397_irq_init(struct mt6397_chip *chip)
 		return -ENOMEM;
 	}
 
+	ret = devm_add_action_or_reset(chip->dev, mt6397_irq_domain_remove,
+				       chip);
+	if (ret)
+		return ret;
+
 	ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL,
 					mt6397_irq_thread, IRQF_ONESHOT,
 					"mt6397-pmic", chip);
 	if (ret) {
 		dev_err(chip->dev, "failed to register irq=%d; err: %d\n",
 			chip->irq, ret);
-		irq_domain_remove(chip->irq_domain);
 		return ret;
 	}
 
-	register_pm_notifier(&chip->pm_nb);
-	return 0;
+	ret = register_pm_notifier(&chip->pm_nb);
+	if (ret) {
+		dev_err(chip->dev, "failed to register PM notifier: %d\n", ret);
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(chip->dev,
+				       mt6397_irq_pm_notifier_unregister, chip);
+
+	return ret;
 }
diff --git a/include/linux/mfd/mt6397/core.h b/include/linux/mfd/mt6397/core.h
index 340fc72e22aa..f9c2cbd17c5c 100644
--- a/include/linux/mfd/mt6397/core.h
+++ b/include/linux/mfd/mt6397/core.h
@@ -66,6 +66,7 @@ struct mt6397_chip {
 	int irq;
 	struct irq_domain *irq_domain;
 	struct mutex irqlock;
+	bool irq_suspended;
 	u16 wake_mask[3];
 	u16 irq_masks_cur[3];
 	u16 irq_masks_cache[3];
-- 
2.43.0



                 reply	other threads:[~2026-06-19 16:57 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260619165649.1067266-1-dbgh9129@gmail.com \
    --to=dbgh9129@gmail.com \
    --cc=angelogioacchino.delregno@collabora.com \
    --cc=lee@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=matthias.bgg@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox