summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Ng Ho Yin <adrianhoyin.ng@altera.com>2025-12-12 12:02:56 +0300
committerAlexandre Belloni <alexandre.belloni@bootlin.com>2026-01-13 20:26:47 +0300
commitec17f14309481318df4af2a0c7f2aa7da9e7ebcb (patch)
tree29e093c5ce8ebe0ee9d52960781d683154fb6075
parentb58f47eb392680d4c6626c8b3b1fcf6412a0a02c (diff)
downloadlinux-ec17f14309481318df4af2a0c7f2aa7da9e7ebcb.tar.xz
i3c: dw: Add support for Device NACK Retry configuration
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>
-rw-r--r--drivers/i3c/master/dw-i3c-master.c40
1 files changed, 40 insertions, 0 deletions
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c
index 889e2ed5bc83..843861ba62bd 100644
--- a/drivers/i3c/master/dw-i3c-master.c
+++ b/drivers/i3c/master/dw-i3c-master.c
@@ -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>
@@ -204,8 +205,12 @@
#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 */