From: Manikanta Guntupalli manikanta.guntupalli@amd.com
[ Upstream commit 17e163f3d7a5449fe9065030048e28c4087b24ce ]
Add shutdown handler to the Synopsys DesignWare I3C master driver, ensuring the device is gracefully disabled during system shutdown.
The shutdown handler cancels any pending hot-join work and disables interrupts.
Signed-off-by: Manikanta Guntupalli manikanta.guntupalli@amd.com Link: https://lore.kernel.org/r/20250730151207.4113708-1-manikanta.guntupalli@amd.... Signed-off-by: Alexandre Belloni alexandre.belloni@bootlin.com Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES
- What Changed - Adds a platform shutdown callback and handler that runs on system shutdown, `dw_i3c_shutdown()` in `drivers/i3c/master/dw-i3c-master.c:1740–1760`. It: - Powers the controller for safe register access via `pm_runtime_resume_and_get()` (1745–1751). - Cancels pending hot-join work to avoid races/UAF, `cancel_work_sync(&master->hj_work)` (1753). - Disables all interrupts by clearing the controller’s enable masks, writing `(u32)~INTR_ALL` to `INTR_STATUS_EN` and `INTR_SIGNAL_EN` (1756–1757). - Balances PM with `pm_runtime_put_autosuspend()` (1759). - Hooks the handler into the driver so it actually runs at shutdown: `.shutdown = dw_i3c_shutdown` in the platform driver struct, `drivers/i3c/master/dw-i3c-master.c:1774–1784`.
- Why It Matters - Prevents hot-join work from running while the system is shutting down: - Hot-join IBI detection queues `hj_work` (see `queue_work(..., &master->hj_work)` at `drivers/i3c/master/dw-i3c-master.c:1451–1453`; initialized at 1595). Without canceling it, shutdown/kexec can race with work that touches device state, risking use-after-free or undefined behavior. This complements the prior “remove” path fix that already cancels `hj_work` before unregister (see `dw_i3c_common_remove()` cancel, 1617). - Ensures the controller won’t assert or signal further interrupts after the OS is going down: - Normal operation explicitly enables only needed IRQs (see `dw_i3c_master_set_intr_regs()` programming `INTR_STATUS_EN` and `INTR_SIGNAL_EN` with `INTR_MASTER_MASK` at 533–534), but previously there was no explicit “all-off” step for system shutdown. - Disabling IRQs in the shutdown path removes a common source of stray interrupts that can disturb kexec/kdump or firmware/bootloader takeover. - Uses runtime PM to guarantee clocks/resets are up before touching registers (1745–1751), mirroring existing, consistent patterns elsewhere in this driver (e.g., CCC, DAA, xfers), minimizing risk of register access with clocks off.
- Scope, Risk, and Backport Considerations - Scope is small and contained to a single driver: one new function and one platform_driver hook. No API/ABI changes, no architecture- level changes, and it runs only during shutdown. - The logic is defensive and mirrors established patterns in this driver: - Work cancellation mirrors the removal path (1617) and addresses the same class of race, now for the shutdown path. - Interrupt gating aligns with how the driver sets them during resume/init (516–540) and the ISR’s gating on `INTR_STATUS_EN` (1463–1470). - Dependencies: relies on runtime PM support and hot-join work being present in the driver (introduced in recent kernels). Backport is straightforward for stable series that already include: - Hot-join support (hj_work/IBI HJ queuing). - The driver’s runtime PM support. - For older trees lacking those pieces, the patch either won’t apply or would need adaptation; it’s most appropriate for newer stable lines (e.g., v6.11+ where PM support landed, v6.10+ for HJ work). - Commit message has no explicit “Cc: stable” or “Fixes:” tag, but this is a classic shutdown-path robustness fix preventing race and stray IRQ issues; it fits stable rules: it fixes a real shutdown bug, is minimal and low risk, and is confined to the I3C DW master driver.
Conclusion: This is a targeted, low-risk shutdown bugfix that prevents pending work races and disables interrupts cleanly. It is suitable for backporting to stable trees that already include the DW I3C hot-join and runtime PM infrastructure.
drivers/i3c/master/dw-i3c-master.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 974122b2d20ee..9ceedf09c3b6a 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1737,6 +1737,28 @@ static const struct dev_pm_ops dw_i3c_pm_ops = { SET_RUNTIME_PM_OPS(dw_i3c_master_runtime_suspend, dw_i3c_master_runtime_resume, NULL) };
+static void dw_i3c_shutdown(struct platform_device *pdev) +{ + struct dw_i3c_master *master = platform_get_drvdata(pdev); + int ret; + + ret = pm_runtime_resume_and_get(master->dev); + if (ret < 0) { + dev_err(master->dev, + "<%s> cannot resume i3c bus master, err: %d\n", + __func__, ret); + return; + } + + cancel_work_sync(&master->hj_work); + + /* Disable interrupts */ + writel((u32)~INTR_ALL, master->regs + INTR_STATUS_EN); + writel((u32)~INTR_ALL, master->regs + INTR_SIGNAL_EN); + + pm_runtime_put_autosuspend(master->dev); +} + static const struct of_device_id dw_i3c_master_of_match[] = { { .compatible = "snps,dw-i3c-master-1.00a", }, {}, @@ -1752,6 +1774,7 @@ MODULE_DEVICE_TABLE(acpi, amd_i3c_device_match); static struct platform_driver dw_i3c_driver = { .probe = dw_i3c_probe, .remove = dw_i3c_remove, + .shutdown = dw_i3c_shutdown, .driver = { .name = "dw-i3c-master", .of_match_table = dw_i3c_master_of_match,