]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
i3c: dw: Add support for Device NACK Retry configuration
authorAdrian Ng Ho Yin <adrianhoyin.ng@altera.com>
Fri, 12 Dec 2025 09:02:56 +0000 (17:02 +0800)
committerAlexandre Belloni <alexandre.belloni@bootlin.com>
Tue, 13 Jan 2026 17:26:47 +0000 (18:26 +0100)
The DesignWare I3C controller supports automatically retrying transactions
when a device NACKs. This is useful for slave devices that may be
temporarily busy and not ready to respond immediately.

Add new ops to configure all active DAT entry with dev_nack_retry during
runtime. Returns error when value exceeds hw specified limit.

Signed-off-by: Adrian Ng Ho Yin <adrianhoyin.ng@altera.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/f09ee67e61d31f0a12a0bf48f01e9057ca9e2fb7.1765529948.git.adrianhoyin.ng@altera.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
drivers/i3c/master/dw-i3c-master.c

index 889e2ed5bc830310c5555eeeab08674d0c579a0e..843861ba62bd862260648d28b463ff02662b0539 100644 (file)
@@ -5,6 +5,7 @@
  * Author: Vitor Soares <vitor.soares@synopsys.com>
  */
 
+#include <linux/bitfield.h>
 #include <linux/bitops.h>
 #include <linux/clk.h>
 #include <linux/completion.h>
 #define EXTENDED_CAPABILITY            0xe8
 #define SLAVE_CONFIG                   0xec
 
+#define DW_I3C_DEV_NACK_RETRY_CNT_MAX  0x3
+#define DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK   GENMASK(30, 29)
 #define DEV_ADDR_TABLE_IBI_MDB         BIT(12)
 #define DEV_ADDR_TABLE_SIR_REJECT      BIT(13)
+#define DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(x) \
+       FIELD_PREP(DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK, (x))
 #define DEV_ADDR_TABLE_LEGACY_I2C_DEV  BIT(31)
 #define DEV_ADDR_TABLE_DYNAMIC_ADDR(x) (((x) << 16) & GENMASK(23, 16))
 #define DEV_ADDR_TABLE_STATIC_ADDR(x)  ((x) & GENMASK(6, 0))
@@ -1489,6 +1494,40 @@ static irqreturn_t dw_i3c_master_irq_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+static int dw_i3c_master_set_dev_nack_retry(struct i3c_master_controller *m,
+                                           unsigned long dev_nack_retry_cnt)
+{
+       struct dw_i3c_master *master = to_dw_i3c_master(m);
+       u32 reg;
+       int i;
+
+       if (dev_nack_retry_cnt > DW_I3C_DEV_NACK_RETRY_CNT_MAX) {
+               dev_err(&master->base.dev,
+                       "Value %ld exceeds maximum %d\n",
+                       dev_nack_retry_cnt, DW_I3C_DEV_NACK_RETRY_CNT_MAX);
+               return -ERANGE;
+       }
+
+       /*
+        * Update DAT entries for all currently attached devices.
+        * We directly iterate through the master's device array.
+        */
+       for (i = 0; i < master->maxdevs; i++) {
+               /* Skip free/empty slots */
+               if (master->free_pos & BIT(i))
+                       continue;
+
+               reg = readl(master->regs +
+                               DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
+               reg &= ~DEV_ADDR_TABLE_DEV_NACK_RETRY_MASK;
+               reg |= DEV_ADDR_TABLE_DEV_NACK_RETRY_CNT(dev_nack_retry_cnt);
+               writel(reg, master->regs +
+                       DEV_ADDR_TABLE_LOC(master->datstartaddr, i));
+       }
+
+       return 0;
+}
+
 static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
        .bus_init = dw_i3c_master_bus_init,
        .bus_cleanup = dw_i3c_master_bus_cleanup,
@@ -1509,6 +1548,7 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
        .recycle_ibi_slot = dw_i3c_master_recycle_ibi_slot,
        .enable_hotjoin = dw_i3c_master_enable_hotjoin,
        .disable_hotjoin = dw_i3c_master_disable_hotjoin,
+       .set_dev_nack_retry = dw_i3c_master_set_dev_nack_retry,
 };
 
 /* default platform ops implementations */