@samitouri / QOSamiQemu / commits / a5f7d65585

hw/i2c/aspeed_i2c: Support the AST2700 master buffer mode

The AST2700 I2C controller can move master DMA payloads through its internal SRAM pool rather than DRAM. The Linux driver calls this "buffer mode" and selects it by default. Buffer mode reuses the master DMA command bits (TX/RX_DMA_EN) and the DMA length registers, so the only difference from a DRAM transfer is where the data comes from and goes to: an offset into the pool programmed in I2CM_DMA_TX/RX_ADDR. The I2CC_VERSION_CTRL FUNC_CFG_DMA_EN bit selects between the two. Implement I2CC_VERSION_CTRL and, when FUNC_CFG_DMA_EN is clear, move the payload through the pool buffer instead of DRAM. I2CC_VERSION_CTRL resets to all ones, so guests that never program it keep targeting DRAM and behave as before. The register sits above the register window of the earlier SoCs, which are therefore unaffected. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Link: https://lore.kernel.org/qemu-devel/20260804081955.1563537-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater <clg@redhat.com>

Jamin Lin committed Aug 4, 2026 at 08:19 UTC a5f7d655854a4da8767b2a17be698fb6cd68d709
2 files changed +81
hw/i2c/aspeed_i2c.c
+79
@@ -159,6 +159,7 @@ static uint64_t aspeed_i2c_bus_new_read(AspeedI2CBus *bus, hwaddr offset,
159 case A_I2CS_INTR_CTRL:
160 case A_I2CS_DMA_LEN_STS:
161 case A_I2CS_INTR_STS:
162 + case A_I2CC_VERSION_CTRL:
163 value = bus->regs[offset / sizeof(*bus->regs)];
164 break;
165 case A_I2CC_DMA_ADDR:
@@ -295,6 +296,65 @@ static int aspeed_i2c_dma_read(AspeedI2CBus *bus, uint8_t *data)
296 return 0;
297 }
298
299 +/*
300 + * In AST2700 buffer mode the master DMA command bits (TX/RX_DMA_EN) and the
301 + * DMA length registers are reused, but data is moved through the controller
302 + * internal SRAM pool at the offset programmed in I2CM_DMA_TX/RX_ADDR instead
303 + * of DRAM. FUNC_CFG_DMA_EN selects between the two (set = DRAM).
304 + */
305 +static bool aspeed_i2c_bus_dma_to_pool(AspeedI2CBus *bus)
306 +{
307 + return aspeed_i2c_is_new_mode(bus->controller) &&
308 + !ARRAY_FIELD_EX32(bus->regs, I2CC_VERSION_CTRL, FUNC_CFG_DMA_EN);
309 +}
310 +
311 +static int aspeed_i2c_bus_send_dma_pool(AspeedI2CBus *bus)
312 +{
313 + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller);
314 + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus);
315 + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus);
316 + uint32_t offset = bus->regs[R_I2CM_DMA_TX_ADDR];
317 + uint8_t *pool_base = aic->bus_pool_base(bus);
318 + int ret = -1;
319 + int i;
320 +
321 + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0);
322 + for (i = 0; bus->regs[reg_dma_len] &&
323 + offset + i < ASPEED_I2C_BUS_POOL_SIZE; i++) {
324 + trace_aspeed_i2c_bus_send("BUFF", i + 1, bus->regs[reg_dma_len],
325 + pool_base[offset + i]);
326 + ret = i2c_send(bus->bus, pool_base[offset + i]);
327 + bus->regs[reg_dma_len]--;
328 + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, i + 1);
329 + if (ret) {
330 + break;
331 + }
332 + }
333 + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_DMA_EN, 0);
334 + return ret;
335 +}
336 +
337 +static void aspeed_i2c_bus_recv_dma_pool(AspeedI2CBus *bus)
338 +{
339 + AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller);
340 + uint32_t reg_dma_len = aspeed_i2c_bus_dma_len_offset(bus);
341 + uint32_t reg_cmd = aspeed_i2c_bus_cmd_offset(bus);
342 + uint32_t offset = bus->regs[R_I2CM_DMA_RX_ADDR];
343 + uint8_t *pool_base = aic->bus_pool_base(bus);
344 + int i;
345 +
346 + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 0);
347 + for (i = 0; bus->regs[reg_dma_len] &&
348 + offset + i < ASPEED_I2C_BUS_POOL_SIZE; i++) {
349 + pool_base[offset + i] = i2c_recv(bus->bus);
350 + trace_aspeed_i2c_bus_recv("BUFF", i + 1, bus->regs[reg_dma_len],
351 + pool_base[offset + i]);
352 + bus->regs[reg_dma_len]--;
353 + ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, i + 1);
354 + }
355 + SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_DMA_EN, 0);
356 +}
357 +
358 static int aspeed_i2c_bus_send(AspeedI2CBus *bus)
359 {
360 AspeedI2CClass *aic = ASPEED_I2C_GET_CLASS(bus->controller);
@@ -320,6 +380,10 @@ static int aspeed_i2c_bus_send(AspeedI2CBus *bus)
380 }
381 SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, TX_BUFF_EN, 0);
382 } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, TX_DMA_EN)) {
383 + /* In buffer mode the DMA moves data through the pool, not DRAM */
384 + if (aspeed_i2c_bus_dma_to_pool(bus)) {
385 + return aspeed_i2c_bus_send_dma_pool(bus);
386 + }
387 /* In new mode, clear how many bytes we TXed */
388 if (aspeed_i2c_is_new_mode(bus->controller)) {
389 ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, TX_LEN, 0);
@@ -385,6 +449,11 @@ static void aspeed_i2c_bus_recv(AspeedI2CBus *bus)
449 SHARED_ARRAY_FIELD_DP32(bus->regs, reg_pool_ctrl, RX_COUNT, i & 0xff);
450 SHARED_ARRAY_FIELD_DP32(bus->regs, reg_cmd, RX_BUFF_EN, 0);
451 } else if (SHARED_ARRAY_FIELD_EX32(bus->regs, reg_cmd, RX_DMA_EN)) {
452 + /* In buffer mode the DMA moves data through the pool, not DRAM */
453 + if (aspeed_i2c_bus_dma_to_pool(bus)) {
454 + aspeed_i2c_bus_recv_dma_pool(bus);
455 + return;
456 + }
457 /* In new mode, clear how many bytes we RXed */
458 if (aspeed_i2c_is_new_mode(bus->controller)) {
459 ARRAY_FIELD_DP32(bus->regs, I2CM_DMA_LEN_STS, RX_LEN, 0);
@@ -854,6 +923,9 @@ static void aspeed_i2c_bus_new_write(AspeedI2CBus *bus, hwaddr offset,
923 I2CS_DMA_RX_ADDR_HI,
924 ADDR_HI);
925 break;
926 + case A_I2CC_VERSION_CTRL:
927 + bus->regs[R_I2CC_VERSION_CTRL] = value;
928 + break;
929 default:
930 qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
931 __func__, offset);
@@ -1497,6 +1569,13 @@ static void aspeed_i2c_bus_reset_hold(Object *obj, ResetType type)
1569 memset(s->regs, 0, sizeof(s->regs));
1570 s->pending_intr_sts = 0;
1571 i2c_end_transfer(s->bus);
1572 + /*
1573 + * I2CC_VERSION_CTRL resets to all-ones. FUNC_CFG_DMA_EN is therefore set,
1574 + * so master DMA targets DRAM unless the guest clears it to select buffer
1575 + * mode. Guests unaware of buffer mode never touch this register and keep
1576 + * doing DRAM DMA.
1577 + */
1578 + s->regs[R_I2CC_VERSION_CTRL] = 0xffffffff;
1579 }
1580
1581 static void aspeed_i2c_bus_realize(DeviceState *dev, Error **errp)
include/hw/i2c/aspeed_i2c.h
+2
@@ -231,6 +231,8 @@ REG32(I2CS_DMA_TX_ADDR_HI, 0x68)
231 FIELD(I2CS_DMA_TX_ADDR_HI, ADDR_HI, 0, 7)
232 REG32(I2CS_DMA_RX_ADDR_HI, 0x6c)
233 FIELD(I2CS_DMA_RX_ADDR_HI, ADDR_HI, 0, 7)
234 +REG32(I2CC_VERSION_CTRL, 0x94)
235 + FIELD(I2CC_VERSION_CTRL, FUNC_CFG_DMA_EN, 2, 1)
236
237 struct AspeedI2CState;
238