[PATCH v2 4/8] i2c/aspeed: Fix DMA receive first-byte handling for block reads
Mikail Sadic <[email protected]> Thu, 30 Jul 2026 15:09:39 -0500
| Newsgroups | org.nongnu.qemu-arm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <[email protected]> |
An SMBus block read (I2C_M_RECV_LEN) reads the block length from the first received byte. The Linux/U-Boot aspeed I2C driver obtains that first byte from the I2CC_STS_AND_BUFF register (modelled here as reg_byte_buf), even when the transfer uses DMA. The DMA receive path, however, only wrote received data to DRAM and never updated reg_byte_buf, so block reads read a stale/zero length. Mirror the first DMA-received byte into reg_byte_buf so that I2C_M_RECV_LEN transfers using DMA report the correct block length. This is required for the ucd9000 driver, which uses i2c_smbus_read_block_data(). Signed-off-by: Mikail Sadic <[email protected]> --- hw/i2c/aspeed_i2c.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index 27afcaecee..facb54d27e 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -365,6 +365,7 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) uint32_t reg_pool_ctrl = aspeed_i2c_bus_pool_ctrl_offset(bus); uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus); + bool first_dma_byte; int pool_rx_count = SHARED_ARRAY_FIELD_EX32(bus->regs, reg_pool_ctrl, RX_SIZE) + 1; @@ -391,6 +392,7 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) } aspeed_i2c_set_rx_dma_dram_offset(bus); + first_dma_byte = true; while (bus->regs[reg_dma_len]) { MemTxResult result; @@ -407,6 +409,11 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) return; } + /* Mirror first byte to reg_byte_buf for I2C_M_RECV_LEN. */ + if (first_dma_byte) { + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, data); + first_dma_byte = false; + } bus->dma_dram_offset++; bus->regs[reg_dma_len]--; /* In new mode, keep track of how many bytes we RXed */ -- 2.53.0