Commit ec17f143 authored by Adrian Ng Ho Yin's avatar Adrian Ng Ho Yin Committed by Alexandre Belloni
Browse files

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: default avatarAdrian Ng Ho Yin <adrianhoyin.ng@altera.com>
Reviewed-by: default avatarFrank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/f09ee67e61d31f0a12a0bf48f01e9057ca9e2fb7.1765529948.git.adrianhoyin.ng@altera.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent b58f47eb
Loading
Loading
Loading
Loading
+40 −0
Original line number Diff line number Diff line
@@ -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 */