RE: [PATCH v1] hw/i2c/aspeed_i2c: Latch first received byte for SMBus block reads
Jamin Lin <[email protected]>
| Newsgroups | org.nongnu.qemu-arm,org.nongnu.qemu-devel |
|---|---|
| Message-ID | <TYZPR06MB49802BFC78FDFB21489FEE83FCDB2@TYZPR06MB4980.apcprd06.prod.outlook.com> |
Hi Mikail, Could you please review this patch and verify that it works with the UCD9000 driver? Thanks, Jamin > -----Original Message----- > From: Jamin Lin <[email protected]> > Sent: Thursday, August 13, 2026 5:02 PM > To: Cédric Le Goater <[email protected]>; Peter Maydell > <[email protected]>; Steven Lee <[email protected]>; Troy > Lee <[email protected]>; Kane Chen <[email protected]>; > Andrew Jeffery <[email protected]>; Joel Stanley > <[email protected]>; open list:ASPEED BMCs <[email protected]>; open > list:All patches CC here <[email protected]> > Cc: Jamin Lin <[email protected]>; Troy Lee > <[email protected]>; [email protected] > Subject: [PATCH v1] hw/i2c/aspeed_i2c: Latch first received byte for SMBus > block reads > > An SMBus block read takes the block length from the first byte of the transfer, > and firmware reads that byte back from a register rather than from the > transfer buffer. The receive paths never updated those registers, so block reads > reported a bogus length. > > On AST2600 the driver reads the length from the receive byte buffer, > I2CC_MS_TXRX_BYTE_BUF[15:8]. The datasheet documents that field as valid > while the DMA buffer is not enabled. The byte mode receive path already > updated it, but the pool buffer path did not, and the driver selects buffer mode > by default. > > On AST2700 the driver reads the length from offset 0x84 instead. > > Add I2CC_BYTE_DATA_LOG at 0x84. Latch the first received byte from the > three receive paths: the pool buffer path, the DMA-to-pool path and the > DMA-to-DRAM path. Each latch updates the byte data log, and updates the > receive byte buffer as well while the DMA buffer is not enabled. The byte data > log is outside the register window of the earlier SoCs, so it is only visible on > AST2700/AST1040. > > Signed-off-by: Jamin Lin <[email protected]> > --- > hw/i2c/aspeed_i2c.c | 31 ++++++++++++++++++++++++++++++- > include/hw/i2c/aspeed_i2c.h | 2 ++ > 2 files changed, 32 insertions(+), 1 deletion(-) > > diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index > 68bdcd0e25..e6d0eee816 100644 > --- a/hw/i2c/aspeed_i2c.c > +++ b/hw/i2c/aspeed_i2c.c > @@ -159,6 +159,7 @@ static uint64_t > aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset, > case A_I2CS_INTR_CTRL: > case A_I2CS_DMA_LEN_STS: > case A_I2CS_INTR_STS: > + case A_I2CC_BYTE_DATA_LOG: > case A_I2CC_VERSION_CTRL: > value = bus->regs[offset / sizeof(*bus->regs)]; > break; > @@ -334,6 +335,24 @@ static int > aspeed_i2c_bus_send_dma_pool(AspeedI2CBus *bus) > return ret; > } > > +/* > + * Latch the first received byte, which firmware reads back as the > +SMBus block > + * length. AST2600 reads it from the receive byte buffer, only valid > +while the > + * DMA buffer is disabled; AST2700 reads it from the byte data log, a > +register > + * the earlier SoCs do not expose. > + */ > +static void aspeed_i2c_bus_latch_rx_len(AspeedI2CBus *bus, bool > dma_buf_en, > + uint8_t data) { > + uint32_t reg_byte_buf = aspeed_i2c_bus_byte_buf_offset(bus); > + > + ARRAY_FIELD_DP32(bus->regs, I2CC_BYTE_DATA_LOG, RX_BUF, data); > + > + if (!dma_buf_en) { > + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_byte_buf, RX_BUF, > data); > + } > +} > + > static void aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus) { > AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller); > @@ -349,6 +368,9 @@ static void > aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus) > pool_base[offset + i] = i2c_recv(bus->bus); > trace_aspeed_i2c_bus_recv("BUFF", i + 1, bus->regs[reg_dma_len], > pool_base[offset + i]); > + if (i == 0) { > + aspeed_i2c_bus_latch_rx_len(bus, true, pool_base[offset]); > + } > bus->regs[reg_dma_len]--; > ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, i + > 1); > } > @@ -443,6 +465,9 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) > pool_base[i] = i2c_recv(bus->bus); > trace_aspeed_i2c_bus_recv("BUF", i + 1, pool_rx_count, > pool_base[i]); > + if (i == 0) { > + aspeed_i2c_bus_latch_rx_len(bus, false, pool_base[0]); > + } > } > > /* Update RX count */ > @@ -460,7 +485,7 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) > } > > aspeed_i2c_set_rx_dma_dram_offset(bus); > - while (bus->regs[reg_dma_len]) { > + for (i = 0; bus->regs[reg_dma_len]; i++) { > MemTxResult result; > > data = i2c_recv(bus->bus); > @@ -476,6 +501,10 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus) > return; > } > > + if (i == 0) { > + aspeed_i2c_bus_latch_rx_len(bus, true, data); > + } > + > bus->dma_dram_offset++; > bus->regs[reg_dma_len]--; > /* In new mode, keep track of how many bytes we RXed */ diff > --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index > 05937a7a0b..c8e6ea54ad 100644 > --- a/include/hw/i2c/aspeed_i2c.h > +++ b/include/hw/i2c/aspeed_i2c.h > @@ -231,6 +231,8 @@ REG32(I2CS_DMA_TX_ADDR_HI, 0x68) > FIELD(I2CS_DMA_TX_ADDR_HI, ADDR_HI, 0, 7) > REG32(I2CS_DMA_RX_ADDR_HI, 0x6c) > FIELD(I2CS_DMA_RX_ADDR_HI, ADDR_HI, 0, 7) > +REG32(I2CC_BYTE_DATA_LOG, 0x84) > + FIELD(I2CC_BYTE_DATA_LOG, RX_BUF, 0, 8) > REG32(I2CC_VERSION_CTRL, 0x94) > FIELD(I2CC_VERSION_CTRL, FUNC_CFG_DMA_EN, 2, 1) > > -- > 2.53.0