diff options
| author | Shubhrajyoti Datta <shubhrajyoti.datta@amd.com> | 2026-04-01 14:14:30 +0530 |
|---|---|---|
| committer | Alexandre Belloni <alexandre.belloni@bootlin.com> | 2026-04-30 12:13:40 +0200 |
| commit | 1d78a8fc97c133b8aee54993a83f86b68ed2fdb8 (patch) | |
| tree | d7178f24af5c1d4c877143fd9d30c78cb42f05b6 | |
| parent | 14236919b0e68ac9be6b425d5149a3ad4fd85914 (diff) | |
| download | linux-1d78a8fc97c133b8aee54993a83f86b68ed2fdb8.tar.gz linux-1d78a8fc97c133b8aee54993a83f86b68ed2fdb8.zip | |
i3c: dw-i3c-master: Fix IBI count register selection for versalnet
On DesignWare I3C controllers where IC_HAS_IBI_DATA=0 (such as versalnet),
the IBI_STS_CNT field (bits [28:24] of QUEUE_STATUS_LEVEL) is hardwired
to 0. The IBI status entry count is instead reported via IBI_BUF_BLR
(bits [23:16] of the same register).
irq_handle_ibis() was unconditionally reading IBI_STS_CNT, causing it to
always see 0 pending IBIs on versalnet and return early without draining
the IBI buffer. Since INTR_IBI_THLD_STAT is level-triggered against the
buffer fill level, this left the interrupt permanently asserted.
Detect IBI data capability at probe time by writing the IBI data threshold
field in QUEUE_THLD_CTRL and reading it back. Use the result to select the
correct register field in irq_handle_ibis().
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@amd.com>
Link: https://patch.msgid.link/20260401084430.436059-1-shubhrajyoti.datta@amd.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
| -rw-r--r-- | drivers/i3c/master/dw-i3c-master.c | 21 | ||||
| -rw-r--r-- | drivers/i3c/master/dw-i3c-master.h | 1 |
2 files changed, 21 insertions, 1 deletions
diff --git a/drivers/i3c/master/dw-i3c-master.c b/drivers/i3c/master/dw-i3c-master.c index 655693a2187e..a7593d6efac5 100644 --- a/drivers/i3c/master/dw-i3c-master.c +++ b/drivers/i3c/master/dw-i3c-master.c @@ -1435,7 +1435,11 @@ static void dw_i3c_master_irq_handle_ibis(struct dw_i3c_master *master) u32 reg; reg = readl(master->regs + QUEUE_STATUS_LEVEL); - n_ibis = QUEUE_STATUS_IBI_STATUS_CNT(reg); + if (master->has_ibi_data) + n_ibis = QUEUE_STATUS_IBI_STATUS_CNT(reg); + else + n_ibis = QUEUE_STATUS_IBI_BUF_BLR(reg); + if (!n_ibis) return; @@ -1566,6 +1570,7 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, struct platform_device *pdev) { int ret, irq; + u32 thld_ctrl; const struct dw_i3c_drvdata *drvdata; unsigned long quirks = 0; @@ -1623,6 +1628,20 @@ int dw_i3c_common_probe(struct dw_i3c_master *master, master->maxdevs = ret >> 16; master->free_pos = GENMASK(master->maxdevs - 1, 0); + /* + * Detect IBI data capability (IC_HAS_IBI_DATA): write a non-zero value + * to IBI_DATA_THLD and read back. On controllers like Versalnet + * the field is hardwired to 0 and the write is ignored. Restore the + * original register value after detection. + */ + thld_ctrl = readl(master->regs + QUEUE_THLD_CTRL); + ret = thld_ctrl | QUEUE_THLD_CTRL_IBI_DATA(2); + writel(ret, master->regs + QUEUE_THLD_CTRL); + ret = readl(master->regs + QUEUE_THLD_CTRL); + if (ret & QUEUE_THLD_CTRL_IBI_DATA_MASK) + master->has_ibi_data = true; + writel(thld_ctrl, master->regs + QUEUE_THLD_CTRL); + if (has_acpi_companion(&pdev->dev)) { quirks = (unsigned long)device_get_match_data(&pdev->dev); } else if (pdev->dev.of_node) { diff --git a/drivers/i3c/master/dw-i3c-master.h b/drivers/i3c/master/dw-i3c-master.h index c5cb695c16ab..306e25a08937 100644 --- a/drivers/i3c/master/dw-i3c-master.h +++ b/drivers/i3c/master/dw-i3c-master.h @@ -51,6 +51,7 @@ struct dw_i3c_master { u32 i2c_fm_timing; u32 i2c_fmp_timing; u32 quirks; + bool has_ibi_data; /* * Per-device hardware data, used to manage the device address table * (DAT) |
