All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1] hw/i2c/aspeed_i2c: Latch first received byte for SMBus block reads
@ 2026-08-13  9:02 Jamin Lin
  2026-08-13  9:08 ` Jamin Lin
  0 siblings, 1 reply; 4+ messages in thread
From: Jamin Lin @ 2026-08-13  9:02 UTC (permalink / raw)
  To: Cédric Le Goater, Peter Maydell, Steven Lee, Troy Lee,
	Kane Chen, Andrew Jeffery, Joel Stanley, open list:ASPEED BMCs,
	open list:All patches CC here
  Cc: Jamin Lin, Troy Lee, Mikail.Sadic@ibm.com

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 <jamin_lin@aspeedtech.com>
---
 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


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-08-14  2:20 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13  9:02 [PATCH v1] hw/i2c/aspeed_i2c: Latch first received byte for SMBus block reads Jamin Lin
2026-08-13  9:08 ` Jamin Lin
2026-08-13 15:54   ` Mikail Sadic
2026-08-14  2:19     ` Jamin Lin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.