Commit 2d3dc20d9d for qemu.org
commit 2d3dc20d9d716729e06093311f678dc739affe5b
Author: Jithu Joseph <jithu.joseph@oss.qualcomm.com>
Date: Thu Jun 4 07:22:04 2026 -0700
hw/i3c: fix CMD/data FIFO depth reset values to match real silicon
The Linux DW-I3C master driver infers controller queue depths at probe
by reading two status registers that report free queue slots, which at
probe (queues empty) equals the full depth. It then uses those values
to gate every I3C transfer -- any batch whose word count exceeds the
advertised depth is rejected with -EOPNOTSUPP.
QUEUE_STATUS_LEVEL (0x4c) [7:0] -> cmdfifodepth (cmd slots)
DATA_BUFFER_STATUS_LEVEL (0x50) [7:0] -> datafifodepth (32-bit words)
Per the AST2600 datasheet the reset values are 0x10 and 0x40 (16 cmd
slots, 64 words = 256 B). QEMU was advertising 0x02 and 0x10, making
the kernel believe the controller can only do 64-byte transfers. The
visible symptom was -EOPNOTSUPP on any I3C transfer whose payload
exceeded 64 B (datafifodepth = 0x10 = 16 words = 64 B).
The underlying FIFOs in QEMU were already allocated at the right size
(fifo32_create takes word counts; the existing defaults give 16 cmd
slots and 64 data words). Only the advertised reset values were wrong.
Correct the reset values in dw_i3c_resets[], and additionally drive the
advertised depths from the queue-capacity configs in the reset handlers
(as is already done for the device/char table pointers), so a configured
override is reflected in what the guest reads instead of being silently
ignored. The advertised fields are 8-bit, so the depth saturates at 255
regardless of the wider capacity configs.
With this fix the guest sees datafifodepth=64 words and accepts
transfers up to 256 B.
Fixes: e974c6957576 ("hw/i3c/dw-i3c: Add more reset values")
Cc: qemu-stable@nongnu.org
Signed-off-by: Jithu Joseph <jithu.joseph@oss.qualcomm.com>
Reviewed-by: Jamin Lin <jamin_lin@aspeedtech.com>
Link: https://lore.kernel.org/qemu-devel/20260604142207.2118098-2-jithu.joseph@oss.qualcomm.com
Signed-off-by: Cédric Le Goater <clg@redhat.com>
diff --git a/hw/i3c/dw-i3c.c b/hw/i3c/dw-i3c.c
index 17ff484c5d..06c2d55f59 100644
--- a/hw/i3c/dw-i3c.c
+++ b/hw/i3c/dw-i3c.c
@@ -282,8 +282,8 @@ static const uint32_t dw_i3c_resets[DW_I3C_NR_REGS] = {
[R_QUEUE_THLD_CTRL] = 0x01000101,
[R_DATA_BUFFER_THLD_CTRL] = 0x01010100,
[R_SLV_EVENT_CTRL] = 0x0000000b,
- [R_QUEUE_STATUS_LEVEL] = 0x00000002,
- [R_DATA_BUFFER_STATUS_LEVEL] = 0x00000010,
+ [R_QUEUE_STATUS_LEVEL] = 0x00000010,
+ [R_DATA_BUFFER_STATUS_LEVEL] = 0x00000040,
[R_PRESENT_STATE] = 0x00000003,
[R_I3C_VER_ID] = 0x3130302a,
[R_I3C_VER_TYPE] = 0x6c633033,
@@ -947,6 +947,10 @@ static void dw_i3c_reset(DeviceState *dev)
s->cfg.dev_char_table_pointer);
ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH,
s->cfg.dev_char_table_depth);
+ ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
+ s->cfg.cmd_resp_queue_capacity_bytes);
+ ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
+ s->cfg.tx_rx_queue_capacity_bytes);
dw_i3c_cmd_queue_reset(s);
dw_i3c_resp_queue_reset(s);
@@ -1793,6 +1797,10 @@ static void dw_i3c_reset_enter(Object *obj, ResetType type)
s->cfg.dev_char_table_pointer);
ARRAY_FIELD_DP32(s->regs, DEV_CHAR_TABLE_POINTER, DEV_CHAR_TABLE_DEPTH,
s->cfg.dev_char_table_depth);
+ ARRAY_FIELD_DP32(s->regs, QUEUE_STATUS_LEVEL, CMD_QUEUE_EMPTY_LOC,
+ s->cfg.cmd_resp_queue_capacity_bytes);
+ ARRAY_FIELD_DP32(s->regs, DATA_BUFFER_STATUS_LEVEL, TX_BUF_EMPTY_LOC,
+ s->cfg.tx_rx_queue_capacity_bytes);
}
static void dw_i3c_realize(DeviceState *dev, Error **errp)