[PATCH v3 1/4] i2c: designware: Introduce shutdown exported function

William A. Kennington III
2026-05-04T20:15:05Z

Introduce an exported shutdown function to safely shutdown the
DesignWare I2C controller.

This shutdown hook gracefully sets the slave disable bit before disabling
the controller. This guarantees that any incoming requests from the master
are immediately NACKed during shutdown, preventing the bus from hanging.

Signed-off-by: William A. Kennington III <william@wkennington.com>
---
drivers/i2c/busses/i2c-designware-common.c | 24 ++++++++++++++++++++++++
drivers/i2c/busses/i2c-designware-core.h | 1 +
2 files changed, 25 insertions(+)

diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index 4dc57fd56170..0703fb29038c 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -1028,5 +1028,29 @@ EXPORT_GPL_DEV_PM_OPS(i2c_dw_dev_pm_ops) = {
RUNTIME_PM_OPS(i2c_dw_runtime_suspend, i2c_dw_runtime_resume, NULL)
};

+void i2c_dw_shutdown(struct dw_i2c_dev *dev)
+{
+ unsigned int con;
+
+ /*
+ * We only need to handle shutdown for slave mode to ensure
+ * we NACK any incoming master requests. Master mode cleanup
+ * is handled after each transfer in i2c_dw_xfer.
+ */
+ if (dev->mode != DW_IC_SLAVE)
+ return;
+
+ /*
+ * To quickly NACK the master during shutdown, we set the slave
+ * disable bit while the controller is still enabled.
+ */
+ regmap_read(dev->map, DW_IC_CON, &con);
+ con |= DW_IC_CON_SLAVE_DISABLE;
+ regmap_write(dev->map, DW_IC_CON, con);
+
+ i2c_dw_disable(dev);
+}
+EXPORT_SYMBOL_GPL(i2c_dw_shutdown);
+
MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter core");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index 9d8d104cc391..8b422249acbd 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -393,6 +393,7 @@ static inline void __i2c_dw_read_intr_mask(struct dw_i2c_dev *dev,

void __i2c_dw_disable(struct dw_i2c_dev *dev);
void i2c_dw_disable(struct dw_i2c_dev *dev);
+void i2c_dw_shutdown(struct dw_i2c_dev *dev);

extern void i2c_dw_configure_master(struct dw_i2c_dev *dev);
extern int i2c_dw_probe_master(struct dw_i2c_dev *dev);

--
2.54.0.545.g6539524ca2-goog