[PATCH v1] extcon: rtk-type-c: Stop IRQ before cancelling delayed work

From: Yibo Tan

Date: Sat Sep 19 2026 - 09:59:20 EST


type_c_detect_irq() can cancel and reschedule delayed_work. The remove
callback currently cancels the work before disabling and freeing the IRQ,
so an interrupt can reschedule it after the final drain. The
device-managed type_c allocation may then be released while the work is
still pending.

In three ARM64 KASAN runs, dispatching the real IRQ after the final drain
made the delayed callback access the freed type_c object. All three runs
with the IRQ stopped before the drain completed without a kernel
diagnostic.

Mask the hardware interrupt source first, release and synchronize the IRQ,
and only then cancel the delayed work. Also use devm_free_irq() to match
the devm_request_irq() allocation and remove its devres entry.

Fixes: 8a590d7371f0 ("extcon: add Realtek DHC RTD SoC Type-C driver")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Codex:GPT-5
Signed-off-by: Yibo Tan <lhfff@xxxxxxxxxx>
---
drivers/extcon/extcon-rtk-type-c.c | 25 ++++++++++++-------------
1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/drivers/extcon/extcon-rtk-type-c.c b/drivers/extcon/extcon-rtk-type-c.c
index 82b60b927e41a..9d77ce5c7ee50 100644
--- a/drivers/extcon/extcon-rtk-type-c.c
+++ b/drivers/extcon/extcon-rtk-type-c.c
@@ -1409,17 +1409,6 @@ static void extcon_rtk_type_c_remove(struct platform_device *pdev)
u32 default_ctrl;
unsigned long flags;

- remove_debug_files(type_c);
-
- if (type_c->port) {
- typec_unregister_port(type_c->port);
- type_c->port = NULL;
- }
-
- cancel_delayed_work_sync(&type_c->delayed_work);
- flush_delayed_work(&type_c->delayed_work);
- WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work));
-
spin_lock_irqsave(&type_c->lock, flags);
/* disable interrupt */
default_ctrl = readl(type_c->reg_base + USB_TYPEC_CTRL) &
@@ -1431,12 +1420,22 @@ static void extcon_rtk_type_c_remove(struct platform_device *pdev)
writel(0, type_c->reg_base + USB_TYPEC_CTRL_CC2_0);

spin_unlock_irqrestore(&type_c->lock, flags);
+ devm_free_irq(dev, type_c->irq, type_c);
+
+ cancel_delayed_work_sync(&type_c->delayed_work);
+ flush_delayed_work(&type_c->delayed_work);
+ WARN_ON_ONCE(delayed_work_pending(&type_c->delayed_work));
+
+ remove_debug_files(type_c);
+
+ if (type_c->port) {
+ typec_unregister_port(type_c->port);
+ type_c->port = NULL;
+ }

if (type_c->rd_ctrl_gpio_desc)
gpiod_put(type_c->rd_ctrl_gpio_desc);
type_c->rd_ctrl_gpio_desc = NULL;
-
- free_irq(type_c->irq, type_c);
}

static const struct type_c_cfg rtd1295_type_c_cfg = {

base-commit: 40288c9206c17eb66a603262e06a58d300d0f279
--
2.39.5