* [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine
@ 2026-04-25 13:17 Joel Stanley
2026-04-25 13:17 ` [PATCH v4 01/13] hw/i2c: Add designware i2c controller Joel Stanley
` (12 more replies)
0 siblings, 13 replies; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Michael Ellerman, Nicholas Piggin, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
v4 addresses the review comments from Phil and picks up some r-b from
Chao, Phil and Alistair. Thanks for the reviews.
- Describe PCIe mappings separately from the MemMapEntry table so
the mappings aren't mixed with fixed hardware addresses. The
mappings used are changed too, and the XXX comment and check was
dropped
- Move temperature sensor from bus 0 to bus 4 and use ti,tmp105
compatible instead of national,lm75 to match the QEMU model name
- Add i2c_get_bus() to do bounds checking on getting i2c buses
- Make halting payload const
- Don't remove rootfs in functional test, the test framework handles
cleanup
- Rename Svadu patch to to better describe what it does
- Other minor cleanups
v3: https://lore.kernel.org/qemu-devel/20260421053140.752059-1-joel@jms.id.au/
Original cover letter:
Introducing Tenstorrent Atlantis!
The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
and CoreLab Technology. It is based on the Atlantis SoC, which includes
the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant
RISC-V CPU.
This initial series adds the base machine support including:
- AIA (Advanced Interrupt Architecture) support
- PCIe controller and DesignWare I2C integration
- Serial console and device tree generation
- Functional tests for OpenSBI+Linux boot
Chris Rauer (1):
hw/i2c: Add designware i2c controller
Joel Stanley (5):
hw/riscv/virt: Move AIA initialisation to helper file
hw/riscv/aia: Provide number of irq sources
hw/riscv: Add Tenstorrent Atlantis machine
hw/riscv/atlantis: Integrate i2c buses
hw/riscv/atlantis: Add some i2c peripherals
Nicholas Piggin (7):
hw/riscv/boot: Describe discontiguous memory in boot_info
hw/riscv/boot: Account for discontiguous memory when loading firmware
hw/riscv/boot: Provide a simple halting payload
target/riscv: tt-ascalon: Enable Zkr extension
target/riscv: tt-ascalon: Enable Svadu by removing Svade
hw/riscv/atlantis: Add PCIe controller
tests/functional/riscv64: Add tt-atlantis tests
MAINTAINERS | 20 +
docs/system/riscv/tt_atlantis.rst | 38 +
docs/system/target-riscv.rst | 1 +
hw/riscv/aia.h | 25 +
include/hw/i2c/designware_i2c.h | 101 ++
include/hw/riscv/boot.h | 13 +-
include/hw/riscv/tt_atlantis.h | 86 ++
include/hw/riscv/virt.h | 2 +-
hw/i2c/designware_i2c.c | 817 ++++++++++++++++
hw/riscv/aia.c | 93 ++
hw/riscv/boot.c | 45 +-
hw/riscv/microchip_pfsoc.c | 6 +-
hw/riscv/opentitan.c | 6 +-
hw/riscv/shakti_c.c | 6 +-
hw/riscv/sifive_u.c | 6 +-
hw/riscv/spike.c | 6 +-
hw/riscv/tt_atlantis.c | 944 +++++++++++++++++++
hw/riscv/virt-acpi-build.c | 27 +-
hw/riscv/virt.c | 96 +-
hw/riscv/xiangshan_kmh.c | 6 +-
target/riscv/cpu.c | 2 +-
hw/i2c/Kconfig | 4 +
hw/i2c/meson.build | 1 +
hw/i2c/trace-events | 4 +
hw/riscv/Kconfig | 19 +
hw/riscv/meson.build | 3 +-
tests/functional/riscv64/meson.build | 1 +
tests/functional/riscv64/test_opensbi.py | 4 +
tests/functional/riscv64/test_tt_atlantis.py | 59 ++
29 files changed, 2333 insertions(+), 108 deletions(-)
create mode 100644 docs/system/riscv/tt_atlantis.rst
create mode 100644 hw/riscv/aia.h
create mode 100644 include/hw/i2c/designware_i2c.h
create mode 100644 include/hw/riscv/tt_atlantis.h
create mode 100644 hw/i2c/designware_i2c.c
create mode 100644 hw/riscv/aia.c
create mode 100644 hw/riscv/tt_atlantis.c
create mode 100755 tests/functional/riscv64/test_tt_atlantis.py
--
2.47.3
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v4 01/13] hw/i2c: Add designware i2c controller
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-30 3:53 ` Alistair Francis
2026-05-05 7:57 ` Philippe Mathieu-Daudé
2026-04-25 13:17 ` [PATCH v4 02/13] hw/riscv/boot: Describe discontiguous memory in boot_info Joel Stanley
` (11 subsequent siblings)
12 siblings, 2 replies; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Chris Rauer, Michael Ellerman, Nicholas Piggin,
Joel Stanley, Anirudh Srinivasan, Portia Stephens, qemu-riscv,
qemu-devel, Hao Wu
From: Chris Rauer <crauer@google.com>
In the past this model has been submitted for use with the arm virt
machine, however in this case it will be used by the upcoming
Tenstorrent Atlantis RISC-V machine.
This is a re-submission of the model with Chris' permission, with a
light touch of updates to make it build with qemu master.
Reviewed-by: Hao Wu <wuhaotsh@google.com>
Signed-off-by: Chris Rauer <crauer@google.com>
Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com
[jms: rebase and minor build fixes for class_init and reset callback]
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v4: Remove unused random header
v2: Add trace event for read and write, document Alano and myself as
reviewers.
---
MAINTAINERS | 8 +
include/hw/i2c/designware_i2c.h | 101 ++++
hw/i2c/designware_i2c.c | 817 ++++++++++++++++++++++++++++++++
hw/i2c/Kconfig | 4 +
hw/i2c/meson.build | 1 +
hw/i2c/trace-events | 4 +
6 files changed, 935 insertions(+)
create mode 100644 include/hw/i2c/designware_i2c.h
create mode 100644 hw/i2c/designware_i2c.c
diff --git a/MAINTAINERS b/MAINTAINERS
index aa4267b15806..e1942a86eba5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2716,6 +2716,14 @@ S: Orphan
F: hw/gpio/pcf8574.c
F: include/gpio/pcf8574.h
+DesignWare I2C
+M: Chris Rauer <crauer@google.com>
+R: Alano Song <alanosong@163.com>
+R: Joel Stanley <joel@jms.id.au>
+S: Maintained
+F: hw/i2c/designware_i2c.c
+F: include/hw/i2c/designware_i2c.h
+
Generic Loader
M: Alistair Francis <alistair@alistair23.me>
S: Maintained
diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h
new file mode 100644
index 000000000000..0d8f904f51b7
--- /dev/null
+++ b/include/hw/i2c/designware_i2c.h
@@ -0,0 +1,101 @@
+/*
+ * DesignWare I2C Module.
+ *
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef DESIGNWARE_I2C_H
+#define DESIGNWARE_I2C_H
+
+#include "hw/i2c/i2c.h"
+#include "hw/core/irq.h"
+#include "hw/core/sysbus.h"
+
+/* Size of the FIFO buffers. */
+#define DESIGNWARE_I2C_RX_FIFO_SIZE 16
+#define DESIGNWARE_I2C_TX_FIFO_SIZE 16
+
+typedef enum DesignWareI2CStatus {
+ DW_I2C_STATUS_IDLE,
+ DW_I2C_STATUS_SENDING_ADDRESS,
+ DW_I2C_STATUS_SENDING,
+ DW_I2C_STATUS_RECEIVING,
+} DesignWareI2CStatus;
+
+/*
+ * struct DesignWareI2CState - DesignWare I2C device state.
+ * @bus: The underlying I2C Bus
+ * @irq: GIC interrupt line to fire on events
+ * @ic_con: : I2C control register
+ * @ic_tar: I2C target address register
+ * @ic_sar: I2C slave address register
+ * @ic_ss_scl_hcnt: Standard speed i2c clock scl high count register
+ * @ic_ss_scl_lcnt: Standard speed i2c clock scl low count register
+ * @ic_fs_scl_hcnt: Fast mode or fast mode plus i2c clock scl high count
+ * register
+ * @ic_fs_scl_lcnt:Fast mode or fast mode plus i2c clock scl low count
+ * register
+ * @ic_intr_mask: I2C Interrupt Mask Register
+ * @ic_raw_intr_stat: I2C raw interrupt status register
+ * @ic_rx_tl: I2C receive FIFO threshold register
+ * @ic_tx_tl: I2C transmit FIFO threshold register
+ * @ic_enable: I2C enable register
+ * @ic_status: I2C status register
+ * @ic_txflr: I2C transmit fifo level register
+ * @ic_rxflr: I2C receive fifo level register
+ * @ic_sda_hold: I2C SDA hold time length register
+ * @ic_tx_abrt_source: The I2C transmit abort source register
+ * @ic_sda_setup: I2C SDA setup register
+ * @ic_enable_status: I2C enable status register
+ * @ic_fs_spklen: I2C SS, FS or FM+ spike suppression limit
+ * @ic_comp_param_1: Component parameter register
+ * @ic_comp_version: I2C component version register
+ * @ic_comp_type: I2C component type register
+ * @rx_fifo: The FIFO buffer for receiving in FIFO mode.
+ * @rx_cur: The current position of rx_fifo.
+ * @status: The current status of the SMBus.
+ */
+typedef struct DesignWareI2CState {
+ SysBusDevice parent;
+
+ MemoryRegion iomem;
+
+ I2CBus *bus;
+ qemu_irq irq;
+
+ uint32_t ic_con;
+ uint32_t ic_tar;
+ uint32_t ic_sar;
+ uint32_t ic_ss_scl_hcnt;
+ uint32_t ic_ss_scl_lcnt;
+ uint32_t ic_fs_scl_hcnt;
+ uint32_t ic_fs_scl_lcnt;
+ uint32_t ic_intr_mask;
+ uint32_t ic_raw_intr_stat;
+ uint32_t ic_rx_tl;
+ uint32_t ic_tx_tl;
+ uint32_t ic_enable;
+ uint32_t ic_status;
+ uint32_t ic_txflr;
+ uint32_t ic_rxflr;
+ uint32_t ic_sda_hold;
+ uint32_t ic_tx_abrt_source;
+ uint32_t ic_sda_setup;
+ uint32_t ic_enable_status;
+ uint32_t ic_fs_spklen;
+ uint32_t ic_comp_param_1;
+ uint32_t ic_comp_version;
+ uint32_t ic_comp_type;
+
+ uint8_t rx_fifo[DESIGNWARE_I2C_RX_FIFO_SIZE];
+ uint8_t rx_cur;
+
+ DesignWareI2CStatus status;
+} DesignWareI2CState;
+
+#define TYPE_DESIGNWARE_I2C "designware-i2c"
+#define DESIGNWARE_I2C(obj) OBJECT_CHECK(DesignWareI2CState, (obj), \
+ TYPE_DESIGNWARE_I2C)
+
+#endif /* DESIGNWARE_I2C_H */
diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c
new file mode 100644
index 000000000000..7d8b1c13533e
--- /dev/null
+++ b/hw/i2c/designware_i2c.c
@@ -0,0 +1,817 @@
+/*
+ * DesignWare I2C Module.
+ *
+ * Copyright 2021 Google LLC
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/i2c/designware_i2c.h"
+#include "migration/vmstate.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+enum DesignWareI2CRegister {
+ DW_IC_CON = 0x00,
+ DW_IC_TAR = 0x04,
+ DW_IC_SAR = 0x08,
+ DW_IC_DATA_CMD = 0x10,
+ DW_IC_SS_SCL_HCNT = 0x14,
+ DW_IC_SS_SCL_LCNT = 0x18,
+ DW_IC_FS_SCL_HCNT = 0x1c,
+ DW_IC_FS_SCL_LCNT = 0x20,
+ DW_IC_INTR_STAT = 0x2c,
+ DW_IC_INTR_MASK = 0x30,
+ DW_IC_RAW_INTR_STAT = 0x34,
+ DW_IC_RX_TL = 0x38,
+ DW_IC_TX_TL = 0x3c,
+ DW_IC_CLR_INTR = 0x40,
+ DW_IC_CLR_RX_UNDER = 0x44,
+ DW_IC_CLR_RX_OVER = 0x48,
+ DW_IC_CLR_TX_OVER = 0x4c,
+ DW_IC_CLR_RD_REQ = 0x50,
+ DW_IC_CLR_TX_ABRT = 0x54,
+ DW_IC_CLR_RX_DONE = 0x58,
+ DW_IC_CLR_ACTIVITY = 0x5c,
+ DW_IC_CLR_STOP_DET = 0x60,
+ DW_IC_CLR_START_DET = 0x64,
+ DW_IC_CLR_GEN_CALL = 0x68,
+ DW_IC_ENABLE = 0x6c,
+ DW_IC_STATUS = 0x70,
+ DW_IC_TXFLR = 0x74,
+ DW_IC_RXFLR = 0x78,
+ DW_IC_SDA_HOLD = 0x7c,
+ DW_IC_TX_ABRT_SOURCE = 0x80,
+ DW_IC_SLV_DATA_NACK_ONLY = 0x84,
+ DW_IC_DMA_CR = 0x88,
+ DW_IC_DMA_TDLR = 0x8c,
+ DW_IC_DMA_RDLR = 0x90,
+ DW_IC_SDA_SETUP = 0x94,
+ DW_IC_ACK_GENERAL_CALL = 0x98,
+ DW_IC_ENABLE_STATUS = 0x9c,
+ DW_IC_FS_SPKLEN = 0xa0,
+ DW_IC_CLR_RESTART_DET = 0xa8,
+ DW_IC_COMP_PARAM_1 = 0xf4,
+ DW_IC_COMP_VERSION = 0xf8,
+ DW_IC_COMP_TYPE = 0xfc,
+};
+
+/* DW_IC_CON fields */
+#define DW_IC_CON_STOP_DET_IF_MASTER_ACTIV BIT(10)
+#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9)
+#define DW_IC_CON_TX_EMPTY_CTRL BIT(8)
+#define DW_IC_CON_STOP_IF_ADDRESSED BIT(7)
+#define DW_IC_CON_SLAVE_DISABLE BIT(6)
+#define DW_IC_CON_IC_RESTART_EN BIT(5)
+#define DW_IC_CON_10BITADDR_MASTER BIT(4)
+#define DW_IC_CON_10BITADDR_SLAVE BIT(3)
+#define DW_IC_CON_SPEED(rv) extract32((rv), 1, 2)
+#define DW_IC_CON_MASTER_MODE BIT(0)
+
+/* DW_IC_TAR fields */
+#define DW_IC_TAR_IC_10BITADDR_MASTER BIT(12)
+#define DW_IC_TAR_SPECIAL BIT(11)
+#define DW_IC_TAR_GC_OR_START BIT(10)
+#define DW_IC_TAR_ADDRESS(rv) extract32((rv), 0, 10)
+
+/* DW_IC_DATA_CMD fields */
+#define DW_IC_DATA_CMD_RESTART BIT(10)
+#define DW_IC_DATA_CMD_STOP BIT(9)
+#define DW_IC_DATA_CMD_CMD BIT(8)
+#define DW_IC_DATA_CMD_DAT(rv) extract32((rv), 0, 8)
+
+/* DW_IC_INTR_STAT/INTR_MASK/RAW_INTR_STAT fields */
+#define DW_IC_INTR_RESTART_DET BIT(12)
+#define DW_IC_INTR_GEN_CALL BIT(11)
+#define DW_IC_INTR_START_DET BIT(10)
+#define DW_IC_INTR_STOP_DET BIT(9)
+#define DW_IC_INTR_ACTIVITY BIT(8)
+#define DW_IC_INTR_RX_DONE BIT(7)
+#define DW_IC_INTR_TX_ABRT BIT(6)
+#define DW_IC_INTR_RD_REQ BIT(5)
+#define DW_IC_INTR_TX_EMPTY BIT(4) /* Hardware clear only. */
+#define DW_IC_INTR_TX_OVER BIT(3)
+#define DW_IC_INTR_RX_FULL BIT(2) /* Hardware clear only. */
+#define DW_IC_INTR_RX_OVER BIT(1)
+#define DW_IC_INTR_RX_UNDER BIT(0)
+
+/* DW_IC_ENABLE fields */
+#define DW_IC_ENABLE_TX_CMD_BLOCK BIT(2)
+#define DW_IC_ENABLE_ABORT BIT(1)
+#define DW_IC_ENABLE_ENABLE BIT(0)
+
+/* DW_IC_STATUS fields */
+#define DW_IC_STATUS_SLV_ACTIVITY BIT(6)
+#define DW_IC_STATUS_MST_ACTIVITY BIT(5)
+#define DW_IC_STATUS_RFF BIT(4)
+#define DW_IC_STATUS_RFNE BIT(3)
+#define DW_IC_STATUS_TFE BIT(2)
+#define DW_IC_STATUS_TFNF BIT(1)
+#define DW_IC_STATUS_ACTIVITY BIT(0)
+
+/* DW_IC_TX_ABRT_SOURCE fields */
+#define DW_IC_TX_TX_FLUSH_CNT extract32((rv), 23, 9)
+#define DW_IC_TX_ABRT_USER_ABRT BIT(16)
+#define DW_IC_TX_ABRT_SLVRD_INTX BIT(15)
+#define DW_IC_TX_ABRT_SLV_ARBLOST BIT(14)
+#define DW_IC_TX_ABRT_SLVFLUSH_TXFIFO BIT(13)
+#define DW_IC_TX_ARB_LOST BIT(12)
+#define DW_IC_TX_ABRT_MASTER_DIS BIT(11)
+#define DW_IC_TX_ABRT_10B_RD_NORSTRT BIT(10)
+#define DW_IC_TX_ABRT_SBYTE_NORSTRT BIT(9)
+#define DW_IC_TX_ABRT_HS_NORSTRT BIT(8)
+#define DW_IC_TX_ABRT_SBYTE_ACKDET BIT(7)
+#define DW_IC_TX_ABRT_HS_ACKDET BIT(6)
+#define DW_IC_TX_ABRT_GCALL_READ BIT(5)
+#define DW_IC_TX_ABRT_GCALL_NOACK BIT(4)
+#define DW_IC_TX_ABRT_TXDATA_NOACK BIT(3)
+#define DW_IC_TX_ABRT_10ADDR2_NOACK BIT(2)
+#define DW_IC_TX_ABRT_10ADDR1_NOACK BIT(1)
+#define DW_IC_TX_ABRT_7B_ADDR_NOACK BIT(0)
+
+
+/* IC_ENABLE_STATUS fields */
+#define DW_IC_ENABLE_STATUS_SLV_RX_DATA_LOST BIT(2)
+#define DW_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY BIT(1)
+#define DW_IC_ENABLE_STATUS_IC_EN BIT(0)
+
+/* Masks for writable registers. */
+#define DW_IC_CON_MASK 0x000003ff
+#define DW_IC_TAR_MASK 0x00000fff
+#define DW_IC_SAR_MASK 0x000003ff
+#define DW_IC_SS_SCL_HCNT_MASK 0x0000ffff
+#define DW_IC_SS_SCL_LCNT_MASK 0x0000ffff
+#define DW_IC_FS_SCL_HCNT_MASK 0x0000ffff
+#define DW_IC_FS_SCL_LCNT_MASK 0x0000ffff
+#define DW_IC_INTR_MASK_MASK 0x00001fff
+#define DW_IC_ENABLE_MASK 0x00000007
+#define DW_IC_SDA_HOLD_MASK 0x00ffffff
+#define DW_IC_SDA_SETUP_MASK 0x000000ff
+#define DW_IC_FS_SPKLEN_MASK 0x000000ff
+
+/* Reset values */
+#define DW_IC_CON_INIT_VAL 0x7d
+#define DW_IC_TAR_INIT_VAL 0x1055
+#define DW_IC_SAR_INIT_VAL 0x55
+#define DW_IC_SS_SCL_HCNT_INIT_VAL 0x190
+#define DW_IC_SS_SCL_LCNT_INIT_VAL 0x1d6
+#define DW_IC_FS_SCL_HCNT_INIT_VAL 0x3c
+#define DW_IC_FS_SCL_LCNT_INIT_VAL 0x82
+#define DW_IC_INTR_MASK_INIT_VAL 0x8ff
+#define DW_IC_STATUS_INIT_VAL 0x6
+#define DW_IC_SDA_HOLD_INIT_VAL 0x1
+#define DW_IC_SDA_SETUP_INIT_VAL 0x64
+#define DW_IC_FS_SPKLEN_INIT_VAL 0x2
+
+#define DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS BIT(7)
+#define DW_IC_COMP_PARAM_1_HAS_DMA 0 /* bit 6 - DMA disabled. */
+#define DW_IC_COMP_PARAM_1_INTR_IO BIT(5)
+#define DW_IC_COMP_PARAM_1_HC_COUNT_VAL 0 /* bit 4 - disabled */
+#define DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE (BIT(2) | BIT(3))
+#define DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 BIT(1) /* bits 0, 1 */
+#define DW_IC_COMP_PARAM_1_INIT_VAL \
+ (DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 | \
+ DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE | \
+ DW_IC_COMP_PARAM_1_HC_COUNT_VAL | \
+ DW_IC_COMP_PARAM_1_INTR_IO | \
+ DW_IC_COMP_PARAM_1_HAS_DMA | \
+ DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS | \
+ ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1) << 8) | \
+ ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1) << 16))
+#define DW_IC_COMP_VERSION_INIT_VAL 0x3132302a
+#define DW_IC_COMP_TYPE_INIT_VAL 0x44570140
+
+static void dw_i2c_update_irq(DesignWareI2CState *s)
+{
+ int level;
+ uint32_t intr = s->ic_raw_intr_stat & s->ic_intr_mask;
+
+ level = !!((intr & DW_IC_INTR_RX_UNDER) |
+ (intr & DW_IC_INTR_RX_OVER) |
+ (intr & DW_IC_INTR_RX_FULL) |
+ (intr & DW_IC_INTR_TX_OVER) |
+ (intr & DW_IC_INTR_TX_EMPTY) |
+ (intr & DW_IC_INTR_RD_REQ) |
+ (intr & DW_IC_INTR_TX_ABRT) |
+ (intr & DW_IC_INTR_RX_DONE) |
+ (intr & DW_IC_INTR_ACTIVITY) |
+ (intr & DW_IC_INTR_STOP_DET) |
+ (intr & DW_IC_INTR_START_DET) |
+ (intr & DW_IC_INTR_GEN_CALL) |
+ (intr & DW_IC_INTR_RESTART_DET)
+ );
+ qemu_set_irq(s->irq, level);
+}
+
+static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s)
+{
+ uint32_t value = s->rx_fifo[s->rx_cur];
+
+ if (s->status != DW_I2C_STATUS_RECEIVING) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Attempted to read from RX fifo when not in receive "
+ "state.\n", DEVICE(s)->canonical_path);
+ if (s->status != DW_I2C_STATUS_IDLE) {
+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
+ dw_i2c_update_irq(s);
+ }
+ return 0;
+ }
+
+ s->rx_cur = (s->rx_cur + 1) % DESIGNWARE_I2C_RX_FIFO_SIZE;
+
+ if (s->ic_rxflr > 0) {
+ s->ic_rxflr--;
+ } else {
+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
+ dw_i2c_update_irq(s);
+ return 0;
+ }
+
+ if (s->ic_rxflr <= s->ic_rx_tl) {
+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL;
+ dw_i2c_update_irq(s);
+ }
+
+ return value;
+}
+
+static uint64_t dw_i2c_read(void *opaque, hwaddr offset, unsigned size)
+{
+ uint64_t value = 0;
+
+ DesignWareI2CState *s = opaque;
+
+ switch (offset) {
+ case DW_IC_CON:
+ value = s->ic_con;
+ break;
+ case DW_IC_TAR:
+ value = s->ic_tar;
+ break;
+ case DW_IC_SAR:
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_sar\n",
+ DEVICE(s)->canonical_path);
+ value = s->ic_sar;
+ break;
+ case DW_IC_DATA_CMD:
+ value = dw_i2c_read_ic_data_cmd(s);
+ break;
+ case DW_IC_SS_SCL_HCNT:
+ value = s->ic_ss_scl_hcnt;
+ break;
+ case DW_IC_SS_SCL_LCNT:
+ value = s->ic_ss_scl_lcnt;
+ break;
+ case DW_IC_FS_SCL_HCNT:
+ value = s->ic_fs_scl_hcnt;
+ break;
+ case DW_IC_FS_SCL_LCNT:
+ value = s->ic_fs_scl_lcnt;
+ break;
+ case DW_IC_INTR_STAT:
+ value = s->ic_raw_intr_stat & s->ic_intr_mask;
+ break;
+ case DW_IC_INTR_MASK:
+ value = s->ic_intr_mask;
+ break;
+ case DW_IC_RAW_INTR_STAT:
+ value = s->ic_raw_intr_stat;
+ break;
+ case DW_IC_RX_TL:
+ value = s->ic_rx_tl;
+ break;
+ case DW_IC_TX_TL:
+ value = s->ic_tx_tl;
+ break;
+ case DW_IC_CLR_INTR:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_GEN_CALL |
+ DW_IC_INTR_RESTART_DET |
+ DW_IC_INTR_START_DET |
+ DW_IC_INTR_STOP_DET |
+ DW_IC_INTR_ACTIVITY |
+ DW_IC_INTR_RX_DONE |
+ DW_IC_INTR_TX_ABRT |
+ DW_IC_INTR_RD_REQ |
+ DW_IC_INTR_TX_OVER |
+ DW_IC_INTR_RX_OVER |
+ DW_IC_INTR_RX_UNDER);
+ s->ic_tx_abrt_source = 0;
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_RX_UNDER:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_UNDER);
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_RX_OVER:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_OVER);
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_TX_OVER:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_TX_OVER);
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_RD_REQ:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RD_REQ);
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_TX_ABRT:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_TX_ABRT);
+ s->ic_tx_abrt_source = 0;
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_RX_DONE:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_DONE);
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_ACTIVITY:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_ACTIVITY);
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_STOP_DET:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_STOP_DET);
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_START_DET:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_START_DET);
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_CLR_GEN_CALL:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_GEN_CALL);
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_ENABLE:
+ value = s->ic_enable;
+ break;
+ case DW_IC_STATUS:
+ value = s->ic_status;
+ break;
+ case DW_IC_TXFLR:
+ value = s->ic_txflr;
+ break;
+ case DW_IC_RXFLR:
+ value = s->ic_rxflr;
+ break;
+ case DW_IC_SDA_HOLD:
+ value = s->ic_sda_hold;
+ break;
+ case DW_IC_TX_ABRT_SOURCE:
+ value = s->ic_tx_abrt_source;
+ break;
+ case DW_IC_SLV_DATA_NACK_ONLY:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unsupported read - ic_slv_data_nack_only\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_DMA_CR:
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_cr\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_DMA_TDLR:
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_tdlr\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_DMA_RDLR:
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_rdlr\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_SDA_SETUP:
+ value = s->ic_sda_setup;
+ break;
+ case DW_IC_ACK_GENERAL_CALL:
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_ack_general_call\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_ENABLE_STATUS:
+ value = s->ic_enable_status;
+ break;
+ case DW_IC_FS_SPKLEN:
+ value = s->ic_fs_spklen;
+ break;
+ case DW_IC_CLR_RESTART_DET:
+ s->ic_raw_intr_stat &= ~(DW_IC_INTR_RESTART_DET);
+ break;
+ case DW_IC_COMP_PARAM_1:
+ value = s->ic_comp_param_1;
+ break;
+ case DW_IC_COMP_VERSION:
+ value = s->ic_comp_version;
+ break;
+ case DW_IC_COMP_TYPE:
+ value = s->ic_comp_type;
+ break;
+
+ /* This register is invalid at this point. */
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: read from invalid offset 0x%" HWADDR_PRIx "\n",
+ DEVICE(s)->canonical_path, offset);
+ break;
+ }
+
+ trace_dw_i2c_read(DEVICE(s)->canonical_path, offset, value);
+
+ return value;
+}
+
+static void dw_i2c_write_ic_con(DesignWareI2CState *s, uint32_t value)
+{
+ if (value & DW_IC_CON_RX_FIFO_FULL_HLD_CTRL) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unsupported ic_con flag - RX_FIFO_FULL_HLD_CTRL\n",
+ DEVICE(s)->canonical_path);
+ }
+
+ if (!(s->ic_enable & DW_IC_ENABLE_ENABLE)) {
+ s->ic_con = value & DW_IC_CON_MASK;
+ } else {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid setting to ic_con %d when ic_enable[0]==1\n",
+ DEVICE(s)->canonical_path, value);
+ }
+}
+
+static void dw_i2c_reset_to_idle(DesignWareI2CState *s)
+{
+ s->ic_enable_status &= ~DW_IC_ENABLE_STATUS_IC_EN;
+ s->ic_raw_intr_stat &= ~DW_IC_INTR_TX_EMPTY;
+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL;
+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_OVER;
+ s->ic_rxflr = 0;
+ s->ic_status &= ~DW_IC_STATUS_ACTIVITY;
+ s->status = DW_I2C_STATUS_IDLE;
+ dw_i2c_update_irq(s);
+}
+
+static void dw_ic_tx_abort(DesignWareI2CState *s, uint32_t src)
+{
+ s->ic_tx_abrt_source |= src;
+ s->ic_raw_intr_stat |= DW_IC_INTR_TX_ABRT;
+ dw_i2c_reset_to_idle(s);
+ dw_i2c_update_irq(s);
+}
+
+static void dw_i2c_write_ic_data_cmd(DesignWareI2CState *s, uint32_t value)
+{
+ int recv = !!(value & DW_IC_DATA_CMD_CMD);
+
+ if (s->status == DW_I2C_STATUS_IDLE ||
+ s->ic_raw_intr_stat & DW_IC_INTR_TX_ABRT) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Attempted to write to TX fifo when it is held in "
+ "reset.\n", DEVICE(s)->canonical_path);
+ return;
+ }
+
+ /* Send the address if it hasn't been sent yet. */
+ if (s->status == DW_I2C_STATUS_SENDING_ADDRESS) {
+ int rv = i2c_start_transfer(s->bus, DW_IC_TAR_ADDRESS(s->ic_tar), recv);
+ if (rv) {
+ dw_ic_tx_abort(s, DW_IC_TX_ABRT_7B_ADDR_NOACK);
+ return;
+ }
+ s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING;
+ }
+
+ /* Send data */
+ if (!recv) {
+ int rv = i2c_send(s->bus, DW_IC_DATA_CMD_DAT(value));
+ if (rv) {
+ i2c_end_transfer(s->bus);
+ dw_ic_tx_abort(s, DW_IC_TX_ABRT_TXDATA_NOACK);
+ return;
+ }
+ dw_i2c_update_irq(s);
+ }
+
+ /* Restart command */
+ if (value & DW_IC_DATA_CMD_RESTART && s->ic_con & DW_IC_CON_IC_RESTART_EN) {
+ s->ic_raw_intr_stat |= DW_IC_INTR_RESTART_DET |
+ DW_IC_INTR_START_DET |
+ DW_IC_INTR_ACTIVITY;
+ s->ic_status |= DW_IC_STATUS_ACTIVITY;
+ dw_i2c_update_irq(s);
+
+ if (i2c_start_transfer(s->bus, DW_IC_TAR_ADDRESS(s->ic_tar), recv)) {
+ dw_ic_tx_abort(s, DW_IC_TX_ABRT_7B_ADDR_NOACK);
+ return;
+ }
+
+ s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING;
+ }
+
+ /* Receive data */
+ if (recv) {
+ uint8_t pos = (s->rx_cur + s->ic_rxflr) % DESIGNWARE_I2C_RX_FIFO_SIZE;
+
+ if (s->ic_rxflr < DESIGNWARE_I2C_RX_FIFO_SIZE) {
+ s->rx_fifo[pos] = i2c_recv(s->bus);
+ s->ic_rxflr++;
+ } else {
+ s->ic_raw_intr_stat |= DW_IC_INTR_RX_OVER;
+ dw_i2c_update_irq(s);
+ }
+
+ if (s->ic_rxflr > s->ic_rx_tl) {
+ s->ic_raw_intr_stat |= DW_IC_INTR_RX_FULL;
+ dw_i2c_update_irq(s);
+ }
+ if (value & DW_IC_DATA_CMD_STOP) {
+ i2c_nack(s->bus);
+ }
+ }
+
+ /* Stop command */
+ if (value & DW_IC_DATA_CMD_STOP) {
+ s->ic_raw_intr_stat |= DW_IC_INTR_STOP_DET;
+ s->ic_status &= ~DW_IC_STATUS_ACTIVITY;
+ s->ic_raw_intr_stat &= ~DW_IC_INTR_TX_EMPTY;
+ i2c_end_transfer(s->bus);
+ dw_i2c_update_irq(s);
+ }
+}
+
+static void dw_i2c_write_ic_enable(DesignWareI2CState *s, uint32_t value)
+{
+ if (value & DW_IC_ENABLE_ENABLE && !(s->ic_con & DW_IC_CON_SLAVE_DISABLE)) {
+ qemu_log_mask(LOG_UNIMP,
+ "%s: Designware I2C slave mode is not supported.\n",
+ DEVICE(s)->canonical_path);
+ return;
+ }
+
+ s->ic_enable = value & DW_IC_ENABLE_MASK;
+
+ if (value & DW_IC_ENABLE_ABORT || value & DW_IC_ENABLE_TX_CMD_BLOCK) {
+ dw_ic_tx_abort(s, DW_IC_TX_ABRT_USER_ABRT);
+ return;
+ }
+
+ if (value & DW_IC_ENABLE_ENABLE) {
+ s->ic_enable_status |= DW_IC_ENABLE_STATUS_IC_EN;
+ s->ic_status |= DW_IC_STATUS_ACTIVITY;
+ s->ic_raw_intr_stat |= DW_IC_INTR_ACTIVITY |
+ DW_IC_INTR_START_DET |
+ DW_IC_INTR_TX_EMPTY;
+ s->status = DW_I2C_STATUS_SENDING_ADDRESS;
+ dw_i2c_update_irq(s);
+ } else if ((value & DW_IC_ENABLE_ENABLE) == 0) {
+ dw_i2c_reset_to_idle(s);
+ }
+
+}
+
+static void dw_i2c_write_ic_rx_tl(DesignWareI2CState *s, uint32_t value)
+{
+ /* Note that a value of 0 for ic_rx_tl indicates a threashold of 1. */
+ if (value > DESIGNWARE_I2C_RX_FIFO_SIZE - 1) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid setting to ic_rx_tl %d\n",
+ DEVICE(s)->canonical_path, value);
+ s->ic_rx_tl = DESIGNWARE_I2C_RX_FIFO_SIZE - 1;
+ } else {
+ s->ic_rx_tl = value;
+ }
+
+ if (s->ic_rxflr > s->ic_rx_tl && s->ic_enable & DW_IC_ENABLE_ENABLE) {
+ s->ic_raw_intr_stat |= DW_IC_INTR_RX_FULL;
+ } else {
+ s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL;
+ }
+ dw_i2c_update_irq(s);
+}
+
+static void dw_i2c_write_ic_tx_tl(DesignWareI2CState *s, uint32_t value)
+{
+ /*
+ * Note that a value of 0 for ic_tx_tl indicates a threashold of 1.
+ * However, the tx threshold is not used in the model because commands are
+ * always sent out as soon as they are written.
+ */
+ if (value > DESIGNWARE_I2C_TX_FIFO_SIZE - 1) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid setting to ic_tx_tl %d\n",
+ DEVICE(s)->canonical_path, value);
+ s->ic_tx_tl = DESIGNWARE_I2C_TX_FIFO_SIZE - 1;
+ } else {
+ s->ic_tx_tl = value;
+ }
+}
+
+static void dw_i2c_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ DesignWareI2CState *s = opaque;
+
+ trace_dw_i2c_write(DEVICE(s)->canonical_path, offset, value);
+
+ /* The order of the registers are their order in memory. */
+ switch (offset) {
+ case DW_IC_CON:
+ dw_i2c_write_ic_con(s, value);
+ break;
+ case DW_IC_TAR:
+ s->ic_tar = value & DW_IC_TAR_MASK;
+ break;
+ case DW_IC_SAR:
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_sar\n",
+ DEVICE(s)->canonical_path);
+ s->ic_sar = value & DW_IC_SAR_MASK;
+ break;
+ case DW_IC_DATA_CMD:
+ dw_i2c_write_ic_data_cmd(s, value);
+ break;
+ case DW_IC_SS_SCL_HCNT:
+ s->ic_ss_scl_hcnt = value & DW_IC_SS_SCL_HCNT_MASK;
+ break;
+ case DW_IC_SS_SCL_LCNT:
+ s->ic_ss_scl_lcnt = value & DW_IC_SS_SCL_LCNT_MASK;
+ break;
+ case DW_IC_FS_SCL_HCNT:
+ s->ic_fs_scl_hcnt = value & DW_IC_FS_SCL_HCNT_MASK;
+ break;
+ case DW_IC_FS_SCL_LCNT:
+ s->ic_fs_scl_lcnt = value & DW_IC_FS_SCL_LCNT_MASK;
+ break;
+ case DW_IC_INTR_MASK:
+ s->ic_intr_mask = value & DW_IC_INTR_MASK_MASK;
+ dw_i2c_update_irq(s);
+ break;
+ case DW_IC_RX_TL:
+ dw_i2c_write_ic_rx_tl(s, value);
+ break;
+ case DW_IC_TX_TL:
+ dw_i2c_write_ic_tx_tl(s, value);
+ break;
+ case DW_IC_ENABLE:
+ dw_i2c_write_ic_enable(s, value);
+ break;
+ case DW_IC_SDA_HOLD:
+ s->ic_sda_hold = value & DW_IC_SDA_HOLD_MASK;
+ break;
+ case DW_IC_SLV_DATA_NACK_ONLY:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unsupported write - ic_slv_data_nack_only\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_DMA_CR:
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_cr\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_DMA_TDLR:
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_tdlr\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_DMA_RDLR:
+ qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_rdlr\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_SDA_SETUP:
+ s->ic_sda_setup = value & DW_IC_SDA_SETUP_MASK;
+ break;
+ case DW_IC_ACK_GENERAL_CALL:
+ qemu_log_mask(LOG_UNIMP,
+ "%s: unsupported write - ic_ack_general_call\n",
+ DEVICE(s)->canonical_path);
+ break;
+ case DW_IC_FS_SPKLEN:
+ s->ic_fs_spklen = value & DW_IC_FS_SPKLEN_MASK;
+ break;
+
+ /* This register is invalid at this point. */
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: write to invalid offset or readonly register 0x%"
+ HWADDR_PRIx "\n",
+ DEVICE(s)->canonical_path, offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps designware_i2c_ops = {
+ .read = dw_i2c_read,
+ .write = dw_i2c_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ .unaligned = false,
+ },
+};
+
+static void designware_i2c_enter_reset(Object *obj, ResetType type)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(obj);
+
+ s->ic_con = DW_IC_CON_INIT_VAL;
+ s->ic_tar = DW_IC_TAR_INIT_VAL;
+ s->ic_sar = DW_IC_SAR_INIT_VAL;
+ s->ic_ss_scl_hcnt = DW_IC_SS_SCL_HCNT_INIT_VAL;
+ s->ic_ss_scl_lcnt = DW_IC_SS_SCL_LCNT_INIT_VAL;
+ s->ic_fs_scl_hcnt = DW_IC_FS_SCL_HCNT_INIT_VAL;
+ s->ic_fs_scl_lcnt = DW_IC_FS_SCL_LCNT_INIT_VAL;
+ s->ic_intr_mask = DW_IC_INTR_MASK_INIT_VAL;
+ s->ic_raw_intr_stat = 0;
+ s->ic_rx_tl = 0;
+ s->ic_tx_tl = 0;
+ s->ic_enable = 0;
+ s->ic_status = DW_IC_STATUS_INIT_VAL;
+ s->ic_txflr = 0;
+ s->ic_rxflr = 0;
+ s->ic_sda_hold = DW_IC_SDA_HOLD_INIT_VAL;
+ s->ic_tx_abrt_source = 0;
+ s->ic_sda_setup = DW_IC_SDA_SETUP_INIT_VAL;
+ s->ic_enable_status = 0;
+ s->ic_fs_spklen = DW_IC_FS_SPKLEN_INIT_VAL;
+ s->ic_comp_param_1 = DW_IC_COMP_PARAM_1_INIT_VAL;
+ s->ic_comp_version = DW_IC_COMP_VERSION_INIT_VAL;
+ s->ic_comp_type = DW_IC_COMP_TYPE_INIT_VAL;
+
+ s->rx_cur = 0;
+ s->status = DW_I2C_STATUS_IDLE;
+}
+
+static void designware_i2c_hold_reset(Object *obj, ResetType type)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(obj);
+
+ qemu_irq_lower(s->irq);
+}
+
+static const VMStateDescription vmstate_designware_i2c = {
+ .name = TYPE_DESIGNWARE_I2C,
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(ic_con, DesignWareI2CState),
+ VMSTATE_UINT32(ic_tar, DesignWareI2CState),
+ VMSTATE_UINT32(ic_sar, DesignWareI2CState),
+ VMSTATE_UINT32(ic_ss_scl_hcnt, DesignWareI2CState),
+ VMSTATE_UINT32(ic_ss_scl_lcnt, DesignWareI2CState),
+ VMSTATE_UINT32(ic_fs_scl_hcnt, DesignWareI2CState),
+ VMSTATE_UINT32(ic_fs_scl_lcnt, DesignWareI2CState),
+ VMSTATE_UINT32(ic_intr_mask, DesignWareI2CState),
+ VMSTATE_UINT32(ic_raw_intr_stat, DesignWareI2CState),
+ VMSTATE_UINT32(ic_rx_tl, DesignWareI2CState),
+ VMSTATE_UINT32(ic_tx_tl, DesignWareI2CState),
+ VMSTATE_UINT32(ic_enable, DesignWareI2CState),
+ VMSTATE_UINT32(ic_status, DesignWareI2CState),
+ VMSTATE_UINT32(ic_txflr, DesignWareI2CState),
+ VMSTATE_UINT32(ic_rxflr, DesignWareI2CState),
+ VMSTATE_UINT32(ic_sda_hold, DesignWareI2CState),
+ VMSTATE_UINT32(ic_tx_abrt_source, DesignWareI2CState),
+ VMSTATE_UINT32(ic_sda_setup, DesignWareI2CState),
+ VMSTATE_UINT32(ic_enable_status, DesignWareI2CState),
+ VMSTATE_UINT32(ic_fs_spklen, DesignWareI2CState),
+ VMSTATE_UINT32(ic_comp_param_1, DesignWareI2CState),
+ VMSTATE_UINT32(ic_comp_version, DesignWareI2CState),
+ VMSTATE_UINT32(ic_comp_type, DesignWareI2CState),
+ VMSTATE_UINT32(status, DesignWareI2CState),
+ VMSTATE_UINT8_ARRAY(rx_fifo, DesignWareI2CState,
+ DESIGNWARE_I2C_RX_FIFO_SIZE),
+ VMSTATE_UINT8(rx_cur, DesignWareI2CState),
+ VMSTATE_END_OF_LIST(),
+ },
+};
+
+static void designware_i2c_smbus_init(Object *obj)
+{
+ DesignWareI2CState *s = DESIGNWARE_I2C(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+ sysbus_init_irq(sbd, &s->irq);
+
+ memory_region_init_io(&s->iomem, obj, &designware_i2c_ops, s,
+ "regs", 4 * KiB);
+ sysbus_init_mmio(sbd, &s->iomem);
+
+ s->bus = i2c_init_bus(DEVICE(s), "i2c-bus");
+}
+
+static void designware_i2c_class_init(ObjectClass *klass, const void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->desc = "Designware I2C";
+ dc->vmsd = &vmstate_designware_i2c;
+ rc->phases.enter = designware_i2c_enter_reset;
+ rc->phases.hold = designware_i2c_hold_reset;
+}
+
+static const TypeInfo designware_i2c_types[] = {
+ {
+ .name = TYPE_DESIGNWARE_I2C,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(DesignWareI2CState),
+ .class_init = designware_i2c_class_init,
+ .instance_init = designware_i2c_smbus_init,
+ },
+};
+DEFINE_TYPES(designware_i2c_types);
diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig
index 596a7a3165ad..d3f394edeb9c 100644
--- a/hw/i2c/Kconfig
+++ b/hw/i2c/Kconfig
@@ -18,6 +18,10 @@ config ARM_SBCON_I2C
bool
select BITBANG_I2C
+config DESIGNWARE_I2C
+ bool
+ select I2C
+
config ACPI_SMBUS
bool
select SMBUS
diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build
index c459adcb596c..88aea35662dd 100644
--- a/hw/i2c/meson.build
+++ b/hw/i2c/meson.build
@@ -11,6 +11,7 @@ i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: files('mpc_i2c.c'))
i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true: files('allwinner-i2c.c'))
i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c'))
i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c'))
+i2c_ss.add(when: 'CONFIG_DESIGNWARE_I2C', if_true: files('designware_i2c.c'))
i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c'))
i2c_ss.add(when: 'CONFIG_ARM_SBCON_I2C', if_true: files('arm_sbcon_i2c.c'))
i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c'))
diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events
index 1ad0e95c0e60..8a78d2d3c8de 100644
--- a/hw/i2c/trace-events
+++ b/hw/i2c/trace-events
@@ -61,3 +61,7 @@ pca954x_read_data(uint8_t value) "PCA954X read data: 0x%02x"
imx_i2c_read(const char *id, const char *reg, uint64_t ofs, uint64_t value) "%s:[%s (0x%" PRIx64 ")] -> 0x%02" PRIx64
imx_i2c_write(const char *id, const char *reg, uint64_t ofs, uint64_t value) "%s:[%s (0x%" PRIx64 ")] <- 0x%02" PRIx64
+
+# designware_i2c.c
+dw_i2c_read(const char *id, uint64_t ofs, uint64_t value) "%s: offset 0x%02" PRIx64 " -> value: 0x%02" PRIx64
+dw_i2c_write(const char *id, uint64_t ofs, uint64_t value) "%s: offset: 0x%02" PRIx64 " <- value: 0x%02" PRIx64
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 02/13] hw/riscv/boot: Describe discontiguous memory in boot_info
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
2026-04-25 13:17 ` [PATCH v4 01/13] hw/i2c: Add designware i2c controller Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-25 13:17 ` [PATCH v4 03/13] hw/riscv/boot: Account for discontiguous memory when loading firmware Joel Stanley
` (10 subsequent siblings)
12 siblings, 0 replies; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
From: Nicholas Piggin <npiggin@gmail.com>
Machines that have discontiguous memory may need to adjust where
firmware and images are loaded at boot. Provide an interface for
machines to describe a discontiguous low/high RAM scheme for this
purpose.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
include/hw/riscv/boot.h | 7 +++++++
hw/riscv/boot.c | 11 +++++++++++
2 files changed, 18 insertions(+)
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
index f00b3ca12245..115e3222174f 100644
--- a/include/hw/riscv/boot.h
+++ b/include/hw/riscv/boot.h
@@ -28,6 +28,10 @@
#define RISCV64_BIOS_BIN "opensbi-riscv64-generic-fw_dynamic.bin"
typedef struct RISCVBootInfo {
+ /* First contiguous RAM region. If size is zero then assume entire RAM */
+ hwaddr ram_low_start;
+ hwaddr ram_low_size;
+
ssize_t kernel_size;
hwaddr image_low_addr;
hwaddr image_high_addr;
@@ -43,6 +47,9 @@ bool riscv_is_32bit(RISCVHartArrayState *harts);
char *riscv_plic_hart_config_string(int hart_count);
void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts);
+void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info,
+ RISCVHartArrayState *harts,
+ hwaddr start, hwaddr size);
vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info,
hwaddr firmware_end_addr);
hwaddr riscv_find_and_load_firmware(MachineState *machine,
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index 9086793b7a7b..5c9547429a36 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -69,11 +69,22 @@ char *riscv_plic_hart_config_string(int hart_count)
void riscv_boot_info_init(RISCVBootInfo *info, RISCVHartArrayState *harts)
{
+ info->ram_low_start = 0;
+ info->ram_low_size = 0;
info->kernel_size = 0;
info->initrd_size = 0;
info->is_32bit = riscv_is_32bit(harts);
}
+void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info,
+ RISCVHartArrayState *harts,
+ hwaddr start, hwaddr size)
+{
+ riscv_boot_info_init(info, harts);
+ info->ram_low_start = start;
+ info->ram_low_size = size;
+}
+
vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info,
hwaddr firmware_end_addr) {
if (info->is_32bit) {
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 03/13] hw/riscv/boot: Account for discontiguous memory when loading firmware
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
2026-04-25 13:17 ` [PATCH v4 01/13] hw/i2c: Add designware i2c controller Joel Stanley
2026-04-25 13:17 ` [PATCH v4 02/13] hw/riscv/boot: Describe discontiguous memory in boot_info Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-29 23:34 ` Alistair Francis
2026-04-25 13:17 ` [PATCH v4 04/13] hw/riscv/boot: Provide a simple halting payload Joel Stanley
` (9 subsequent siblings)
12 siblings, 1 reply; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
From: Nicholas Piggin <npiggin@gmail.com>
This loads firmware into the first (low) memory range,
accounting for machines having discontiguous memory regions.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v4: Make RISCVBootInfo *info const in riscv_load_firmware
v3: Call riscv_boot_info_init before riscv_find_and_load_firmware in
sifive_u
---
include/hw/riscv/boot.h | 5 ++++-
hw/riscv/boot.c | 18 ++++++++++++------
hw/riscv/microchip_pfsoc.c | 6 ++++--
hw/riscv/opentitan.c | 6 ++++--
hw/riscv/shakti_c.c | 6 +++++-
hw/riscv/sifive_u.c | 6 ++++--
hw/riscv/spike.c | 6 ++++--
hw/riscv/virt.c | 7 ++++---
hw/riscv/xiangshan_kmh.c | 6 +++++-
9 files changed, 46 insertions(+), 20 deletions(-)
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
index 115e3222174f..b2ef64fb1d14 100644
--- a/include/hw/riscv/boot.h
+++ b/include/hw/riscv/boot.h
@@ -53,13 +53,16 @@ void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info,
vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info,
hwaddr firmware_end_addr);
hwaddr riscv_find_and_load_firmware(MachineState *machine,
+ RISCVBootInfo *info,
const char *default_machine_firmware,
hwaddr *firmware_load_addr,
symbol_fn_t sym_cb);
const char *riscv_default_firmware_name(RISCVHartArrayState *harts);
char *riscv_find_firmware(const char *firmware_filename,
const char *default_machine_firmware);
-hwaddr riscv_load_firmware(const char *firmware_filename,
+hwaddr riscv_load_firmware(MachineState *machine,
+ const RISCVBootInfo *info,
+ const char *firmware_filename,
hwaddr *firmware_load_addr,
symbol_fn_t sym_cb);
void riscv_load_kernel(MachineState *machine,
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index 5c9547429a36..4fbc778263cf 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -145,6 +145,7 @@ char *riscv_find_firmware(const char *firmware_filename,
}
hwaddr riscv_find_and_load_firmware(MachineState *machine,
+ RISCVBootInfo *info,
const char *default_machine_firmware,
hwaddr *firmware_load_addr,
symbol_fn_t sym_cb)
@@ -157,7 +158,8 @@ hwaddr riscv_find_and_load_firmware(MachineState *machine,
if (firmware_filename) {
/* If not "none" load the firmware */
- firmware_end_addr = riscv_load_firmware(firmware_filename,
+ firmware_end_addr = riscv_load_firmware(machine, info,
+ firmware_filename,
firmware_load_addr, sym_cb);
g_free(firmware_filename);
}
@@ -165,10 +167,13 @@ hwaddr riscv_find_and_load_firmware(MachineState *machine,
return firmware_end_addr;
}
-hwaddr riscv_load_firmware(const char *firmware_filename,
+hwaddr riscv_load_firmware(MachineState *machine,
+ const RISCVBootInfo *info,
+ const char *firmware_filename,
hwaddr *firmware_load_addr,
symbol_fn_t sym_cb)
{
+ uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
uint64_t firmware_entry, firmware_end;
ssize_t firmware_size;
@@ -197,7 +202,7 @@ hwaddr riscv_load_firmware(const char *firmware_filename,
firmware_size = load_image_targphys_as(firmware_filename,
*firmware_load_addr,
- current_machine->ram_size, NULL,
+ mem_size, NULL,
NULL);
if (firmware_size > 0) {
@@ -212,7 +217,7 @@ hwaddr riscv_load_firmware(const char *firmware_filename,
static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info)
{
const char *filename = machine->initrd_filename;
- uint64_t mem_size = machine->ram_size;
+ uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
void *fdt = machine->fdt;
hwaddr start, end;
ssize_t size;
@@ -258,6 +263,7 @@ void riscv_load_kernel(MachineState *machine,
bool load_initrd,
symbol_fn_t sym_cb)
{
+ uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
const char *kernel_filename = machine->kernel_filename;
ssize_t kernel_size;
void *fdt = machine->fdt;
@@ -289,7 +295,7 @@ void riscv_load_kernel(MachineState *machine,
}
kernel_size = load_image_targphys_as(kernel_filename, kernel_start_addr,
- current_machine->ram_size, NULL, NULL);
+ mem_size, NULL, NULL);
if (kernel_size > 0) {
info->kernel_size = kernel_size;
info->image_low_addr = kernel_start_addr;
@@ -385,7 +391,7 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size,
dtb_start = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB);
if (dtb_start_limit && (dtb_start < dtb_start_limit)) {
- error_report("No enough memory to place DTB after kernel/initrd");
+ error_report("Not enough memory to place DTB after kernel/initrd");
exit(1);
}
diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c
index 743f31f00578..1d1ddb05a882 100644
--- a/hw/riscv/microchip_pfsoc.c
+++ b/hw/riscv/microchip_pfsoc.c
@@ -618,18 +618,20 @@ static void microchip_icicle_kit_machine_init(MachineState *machine)
firmware_load_addr = RESET_VECTOR;
}
+ riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
+
/* Load the firmware if necessary */
firmware_end_addr = firmware_load_addr;
if (firmware_name) {
char *filename = riscv_find_firmware(firmware_name, NULL);
if (filename) {
- firmware_end_addr = riscv_load_firmware(filename,
+ firmware_end_addr = riscv_load_firmware(machine, &boot_info,
+ filename,
&firmware_load_addr, NULL);
g_free(filename);
}
}
- riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c
index 309125e854bc..8cd660dd4154 100644
--- a/hw/riscv/opentitan.c
+++ b/hw/riscv/opentitan.c
@@ -99,12 +99,14 @@ static void opentitan_machine_init(MachineState *machine)
memory_region_add_subregion(sys_mem,
memmap[IBEX_DEV_RAM].base, machine->ram);
+ riscv_boot_info_init(&boot_info, &s->soc.cpus);
+
if (machine->firmware) {
hwaddr firmware_load_addr = memmap[IBEX_DEV_RAM].base;
- riscv_load_firmware(machine->firmware, &firmware_load_addr, NULL);
+ riscv_load_firmware(machine, &boot_info, machine->firmware,
+ &firmware_load_addr, NULL);
}
- riscv_boot_info_init(&boot_info, &s->soc.cpus);
if (machine->kernel_filename) {
riscv_load_kernel(machine, &boot_info,
memmap[IBEX_DEV_RAM].base,
diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c
index 49a39b30212d..eb720d9cdf5d 100644
--- a/hw/riscv/shakti_c.c
+++ b/hw/riscv/shakti_c.c
@@ -45,6 +45,7 @@ static void shakti_c_machine_state_init(MachineState *mstate)
{
ShaktiCMachineState *sms = RISCV_SHAKTI_MACHINE(mstate);
MemoryRegion *system_memory = get_system_memory();
+ RISCVBootInfo boot_info;
hwaddr firmware_load_addr = shakti_c_memmap[SHAKTI_C_RAM].base;
/* Initialize SoC */
@@ -57,8 +58,11 @@ static void shakti_c_machine_state_init(MachineState *mstate)
shakti_c_memmap[SHAKTI_C_RAM].base,
mstate->ram);
+ riscv_boot_info_init(&boot_info, &sms->soc.cpus);
+
if (mstate->firmware) {
- riscv_load_firmware(mstate->firmware, &firmware_load_addr, NULL);
+ riscv_load_firmware(mstate, &boot_info, mstate->firmware,
+ &firmware_load_addr, NULL);
}
/* ROM reset vector */
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 7ec67b256514..dda8687bfdd8 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -589,11 +589,13 @@ static void sifive_u_machine_init(MachineState *machine)
break;
}
+ riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
+
firmware_name = riscv_default_firmware_name(&s->soc.u_cpus);
- firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name,
+ firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
+ firmware_name,
&start_addr, NULL);
- riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
index 35c696f891d8..6ee915a8ba4e 100644
--- a/hw/riscv/spike.c
+++ b/hw/riscv/spike.c
@@ -281,9 +281,12 @@ static void spike_board_init(MachineState *machine)
}
}
+ riscv_boot_info_init(&boot_info, &s->soc[0]);
+
/* Load firmware */
if (firmware_name) {
- firmware_end_addr = riscv_load_firmware(firmware_name,
+ firmware_end_addr = riscv_load_firmware(machine, &boot_info,
+ firmware_name,
&firmware_load_addr,
htif_symbol_callback);
g_free(firmware_name);
@@ -293,7 +296,6 @@ static void spike_board_init(MachineState *machine)
create_fdt(s, memmap, riscv_is_32bit(&s->soc[0]), htif_custom_base);
/* Load kernel */
- riscv_boot_info_init(&boot_info, &s->soc[0]);
if (machine->kernel_filename) {
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index a1c323e66dfd..4501d5581b62 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -1457,7 +1457,10 @@ static void virt_machine_done(Notifier *notifier, void *data)
}
}
- firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name,
+ riscv_boot_info_init(&boot_info, &s->soc[0]);
+
+ firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
+ firmware_name,
&start_addr, NULL);
pflash_blk0 = pflash_cfi01_get_blk(s->flash[0]);
@@ -1480,8 +1483,6 @@ static void virt_machine_done(Notifier *notifier, void *data)
}
}
- riscv_boot_info_init(&boot_info, &s->soc[0]);
-
if (machine->kernel_filename && !kernel_entry) {
kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
firmware_end_addr);
diff --git a/hw/riscv/xiangshan_kmh.c b/hw/riscv/xiangshan_kmh.c
index 436e51c1c593..247a0b5d1f21 100644
--- a/hw/riscv/xiangshan_kmh.c
+++ b/hw/riscv/xiangshan_kmh.c
@@ -166,6 +166,7 @@ static void xiangshan_kmh_machine_init(MachineState *machine)
const MemMapEntry *memmap = xiangshan_kmh_memmap;
MemoryRegion *system_memory = get_system_memory();
hwaddr start_addr = memmap[XIANGSHAN_KMH_DRAM].base;
+ RISCVBootInfo boot_info;
/* Initialize SoC */
object_initialize_child(OBJECT(machine), "soc", &s->soc,
@@ -177,13 +178,16 @@ static void xiangshan_kmh_machine_init(MachineState *machine)
memmap[XIANGSHAN_KMH_DRAM].base,
machine->ram);
+ riscv_boot_info_init(&boot_info, &s->soc.cpus);
+
/* ROM reset vector */
riscv_setup_rom_reset_vec(machine, &s->soc.cpus,
start_addr,
memmap[XIANGSHAN_KMH_ROM].base,
memmap[XIANGSHAN_KMH_ROM].size, 0, 0);
if (machine->firmware) {
- riscv_load_firmware(machine->firmware, &start_addr, NULL);
+ riscv_load_firmware(machine, &boot_info, machine->firmware,
+ &start_addr, NULL);
}
/* Note: dtb has been integrated into firmware(OpenSBI) when compiling */
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 04/13] hw/riscv/boot: Provide a simple halting payload
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (2 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 03/13] hw/riscv/boot: Account for discontiguous memory when loading firmware Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-29 23:35 ` Alistair Francis
2026-04-25 13:17 ` [PATCH v4 05/13] hw/riscv/virt: Move AIA initialisation to helper file Joel Stanley
` (8 subsequent siblings)
12 siblings, 1 reply; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
From: Nicholas Piggin <npiggin@gmail.com>
OpenSBI hangs before any console output if the domain init code sees the
next stage is not in an executable region.
If no kernel payload is provided to QEMU, the next stage address is
NULL, and the riscv virt machine memory map ends up covering the 0
address with the catch all S-mode RWX region and so OpenSBI prints
console messages and does not hang until the next stage boot.
The soon to be added Tenstorrent Atlantis board address map has RAM
starting at 0 and it loads OpenSBI there, so it is M-mode and not
accessible by S-mode, tripping the early check and hang.
Add a helper to set up a simple payload that gets OpenSBI messages
to console.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v4: Make vec instructions const
v3: MachineState argument was unused
---
include/hw/riscv/boot.h | 1 +
hw/riscv/boot.c | 16 ++++++++++++++++
2 files changed, 17 insertions(+)
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
index b2ef64fb1d14..3dfb0e98db4c 100644
--- a/include/hw/riscv/boot.h
+++ b/include/hw/riscv/boot.h
@@ -78,6 +78,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts
hwaddr rom_base, hwaddr rom_size,
uint64_t kernel_entry,
uint64_t fdt_load_addr);
+void riscv_setup_halting_payload(RISCVBootInfo *info, hwaddr addr);
void riscv_rom_copy_firmware_info(MachineState *machine,
RISCVHartArrayState *harts,
hwaddr rom_base,
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index 4fbc778263cf..3c2501b3c721 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -518,6 +518,22 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts
kernel_entry);
}
+/* Simple payload so OpenSBI does not hang early with no output */
+void riscv_setup_halting_payload(RISCVBootInfo *info, hwaddr addr)
+{
+ /* Store the payload vector in little_endian byte order */
+ static const uint32_t payload_vec[] = {
+ const_le32(0x10500073), /* 1: wfi */
+ const_le32(0xffdff06f), /* j 1b */
+ };
+ rom_add_blob_fixed_as("mrom.payload", payload_vec, sizeof(payload_vec),
+ addr, &address_space_memory);
+
+ info->kernel_size = sizeof(payload_vec);
+ info->image_low_addr = addr;
+ info->image_high_addr = info->image_low_addr + info->kernel_size;
+}
+
void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr)
{
CPUState *cs;
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 05/13] hw/riscv/virt: Move AIA initialisation to helper file
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (3 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 04/13] hw/riscv/boot: Provide a simple halting payload Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-25 13:17 ` [PATCH v4 06/13] hw/riscv/aia: Provide number of irq sources Joel Stanley
` (7 subsequent siblings)
12 siblings, 0 replies; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Michael Ellerman, Nicholas Piggin, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel,
Nutty Liu, Philippe Mathieu-Daudé
The AIA init will be used by any server class riscv machine. Separate it
out in order to share code with such systems.
The virt machine keeps machine specific #defines such as
VIRT_IRQCHIP_NUM_MSIS, VIRT_IRQCHIP_NUM_PRIO_BITS.
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v3:
- Add r-b
- Move fewer things out of virt machine into aia common code, as they
are virt machine specific and not part of aia. Done as part of this
movement patch to avoid moving them out only put them back in the
next patch.
---
hw/riscv/aia.h | 26 +++++++++++
include/hw/riscv/virt.h | 1 -
hw/riscv/aia.c | 89 ++++++++++++++++++++++++++++++++++++++
hw/riscv/virt-acpi-build.c | 2 +
hw/riscv/virt.c | 87 +++++--------------------------------
hw/riscv/meson.build | 2 +-
6 files changed, 129 insertions(+), 78 deletions(-)
create mode 100644 hw/riscv/aia.h
create mode 100644 hw/riscv/aia.c
diff --git a/hw/riscv/aia.h b/hw/riscv/aia.h
new file mode 100644
index 000000000000..dbb833340276
--- /dev/null
+++ b/hw/riscv/aia.h
@@ -0,0 +1,26 @@
+/*
+ * QEMU RISC-V Advanced Interrupt Architecture (AIA)
+ *
+ * Copyright (C) 2019 Western Digital Corporation or its affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_RISCV_AIA_H
+#define HW_RISCV_AIA_H
+
+#include "exec/hwaddr.h"
+
+#define VIRT_IRQCHIP_NUM_SOURCES 96
+
+uint32_t imsic_num_bits(uint32_t count);
+
+DeviceState *riscv_create_aia(bool msimode, int aia_guests,
+ const MemMapEntry *aplic_m,
+ const MemMapEntry *aplic_s,
+ const MemMapEntry *imsic_m,
+ const MemMapEntry *imsic_s,
+ int socket, int base_hartid, int hart_count,
+ uint32_t num_msis, uint32_t num_prio_bits);
+
+#endif
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index 18a2a323a344..ad858deb76ad 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -135,7 +135,6 @@ enum {
bool virt_is_acpi_enabled(RISCVVirtState *s);
bool virt_is_iommu_sys_enabled(RISCVVirtState *s);
void virt_acpi_setup(RISCVVirtState *vms);
-uint32_t imsic_num_bits(uint32_t count);
/*
* The virt machine physical address space used by some of the devices
diff --git a/hw/riscv/aia.c b/hw/riscv/aia.c
new file mode 100644
index 000000000000..c724612a50a5
--- /dev/null
+++ b/hw/riscv/aia.c
@@ -0,0 +1,89 @@
+/*
+ * QEMU RISC-V Advanced Interrupt Architecture (AIA)
+ *
+ * Copyright (C) 2019 Western Digital Corporation or its affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "system/kvm.h"
+#include "hw/intc/riscv_aplic.h"
+#include "hw/intc/riscv_imsic.h"
+
+#include "aia.h"
+
+uint32_t imsic_num_bits(uint32_t count)
+{
+ uint32_t ret = 0;
+
+ while (BIT(ret) < count) {
+ ret++;
+ }
+
+ return ret;
+}
+
+DeviceState *riscv_create_aia(bool msimode, int aia_guests,
+ const MemMapEntry *aplic_m,
+ const MemMapEntry *aplic_s,
+ const MemMapEntry *imsic_m,
+ const MemMapEntry *imsic_s,
+ int socket, int base_hartid, int hart_count,
+ uint32_t num_msis, uint32_t num_prio_bits)
+{
+ int i;
+ hwaddr addr = 0;
+ uint32_t guest_bits;
+ DeviceState *aplic_s_dev = NULL;
+ DeviceState *aplic_m_dev = NULL;
+
+ if (msimode) {
+ if (!kvm_enabled()) {
+ /* Per-socket M-level IMSICs */
+ addr = imsic_m->base + socket * (1U << IMSIC_MMIO_GROUP_MIN_SHIFT);
+ for (i = 0; i < hart_count; i++) {
+ riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0),
+ base_hartid + i, true, 1,
+ num_msis);
+ }
+ }
+
+ /* Per-socket S-level IMSICs */
+ guest_bits = imsic_num_bits(aia_guests + 1);
+ addr = imsic_s->base + socket * (1U << IMSIC_MMIO_GROUP_MIN_SHIFT);
+ for (i = 0; i < hart_count; i++) {
+ riscv_imsic_create(addr + i * IMSIC_HART_SIZE(guest_bits),
+ base_hartid + i, false, 1 + aia_guests,
+ num_msis);
+ }
+ }
+
+ if (!kvm_enabled()) {
+ /* Per-socket M-level APLIC */
+ aplic_m_dev = riscv_aplic_create(aplic_m->base +
+ socket * aplic_m->size,
+ aplic_m->size,
+ (msimode) ? 0 : base_hartid,
+ (msimode) ? 0 : hart_count,
+ VIRT_IRQCHIP_NUM_SOURCES,
+ num_prio_bits,
+ msimode, true, NULL);
+ }
+
+ /* Per-socket S-level APLIC */
+ aplic_s_dev = riscv_aplic_create(aplic_s->base +
+ socket * aplic_s->size,
+ aplic_s->size,
+ (msimode) ? 0 : base_hartid,
+ (msimode) ? 0 : hart_count,
+ VIRT_IRQCHIP_NUM_SOURCES,
+ num_prio_bits,
+ msimode, false, aplic_m_dev);
+
+ if (kvm_enabled() && msimode) {
+ riscv_aplic_set_kvm_msicfgaddr(RISCV_APLIC(aplic_s_dev), addr);
+ }
+
+ return kvm_enabled() ? aplic_s_dev : aplic_m_dev;
+}
diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
index fd6ca5dbc4ff..145f8d92ad69 100644
--- a/hw/riscv/virt-acpi-build.c
+++ b/hw/riscv/virt-acpi-build.c
@@ -42,6 +42,8 @@
#include "system/kvm.h"
#include "system/reset.h"
+#include "aia.h"
+
#define ACPI_BUILD_TABLE_SIZE 0x20000
#define ACPI_BUILD_INTC_ID(socket, index) ((socket << 24) | (index))
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 4501d5581b62..ce0fd6f50c4a 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -59,6 +59,8 @@
#include "hw/virtio/virtio-iommu.h"
#include "hw/uefi/var-service-api.h"
+#include "aia.h"
+
/* KVM AIA only supports APLIC MSI. APLIC Wired is always emulated by QEMU. */
static bool virt_use_kvm_aia_aplic_imsic(RISCVVirtAIAType aia_type)
{
@@ -509,17 +511,6 @@ static void create_fdt_socket_plic(RISCVVirtState *s,
}
}
-uint32_t imsic_num_bits(uint32_t count)
-{
- uint32_t ret = 0;
-
- while (BIT(ret) < count) {
- ret++;
- }
-
- return ret;
-}
-
static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr,
uint32_t *intc_phandles, uint32_t msi_phandle,
bool m_mode, uint32_t imsic_guest_bits)
@@ -1293,68 +1284,6 @@ static DeviceState *virt_create_plic(const MemMapEntry *memmap, int socket,
memmap[VIRT_PLIC].size);
}
-static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests,
- const MemMapEntry *memmap, int socket,
- int base_hartid, int hart_count)
-{
- int i;
- hwaddr addr = 0;
- uint32_t guest_bits;
- DeviceState *aplic_s = NULL;
- DeviceState *aplic_m = NULL;
- bool msimode = aia_type == VIRT_AIA_TYPE_APLIC_IMSIC;
-
- if (msimode) {
- if (!kvm_enabled()) {
- /* Per-socket M-level IMSICs */
- addr = memmap[VIRT_IMSIC_M].base +
- socket * VIRT_IMSIC_GROUP_MAX_SIZE;
- for (i = 0; i < hart_count; i++) {
- riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0),
- base_hartid + i, true, 1,
- VIRT_IRQCHIP_NUM_MSIS);
- }
- }
-
- /* Per-socket S-level IMSICs */
- guest_bits = imsic_num_bits(aia_guests + 1);
- addr = memmap[VIRT_IMSIC_S].base + socket * VIRT_IMSIC_GROUP_MAX_SIZE;
- for (i = 0; i < hart_count; i++) {
- riscv_imsic_create(addr + i * IMSIC_HART_SIZE(guest_bits),
- base_hartid + i, false, 1 + aia_guests,
- VIRT_IRQCHIP_NUM_MSIS);
- }
- }
-
- if (!kvm_enabled()) {
- /* Per-socket M-level APLIC */
- aplic_m = riscv_aplic_create(memmap[VIRT_APLIC_M].base +
- socket * memmap[VIRT_APLIC_M].size,
- memmap[VIRT_APLIC_M].size,
- (msimode) ? 0 : base_hartid,
- (msimode) ? 0 : hart_count,
- VIRT_IRQCHIP_NUM_SOURCES,
- VIRT_IRQCHIP_NUM_PRIO_BITS,
- msimode, true, NULL);
- }
-
- /* Per-socket S-level APLIC */
- aplic_s = riscv_aplic_create(memmap[VIRT_APLIC_S].base +
- socket * memmap[VIRT_APLIC_S].size,
- memmap[VIRT_APLIC_S].size,
- (msimode) ? 0 : base_hartid,
- (msimode) ? 0 : hart_count,
- VIRT_IRQCHIP_NUM_SOURCES,
- VIRT_IRQCHIP_NUM_PRIO_BITS,
- msimode, false, aplic_m);
-
- if (kvm_enabled() && msimode) {
- riscv_aplic_set_kvm_msicfgaddr(RISCV_APLIC(aplic_s), addr);
- }
-
- return kvm_enabled() ? aplic_s : aplic_m;
-}
-
static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip)
{
DeviceState *dev;
@@ -1617,9 +1546,15 @@ static void virt_machine_init(MachineState *machine)
s->irqchip[i] = virt_create_plic(s->memmap, i,
base_hartid, hart_count);
} else {
- s->irqchip[i] = virt_create_aia(s->aia_type, s->aia_guests,
- s->memmap, i, base_hartid,
- hart_count);
+ s->irqchip[i] = riscv_create_aia(s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC,
+ s->aia_guests,
+ &s->memmap[VIRT_APLIC_M],
+ &s->memmap[VIRT_APLIC_S],
+ &s->memmap[VIRT_IMSIC_M],
+ &s->memmap[VIRT_IMSIC_S],
+ i, base_hartid, hart_count,
+ VIRT_IRQCHIP_NUM_MSIS,
+ VIRT_IRQCHIP_NUM_PRIO_BITS);
}
/* Try to use different IRQCHIP instance based device type */
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index 533472e22aef..e53c180d0d10 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -1,5 +1,5 @@
riscv_ss = ss.source_set()
-riscv_ss.add(files('boot.c'))
+riscv_ss.add(files('boot.c', 'aia.c'))
riscv_ss.add(when: 'CONFIG_RISCV_NUMA', if_true: files('numa.c'))
riscv_ss.add(files('riscv_hart.c'))
riscv_ss.add(when: 'CONFIG_OPENTITAN', if_true: files('opentitan.c'))
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 06/13] hw/riscv/aia: Provide number of irq sources
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (4 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 05/13] hw/riscv/virt: Move AIA initialisation to helper file Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-25 13:17 ` [PATCH v4 07/13] target/riscv: tt-ascalon: Enable Zkr extension Joel Stanley
` (6 subsequent siblings)
12 siblings, 0 replies; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Michael Ellerman, Nicholas Piggin, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel,
Nutty Liu, Philippe Mathieu-Daudé
Instead of hard coding the number of IRQ sources used by the APLIC pass
it in as a parameter. This allows other machines to configure this as
required.
The maximum number of sources is 1023.
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v3: Add r-b
---
hw/riscv/aia.h | 3 +--
include/hw/riscv/virt.h | 1 +
hw/riscv/aia.c | 8 ++++++--
hw/riscv/virt-acpi-build.c | 25 ++++++++++++++++---------
hw/riscv/virt.c | 2 ++
5 files changed, 26 insertions(+), 13 deletions(-)
diff --git a/hw/riscv/aia.h b/hw/riscv/aia.h
index dbb833340276..5ad0a902be0d 100644
--- a/hw/riscv/aia.h
+++ b/hw/riscv/aia.h
@@ -11,11 +11,10 @@
#include "exec/hwaddr.h"
-#define VIRT_IRQCHIP_NUM_SOURCES 96
-
uint32_t imsic_num_bits(uint32_t count);
DeviceState *riscv_create_aia(bool msimode, int aia_guests,
+ uint16_t num_sources,
const MemMapEntry *aplic_m,
const MemMapEntry *aplic_s,
const MemMapEntry *imsic_m,
diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h
index ad858deb76ad..36a2def41096 100644
--- a/include/hw/riscv/virt.h
+++ b/include/hw/riscv/virt.h
@@ -64,6 +64,7 @@ struct RISCVVirtState {
struct GPEXHost *gpex_host;
OnOffAuto iommu_sys;
uint16_t pci_iommu_bdf;
+ uint16_t num_sources;
};
enum {
diff --git a/hw/riscv/aia.c b/hw/riscv/aia.c
index c724612a50a5..82ea9d48ea75 100644
--- a/hw/riscv/aia.c
+++ b/hw/riscv/aia.c
@@ -25,6 +25,7 @@ uint32_t imsic_num_bits(uint32_t count)
}
DeviceState *riscv_create_aia(bool msimode, int aia_guests,
+ uint16_t num_sources,
const MemMapEntry *aplic_m,
const MemMapEntry *aplic_s,
const MemMapEntry *imsic_m,
@@ -38,6 +39,9 @@ DeviceState *riscv_create_aia(bool msimode, int aia_guests,
DeviceState *aplic_s_dev = NULL;
DeviceState *aplic_m_dev = NULL;
+ /* The RISC-V Advanced Interrupt Architecture, Chapter 1.2. Limits */
+ g_assert(num_sources <= 1023);
+
if (msimode) {
if (!kvm_enabled()) {
/* Per-socket M-level IMSICs */
@@ -66,7 +70,7 @@ DeviceState *riscv_create_aia(bool msimode, int aia_guests,
aplic_m->size,
(msimode) ? 0 : base_hartid,
(msimode) ? 0 : hart_count,
- VIRT_IRQCHIP_NUM_SOURCES,
+ num_sources,
num_prio_bits,
msimode, true, NULL);
}
@@ -77,7 +81,7 @@ DeviceState *riscv_create_aia(bool msimode, int aia_guests,
aplic_s->size,
(msimode) ? 0 : base_hartid,
(msimode) ? 0 : hart_count,
- VIRT_IRQCHIP_NUM_SOURCES,
+ num_sources,
num_prio_bits,
msimode, false, aplic_m_dev);
diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c
index 145f8d92ad69..9ef3ef842a28 100644
--- a/hw/riscv/virt-acpi-build.c
+++ b/hw/riscv/virt-acpi-build.c
@@ -146,6 +146,7 @@ static void acpi_dsdt_add_cpus(Aml *scope, RISCVVirtState *s)
}
static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count,
+ uint16_t num_sources,
uint64_t mmio_base, uint64_t mmio_size,
const char *hid)
{
@@ -153,9 +154,12 @@ static void acpi_dsdt_add_plic_aplic(Aml *scope, uint8_t socket_count,
uint32_t gsi_base;
uint8_t socket;
+ /* The RISC-V Advanced Interrupt Architecture, Chapter 1.2. Limits */
+ g_assert(num_sources <= 1023);
+
for (socket = 0; socket < socket_count; socket++) {
plic_aplic_addr = mmio_base + mmio_size * socket;
- gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket;
+ gsi_base = num_sources * socket;
Aml *dev = aml_device("IC%.02X", socket);
aml_append(dev, aml_name_decl("_HID", aml_string("%s", hid)));
aml_append(dev, aml_name_decl("_UID", aml_int(socket)));
@@ -474,10 +478,13 @@ static void build_dsdt(GArray *table_data,
socket_count = riscv_socket_count(ms);
if (s->aia_type == VIRT_AIA_TYPE_NONE) {
- acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_PLIC].base,
- memmap[VIRT_PLIC].size, "RSCV0001");
+ acpi_dsdt_add_plic_aplic(scope, socket_count, s->num_sources,
+ memmap[VIRT_PLIC].base,
+ memmap[VIRT_PLIC].size,
+ "RSCV0001");
} else {
- acpi_dsdt_add_plic_aplic(scope, socket_count, memmap[VIRT_APLIC_S].base,
+ acpi_dsdt_add_plic_aplic(scope, socket_count, s->num_sources,
+ memmap[VIRT_APLIC_S].base,
memmap[VIRT_APLIC_S].size, "RSCV0002");
}
@@ -494,15 +501,15 @@ static void build_dsdt(GArray *table_data,
} else if (socket_count == 2) {
virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base,
memmap[VIRT_VIRTIO].size,
- VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0,
+ VIRTIO_IRQ + s->num_sources, 0,
VIRTIO_COUNT);
- acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES);
+ acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + s->num_sources);
} else {
virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base,
memmap[VIRT_VIRTIO].size,
- VIRTIO_IRQ + VIRT_IRQCHIP_NUM_SOURCES, 0,
+ VIRTIO_IRQ + s->num_sources, 0,
VIRTIO_COUNT);
- acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + VIRT_IRQCHIP_NUM_SOURCES * 2);
+ acpi_dsdt_add_gpex_host(scope, PCIE_IRQ + s->num_sources * 2);
}
aml_append(dsdt, scope);
@@ -581,7 +588,7 @@ static void build_madt(GArray *table_data,
for (socket = 0; socket < riscv_socket_count(ms); socket++) {
aplic_addr = s->memmap[VIRT_APLIC_S].base +
s->memmap[VIRT_APLIC_S].size * socket;
- gsi_base = VIRT_IRQCHIP_NUM_SOURCES * socket;
+ gsi_base = s->num_sources * socket;
build_append_int_noprefix(table_data, 0x1A, 1); /* Type */
build_append_int_noprefix(table_data, 36, 1); /* Length */
build_append_int_noprefix(table_data, 1, 1); /* Version */
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index ce0fd6f50c4a..6c5bcd43dc54 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -1548,6 +1548,7 @@ static void virt_machine_init(MachineState *machine)
} else {
s->irqchip[i] = riscv_create_aia(s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC,
s->aia_guests,
+ s->num_sources,
&s->memmap[VIRT_APLIC_M],
&s->memmap[VIRT_APLIC_S],
&s->memmap[VIRT_IMSIC_M],
@@ -1691,6 +1692,7 @@ static void virt_machine_instance_init(Object *obj)
s->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8);
s->acpi = ON_OFF_AUTO_AUTO;
s->iommu_sys = ON_OFF_AUTO_AUTO;
+ s->num_sources = VIRT_IRQCHIP_NUM_SOURCES;
}
static char *virt_get_aia_guests(Object *obj, Error **errp)
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 07/13] target/riscv: tt-ascalon: Enable Zkr extension
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (5 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 06/13] hw/riscv/aia: Provide number of irq sources Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-29 23:36 ` Alistair Francis
2026-04-25 13:17 ` [PATCH v4 08/13] target/riscv: tt-ascalon: Enable Svadu by removing Svade Joel Stanley
` (5 subsequent siblings)
12 siblings, 1 reply; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
From: Nicholas Piggin <npiggin@gmail.com>
Ascalon supports Zkr and the SEED CSR.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
target/riscv/cpu.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index ce15a17c37de..69649462dd89 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -3190,6 +3190,7 @@ static const TypeInfo riscv_cpu_type_infos[] = {
.cfg.ext_zba = true,
.cfg.ext_zbb = true,
.cfg.ext_zbs = true,
+ .cfg.ext_zkr = true,
.cfg.ext_zkt = true,
.cfg.ext_zvbb = true,
.cfg.ext_zvbc = true,
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 08/13] target/riscv: tt-ascalon: Enable Svadu by removing Svade
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (6 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 07/13] target/riscv: tt-ascalon: Enable Zkr extension Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-29 23:41 ` Alistair Francis
2026-04-25 13:17 ` [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine Joel Stanley
` (4 subsequent siblings)
12 siblings, 1 reply; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
From: Nicholas Piggin <npiggin@gmail.com>
Ascalon supports Svadu (hardware A/D bit updates).
QEMU makes Svadu and Svade mutually exclusive, remove Svade so
Ascalon comes up with Svadu working.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
target/riscv/cpu.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 69649462dd89..97e35672649e 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -3204,7 +3204,6 @@ static const TypeInfo riscv_cpu_type_infos[] = {
.cfg.ext_ssaia = true,
.cfg.ext_sscofpmf = true,
.cfg.ext_sstc = true,
- .cfg.ext_svade = true,
.cfg.ext_svinval = true,
.cfg.ext_svnapot = true,
.cfg.ext_svpbmt = true,
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (7 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 08/13] target/riscv: tt-ascalon: Enable Svadu by removing Svade Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-29 23:57 ` Alistair Francis
2026-04-25 13:17 ` [PATCH v4 10/13] hw/riscv/atlantis: Add PCIe controller Joel Stanley
` (3 subsequent siblings)
12 siblings, 1 reply; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Michael Ellerman, Nicholas Piggin, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
and CoreLab Technology. It is based on the Atlantis SoC, which includes
the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant
RISC-V CPU.
This adds the machine containing serial console, interrupt controllers
and device tree support.
qemu-system-riscv64 -M tt-atlantis -m 512M \
-kernel Image -initrd rootfs.cpio -nographic
If there is no kernel provided the simple payload is loaded to avoid
OpenSBI hanging with no messages.
Co-Developed-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v4:
- Clean up unused PCIe defines
v3:
- Merge simple payload change into this patch
- Fix fw_cfg_init_mem_dma API change
- Clean up irqchip #defines
- Add Michael and Nick as reviewers
- create_fdt_pmu was copying RISCVCPU by value
- Remove unused platform_bus
remove unused defines
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
MAINTAINERS | 11 +
docs/system/riscv/tt_atlantis.rst | 38 ++
docs/system/target-riscv.rst | 1 +
include/hw/riscv/tt_atlantis.h | 61 +++
hw/riscv/tt_atlantis.c | 632 ++++++++++++++++++++++++++++++
hw/riscv/Kconfig | 15 +
hw/riscv/meson.build | 1 +
7 files changed, 759 insertions(+)
create mode 100644 docs/system/riscv/tt_atlantis.rst
create mode 100644 include/hw/riscv/tt_atlantis.h
create mode 100644 hw/riscv/tt_atlantis.c
diff --git a/MAINTAINERS b/MAINTAINERS
index e1942a86eba5..635a380945fd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1760,6 +1760,17 @@ F: hw/*/*sifive*.c
F: include/hw/*/*sifive*.h
F: tests/functional/test_riscv64_sifive_u.py
+Tenstorrent Machines
+M: Joel Stanley <joel@jms.id.au>
+R: Nicholas Piggin <npiggin@gmail.com>
+R: Michael Ellerman <mpe@kernel.org>
+L: qemu-riscv@nongnu.org
+S: Supported
+F: docs/system/riscv/tt_*.rst
+F: hw/riscv/tt_*.c
+F: hw/riscv/aia.[ch]
+F: include/hw/riscv/tt_*.h
+
AMD Microblaze-V Generic Board
M: Sai Pavan Boddu <sai.pavan.boddu@amd.com>
S: Maintained
diff --git a/docs/system/riscv/tt_atlantis.rst b/docs/system/riscv/tt_atlantis.rst
new file mode 100644
index 000000000000..640cabf7b046
--- /dev/null
+++ b/docs/system/riscv/tt_atlantis.rst
@@ -0,0 +1,38 @@
+Tenstorrent Atlantis (``tt-atlantis``)
+======================================
+
+The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
+and CoreLab Technology. It is based on the Atlantis SoC, which includes
+the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
+
+The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant
+RISC-V CPU.
+
+Features
+--------
+
+* 8-core Ascalon-X CPU Cluster
+* Dual x32 LPDDR5 @ 6400 MT/s
+* RISC-V compliant Advanced Interrupt Architecture
+* PCIe Gen4
+* RISC-V compliant IOMMU
+* GPU and Video subsystem
+* 2x USB3.1 & 2x USB2.0
+* 2x 1GbE Ethernet
+* 2x eMMC5.1/SDIO3.0 storage
+* Extensive connectivity (SPI, I2C, UART, GPIO, CANFD)
+
+Note: the QEMU tt-atlantis machine does not model the platform
+exactly or all devices, but it is undergoing improvement.
+
+Supported software
+------------------
+
+The Tenstorrent Ascalon CPUs avoid proprietary or non-standard
+extensions, so compatibility with existing software is generally
+good. The QEMU tt-atlantis machine works with upstream OpenSBI
+and Linux with default configurations.
+
+The development board hardware will require some implementation
+specific setup in firmware which is being developed and may
+become a requirement or option for the tt-atlantis machine.
diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst
index 3ad5d1ddafbb..a8e6b3342186 100644
--- a/docs/system/target-riscv.rst
+++ b/docs/system/target-riscv.rst
@@ -71,6 +71,7 @@ undocumented; you can get a complete list by running
riscv/mips
riscv/shakti-c
riscv/sifive_u
+ riscv/tt_atlantis
riscv/virt
riscv/xiangshan-kunminghu
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
new file mode 100644
index 000000000000..9fe50d9c9040
--- /dev/null
+++ b/include/hw/riscv/tt_atlantis.h
@@ -0,0 +1,61 @@
+/*
+ * Tenstorrent Atlantis RISC-V System on Chip
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright 2025 Tenstorrent, Joel Stanley <joel@jms.id.au>
+ */
+
+#ifndef HW_RISCV_TT_ATLANTIS_H
+#define HW_RISCV_TT_ATLANTIS_H
+
+#include "hw/core/boards.h"
+#include "hw/core/sysbus.h"
+#include "hw/intc/riscv_imsic.h"
+#include "hw/riscv/riscv_hart.h"
+
+#define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis")
+OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE)
+
+struct TTAtlantisState {
+ /*< private >*/
+ MachineState parent;
+
+ /*< public >*/
+ Notifier machine_done;
+ FWCfgState *fw_cfg;
+ const MemMapEntry *memmap;
+
+ RISCVHartArrayState soc;
+ DeviceState *irqchip;
+
+ int fdt_size;
+};
+
+enum {
+ TT_ATL_SYSCON_IRQ = 10,
+ TT_ATL_UART0_IRQ = 38,
+ TT_ATL_UART1_IRQ = 39,
+ TT_ATL_UART2_IRQ = 40,
+ TT_ATL_UART3_IRQ = 41,
+ TT_ATL_UART4_IRQ = 42,
+};
+
+enum {
+ TT_ATL_ACLINT,
+ TT_ATL_BOOTROM,
+ TT_ATL_DDR_LO,
+ TT_ATL_DDR_HI,
+ TT_ATL_FW_CFG,
+ TT_ATL_I2C0,
+ TT_ATL_MAPLIC,
+ TT_ATL_MIMSIC,
+ TT_ATL_SAPLIC,
+ TT_ATL_SIMSIC,
+ TT_ATL_SYSCON,
+ TT_ATL_TIMER,
+ TT_ATL_UART0,
+ TT_ATL_WDT0,
+};
+
+#endif
diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
new file mode 100644
index 000000000000..5b66d7e95bb9
--- /dev/null
+++ b/hw/riscv/tt_atlantis.c
@@ -0,0 +1,632 @@
+/*
+ * Tenstorrent Atlantis RISC-V System on Chip
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Copyright 2025 Tenstorrent, Joel Stanley <joel@jms.id.au>
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cutils.h"
+#include "qemu/error-report.h"
+#include "qemu/guest-random.h"
+#include "qemu/units.h"
+
+#include "hw/core/boards.h"
+#include "hw/core/loader.h"
+#include "hw/core/sysbus.h"
+
+#include "target/riscv/cpu.h"
+#include "target/riscv/pmu.h"
+
+#include "hw/riscv/boot.h"
+#include "hw/riscv/numa.h"
+#include "hw/riscv/riscv_hart.h"
+
+#include "hw/char/serial-mm.h"
+#include "hw/intc/riscv_aclint.h"
+#include "hw/intc/riscv_aplic.h"
+#include "hw/misc/pvpanic.h"
+
+#include "system/system.h"
+#include "system/device_tree.h"
+
+#include "hw/riscv/tt_atlantis.h"
+
+#include "aia.h"
+
+#define TT_IRQCHIP_NUM_MSIS 255
+#define TT_IRQCHIP_NUM_SOURCES 128
+#define TT_IRQCHIP_NUM_PRIO_BITS 3
+#define TT_IRQCHIP_GUESTS 7 /* aia_guests */
+
+#define FDT_PCI_ADDR_CELLS 3
+#define FDT_PCI_INT_CELLS 1
+#define FDT_MAX_INT_CELLS 2
+#define FDT_MAX_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \
+ 1 + FDT_MAX_INT_CELLS)
+
+#define TT_ACLINT_MTIME_SIZE 0x8050
+#define TT_ACLINT_MTIME 0x0
+#define TT_ACLINT_MTIMECMP 0x8000
+#define TT_ACLINT_TIMEBASE_FREQ 1000000000
+
+static const MemMapEntry tt_atlantis_memmap[] = {
+ /* Keep sorted with :'<,'>!sort -g -k 4 */
+ [TT_ATL_DDR_LO] = { 0x00000000, 0x80000000 },
+ [TT_ATL_BOOTROM] = { 0x80000000, 0x2000 },
+ [TT_ATL_FW_CFG] = { 0x80002000, 0xff }, /* qemu only */
+ [TT_ATL_SYSCON] = { 0x80004000, 0x1000 }, /* qemu only */
+ [TT_ATL_MIMSIC] = { 0xa0000000, 0x200000 },
+ [TT_ATL_ACLINT] = { 0xa2180000, 0x10000 },
+ [TT_ATL_SIMSIC] = { 0xa4000000, 0x200000 },
+ [TT_ATL_TIMER] = { 0xa8020000, 0x10000 },
+ [TT_ATL_WDT0] = { 0xa8030000, 0x10000 },
+ [TT_ATL_UART0] = { 0xb0100000, 0x10000 },
+ [TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 },
+ [TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 },
+ [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 },
+};
+
+static uint32_t next_phandle(void)
+{
+ static uint32_t phandle = 1;
+ return phandle++;
+}
+
+static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
+{
+ uint32_t cpu_phandle;
+ void *fdt = MACHINE(s)->fdt;
+
+ for (int cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
+ RISCVCPU *cpu_ptr = &s->soc.harts[cpu];
+ g_autofree char *cpu_name = NULL;
+ g_autofree char *intc_name = NULL;
+
+ cpu_phandle = next_phandle();
+
+ cpu_name = g_strdup_printf("/cpus/cpu@%d", s->soc.hartid_base + cpu);
+ qemu_fdt_add_subnode(fdt, cpu_name);
+
+ qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv57");
+
+ riscv_isa_write_fdt(cpu_ptr, fdt, cpu_name);
+
+ qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbom-block-size",
+ cpu_ptr->cfg.cbom_blocksize);
+
+ qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cboz-block-size",
+ cpu_ptr->cfg.cboz_blocksize);
+
+ qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbop-block-size",
+ cpu_ptr->cfg.cbop_blocksize);
+
+ qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
+ qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
+ qemu_fdt_setprop_cell(fdt, cpu_name, "reg", s->soc.hartid_base + cpu);
+ qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
+ qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle);
+
+ intc_phandles[cpu] = next_phandle();
+
+ intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
+ qemu_fdt_add_subnode(fdt, intc_name);
+ qemu_fdt_setprop_cell(fdt, intc_name, "phandle",
+ intc_phandles[cpu]);
+ qemu_fdt_setprop_string(fdt, intc_name, "compatible",
+ "riscv,cpu-intc");
+ qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
+ }
+}
+
+static void create_fdt_memory_node(TTAtlantisState *s,
+ hwaddr addr, hwaddr size)
+{
+ void *fdt = MACHINE(s)->fdt;
+ g_autofree char *name = g_strdup_printf("/memory@%"HWADDR_PRIX, addr);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, addr, 2, size);
+ qemu_fdt_setprop_string(fdt, name, "device_type", "memory");
+}
+
+static void create_fdt_memory(TTAtlantisState *s)
+{
+ hwaddr size_lo = MACHINE(s)->ram_size;
+ hwaddr size_hi = 0;
+
+ if (size_lo > s->memmap[TT_ATL_DDR_LO].size) {
+ size_lo = s->memmap[TT_ATL_DDR_LO].size;
+ size_hi = MACHINE(s)->ram_size - size_lo;
+ }
+
+ create_fdt_memory_node(s, s->memmap[TT_ATL_DDR_LO].base, size_lo);
+ if (size_hi) {
+ /*
+ * The first part of the HI address is aliased at the LO address
+ * so do not include that as usable memory. Is there any way
+ * (or good reason) to describe that aliasing 2GB with DT?
+ */
+ create_fdt_memory_node(s, s->memmap[TT_ATL_DDR_HI].base + size_lo,
+ size_hi);
+ }
+}
+
+static void create_fdt_aclint(TTAtlantisState *s, uint32_t *intc_phandles)
+{
+ void *fdt = MACHINE(s)->fdt;
+ g_autofree char *name = NULL;
+ g_autofree uint32_t *aclint_mtimer_cells = NULL;
+ uint32_t aclint_cells_size;
+ hwaddr addr;
+
+ aclint_mtimer_cells = g_new0(uint32_t, s->soc.num_harts * 2);
+
+ for (int cpu = 0; cpu < s->soc.num_harts; cpu++) {
+ aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
+ }
+ aclint_cells_size = s->soc.num_harts * sizeof(uint32_t) * 2;
+
+ addr = s->memmap[TT_ATL_ACLINT].base;
+
+ name = g_strdup_printf("/soc/mtimer@%"HWADDR_PRIX, addr);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aclint-mtimer");
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg",
+ 2, addr + TT_ACLINT_MTIME,
+ 2, 0x1000,
+ 2, addr + TT_ACLINT_MTIMECMP,
+ 2, 0x1000);
+ qemu_fdt_setprop(fdt, name, "interrupts-extended",
+ aclint_mtimer_cells, aclint_cells_size);
+}
+
+static void create_fdt_one_imsic(void *fdt, const MemMapEntry *mem, int cpus,
+ uint32_t *intc_phandles, uint32_t msi_phandle,
+ int irq_line, uint32_t imsic_guest_bits)
+{
+ g_autofree char *name = NULL;
+ g_autofree uint32_t *imsic_cells = g_new0(uint32_t, cpus * 2);
+
+ for (int cpu = 0; cpu < cpus; cpu++) {
+ imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ imsic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line);
+ }
+
+ name = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,imsics");
+
+ qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 0);
+ qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop(fdt, name, "msi-controller", NULL, 0);
+ qemu_fdt_setprop(fdt, name, "interrupts-extended",
+ imsic_cells, sizeof(uint32_t) * cpus * 2);
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop_cell(fdt, name, "riscv,num-ids", TT_IRQCHIP_NUM_MSIS);
+
+ if (imsic_guest_bits) {
+ qemu_fdt_setprop_cell(fdt, name, "riscv,guest-index-bits",
+ imsic_guest_bits);
+ }
+ qemu_fdt_setprop_cell(fdt, name, "phandle", msi_phandle);
+}
+
+static void create_fdt_one_aplic(void *fdt,
+ const MemMapEntry *mem,
+ uint32_t msi_phandle,
+ uint32_t *intc_phandles,
+ uint32_t aplic_phandle,
+ uint32_t aplic_child_phandle,
+ int irq_line, int num_harts)
+{
+ g_autofree char *name =
+ g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base);
+ g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2);
+
+ for (int cpu = 0; cpu < num_harts; cpu++) {
+ aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
+ aplic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line);
+ }
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aplic");
+ qemu_fdt_setprop_cell(fdt, name, "#address-cells", 0);
+ qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 2);
+ qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0);
+
+ qemu_fdt_setprop(fdt, name, "interrupts-extended",
+ aplic_cells, num_harts * sizeof(uint32_t) * 2);
+ qemu_fdt_setprop_cell(fdt, name, "msi-parent", msi_phandle);
+
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop_cell(fdt, name, "riscv,num-sources",
+ TT_IRQCHIP_NUM_SOURCES);
+
+ if (aplic_child_phandle) {
+ qemu_fdt_setprop_cell(fdt, name, "riscv,children",
+ aplic_child_phandle);
+ qemu_fdt_setprop_cells(fdt, name, "riscv,delegation",
+ aplic_child_phandle, 1, TT_IRQCHIP_NUM_SOURCES);
+ }
+
+ qemu_fdt_setprop_cell(fdt, name, "phandle", aplic_phandle);
+}
+
+static void create_fdt_pmu(TTAtlantisState *s)
+{
+ g_autofree char *pmu_name = g_strdup_printf("/pmu");
+ void *fdt = MACHINE(s)->fdt;
+ RISCVCPU *hart = &s->soc.harts[0];
+
+ qemu_fdt_add_subnode(fdt, pmu_name);
+ qemu_fdt_setprop_string(fdt, pmu_name, "compatible", "riscv,pmu");
+ riscv_pmu_generate_fdt_node(fdt, hart->pmu_avail_ctrs, pmu_name);
+}
+
+static void create_fdt_cpu(TTAtlantisState *s, const MemMapEntry *memmap,
+ uint32_t aplic_s_phandle,
+ uint32_t imsic_s_phandle)
+{
+ MachineState *ms = MACHINE(s);
+ void *fdt = MACHINE(s)->fdt;
+ g_autofree uint32_t *intc_phandles = NULL;
+
+ qemu_fdt_add_subnode(fdt, "/cpus");
+ qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
+ TT_ACLINT_TIMEBASE_FREQ);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
+ qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
+
+ intc_phandles = g_new0(uint32_t, ms->smp.cpus);
+
+ create_fdt_cpus(s, intc_phandles);
+
+ create_fdt_memory(s);
+
+ create_fdt_aclint(s, intc_phandles);
+
+ /* M-level IMSIC node */
+ uint32_t msi_m_phandle = next_phandle();
+ create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_MIMSIC], ms->smp.cpus,
+ intc_phandles, msi_m_phandle,
+ IRQ_M_EXT, 0);
+
+ /* S-level IMSIC node */
+ create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_SIMSIC], ms->smp.cpus,
+ intc_phandles, imsic_s_phandle,
+ IRQ_S_EXT, imsic_num_bits(TT_IRQCHIP_GUESTS + 1));
+
+ uint32_t aplic_m_phandle = next_phandle();
+
+ /* M-level APLIC node */
+ create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_MAPLIC],
+ msi_m_phandle, intc_phandles,
+ aplic_m_phandle, aplic_s_phandle,
+ IRQ_M_EXT, s->soc.num_harts);
+
+ /* S-level APLIC node */
+ create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_SAPLIC],
+ imsic_s_phandle, intc_phandles,
+ aplic_s_phandle, 0,
+ IRQ_S_EXT, s->soc.num_harts);
+}
+
+static void create_fdt_reset(void *fdt, const MemMapEntry *mem)
+{
+ uint32_t syscon_phandle = next_phandle();
+ char *name;
+
+ name = g_strdup_printf("/soc/syscon@%"HWADDR_PRIX, mem->base);
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "syscon");
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop_cell(fdt, name, "phandle", syscon_phandle);
+ g_free(name);
+
+ name = g_strdup_printf("/poweroff");
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-poweroff");
+ qemu_fdt_setprop_cell(fdt, name, "regmap", syscon_phandle);
+ qemu_fdt_setprop_cell(fdt, name, "offset", 0x0);
+ qemu_fdt_setprop_cell(fdt, name, "value", PVPANIC_SHUTDOWN);
+ g_free(name);
+}
+
+static void create_fdt_uart(void *fdt, const MemMapEntry *mem, int irq,
+ int irqchip_phandle)
+{
+ g_autofree char *name = g_strdup_printf("/soc/serial@%"HWADDR_PRIX,
+ mem->base);
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a");
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop_cell(fdt, name, "reg-shift", 2);
+ qemu_fdt_setprop_cell(fdt, name, "reg-io-width", 4);
+ qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 3686400);
+ qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", irqchip_phandle);
+ qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x4);
+
+ qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", name);
+ qemu_fdt_setprop_string(fdt, "/aliases", "serial0", name);
+}
+
+static void create_fdt_fw_cfg(void *fdt, const MemMapEntry *mem)
+{
+ g_autofree char *name = g_strdup_printf("/fw-cfg@%"HWADDR_PRIX, mem->base);
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "qemu,fw-cfg-mmio");
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0);
+}
+
+static void finalize_fdt(TTAtlantisState *s)
+{
+ uint32_t aplic_s_phandle = next_phandle();
+ uint32_t imsic_s_phandle = next_phandle();
+ void *fdt = MACHINE(s)->fdt;
+
+ create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
+
+ /*
+ * We want to do this, but the Linux aplic driver was broken before v6.16
+ *
+ * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
+ * aplic_s_phandle);
+ */
+
+ create_fdt_reset(fdt, &s->memmap[TT_ATL_SYSCON]);
+
+ create_fdt_uart(fdt, &s->memmap[TT_ATL_UART0], TT_ATL_UART0_IRQ,
+ aplic_s_phandle);
+}
+
+static void create_fdt(TTAtlantisState *s)
+{
+ MachineState *ms = MACHINE(s);
+ uint8_t rng_seed[32];
+ g_autofree char *name = NULL;
+ void *fdt;
+
+ fdt = create_device_tree(&s->fdt_size);
+ if (!fdt) {
+ error_report("create_device_tree() failed");
+ exit(1);
+ }
+ ms->fdt = fdt;
+
+ qemu_fdt_setprop_string(fdt, "/", "model",
+ "Tenstorrent Atlantis RISC-V Machine");
+ qemu_fdt_setprop_string(fdt, "/", "compatible", "tenstorrent,atlantis");
+ qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
+
+ qemu_fdt_add_subnode(fdt, "/soc");
+ qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
+ qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
+ qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
+ qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+
+ qemu_fdt_add_subnode(fdt, "/chosen");
+
+ /* Pass seed to RNG */
+ qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
+ qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
+
+ qemu_fdt_add_subnode(fdt, "/aliases");
+
+ create_fdt_fw_cfg(fdt, &s->memmap[TT_ATL_FW_CFG]);
+ create_fdt_pmu(s);
+}
+
+static DeviceState *create_reboot_device(const MemMapEntry *mem)
+{
+ DeviceState *dev = qdev_new(TYPE_PVPANIC_MMIO_DEVICE);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ qdev_prop_set_uint32(dev, "events", PVPANIC_SHUTDOWN | PVPANIC_PANICKED);
+
+ sysbus_realize_and_unref(sbd, &error_fatal);
+ sysbus_mmio_map(sbd, 0, mem->base);
+
+ return dev;
+}
+
+static FWCfgState *create_fw_cfg(const MemMapEntry *mem, int num_cpus)
+{
+ FWCfgState *fw_cfg;
+ hwaddr base = mem->base;
+
+ fw_cfg = fw_cfg_init_mem_dma(base + 8, base, 8, base + 16,
+ &address_space_memory);
+ fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, num_cpus);
+
+ return fw_cfg;
+}
+
+static void tt_atlantis_machine_done(Notifier *notifier, void *data)
+{
+ TTAtlantisState *s = container_of(notifier, TTAtlantisState, machine_done);
+ MachineState *machine = MACHINE(s);
+ hwaddr start_addr = s->memmap[TT_ATL_DDR_LO].base;
+ hwaddr mem_size;
+ target_ulong firmware_end_addr, kernel_start_addr;
+ const char *firmware_name = riscv_default_firmware_name(&s->soc);
+ uint64_t fdt_load_addr;
+ uint64_t kernel_entry;
+ RISCVBootInfo boot_info;
+
+ /*
+ * A user provided dtb must include everything, including
+ * dynamic sysbus devices. Our FDT needs to be finalized.
+ */
+ if (machine->dtb == NULL) {
+ finalize_fdt(s);
+ }
+
+ mem_size = machine->ram_size;
+ if (mem_size > s->memmap[TT_ATL_DDR_LO].size) {
+ mem_size = s->memmap[TT_ATL_DDR_LO].size;
+ }
+ riscv_boot_info_init_discontig_mem(&boot_info, &s->soc,
+ s->memmap[TT_ATL_DDR_LO].base,
+ mem_size);
+
+ firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
+ firmware_name,
+ &start_addr, NULL);
+
+ kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
+ firmware_end_addr);
+ if (machine->kernel_filename) {
+ riscv_load_kernel(machine, &boot_info, kernel_start_addr,
+ true, NULL);
+ } else {
+ riscv_setup_halting_payload(&boot_info, kernel_start_addr);
+ }
+ kernel_entry = boot_info.image_low_addr;
+
+ fdt_load_addr = riscv_compute_fdt_addr(s->memmap[TT_ATL_DDR_LO].base,
+ s->memmap[TT_ATL_DDR_LO].size,
+ machine, &boot_info);
+ riscv_load_fdt(fdt_load_addr, machine->fdt);
+
+ /* load the reset vector */
+ riscv_setup_rom_reset_vec(machine, &s->soc, start_addr,
+ s->memmap[TT_ATL_BOOTROM].base,
+ s->memmap[TT_ATL_BOOTROM].size,
+ kernel_entry,
+ fdt_load_addr);
+
+}
+
+static void tt_atlantis_machine_init(MachineState *machine)
+{
+ TTAtlantisState *s = TT_ATLANTIS_MACHINE(machine);
+
+ MemoryRegion *system_memory = get_system_memory();
+ MemoryRegion *ram_hi = g_new(MemoryRegion, 1);
+ MemoryRegion *ram_lo = g_new(MemoryRegion, 1);
+ MemoryRegion *bootrom = g_new(MemoryRegion, 1);
+ ram_addr_t lo_ram_size, hi_ram_size;
+ int hart_count = machine->smp.cpus;
+ int base_hartid = 0;
+
+ s->memmap = tt_atlantis_memmap;
+
+ object_initialize_child(OBJECT(machine), "soc", &s->soc,
+ TYPE_RISCV_HART_ARRAY);
+ object_property_set_str(OBJECT(&s->soc), "cpu-type", machine->cpu_type,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), "hartid-base", base_hartid,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), "num-harts", hart_count,
+ &error_abort);
+ object_property_set_int(OBJECT(&s->soc), "resetvec",
+ s->memmap[TT_ATL_BOOTROM].base,
+ &error_abort);
+ sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal);
+
+ s->irqchip = riscv_create_aia(true, TT_IRQCHIP_GUESTS,
+ TT_IRQCHIP_NUM_SOURCES,
+ &s->memmap[TT_ATL_MAPLIC],
+ &s->memmap[TT_ATL_SAPLIC],
+ &s->memmap[TT_ATL_MIMSIC],
+ &s->memmap[TT_ATL_SIMSIC],
+ 0, base_hartid, hart_count,
+ TT_IRQCHIP_NUM_MSIS,
+ TT_IRQCHIP_NUM_PRIO_BITS);
+
+ riscv_aclint_mtimer_create(s->memmap[TT_ATL_ACLINT].base,
+ TT_ACLINT_MTIME_SIZE,
+ base_hartid, hart_count,
+ TT_ACLINT_MTIMECMP,
+ TT_ACLINT_MTIME,
+ TT_ACLINT_TIMEBASE_FREQ, true);
+
+ /* DDR */
+
+ /* The high address covers all of RAM, the low address just the first 2GB */
+ lo_ram_size = s->memmap[TT_ATL_DDR_LO].size;
+ hi_ram_size = s->memmap[TT_ATL_DDR_HI].size;
+ if (machine->ram_size > hi_ram_size) {
+ char *sz = size_to_str(hi_ram_size);
+ error_report("RAM size is too large, maximum is %s", sz);
+ g_free(sz);
+ exit(EXIT_FAILURE);
+ }
+
+ memory_region_init_alias(ram_lo, OBJECT(machine), "ram.low", machine->ram,
+ 0, lo_ram_size);
+ memory_region_init_alias(ram_hi, OBJECT(machine), "ram.high", machine->ram,
+ 0, hi_ram_size);
+ memory_region_add_subregion(system_memory,
+ s->memmap[TT_ATL_DDR_LO].base, ram_lo);
+ memory_region_add_subregion(system_memory,
+ s->memmap[TT_ATL_DDR_HI].base, ram_hi);
+
+ /* Boot ROM */
+ memory_region_init_rom(bootrom, NULL, "tt-atlantis.bootrom",
+ s->memmap[TT_ATL_BOOTROM].size, &error_fatal);
+ memory_region_add_subregion(system_memory, s->memmap[TT_ATL_BOOTROM].base,
+ bootrom);
+
+ /*
+ * Init fw_cfg. Must be done before riscv_load_fdt, otherwise the
+ * device tree cannot be altered and we get FDT_ERR_NOSPACE.
+ */
+ s->fw_cfg = create_fw_cfg(&s->memmap[TT_ATL_FW_CFG], machine->smp.cpus);
+ rom_set_fw(s->fw_cfg);
+
+ /* Reboot and exit */
+ create_reboot_device(&s->memmap[TT_ATL_SYSCON]);
+
+ /* UART */
+ serial_mm_init(system_memory, s->memmap[TT_ATL_UART0].base, 2,
+ qdev_get_gpio_in(s->irqchip, TT_ATL_UART0_IRQ),
+ 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN);
+
+ /* Load or create device tree */
+ if (machine->dtb) {
+ machine->fdt = load_device_tree(machine->dtb, &s->fdt_size);
+ if (!machine->fdt) {
+ error_report("load_device_tree() failed");
+ exit(1);
+ }
+ } else {
+ create_fdt(s);
+ }
+
+ s->machine_done.notify = tt_atlantis_machine_done;
+ qemu_add_machine_init_done_notifier(&s->machine_done);
+}
+
+static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "Tenstorrent Atlantis RISC-V SoC";
+ mc->init = tt_atlantis_machine_init;
+ mc->max_cpus = 8;
+ mc->default_cpus = 8;
+ mc->default_ram_size = 2 * GiB;
+ mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON;
+ mc->block_default_type = IF_VIRTIO;
+ mc->no_cdrom = 1;
+ mc->default_ram_id = "tt_atlantis.ram";
+}
+
+static const TypeInfo tt_atlantis_types[] = {
+ {
+ .name = MACHINE_TYPE_NAME("tt-atlantis"),
+ .parent = TYPE_MACHINE,
+ .class_init = tt_atlantis_machine_class_init,
+ .instance_size = sizeof(TTAtlantisState),
+ },
+};
+
+DEFINE_TYPES(tt_atlantis_types)
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 0222c93f878b..0601ae1a7494 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -120,6 +120,21 @@ config SPIKE
select RISCV_ACLINT
select SIFIVE_PLIC
+config TENSTORRENT
+ bool
+ default y
+ depends on RISCV64
+ imply PCI_DEVICES
+ imply TEST_DEVICES
+ select DEVICE_TREE
+ select RISCV_NUMA
+ select PVPANIC_MMIO
+ select SERIAL_MM
+ select RISCV_ACLINT
+ select RISCV_APLIC
+ select RISCV_IMSIC
+ select FW_CFG_DMA
+
config XIANGSHAN_KUNMINGHU
bool
default y
diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
index e53c180d0d10..026e79591f4b 100644
--- a/hw/riscv/meson.build
+++ b/hw/riscv/meson.build
@@ -9,6 +9,7 @@ riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c'))
riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c'))
riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c'))
+riscv_ss.add(when: 'CONFIG_TENSTORRENT', if_true: files('tt_atlantis.c'))
riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c'))
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 10/13] hw/riscv/atlantis: Add PCIe controller
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (8 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-30 0:04 ` Alistair Francis
2026-04-25 13:17 ` [PATCH v4 11/13] tests/functional/riscv64: Add tt-atlantis tests Joel Stanley
` (2 subsequent siblings)
12 siblings, 1 reply; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
From: Nicholas Piggin <npiggin@gmail.com>
Atlantis has 3 DesignWare PCIe host controllers, however the boot firmware
has not been tested with the QEMU DesignWare model so a generic ECAM
controller is used for the interim. The system bus aperture and PCIe IO
mappings match the hardware configuration. Only a single PCIe controller is
implemented, because the gpex model does not support multiple controllers.
Booting Linux v6.19:
pci-host-generic 1110000000.pci: host bridge /soc/pci@1110000000 ranges:
pci-host-generic 1110000000.pci: IO 0x30011000000..0x300110fffff -> 0x0000000000
pci-host-generic 1110000000.pci: MEM 0x30080000000..0x300ffffffff -> 0x0080000000
pci-host-generic 1110000000.pci: MEM 0x30100000000..0x3ff7fffffff -> 0x30100000000
pci-host-generic 1110000000.pci: ECAM at [mem 0x1110000000-0x111fffffff] for [bus 00-ff]
pci-host-generic 1110000000.pci: PCI host bridge to bus 0000:00
pci_bus 0000:00: root bus resource [bus 00-ff]
pci_bus 0000:00: root bus resource [io 0x0000-0xfffff]
pci_bus 0000:00: root bus resource [mem 0x30080000000-0x300ffffffff] (bus address [0x80000000-0xffffffff])
pci_bus 0000:00: root bus resource [mem 0x30100000000-0x3ff7fffffff pref]
pci 0000:00:00.0: [1b36:0008] type 00 class 0x060000 conventional PCI endpoint
pci 0000:00:01.0: [1af4:1001] type 00 class 0x010000 conventional PCI endpoint
pci 0000:00:01.0: BAR 0 [io 0x0000-0x007f]
pci 0000:00:01.0: BAR 1 [mem 0x00000000-0x00000fff]
pci 0000:00:01.0: BAR 4 [mem 0x00000000-0x00003fff 64bit pref]
pci 0000:00:01.0: BAR 4 [mem 0x30100000000-0x30100003fff 64bit pref]: assigned
pci 0000:00:01.0: BAR 1 [mem 0x30080000000-0x30080000fff]: assigned
pci 0000:00:01.0: BAR 0 [io 0x0080-0x00ff]: assigned
pci_bus 0000:00: resource 4 [io 0x0000-0xfffff]
pci_bus 0000:00: resource 5 [mem 0x30080000000-0x300ffffffff]
pci_bus 0000:00: resource 6 [mem 0x30100000000-0x3ff7fffffff pref]
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v4:
- Remove select PCI
- Rework addressing, use a table for describing the map
- Remove traces of controller 2 and 3; they will be implemented in
future patches
v3: Avoid leaks in the dt string allocation
---
include/hw/riscv/tt_atlantis.h | 12 ++
hw/riscv/tt_atlantis.c | 234 ++++++++++++++++++++++++++++++++-
hw/riscv/Kconfig | 1 +
3 files changed, 246 insertions(+), 1 deletion(-)
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
index 9fe50d9c9040..070f53eeb450 100644
--- a/include/hw/riscv/tt_atlantis.h
+++ b/include/hw/riscv/tt_atlantis.h
@@ -17,6 +17,13 @@
#define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis")
OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE)
+typedef struct {
+ hwaddr pcie_addr; /* Device side address */
+ hwaddr window_offset; /* Host side offset */
+ hwaddr size;
+ uint32_t flags;
+} PciMapEntry;
+
struct TTAtlantisState {
/*< private >*/
MachineState parent;
@@ -25,9 +32,11 @@ struct TTAtlantisState {
Notifier machine_done;
FWCfgState *fw_cfg;
const MemMapEntry *memmap;
+ const PciMapEntry *pcimap;
RISCVHartArrayState soc;
DeviceState *irqchip;
+ GPEXHost gpex_host;
int fdt_size;
};
@@ -39,6 +48,7 @@ enum {
TT_ATL_UART2_IRQ = 40,
TT_ATL_UART3_IRQ = 41,
TT_ATL_UART4_IRQ = 42,
+ TT_ATL_PCIE0_INTA_IRQ = 96,
};
enum {
@@ -50,6 +60,8 @@ enum {
TT_ATL_I2C0,
TT_ATL_MAPLIC,
TT_ATL_MIMSIC,
+ TT_ATL_PCIE_ECAM0,
+ TT_ATL_PCIE_MMIO0,
TT_ATL_SAPLIC,
TT_ATL_SIMSIC,
TT_ATL_SYSCON,
diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index 5b66d7e95bb9..1eb473907ad7 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -27,6 +27,7 @@
#include "hw/intc/riscv_aclint.h"
#include "hw/intc/riscv_aplic.h"
#include "hw/misc/pvpanic.h"
+#include "hw/pci-host/gpex.h"
#include "system/system.h"
#include "system/device_tree.h"
@@ -51,6 +52,13 @@
#define TT_ACLINT_MTIMECMP 0x8000
#define TT_ACLINT_TIMEBASE_FREQ 1000000000
+
+enum {
+ PCIE0_PIO,
+ PCIE0_MMIO_32,
+ PCIE0_MMIO_64,
+};
+
static const MemMapEntry tt_atlantis_memmap[] = {
/* Keep sorted with :'<,'>!sort -g -k 4 */
[TT_ATL_DDR_LO] = { 0x00000000, 0x80000000 },
@@ -66,6 +74,19 @@ static const MemMapEntry tt_atlantis_memmap[] = {
[TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 },
[TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 },
[TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 },
+ [TT_ATL_PCIE_ECAM0] = { 0x01110000000, 0x10000000 },
+ [TT_ATL_PCIE_MMIO0] = { 0x30000000000, 0x10000000000 },
+};
+
+static const PciMapEntry tt_atlantis_pci_map[] = {
+ /* pcie_addr window_offset size flags */
+ { 0x0, 0x11000000, 1 * MiB, FDT_PCI_RANGE_IOPORT |
+ FDT_PCI_RANGE_RELOCATABLE },
+ { 0x80000000, 0x80000000, 2 * GiB, FDT_PCI_RANGE_MMIO |
+ FDT_PCI_RANGE_RELOCATABLE },
+ { 0x30100000000, 0x100000000, 1018 * GiB, FDT_PCI_RANGE_MMIO_64BIT |
+ FDT_PCI_RANGE_RELOCATABLE |
+ FDT_PCI_RANGE_PREFETCHABLE },
};
static uint32_t next_phandle(void)
@@ -74,6 +95,59 @@ static uint32_t next_phandle(void)
return phandle++;
}
+static void create_pcie_irq_map(void *fdt, char *nodename, int legacy_irq,
+ uint32_t irqchip_phandle)
+{
+ int pin, dev;
+ uint32_t irq_map_stride = 0;
+ uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS *
+ FDT_MAX_INT_MAP_WIDTH] = {};
+ uint32_t *irq_map = full_irq_map;
+
+ /*
+ * This code creates a standard swizzle of interrupts such that
+ * each device's first interrupt is based on it's PCI_SLOT number.
+ * (See pci_swizzle_map_irq_fn())
+ *
+ * We only need one entry per interrupt in the table (not one per
+ * possible slot) seeing the interrupt-map-mask will allow the table
+ * to wrap to any number of devices.
+ */
+ for (dev = 0; dev < PCI_NUM_PINS; dev++) {
+ int devfn = dev * 0x8;
+
+ for (pin = 0; pin < PCI_NUM_PINS; pin++) {
+ int irq_nr = legacy_irq + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
+ int i = 0;
+
+ /* Fill PCI address cells */
+ irq_map[i] = cpu_to_be32(devfn << 8);
+ i += FDT_PCI_ADDR_CELLS;
+
+ /* Fill PCI Interrupt cells */
+ irq_map[i] = cpu_to_be32(pin + 1);
+ i += FDT_PCI_INT_CELLS;
+
+ /* Fill interrupt controller phandle and cells */
+ irq_map[i++] = cpu_to_be32(irqchip_phandle);
+ irq_map[i++] = cpu_to_be32(irq_nr);
+ irq_map[i++] = cpu_to_be32(0x4);
+
+ if (!irq_map_stride) {
+ irq_map_stride = i;
+ }
+ irq_map += irq_map_stride;
+ }
+ }
+
+ qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map,
+ PCI_NUM_PINS * PCI_NUM_PINS *
+ irq_map_stride * sizeof(uint32_t));
+
+ qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask",
+ 0x1800, 0, 0, 0x7);
+}
+
static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
{
uint32_t cpu_phandle;
@@ -314,6 +388,52 @@ static void create_fdt_cpu(TTAtlantisState *s, const MemMapEntry *memmap,
IRQ_S_EXT, s->soc.num_harts);
}
+static void create_fdt_pcie(void *fdt,
+ const MemMapEntry *ecam_mem,
+ const MemMapEntry *pcie_mem,
+ const PciMapEntry *pio,
+ const PciMapEntry *mmio32,
+ const PciMapEntry *mmio64,
+ int legacy_irq,
+ uint32_t aplic_s_phandle,
+ uint32_t imsic_s_phandle)
+{
+ g_autofree char *name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX,
+ ecam_mem->base);
+
+ qemu_fdt_setprop_cell(fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS);
+ qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", FDT_PCI_INT_CELLS);
+ qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0x2);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "pci-host-ecam-generic");
+ qemu_fdt_setprop_string(fdt, name, "device_type", "pci");
+ qemu_fdt_setprop_cells(fdt, name, "bus-range", 0,
+ ecam_mem->size / PCIE_MMCFG_SIZE_MIN - 1);
+ qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0);
+ qemu_fdt_setprop_cell(fdt, name, "msi-parent", imsic_s_phandle);
+
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg",
+ 2, ecam_mem->base,
+ 2, ecam_mem->size);
+
+ qemu_fdt_setprop_sized_cells(fdt, name, "ranges",
+ 1, pio->flags,
+ 2, pio->pcie_addr,
+ 2, pio->window_offset + pcie_mem->base,
+ 2, pio->size,
+
+ 1, mmio32->flags,
+ 2, mmio32->pcie_addr,
+ 2, mmio32->window_offset + pcie_mem->base,
+ 2, mmio32->size,
+
+ 1, mmio64->flags,
+ 2, mmio64->pcie_addr,
+ 2, mmio64->window_offset + pcie_mem->base,
+ 2, mmio64->size);
+
+ create_pcie_irq_map(fdt, name, legacy_irq, aplic_s_phandle);
+}
+
static void create_fdt_reset(void *fdt, const MemMapEntry *mem)
{
uint32_t syscon_phandle = next_phandle();
@@ -379,6 +499,15 @@ static void finalize_fdt(TTAtlantisState *s)
* aplic_s_phandle);
*/
+ create_fdt_pcie(fdt,
+ &s->memmap[TT_ATL_PCIE_ECAM0],
+ &s->memmap[TT_ATL_PCIE_MMIO0],
+ &s->pcimap[PCIE0_PIO],
+ &s->pcimap[PCIE0_MMIO_32],
+ &s->pcimap[PCIE0_MMIO_64],
+ TT_ATL_PCIE0_INTA_IRQ,
+ aplic_s_phandle, imsic_s_phandle);
+
create_fdt_reset(fdt, &s->memmap[TT_ATL_SYSCON]);
create_fdt_uart(fdt, &s->memmap[TT_ATL_UART0], TT_ATL_UART0_IRQ,
@@ -389,7 +518,7 @@ static void create_fdt(TTAtlantisState *s)
{
MachineState *ms = MACHINE(s);
uint8_t rng_seed[32];
- g_autofree char *name = NULL;
+ char *name;
void *fdt;
fdt = create_device_tree(&s->fdt_size);
@@ -411,6 +540,15 @@ static void create_fdt(TTAtlantisState *s)
qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
+ /*
+ * The "/soc/pci@..." node is needed for PCIE hotplugs
+ * that might happen before finalize_fdt().
+ */
+ name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX,
+ s->memmap[TT_ATL_PCIE_ECAM0].base);
+ qemu_fdt_add_subnode(fdt, name);
+ g_free(name);
+
qemu_fdt_add_subnode(fdt, "/chosen");
/* Pass seed to RNG */
@@ -423,6 +561,95 @@ static void create_fdt(TTAtlantisState *s)
create_fdt_pmu(s);
}
+static void gpex_pcie_init_one(TTAtlantisState *s, GPEXHost *gpex_host,
+ MemoryRegion *mr,
+ const MemMapEntry *ecam_mem,
+ const MemMapEntry *pcie_mem,
+ const PciMapEntry *mem_pio,
+ const PciMapEntry *mem_mmio32,
+ const PciMapEntry *mem_mmio64,
+ int legacy_irq)
+{
+ DeviceState *dev;
+ Object *obj;
+ MemoryRegion *ecam_alias, *ecam_reg;
+ MemoryRegion *mmio32_alias, *mmio64_alias, *mmio_reg;
+ hwaddr ecam_base = ecam_mem->base;
+ hwaddr ecam_size = ecam_mem->size;
+ hwaddr pio_base = mem_pio->window_offset + pcie_mem->base;
+ hwaddr pio_size = mem_pio->size;
+ hwaddr mmio32_base = mem_mmio32->window_offset + pcie_mem->base;
+ hwaddr mmio32_size = mem_mmio32->size;
+ hwaddr mmio64_base = mem_mmio64->window_offset + pcie_mem->base;
+ hwaddr mmio64_size = mem_mmio64->size;
+ qemu_irq irq;
+ char name[16];
+ int i;
+
+ snprintf(name, sizeof(name), "pcie");
+ object_initialize_child(OBJECT(s), name, gpex_host, TYPE_GPEX_HOST);
+ dev = DEVICE(gpex_host);
+ obj = OBJECT(dev);
+
+ object_property_set_uint(obj, PCI_HOST_ECAM_BASE, ecam_base, &error_abort);
+ object_property_set_int(obj, PCI_HOST_ECAM_SIZE, ecam_size, &error_abort);
+ object_property_set_uint(obj, PCI_HOST_BELOW_4G_MMIO_BASE, mmio32_base,
+ &error_abort);
+ object_property_set_int(obj, PCI_HOST_BELOW_4G_MMIO_SIZE, mmio32_size,
+ &error_abort);
+ object_property_set_uint(obj, PCI_HOST_ABOVE_4G_MMIO_BASE, mmio64_base,
+ &error_abort);
+ object_property_set_int(obj, PCI_HOST_ABOVE_4G_MMIO_SIZE, mmio64_size,
+ &error_abort);
+ object_property_set_uint(obj, PCI_HOST_PIO_BASE, pio_base, &error_abort);
+ object_property_set_int(obj, PCI_HOST_PIO_SIZE, pio_size, &error_abort);
+
+ sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+ ecam_alias = g_new0(MemoryRegion, 1);
+ ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
+ snprintf(name, sizeof(name), "pcie.ecam");
+ memory_region_init_alias(ecam_alias, obj, name,
+ ecam_reg, 0, ecam_size);
+ memory_region_add_subregion(mr, ecam_base, ecam_alias);
+
+ mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
+
+ mmio32_alias = g_new0(MemoryRegion, 1);
+ snprintf(name, sizeof(name), "pcie.mmio32");
+ memory_region_init_alias(mmio32_alias, obj, name,
+ mmio_reg, mmio32_base & 0xffffffffUL, mmio32_size);
+ memory_region_add_subregion(mr, mmio32_base, mmio32_alias);
+
+ mmio64_alias = g_new0(MemoryRegion, 1);
+ snprintf(name, sizeof(name), "pcie.mmio64");
+ memory_region_init_alias(mmio64_alias, obj, name,
+ mmio_reg, mmio64_base, mmio64_size);
+ memory_region_add_subregion(mr, mmio64_base, mmio64_alias);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base);
+
+ for (i = 0; i < PCI_NUM_PINS; i++) {
+ irq = qdev_get_gpio_in(s->irqchip, legacy_irq + i);
+
+ sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
+ gpex_set_irq_num(GPEX_HOST(dev), i, legacy_irq + i);
+ }
+
+ gpex_host->gpex_cfg.bus = PCI_HOST_BRIDGE(dev)->bus;
+}
+
+static void gpex_pcie_init(TTAtlantisState *s, MemoryRegion *mr)
+{
+ gpex_pcie_init_one(s, &s->gpex_host, mr,
+ &s->memmap[TT_ATL_PCIE_ECAM0],
+ &s->memmap[TT_ATL_PCIE_MMIO0],
+ &s->pcimap[PCIE0_PIO],
+ &s->pcimap[PCIE0_MMIO_32],
+ &s->pcimap[PCIE0_MMIO_64],
+ TT_ATL_PCIE0_INTA_IRQ);
+}
+
static DeviceState *create_reboot_device(const MemMapEntry *mem)
{
DeviceState *dev = qdev_new(TYPE_PVPANIC_MMIO_DEVICE);
@@ -517,6 +744,7 @@ static void tt_atlantis_machine_init(MachineState *machine)
int base_hartid = 0;
s->memmap = tt_atlantis_memmap;
+ s->pcimap = tt_atlantis_pci_map;
object_initialize_child(OBJECT(machine), "soc", &s->soc,
TYPE_RISCV_HART_ARRAY);
@@ -582,6 +810,9 @@ static void tt_atlantis_machine_init(MachineState *machine)
s->fw_cfg = create_fw_cfg(&s->memmap[TT_ATL_FW_CFG], machine->smp.cpus);
rom_set_fw(s->fw_cfg);
+ /* PCIe */
+ gpex_pcie_init(s, system_memory);
+
/* Reboot and exit */
create_reboot_device(&s->memmap[TT_ATL_SYSCON]);
@@ -617,6 +848,7 @@ static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
+ mc->pci_allow_0_address = true;
mc->default_ram_id = "tt_atlantis.ram";
}
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 0601ae1a7494..f13a05c7b1d1 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -129,6 +129,7 @@ config TENSTORRENT
select DEVICE_TREE
select RISCV_NUMA
select PVPANIC_MMIO
+ select PCI_EXPRESS_GENERIC_BRIDGE
select SERIAL_MM
select RISCV_ACLINT
select RISCV_APLIC
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 11/13] tests/functional/riscv64: Add tt-atlantis tests
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (9 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 10/13] hw/riscv/atlantis: Add PCIe controller Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-25 13:17 ` [PATCH v4 12/13] hw/riscv/atlantis: Integrate i2c buses Joel Stanley
2026-04-25 13:17 ` [PATCH v4 13/13] hw/riscv/atlantis: Add some i2c peripherals Joel Stanley
12 siblings, 0 replies; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
From: Nicholas Piggin <npiggin@gmail.com>
Add OpenSBI and Linux boot tests for the tt-atlantis machine. Based on
tests/functional/riscv64/test_sifive_u.py.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v4: Remove unnecessary os.remove(rootfs_path)
v3:
- Fix path to test files
- Dedupe command line root argument
- Remove unused import
---
MAINTAINERS | 1 +
tests/functional/riscv64/meson.build | 1 +
tests/functional/riscv64/test_opensbi.py | 4 ++
tests/functional/riscv64/test_tt_atlantis.py | 59 ++++++++++++++++++++
4 files changed, 65 insertions(+)
create mode 100755 tests/functional/riscv64/test_tt_atlantis.py
diff --git a/MAINTAINERS b/MAINTAINERS
index 635a380945fd..9622ffc9edb2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1770,6 +1770,7 @@ F: docs/system/riscv/tt_*.rst
F: hw/riscv/tt_*.c
F: hw/riscv/aia.[ch]
F: include/hw/riscv/tt_*.h
+F: tests/functional/riscv64/test_tt_*.py
AMD Microblaze-V Generic Board
M: Sai Pavan Boddu <sai.pavan.boddu@amd.com>
diff --git a/tests/functional/riscv64/meson.build b/tests/functional/riscv64/meson.build
index b996c89d7df9..c4456fabd757 100644
--- a/tests/functional/riscv64/meson.build
+++ b/tests/functional/riscv64/meson.build
@@ -13,5 +13,6 @@ tests_riscv64_system_quick = [
tests_riscv64_system_thorough = [
'boston',
'sifive_u',
+ 'tt_atlantis',
'tuxrun',
]
diff --git a/tests/functional/riscv64/test_opensbi.py b/tests/functional/riscv64/test_opensbi.py
index d077e40f4278..0f8beb7e7a8c 100755
--- a/tests/functional/riscv64/test_opensbi.py
+++ b/tests/functional/riscv64/test_opensbi.py
@@ -28,6 +28,10 @@ def test_riscv_sifive_u(self):
self.set_machine('sifive_u')
self.boot_opensbi()
+ def test_riscv_tt_atlantis(self):
+ self.set_machine('tt-atlantis')
+ self.boot_opensbi()
+
def test_riscv_virt(self):
self.set_machine('virt')
self.boot_opensbi()
diff --git a/tests/functional/riscv64/test_tt_atlantis.py b/tests/functional/riscv64/test_tt_atlantis.py
new file mode 100755
index 000000000000..ce6e7fc63e34
--- /dev/null
+++ b/tests/functional/riscv64/test_tt_atlantis.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+#
+# Functional test that boots a Linux kernel on a Tenstorrent Atlantis machine
+# and checks the console
+#
+# Copyright (c) Linaro Ltd.
+# Copyright 2026 Tenstorrent
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+from qemu_test import Asset, LinuxKernelTest
+
+
+class TTAtlantis(LinuxKernelTest):
+
+ ASSET_KERNEL = Asset(
+ 'https://storage.tuxboot.com/kernels/6.11.9/riscv64/Image',
+ '174f8bb87f08961e54fa3fcd954a8e31f4645f6d6af4dd43983d5e9841490fb0')
+ ASSET_ROOTFS = Asset(
+ ('https://github.com/groeck/linux-build-test/raw/'
+ '9819da19e6eef291686fdd7b029ea00e764dc62f/rootfs/riscv64/'
+ 'rootfs.ext2.gz'),
+ 'b6ed95610310b7956f9bf20c4c9c0c05fea647900df441da9dfe767d24e8b28b')
+
+ def do_test_riscv64_tt_atlantis(self, connect_disk):
+ self.set_machine('tt-atlantis')
+ kernel_path = self.ASSET_KERNEL.fetch()
+ rootfs_path = self.uncompress(self.ASSET_ROOTFS)
+
+ self.vm.set_console()
+ kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon=sbi '
+
+ if connect_disk:
+ kernel_command_line += 'root=/dev/vda panic=-1 noreboot rootwait '
+ self.vm.add_args('-device',
+ 'virtio-blk,drive=drive0,serial=0x1234,bus=pcie.0')
+ self.vm.add_args('-drive',
+ f'file={rootfs_path},if=none,id=drive0,format=raw')
+ pattern = 'Boot successful.'
+ else:
+ kernel_command_line += 'panic=0 noreboot '
+ pattern = 'Cannot open root device'
+
+ self.vm.add_args('-kernel', kernel_path,
+ '-append', kernel_command_line,
+ '-no-reboot')
+
+ self.vm.launch()
+ self.wait_for_console_pattern(pattern)
+
+ def test_riscv64_tt_atlantis(self):
+ self.do_test_riscv64_tt_atlantis(False)
+
+ def test_riscv64_tt_atlantis_disk(self):
+ self.do_test_riscv64_tt_atlantis(True)
+
+
+if __name__ == '__main__':
+ LinuxKernelTest.main()
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 12/13] hw/riscv/atlantis: Integrate i2c buses
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (10 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 11/13] tests/functional/riscv64: Add tt-atlantis tests Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
2026-04-25 13:17 ` [PATCH v4 13/13] hw/riscv/atlantis: Add some i2c peripherals Joel Stanley
12 siblings, 0 replies; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Michael Ellerman, Nicholas Piggin, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
Now that we have the DesignWare model we can add buses to the
tt-atlantis machine.
Provide a fixed clock in the device tree so that the Linux driver probes
without WARNing.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v3:
- Add device tree clock
v2:
- Correct count from 4 to 5
- Fix headers location
- Use HWADDR_PRIX to be consistent
---
include/hw/riscv/tt_atlantis.h | 13 +++++++++
hw/riscv/tt_atlantis.c | 53 ++++++++++++++++++++++++++++++++++
hw/riscv/Kconfig | 1 +
3 files changed, 67 insertions(+)
diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
index 070f53eeb450..a19a8cc0773f 100644
--- a/include/hw/riscv/tt_atlantis.h
+++ b/include/hw/riscv/tt_atlantis.h
@@ -11,12 +11,15 @@
#include "hw/core/boards.h"
#include "hw/core/sysbus.h"
+#include "hw/i2c/designware_i2c.h"
#include "hw/intc/riscv_imsic.h"
#include "hw/riscv/riscv_hart.h"
#define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis")
OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE)
+#define TT_ATL_NUM_I2C 5
+
typedef struct {
hwaddr pcie_addr; /* Device side address */
hwaddr window_offset; /* Host side offset */
@@ -37,12 +40,18 @@ struct TTAtlantisState {
RISCVHartArrayState soc;
DeviceState *irqchip;
GPEXHost gpex_host;
+ DesignWareI2CState i2c[TT_ATL_NUM_I2C];
int fdt_size;
};
enum {
TT_ATL_SYSCON_IRQ = 10,
+ TT_ATL_I2C0_IRQ = 33,
+ TT_ATL_I2C1_IRQ = 34,
+ TT_ATL_I2C2_IRQ = 35,
+ TT_ATL_I2C3_IRQ = 36,
+ TT_ATL_I2C4_IRQ = 37,
TT_ATL_UART0_IRQ = 38,
TT_ATL_UART1_IRQ = 39,
TT_ATL_UART2_IRQ = 40,
@@ -58,6 +67,10 @@ enum {
TT_ATL_DDR_HI,
TT_ATL_FW_CFG,
TT_ATL_I2C0,
+ TT_ATL_I2C1,
+ TT_ATL_I2C2,
+ TT_ATL_I2C3,
+ TT_ATL_I2C4,
TT_ATL_MAPLIC,
TT_ATL_MIMSIC,
TT_ATL_PCIE_ECAM0,
diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index 1eb473907ad7..d517d0aee2d1 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -71,6 +71,11 @@ static const MemMapEntry tt_atlantis_memmap[] = {
[TT_ATL_TIMER] = { 0xa8020000, 0x10000 },
[TT_ATL_WDT0] = { 0xa8030000, 0x10000 },
[TT_ATL_UART0] = { 0xb0100000, 0x10000 },
+ [TT_ATL_I2C0] = { 0xb0400000, 0x10000 },
+ [TT_ATL_I2C1] = { 0xb0500000, 0x10000 },
+ [TT_ATL_I2C2] = { 0xb0600000, 0x10000 },
+ [TT_ATL_I2C3] = { 0xb0700000, 0x10000 },
+ [TT_ATL_I2C4] = { 0xb0800000, 0x10000 },
[TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 },
[TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 },
[TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 },
@@ -484,10 +489,36 @@ static void create_fdt_fw_cfg(void *fdt, const MemMapEntry *mem)
qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0);
}
+static void create_fdt_clk(void *fdt, const char *name, uint32_t clk_phandle)
+{
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "fixed-clock");
+ qemu_fdt_setprop_cell(fdt, name, "#clock-cells", 0);
+ qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 100000000);
+ qemu_fdt_setprop_cell(fdt, name, "phandle", clk_phandle);
+}
+
+static void create_fdt_i2c(void *fdt, const MemMapEntry *mem, uint32_t irq,
+ uint32_t irqchip_phandle, uint32_t clk_phandle)
+{
+ g_autofree char *name = g_strdup_printf("/soc/i2c@%"HWADDR_PRIX, mem->base);
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", "snps,designware-i2c");
+ qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
+ qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", irqchip_phandle);
+ qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x4);
+ qemu_fdt_setprop_cell(fdt, name, "clocks", clk_phandle);
+ qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 100000);
+ qemu_fdt_setprop_cell(fdt, name, "#address-cells", 1);
+ qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0);
+}
+
static void finalize_fdt(TTAtlantisState *s)
{
uint32_t aplic_s_phandle = next_phandle();
uint32_t imsic_s_phandle = next_phandle();
+ uint32_t periph_clk_phandle = next_phandle();
void *fdt = MACHINE(s)->fdt;
create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
@@ -512,6 +543,15 @@ static void finalize_fdt(TTAtlantisState *s)
create_fdt_uart(fdt, &s->memmap[TT_ATL_UART0], TT_ATL_UART0_IRQ,
aplic_s_phandle);
+
+ create_fdt_clk(fdt, "/periph-clk", periph_clk_phandle);
+
+ for (int i = 0; i < TT_ATL_NUM_I2C; i++) {
+ create_fdt_i2c(fdt,
+ &s->memmap[TT_ATL_I2C0 + i],
+ TT_ATL_I2C0_IRQ + i,
+ aplic_s_phandle, periph_clk_phandle);
+ }
}
static void create_fdt(TTAtlantisState *s)
@@ -821,6 +861,19 @@ static void tt_atlantis_machine_init(MachineState *machine)
qdev_get_gpio_in(s->irqchip, TT_ATL_UART0_IRQ),
115200, serial_hd(0), DEVICE_LITTLE_ENDIAN);
+ /* I2C */
+ for (int i = 0; i < TT_ATL_NUM_I2C; i++) {
+ object_initialize_child(OBJECT(s), "i2c[*]", &s->i2c[i],
+ TYPE_DESIGNWARE_I2C);
+ sysbus_realize(SYS_BUS_DEVICE(&s->i2c[i]), &error_fatal);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(&s->i2c[i]);
+ memory_region_add_subregion(system_memory,
+ s->memmap[TT_ATL_I2C0 + i].base,
+ sysbus_mmio_get_region(sbd, 0));
+ sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+ qdev_get_gpio_in(s->irqchip, TT_ATL_I2C0_IRQ + i));
+ }
+
/* Load or create device tree */
if (machine->dtb) {
machine->fdt = load_device_tree(machine->dtb, &s->fdt_size);
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index f13a05c7b1d1..2db8f409b926 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -135,6 +135,7 @@ config TENSTORRENT
select RISCV_APLIC
select RISCV_IMSIC
select FW_CFG_DMA
+ select DESIGNWARE_I2C
config XIANGSHAN_KUNMINGHU
bool
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v4 13/13] hw/riscv/atlantis: Add some i2c peripherals
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
` (11 preceding siblings ...)
2026-04-25 13:17 ` [PATCH v4 12/13] hw/riscv/atlantis: Integrate i2c buses Joel Stanley
@ 2026-04-25 13:17 ` Joel Stanley
12 siblings, 0 replies; 44+ messages in thread
From: Joel Stanley @ 2026-04-25 13:17 UTC (permalink / raw)
To: Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Michael Ellerman, Nicholas Piggin, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
Add an I2C RTC device and a temperature sensor. These are not present
on the board but help for testing.
The tmp105 is a lm75 compatible temperature sensor used by the
SENSORS_LM75 Linux kernel driver.
The ds1338 is a RTC device that is used by the RTC_DRV_DS1307 Linux
kernel driver.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v4:
- Place i2c on bus 0 and 4
- lm75 compatible to tmp105, which is same qemu model and linux driver
- Mention the kernel drivers in the commit message
v3: Use HWADDR_PRIX in i2c device string consistent
---
hw/riscv/tt_atlantis.c | 27 +++++++++++++++++++++++++++
hw/riscv/Kconfig | 2 ++
2 files changed, 29 insertions(+)
diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
index d517d0aee2d1..521748af1b99 100644
--- a/hw/riscv/tt_atlantis.c
+++ b/hw/riscv/tt_atlantis.c
@@ -94,6 +94,13 @@ static const PciMapEntry tt_atlantis_pci_map[] = {
FDT_PCI_RANGE_PREFETCHABLE },
};
+static I2CBus *i2c_get_bus(TTAtlantisState *s, unsigned busnr)
+{
+ assert(busnr < TT_ATL_NUM_I2C);
+
+ return s->i2c[busnr].bus;
+}
+
static uint32_t next_phandle(void)
{
static uint32_t phandle = 1;
@@ -514,6 +521,19 @@ static void create_fdt_i2c(void *fdt, const MemMapEntry *mem, uint32_t irq,
qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0);
}
+static void create_fdt_i2c_device(TTAtlantisState *s, int bus,
+ const char *compat, int addr)
+{
+ void *fdt = MACHINE(s)->fdt;
+ hwaddr base = s->memmap[TT_ATL_I2C0 + bus].base;
+ g_autofree char *name = g_strdup_printf("/soc/i2c@%"HWADDR_PRIX"/sensor@%x",
+ base, addr);
+
+ qemu_fdt_add_subnode(fdt, name);
+ qemu_fdt_setprop_string(fdt, name, "compatible", compat);
+ qemu_fdt_setprop_cell(fdt, name, "reg", addr);
+}
+
static void finalize_fdt(TTAtlantisState *s)
{
uint32_t aplic_s_phandle = next_phandle();
@@ -552,6 +572,9 @@ static void finalize_fdt(TTAtlantisState *s)
TT_ATL_I2C0_IRQ + i,
aplic_s_phandle, periph_clk_phandle);
}
+
+ create_fdt_i2c_device(s, 0, "dallas,ds1338", 0x6f);
+ create_fdt_i2c_device(s, 4, "ti,tmp105", 0x48);
}
static void create_fdt(TTAtlantisState *s)
@@ -874,6 +897,10 @@ static void tt_atlantis_machine_init(MachineState *machine)
qdev_get_gpio_in(s->irqchip, TT_ATL_I2C0_IRQ + i));
}
+ /* I2C peripherals: qemu specific */
+ i2c_slave_create_simple(i2c_get_bus(s, 0), "ds1338", 0x6f);
+ i2c_slave_create_simple(i2c_get_bus(s, 4), "tmp105", 0x48);
+
/* Load or create device tree */
if (machine->dtb) {
machine->fdt = load_device_tree(machine->dtb, &s->fdt_size);
diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
index 2db8f409b926..6ad7b3e07e9e 100644
--- a/hw/riscv/Kconfig
+++ b/hw/riscv/Kconfig
@@ -136,6 +136,8 @@ config TENSTORRENT
select RISCV_IMSIC
select FW_CFG_DMA
select DESIGNWARE_I2C
+ select DS1338
+ select TMP105
config XIANGSHAN_KUNMINGHU
bool
--
2.47.3
^ permalink raw reply related [flat|nested] 44+ messages in thread
* Re: [PATCH v4 03/13] hw/riscv/boot: Account for discontiguous memory when loading firmware
2026-04-25 13:17 ` [PATCH v4 03/13] hw/riscv/boot: Account for discontiguous memory when loading firmware Joel Stanley
@ 2026-04-29 23:34 ` Alistair Francis
2026-05-04 23:45 ` Nicholas Piggin
0 siblings, 1 reply; 44+ messages in thread
From: Alistair Francis @ 2026-04-29 23:34 UTC (permalink / raw)
To: Joel Stanley
Cc: Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
>
> From: Nicholas Piggin <npiggin@gmail.com>
>
> This loads firmware into the first (low) memory range,
> accounting for machines having discontiguous memory regions.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> ---
> v4: Make RISCVBootInfo *info const in riscv_load_firmware
> v3: Call riscv_boot_info_init before riscv_find_and_load_firmware in
> sifive_u
> ---
> include/hw/riscv/boot.h | 5 ++++-
> hw/riscv/boot.c | 18 ++++++++++++------
> hw/riscv/microchip_pfsoc.c | 6 ++++--
> hw/riscv/opentitan.c | 6 ++++--
> hw/riscv/shakti_c.c | 6 +++++-
> hw/riscv/sifive_u.c | 6 ++++--
> hw/riscv/spike.c | 6 ++++--
> hw/riscv/virt.c | 7 ++++---
> hw/riscv/xiangshan_kmh.c | 6 +++++-
> 9 files changed, 46 insertions(+), 20 deletions(-)
>
> diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
> index 115e3222174f..b2ef64fb1d14 100644
> --- a/include/hw/riscv/boot.h
> +++ b/include/hw/riscv/boot.h
> @@ -53,13 +53,16 @@ void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info,
> vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info,
> hwaddr firmware_end_addr);
> hwaddr riscv_find_and_load_firmware(MachineState *machine,
> + RISCVBootInfo *info,
> const char *default_machine_firmware,
> hwaddr *firmware_load_addr,
> symbol_fn_t sym_cb);
> const char *riscv_default_firmware_name(RISCVHartArrayState *harts);
> char *riscv_find_firmware(const char *firmware_filename,
> const char *default_machine_firmware);
> -hwaddr riscv_load_firmware(const char *firmware_filename,
> +hwaddr riscv_load_firmware(MachineState *machine,
> + const RISCVBootInfo *info,
> + const char *firmware_filename,
> hwaddr *firmware_load_addr,
> symbol_fn_t sym_cb);
> void riscv_load_kernel(MachineState *machine,
> diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
> index 5c9547429a36..4fbc778263cf 100644
> --- a/hw/riscv/boot.c
> +++ b/hw/riscv/boot.c
> @@ -145,6 +145,7 @@ char *riscv_find_firmware(const char *firmware_filename,
> }
>
> hwaddr riscv_find_and_load_firmware(MachineState *machine,
> + RISCVBootInfo *info,
> const char *default_machine_firmware,
> hwaddr *firmware_load_addr,
> symbol_fn_t sym_cb)
> @@ -157,7 +158,8 @@ hwaddr riscv_find_and_load_firmware(MachineState *machine,
>
> if (firmware_filename) {
> /* If not "none" load the firmware */
> - firmware_end_addr = riscv_load_firmware(firmware_filename,
> + firmware_end_addr = riscv_load_firmware(machine, info,
> + firmware_filename,
> firmware_load_addr, sym_cb);
> g_free(firmware_filename);
> }
> @@ -165,10 +167,13 @@ hwaddr riscv_find_and_load_firmware(MachineState *machine,
> return firmware_end_addr;
> }
>
> -hwaddr riscv_load_firmware(const char *firmware_filename,
> +hwaddr riscv_load_firmware(MachineState *machine,
> + const RISCVBootInfo *info,
> + const char *firmware_filename,
> hwaddr *firmware_load_addr,
> symbol_fn_t sym_cb)
> {
> + uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
> uint64_t firmware_entry, firmware_end;
> ssize_t firmware_size;
>
> @@ -197,7 +202,7 @@ hwaddr riscv_load_firmware(const char *firmware_filename,
>
> firmware_size = load_image_targphys_as(firmware_filename,
> *firmware_load_addr,
> - current_machine->ram_size, NULL,
> + mem_size, NULL,
> NULL);
>
> if (firmware_size > 0) {
> @@ -212,7 +217,7 @@ hwaddr riscv_load_firmware(const char *firmware_filename,
> static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info)
> {
> const char *filename = machine->initrd_filename;
> - uint64_t mem_size = machine->ram_size;
> + uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
> void *fdt = machine->fdt;
> hwaddr start, end;
> ssize_t size;
> @@ -258,6 +263,7 @@ void riscv_load_kernel(MachineState *machine,
> bool load_initrd,
> symbol_fn_t sym_cb)
> {
> + uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
> const char *kernel_filename = machine->kernel_filename;
> ssize_t kernel_size;
> void *fdt = machine->fdt;
> @@ -289,7 +295,7 @@ void riscv_load_kernel(MachineState *machine,
> }
>
> kernel_size = load_image_targphys_as(kernel_filename, kernel_start_addr,
> - current_machine->ram_size, NULL, NULL);
> + mem_size, NULL, NULL);
> if (kernel_size > 0) {
> info->kernel_size = kernel_size;
> info->image_low_addr = kernel_start_addr;
> @@ -385,7 +391,7 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size,
> dtb_start = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB);
>
> if (dtb_start_limit && (dtb_start < dtb_start_limit)) {
> - error_report("No enough memory to place DTB after kernel/initrd");
> + error_report("Not enough memory to place DTB after kernel/initrd");
> exit(1);
> }
>
> diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c
> index 743f31f00578..1d1ddb05a882 100644
> --- a/hw/riscv/microchip_pfsoc.c
> +++ b/hw/riscv/microchip_pfsoc.c
> @@ -618,18 +618,20 @@ static void microchip_icicle_kit_machine_init(MachineState *machine)
> firmware_load_addr = RESET_VECTOR;
> }
>
> + riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
We should be able to use the new riscv_boot_info_init_discontig_mem() here
Alistair
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 04/13] hw/riscv/boot: Provide a simple halting payload
2026-04-25 13:17 ` [PATCH v4 04/13] hw/riscv/boot: Provide a simple halting payload Joel Stanley
@ 2026-04-29 23:35 ` Alistair Francis
2026-05-04 23:52 ` Nicholas Piggin
0 siblings, 1 reply; 44+ messages in thread
From: Alistair Francis @ 2026-04-29 23:35 UTC (permalink / raw)
To: Joel Stanley
Cc: Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
On Sat, Apr 25, 2026 at 11:21 PM Joel Stanley <joel@jms.id.au> wrote:
>
> From: Nicholas Piggin <npiggin@gmail.com>
>
> OpenSBI hangs before any console output if the domain init code sees the
> next stage is not in an executable region.
That's an OpenSBI bug though and needs to be fixed there and not just
hacked around in QEMU.
Alistair
>
> If no kernel payload is provided to QEMU, the next stage address is
> NULL, and the riscv virt machine memory map ends up covering the 0
> address with the catch all S-mode RWX region and so OpenSBI prints
> console messages and does not hang until the next stage boot.
>
> The soon to be added Tenstorrent Atlantis board address map has RAM
> starting at 0 and it loads OpenSBI there, so it is M-mode and not
> accessible by S-mode, tripping the early check and hang.
>
> Add a helper to set up a simple payload that gets OpenSBI messages
> to console.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> ---
> v4: Make vec instructions const
> v3: MachineState argument was unused
> ---
> include/hw/riscv/boot.h | 1 +
> hw/riscv/boot.c | 16 ++++++++++++++++
> 2 files changed, 17 insertions(+)
>
> diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
> index b2ef64fb1d14..3dfb0e98db4c 100644
> --- a/include/hw/riscv/boot.h
> +++ b/include/hw/riscv/boot.h
> @@ -78,6 +78,7 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts
> hwaddr rom_base, hwaddr rom_size,
> uint64_t kernel_entry,
> uint64_t fdt_load_addr);
> +void riscv_setup_halting_payload(RISCVBootInfo *info, hwaddr addr);
> void riscv_rom_copy_firmware_info(MachineState *machine,
> RISCVHartArrayState *harts,
> hwaddr rom_base,
> diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
> index 4fbc778263cf..3c2501b3c721 100644
> --- a/hw/riscv/boot.c
> +++ b/hw/riscv/boot.c
> @@ -518,6 +518,22 @@ void riscv_setup_rom_reset_vec(MachineState *machine, RISCVHartArrayState *harts
> kernel_entry);
> }
>
> +/* Simple payload so OpenSBI does not hang early with no output */
> +void riscv_setup_halting_payload(RISCVBootInfo *info, hwaddr addr)
> +{
> + /* Store the payload vector in little_endian byte order */
> + static const uint32_t payload_vec[] = {
> + const_le32(0x10500073), /* 1: wfi */
> + const_le32(0xffdff06f), /* j 1b */
> + };
> + rom_add_blob_fixed_as("mrom.payload", payload_vec, sizeof(payload_vec),
> + addr, &address_space_memory);
> +
> + info->kernel_size = sizeof(payload_vec);
> + info->image_low_addr = addr;
> + info->image_high_addr = info->image_low_addr + info->kernel_size;
> +}
> +
> void riscv_setup_direct_kernel(hwaddr kernel_addr, hwaddr fdt_addr)
> {
> CPUState *cs;
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 07/13] target/riscv: tt-ascalon: Enable Zkr extension
2026-04-25 13:17 ` [PATCH v4 07/13] target/riscv: tt-ascalon: Enable Zkr extension Joel Stanley
@ 2026-04-29 23:36 ` Alistair Francis
2026-05-05 0:06 ` Nicholas Piggin
0 siblings, 1 reply; 44+ messages in thread
From: Alistair Francis @ 2026-04-29 23:36 UTC (permalink / raw)
To: Joel Stanley
Cc: Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
On Sat, Apr 25, 2026 at 11:21 PM Joel Stanley <joel@jms.id.au> wrote:
>
> From: Nicholas Piggin <npiggin@gmail.com>
>
> Ascalon supports Zkr and the SEED CSR.
A datasheet would be nice to verify this
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
> Signed-off-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> target/riscv/cpu.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index ce15a17c37de..69649462dd89 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -3190,6 +3190,7 @@ static const TypeInfo riscv_cpu_type_infos[] = {
> .cfg.ext_zba = true,
> .cfg.ext_zbb = true,
> .cfg.ext_zbs = true,
> + .cfg.ext_zkr = true,
> .cfg.ext_zkt = true,
> .cfg.ext_zvbb = true,
> .cfg.ext_zvbc = true,
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 08/13] target/riscv: tt-ascalon: Enable Svadu by removing Svade
2026-04-25 13:17 ` [PATCH v4 08/13] target/riscv: tt-ascalon: Enable Svadu by removing Svade Joel Stanley
@ 2026-04-29 23:41 ` Alistair Francis
0 siblings, 0 replies; 44+ messages in thread
From: Alistair Francis @ 2026-04-29 23:41 UTC (permalink / raw)
To: Joel Stanley
Cc: Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
>
> From: Nicholas Piggin <npiggin@gmail.com>
>
> Ascalon supports Svadu (hardware A/D bit updates).
>
> QEMU makes Svadu and Svade mutually exclusive, remove Svade so
> Ascalon comes up with Svadu working.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> Signed-off-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> ---
> target/riscv/cpu.c | 1 -
> 1 file changed, 1 deletion(-)
>
> diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
> index 69649462dd89..97e35672649e 100644
> --- a/target/riscv/cpu.c
> +++ b/target/riscv/cpu.c
> @@ -3204,7 +3204,6 @@ static const TypeInfo riscv_cpu_type_infos[] = {
> .cfg.ext_ssaia = true,
> .cfg.ext_sscofpmf = true,
> .cfg.ext_sstc = true,
> - .cfg.ext_svade = true,
> .cfg.ext_svinval = true,
> .cfg.ext_svnapot = true,
> .cfg.ext_svpbmt = true,
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-04-25 13:17 ` [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine Joel Stanley
@ 2026-04-29 23:57 ` Alistair Francis
2026-05-05 1:04 ` Nicholas Piggin
2026-05-05 2:00 ` Nicholas Piggin
0 siblings, 2 replies; 44+ messages in thread
From: Alistair Francis @ 2026-04-29 23:57 UTC (permalink / raw)
To: Joel Stanley
Cc: Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Nicholas Piggin, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
>
> The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> and CoreLab Technology. It is based on the Atlantis SoC, which includes
> the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
Can you provide a link to documentation
>
> The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant
> RISC-V CPU.
>
> This adds the machine containing serial console, interrupt controllers
> and device tree support.
>
> qemu-system-riscv64 -M tt-atlantis -m 512M \
> -kernel Image -initrd rootfs.cpio -nographic
>
> If there is no kernel provided the simple payload is loaded to avoid
> OpenSBI hanging with no messages.
>
> Co-Developed-by: Nicholas Piggin <npiggin@gmail.com>
> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
> Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> ---
> v4:
> - Clean up unused PCIe defines
> v3:
> - Merge simple payload change into this patch
> - Fix fw_cfg_init_mem_dma API change
> - Clean up irqchip #defines
> - Add Michael and Nick as reviewers
> - create_fdt_pmu was copying RISCVCPU by value
> - Remove unused platform_bus
>
> remove unused defines
>
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> ---
> MAINTAINERS | 11 +
> docs/system/riscv/tt_atlantis.rst | 38 ++
> docs/system/target-riscv.rst | 1 +
> include/hw/riscv/tt_atlantis.h | 61 +++
> hw/riscv/tt_atlantis.c | 632 ++++++++++++++++++++++++++++++
> hw/riscv/Kconfig | 15 +
> hw/riscv/meson.build | 1 +
> 7 files changed, 759 insertions(+)
> create mode 100644 docs/system/riscv/tt_atlantis.rst
> create mode 100644 include/hw/riscv/tt_atlantis.h
> create mode 100644 hw/riscv/tt_atlantis.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index e1942a86eba5..635a380945fd 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1760,6 +1760,17 @@ F: hw/*/*sifive*.c
> F: include/hw/*/*sifive*.h
> F: tests/functional/test_riscv64_sifive_u.py
>
> +Tenstorrent Machines
> +M: Joel Stanley <joel@jms.id.au>
> +R: Nicholas Piggin <npiggin@gmail.com>
> +R: Michael Ellerman <mpe@kernel.org>
> +L: qemu-riscv@nongnu.org
> +S: Supported
> +F: docs/system/riscv/tt_*.rst
> +F: hw/riscv/tt_*.c
> +F: hw/riscv/aia.[ch]
AIA?
> +F: include/hw/riscv/tt_*.h
> +
> AMD Microblaze-V Generic Board
> M: Sai Pavan Boddu <sai.pavan.boddu@amd.com>
> S: Maintained
> diff --git a/docs/system/riscv/tt_atlantis.rst b/docs/system/riscv/tt_atlantis.rst
> new file mode 100644
> index 000000000000..640cabf7b046
> --- /dev/null
> +++ b/docs/system/riscv/tt_atlantis.rst
> @@ -0,0 +1,38 @@
> +Tenstorrent Atlantis (``tt-atlantis``)
> +======================================
> +
> +The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> +and CoreLab Technology. It is based on the Atlantis SoC, which includes
> +the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
> +
> +The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant
> +RISC-V CPU.
> +
> +Features
> +--------
> +
> +* 8-core Ascalon-X CPU Cluster
> +* Dual x32 LPDDR5 @ 6400 MT/s
> +* RISC-V compliant Advanced Interrupt Architecture
> +* PCIe Gen4
> +* RISC-V compliant IOMMU
IOMMU isn't supported (at least not in this patch), maybe this should
list things that are supported in QEMU (and can then be expanded).
Leave the features list to your official documentation (which is
missing a link here)
> +* GPU and Video subsystem
> +* 2x USB3.1 & 2x USB2.0
> +* 2x 1GbE Ethernet
> +* 2x eMMC5.1/SDIO3.0 storage
> +* Extensive connectivity (SPI, I2C, UART, GPIO, CANFD)
> +
> +Note: the QEMU tt-atlantis machine does not model the platform
> +exactly or all devices, but it is undergoing improvement.
> +
> +Supported software
> +------------------
> +
> +The Tenstorrent Ascalon CPUs avoid proprietary or non-standard
> +extensions, so compatibility with existing software is generally
> +good. The QEMU tt-atlantis machine works with upstream OpenSBI
> +and Linux with default configurations.
> +
> +The development board hardware will require some implementation
> +specific setup in firmware which is being developed and may
> +become a requirement or option for the tt-atlantis machine.
> diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst
> index 3ad5d1ddafbb..a8e6b3342186 100644
> --- a/docs/system/target-riscv.rst
> +++ b/docs/system/target-riscv.rst
> @@ -71,6 +71,7 @@ undocumented; you can get a complete list by running
> riscv/mips
> riscv/shakti-c
> riscv/sifive_u
> + riscv/tt_atlantis
> riscv/virt
> riscv/xiangshan-kunminghu
>
> diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
> new file mode 100644
> index 000000000000..9fe50d9c9040
> --- /dev/null
> +++ b/include/hw/riscv/tt_atlantis.h
> @@ -0,0 +1,61 @@
> +/*
> + * Tenstorrent Atlantis RISC-V System on Chip
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Copyright 2025 Tenstorrent, Joel Stanley <joel@jms.id.au>
> + */
> +
> +#ifndef HW_RISCV_TT_ATLANTIS_H
> +#define HW_RISCV_TT_ATLANTIS_H
> +
> +#include "hw/core/boards.h"
> +#include "hw/core/sysbus.h"
> +#include "hw/intc/riscv_imsic.h"
> +#include "hw/riscv/riscv_hart.h"
> +
> +#define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis")
> +OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE)
> +
> +struct TTAtlantisState {
> + /*< private >*/
> + MachineState parent;
> +
> + /*< public >*/
> + Notifier machine_done;
> + FWCfgState *fw_cfg;
> + const MemMapEntry *memmap;
> +
> + RISCVHartArrayState soc;
> + DeviceState *irqchip;
> +
> + int fdt_size;
> +};
> +
> +enum {
> + TT_ATL_SYSCON_IRQ = 10,
> + TT_ATL_UART0_IRQ = 38,
> + TT_ATL_UART1_IRQ = 39,
> + TT_ATL_UART2_IRQ = 40,
> + TT_ATL_UART3_IRQ = 41,
> + TT_ATL_UART4_IRQ = 42,
> +};
> +
> +enum {
> + TT_ATL_ACLINT,
> + TT_ATL_BOOTROM,
> + TT_ATL_DDR_LO,
> + TT_ATL_DDR_HI,
> + TT_ATL_FW_CFG,
> + TT_ATL_I2C0,
> + TT_ATL_MAPLIC,
> + TT_ATL_MIMSIC,
> + TT_ATL_SAPLIC,
> + TT_ATL_SIMSIC,
> + TT_ATL_SYSCON,
> + TT_ATL_TIMER,
> + TT_ATL_UART0,
> + TT_ATL_WDT0,
> +};
> +
> +#endif
> diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
> new file mode 100644
> index 000000000000..5b66d7e95bb9
> --- /dev/null
> +++ b/hw/riscv/tt_atlantis.c
> @@ -0,0 +1,632 @@
> +/*
> + * Tenstorrent Atlantis RISC-V System on Chip
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Copyright 2025 Tenstorrent, Joel Stanley <joel@jms.id.au>
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/cutils.h"
> +#include "qemu/error-report.h"
> +#include "qemu/guest-random.h"
> +#include "qemu/units.h"
> +
> +#include "hw/core/boards.h"
> +#include "hw/core/loader.h"
> +#include "hw/core/sysbus.h"
> +
> +#include "target/riscv/cpu.h"
> +#include "target/riscv/pmu.h"
> +
> +#include "hw/riscv/boot.h"
> +#include "hw/riscv/numa.h"
> +#include "hw/riscv/riscv_hart.h"
> +
> +#include "hw/char/serial-mm.h"
> +#include "hw/intc/riscv_aclint.h"
> +#include "hw/intc/riscv_aplic.h"
> +#include "hw/misc/pvpanic.h"
> +
> +#include "system/system.h"
> +#include "system/device_tree.h"
> +
> +#include "hw/riscv/tt_atlantis.h"
> +
> +#include "aia.h"
> +
> +#define TT_IRQCHIP_NUM_MSIS 255
> +#define TT_IRQCHIP_NUM_SOURCES 128
> +#define TT_IRQCHIP_NUM_PRIO_BITS 3
> +#define TT_IRQCHIP_GUESTS 7 /* aia_guests */
> +
> +#define FDT_PCI_ADDR_CELLS 3
> +#define FDT_PCI_INT_CELLS 1
> +#define FDT_MAX_INT_CELLS 2
> +#define FDT_MAX_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + \
> + 1 + FDT_MAX_INT_CELLS)
> +
> +#define TT_ACLINT_MTIME_SIZE 0x8050
> +#define TT_ACLINT_MTIME 0x0
> +#define TT_ACLINT_MTIMECMP 0x8000
> +#define TT_ACLINT_TIMEBASE_FREQ 1000000000
> +
> +static const MemMapEntry tt_atlantis_memmap[] = {
> + /* Keep sorted with :'<,'>!sort -g -k 4 */
> + [TT_ATL_DDR_LO] = { 0x00000000, 0x80000000 },
> + [TT_ATL_BOOTROM] = { 0x80000000, 0x2000 },
> + [TT_ATL_FW_CFG] = { 0x80002000, 0xff }, /* qemu only */
> + [TT_ATL_SYSCON] = { 0x80004000, 0x1000 }, /* qemu only */
> + [TT_ATL_MIMSIC] = { 0xa0000000, 0x200000 },
> + [TT_ATL_ACLINT] = { 0xa2180000, 0x10000 },
> + [TT_ATL_SIMSIC] = { 0xa4000000, 0x200000 },
> + [TT_ATL_TIMER] = { 0xa8020000, 0x10000 },
> + [TT_ATL_WDT0] = { 0xa8030000, 0x10000 },
> + [TT_ATL_UART0] = { 0xb0100000, 0x10000 },
> + [TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 },
> + [TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 },
> + [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 },
> +};
> +
> +static uint32_t next_phandle(void)
> +{
> + static uint32_t phandle = 1;
> + return phandle++;
> +}
> +
> +static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
So generally we only create a device tree for virtual boards, that is
boards that don't really exist and are just QEMU constructs.
You said that this is based on the "Atlantis SoC" so I assume it's
real hardware. In the real hardware do you expect the device tree to
be provided by the ROM? Usually for boards users supply the device
tree as part of the boot flow (via u-boot or something similar). In
which case should QEMU actually generate this or can users just supply
their own?
Basically the question is, if you are trying to model the hardware, is
generating the device tree here correct?
Obviously the advantage of doing it here is then the device tree
describes what QEMU supports, which is great. But that can be harder
to manage when the hardware doesn't do that, as then you have
divergence.
> +{
> + uint32_t cpu_phandle;
> + void *fdt = MACHINE(s)->fdt;
> +
> + for (int cpu = s->soc.num_harts - 1; cpu >= 0; cpu--) {
> + RISCVCPU *cpu_ptr = &s->soc.harts[cpu];
> + g_autofree char *cpu_name = NULL;
> + g_autofree char *intc_name = NULL;
> +
> + cpu_phandle = next_phandle();
> +
> + cpu_name = g_strdup_printf("/cpus/cpu@%d", s->soc.hartid_base + cpu);
> + qemu_fdt_add_subnode(fdt, cpu_name);
> +
> + qemu_fdt_setprop_string(fdt, cpu_name, "mmu-type", "riscv,sv57");
> +
> + riscv_isa_write_fdt(cpu_ptr, fdt, cpu_name);
> +
> + qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbom-block-size",
> + cpu_ptr->cfg.cbom_blocksize);
> +
> + qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cboz-block-size",
> + cpu_ptr->cfg.cboz_blocksize);
> +
> + qemu_fdt_setprop_cell(fdt, cpu_name, "riscv,cbop-block-size",
> + cpu_ptr->cfg.cbop_blocksize);
> +
> + qemu_fdt_setprop_string(fdt, cpu_name, "compatible", "riscv");
> + qemu_fdt_setprop_string(fdt, cpu_name, "status", "okay");
> + qemu_fdt_setprop_cell(fdt, cpu_name, "reg", s->soc.hartid_base + cpu);
> + qemu_fdt_setprop_string(fdt, cpu_name, "device_type", "cpu");
> + qemu_fdt_setprop_cell(fdt, cpu_name, "phandle", cpu_phandle);
> +
> + intc_phandles[cpu] = next_phandle();
> +
> + intc_name = g_strdup_printf("%s/interrupt-controller", cpu_name);
> + qemu_fdt_add_subnode(fdt, intc_name);
> + qemu_fdt_setprop_cell(fdt, intc_name, "phandle",
> + intc_phandles[cpu]);
> + qemu_fdt_setprop_string(fdt, intc_name, "compatible",
> + "riscv,cpu-intc");
> + qemu_fdt_setprop(fdt, intc_name, "interrupt-controller", NULL, 0);
> + qemu_fdt_setprop_cell(fdt, intc_name, "#interrupt-cells", 1);
> + }
> +}
> +
> +static void create_fdt_memory_node(TTAtlantisState *s,
> + hwaddr addr, hwaddr size)
> +{
> + void *fdt = MACHINE(s)->fdt;
> + g_autofree char *name = g_strdup_printf("/memory@%"HWADDR_PRIX, addr);
> + qemu_fdt_add_subnode(fdt, name);
> + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, addr, 2, size);
> + qemu_fdt_setprop_string(fdt, name, "device_type", "memory");
> +}
> +
> +static void create_fdt_memory(TTAtlantisState *s)
> +{
> + hwaddr size_lo = MACHINE(s)->ram_size;
> + hwaddr size_hi = 0;
> +
> + if (size_lo > s->memmap[TT_ATL_DDR_LO].size) {
> + size_lo = s->memmap[TT_ATL_DDR_LO].size;
> + size_hi = MACHINE(s)->ram_size - size_lo;
> + }
> +
> + create_fdt_memory_node(s, s->memmap[TT_ATL_DDR_LO].base, size_lo);
> + if (size_hi) {
> + /*
> + * The first part of the HI address is aliased at the LO address
> + * so do not include that as usable memory. Is there any way
> + * (or good reason) to describe that aliasing 2GB with DT?
> + */
> + create_fdt_memory_node(s, s->memmap[TT_ATL_DDR_HI].base + size_lo,
> + size_hi);
> + }
> +}
> +
> +static void create_fdt_aclint(TTAtlantisState *s, uint32_t *intc_phandles)
> +{
> + void *fdt = MACHINE(s)->fdt;
> + g_autofree char *name = NULL;
> + g_autofree uint32_t *aclint_mtimer_cells = NULL;
> + uint32_t aclint_cells_size;
> + hwaddr addr;
> +
> + aclint_mtimer_cells = g_new0(uint32_t, s->soc.num_harts * 2);
> +
> + for (int cpu = 0; cpu < s->soc.num_harts; cpu++) {
> + aclint_mtimer_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
> + aclint_mtimer_cells[cpu * 2 + 1] = cpu_to_be32(IRQ_M_TIMER);
> + }
> + aclint_cells_size = s->soc.num_harts * sizeof(uint32_t) * 2;
> +
> + addr = s->memmap[TT_ATL_ACLINT].base;
> +
> + name = g_strdup_printf("/soc/mtimer@%"HWADDR_PRIX, addr);
> + qemu_fdt_add_subnode(fdt, name);
> + qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aclint-mtimer");
> + qemu_fdt_setprop_sized_cells(fdt, name, "reg",
> + 2, addr + TT_ACLINT_MTIME,
> + 2, 0x1000,
> + 2, addr + TT_ACLINT_MTIMECMP,
> + 2, 0x1000);
> + qemu_fdt_setprop(fdt, name, "interrupts-extended",
> + aclint_mtimer_cells, aclint_cells_size);
> +}
> +
> +static void create_fdt_one_imsic(void *fdt, const MemMapEntry *mem, int cpus,
> + uint32_t *intc_phandles, uint32_t msi_phandle,
> + int irq_line, uint32_t imsic_guest_bits)
> +{
> + g_autofree char *name = NULL;
> + g_autofree uint32_t *imsic_cells = g_new0(uint32_t, cpus * 2);
> +
> + for (int cpu = 0; cpu < cpus; cpu++) {
> + imsic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
> + imsic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line);
> + }
> +
> + name = g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base);
> + qemu_fdt_add_subnode(fdt, name);
> + qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,imsics");
> +
> + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 0);
> + qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0);
> + qemu_fdt_setprop(fdt, name, "msi-controller", NULL, 0);
> + qemu_fdt_setprop(fdt, name, "interrupts-extended",
> + imsic_cells, sizeof(uint32_t) * cpus * 2);
> + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
> + qemu_fdt_setprop_cell(fdt, name, "riscv,num-ids", TT_IRQCHIP_NUM_MSIS);
> +
> + if (imsic_guest_bits) {
> + qemu_fdt_setprop_cell(fdt, name, "riscv,guest-index-bits",
> + imsic_guest_bits);
> + }
> + qemu_fdt_setprop_cell(fdt, name, "phandle", msi_phandle);
> +}
> +
> +static void create_fdt_one_aplic(void *fdt,
> + const MemMapEntry *mem,
> + uint32_t msi_phandle,
> + uint32_t *intc_phandles,
> + uint32_t aplic_phandle,
> + uint32_t aplic_child_phandle,
> + int irq_line, int num_harts)
> +{
> + g_autofree char *name =
> + g_strdup_printf("/soc/interrupt-controller@%"HWADDR_PRIX, mem->base);
> + g_autofree uint32_t *aplic_cells = g_new0(uint32_t, num_harts * 2);
> +
> + for (int cpu = 0; cpu < num_harts; cpu++) {
> + aplic_cells[cpu * 2 + 0] = cpu_to_be32(intc_phandles[cpu]);
> + aplic_cells[cpu * 2 + 1] = cpu_to_be32(irq_line);
> + }
> +
> + qemu_fdt_add_subnode(fdt, name);
> + qemu_fdt_setprop_string(fdt, name, "compatible", "riscv,aplic");
> + qemu_fdt_setprop_cell(fdt, name, "#address-cells", 0);
> + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", 2);
> + qemu_fdt_setprop(fdt, name, "interrupt-controller", NULL, 0);
> +
> + qemu_fdt_setprop(fdt, name, "interrupts-extended",
> + aplic_cells, num_harts * sizeof(uint32_t) * 2);
> + qemu_fdt_setprop_cell(fdt, name, "msi-parent", msi_phandle);
> +
> + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
> + qemu_fdt_setprop_cell(fdt, name, "riscv,num-sources",
> + TT_IRQCHIP_NUM_SOURCES);
> +
> + if (aplic_child_phandle) {
> + qemu_fdt_setprop_cell(fdt, name, "riscv,children",
> + aplic_child_phandle);
> + qemu_fdt_setprop_cells(fdt, name, "riscv,delegation",
> + aplic_child_phandle, 1, TT_IRQCHIP_NUM_SOURCES);
> + }
> +
> + qemu_fdt_setprop_cell(fdt, name, "phandle", aplic_phandle);
> +}
> +
> +static void create_fdt_pmu(TTAtlantisState *s)
> +{
> + g_autofree char *pmu_name = g_strdup_printf("/pmu");
> + void *fdt = MACHINE(s)->fdt;
> + RISCVCPU *hart = &s->soc.harts[0];
> +
> + qemu_fdt_add_subnode(fdt, pmu_name);
> + qemu_fdt_setprop_string(fdt, pmu_name, "compatible", "riscv,pmu");
> + riscv_pmu_generate_fdt_node(fdt, hart->pmu_avail_ctrs, pmu_name);
> +}
> +
> +static void create_fdt_cpu(TTAtlantisState *s, const MemMapEntry *memmap,
> + uint32_t aplic_s_phandle,
> + uint32_t imsic_s_phandle)
> +{
> + MachineState *ms = MACHINE(s);
> + void *fdt = MACHINE(s)->fdt;
> + g_autofree uint32_t *intc_phandles = NULL;
> +
> + qemu_fdt_add_subnode(fdt, "/cpus");
> + qemu_fdt_setprop_cell(fdt, "/cpus", "timebase-frequency",
> + TT_ACLINT_TIMEBASE_FREQ);
> + qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0x0);
> + qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 0x1);
> +
> + intc_phandles = g_new0(uint32_t, ms->smp.cpus);
> +
> + create_fdt_cpus(s, intc_phandles);
> +
> + create_fdt_memory(s);
> +
> + create_fdt_aclint(s, intc_phandles);
> +
> + /* M-level IMSIC node */
> + uint32_t msi_m_phandle = next_phandle();
> + create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_MIMSIC], ms->smp.cpus,
> + intc_phandles, msi_m_phandle,
> + IRQ_M_EXT, 0);
> +
> + /* S-level IMSIC node */
> + create_fdt_one_imsic(fdt, &s->memmap[TT_ATL_SIMSIC], ms->smp.cpus,
> + intc_phandles, imsic_s_phandle,
> + IRQ_S_EXT, imsic_num_bits(TT_IRQCHIP_GUESTS + 1));
> +
> + uint32_t aplic_m_phandle = next_phandle();
> +
> + /* M-level APLIC node */
> + create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_MAPLIC],
> + msi_m_phandle, intc_phandles,
> + aplic_m_phandle, aplic_s_phandle,
> + IRQ_M_EXT, s->soc.num_harts);
> +
> + /* S-level APLIC node */
> + create_fdt_one_aplic(fdt, &s->memmap[TT_ATL_SAPLIC],
> + imsic_s_phandle, intc_phandles,
> + aplic_s_phandle, 0,
> + IRQ_S_EXT, s->soc.num_harts);
> +}
> +
> +static void create_fdt_reset(void *fdt, const MemMapEntry *mem)
> +{
> + uint32_t syscon_phandle = next_phandle();
> + char *name;
> +
> + name = g_strdup_printf("/soc/syscon@%"HWADDR_PRIX, mem->base);
> + qemu_fdt_add_subnode(fdt, name);
> + qemu_fdt_setprop_string(fdt, name, "compatible", "syscon");
> + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
> + qemu_fdt_setprop_cell(fdt, name, "phandle", syscon_phandle);
> + g_free(name);
> +
> + name = g_strdup_printf("/poweroff");
> + qemu_fdt_add_subnode(fdt, name);
> + qemu_fdt_setprop_string(fdt, name, "compatible", "syscon-poweroff");
> + qemu_fdt_setprop_cell(fdt, name, "regmap", syscon_phandle);
> + qemu_fdt_setprop_cell(fdt, name, "offset", 0x0);
> + qemu_fdt_setprop_cell(fdt, name, "value", PVPANIC_SHUTDOWN);
> + g_free(name);
> +}
> +
> +static void create_fdt_uart(void *fdt, const MemMapEntry *mem, int irq,
> + int irqchip_phandle)
> +{
> + g_autofree char *name = g_strdup_printf("/soc/serial@%"HWADDR_PRIX,
> + mem->base);
> +
> + qemu_fdt_add_subnode(fdt, name);
> + qemu_fdt_setprop_string(fdt, name, "compatible", "ns16550a");
> + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
> + qemu_fdt_setprop_cell(fdt, name, "reg-shift", 2);
> + qemu_fdt_setprop_cell(fdt, name, "reg-io-width", 4);
> + qemu_fdt_setprop_cell(fdt, name, "clock-frequency", 3686400);
> + qemu_fdt_setprop_cell(fdt, name, "interrupt-parent", irqchip_phandle);
> + qemu_fdt_setprop_cells(fdt, name, "interrupts", irq, 0x4);
> +
> + qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", name);
> + qemu_fdt_setprop_string(fdt, "/aliases", "serial0", name);
> +}
> +
> +static void create_fdt_fw_cfg(void *fdt, const MemMapEntry *mem)
> +{
> + g_autofree char *name = g_strdup_printf("/fw-cfg@%"HWADDR_PRIX, mem->base);
> +
> + qemu_fdt_add_subnode(fdt, name);
> + qemu_fdt_setprop_string(fdt, name, "compatible", "qemu,fw-cfg-mmio");
> + qemu_fdt_setprop_sized_cells(fdt, name, "reg", 2, mem->base, 2, mem->size);
> + qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0);
> +}
> +
> +static void finalize_fdt(TTAtlantisState *s)
> +{
> + uint32_t aplic_s_phandle = next_phandle();
> + uint32_t imsic_s_phandle = next_phandle();
> + void *fdt = MACHINE(s)->fdt;
> +
> + create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
> +
> + /*
> + * We want to do this, but the Linux aplic driver was broken before v6.16
> + *
> + * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
> + * aplic_s_phandle);
> + */
And now we are stuck with hacks like this, which we really don't want
to be dealing with. Maybe it's best to leave the device tree to the
kernel.
> +
> + create_fdt_reset(fdt, &s->memmap[TT_ATL_SYSCON]);
> +
> + create_fdt_uart(fdt, &s->memmap[TT_ATL_UART0], TT_ATL_UART0_IRQ,
> + aplic_s_phandle);
> +}
> +
> +static void create_fdt(TTAtlantisState *s)
> +{
> + MachineState *ms = MACHINE(s);
> + uint8_t rng_seed[32];
> + g_autofree char *name = NULL;
> + void *fdt;
> +
> + fdt = create_device_tree(&s->fdt_size);
> + if (!fdt) {
> + error_report("create_device_tree() failed");
> + exit(1);
> + }
> + ms->fdt = fdt;
> +
> + qemu_fdt_setprop_string(fdt, "/", "model",
> + "Tenstorrent Atlantis RISC-V Machine");
> + qemu_fdt_setprop_string(fdt, "/", "compatible", "tenstorrent,atlantis");
> + qemu_fdt_setprop_cell(fdt, "/", "#size-cells", 0x2);
> + qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 0x2);
> +
> + qemu_fdt_add_subnode(fdt, "/soc");
> + qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0);
> + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus");
> + qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
> + qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
> +
> + qemu_fdt_add_subnode(fdt, "/chosen");
> +
> + /* Pass seed to RNG */
> + qemu_guest_getrandom_nofail(rng_seed, sizeof(rng_seed));
> + qemu_fdt_setprop(fdt, "/chosen", "rng-seed", rng_seed, sizeof(rng_seed));
> +
> + qemu_fdt_add_subnode(fdt, "/aliases");
> +
> + create_fdt_fw_cfg(fdt, &s->memmap[TT_ATL_FW_CFG]);
> + create_fdt_pmu(s);
> +}
> +
> +static DeviceState *create_reboot_device(const MemMapEntry *mem)
> +{
> + DeviceState *dev = qdev_new(TYPE_PVPANIC_MMIO_DEVICE);
> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +
> + qdev_prop_set_uint32(dev, "events", PVPANIC_SHUTDOWN | PVPANIC_PANICKED);
> +
> + sysbus_realize_and_unref(sbd, &error_fatal);
> + sysbus_mmio_map(sbd, 0, mem->base);
> +
> + return dev;
> +}
> +
> +static FWCfgState *create_fw_cfg(const MemMapEntry *mem, int num_cpus)
> +{
> + FWCfgState *fw_cfg;
> + hwaddr base = mem->base;
> +
> + fw_cfg = fw_cfg_init_mem_dma(base + 8, base, 8, base + 16,
> + &address_space_memory);
> + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, num_cpus);
> +
> + return fw_cfg;
> +}
> +
> +static void tt_atlantis_machine_done(Notifier *notifier, void *data)
> +{
> + TTAtlantisState *s = container_of(notifier, TTAtlantisState, machine_done);
> + MachineState *machine = MACHINE(s);
> + hwaddr start_addr = s->memmap[TT_ATL_DDR_LO].base;
> + hwaddr mem_size;
> + target_ulong firmware_end_addr, kernel_start_addr;
> + const char *firmware_name = riscv_default_firmware_name(&s->soc);
> + uint64_t fdt_load_addr;
> + uint64_t kernel_entry;
> + RISCVBootInfo boot_info;
> +
> + /*
> + * A user provided dtb must include everything, including
> + * dynamic sysbus devices. Our FDT needs to be finalized.
> + */
> + if (machine->dtb == NULL) {
> + finalize_fdt(s);
> + }
> +
> + mem_size = machine->ram_size;
> + if (mem_size > s->memmap[TT_ATL_DDR_LO].size) {
> + mem_size = s->memmap[TT_ATL_DDR_LO].size;
> + }
> + riscv_boot_info_init_discontig_mem(&boot_info, &s->soc,
> + s->memmap[TT_ATL_DDR_LO].base,
> + mem_size);
> +
> + firmware_end_addr = riscv_find_and_load_firmware(machine, &boot_info,
> + firmware_name,
> + &start_addr, NULL);
> +
> + kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info,
> + firmware_end_addr);
> + if (machine->kernel_filename) {
> + riscv_load_kernel(machine, &boot_info, kernel_start_addr,
> + true, NULL);
> + } else {
> + riscv_setup_halting_payload(&boot_info, kernel_start_addr);
> + }
> + kernel_entry = boot_info.image_low_addr;
> +
> + fdt_load_addr = riscv_compute_fdt_addr(s->memmap[TT_ATL_DDR_LO].base,
> + s->memmap[TT_ATL_DDR_LO].size,
> + machine, &boot_info);
> + riscv_load_fdt(fdt_load_addr, machine->fdt);
> +
> + /* load the reset vector */
> + riscv_setup_rom_reset_vec(machine, &s->soc, start_addr,
> + s->memmap[TT_ATL_BOOTROM].base,
> + s->memmap[TT_ATL_BOOTROM].size,
> + kernel_entry,
> + fdt_load_addr);
> +
> +}
> +
> +static void tt_atlantis_machine_init(MachineState *machine)
> +{
> + TTAtlantisState *s = TT_ATLANTIS_MACHINE(machine);
> +
> + MemoryRegion *system_memory = get_system_memory();
> + MemoryRegion *ram_hi = g_new(MemoryRegion, 1);
> + MemoryRegion *ram_lo = g_new(MemoryRegion, 1);
> + MemoryRegion *bootrom = g_new(MemoryRegion, 1);
> + ram_addr_t lo_ram_size, hi_ram_size;
> + int hart_count = machine->smp.cpus;
> + int base_hartid = 0;
> +
> + s->memmap = tt_atlantis_memmap;
> +
> + object_initialize_child(OBJECT(machine), "soc", &s->soc,
> + TYPE_RISCV_HART_ARRAY);
> + object_property_set_str(OBJECT(&s->soc), "cpu-type", machine->cpu_type,
> + &error_abort);
> + object_property_set_int(OBJECT(&s->soc), "hartid-base", base_hartid,
> + &error_abort);
> + object_property_set_int(OBJECT(&s->soc), "num-harts", hart_count,
> + &error_abort);
> + object_property_set_int(OBJECT(&s->soc), "resetvec",
> + s->memmap[TT_ATL_BOOTROM].base,
> + &error_abort);
> + sysbus_realize(SYS_BUS_DEVICE(&s->soc), &error_fatal);
> +
> + s->irqchip = riscv_create_aia(true, TT_IRQCHIP_GUESTS,
> + TT_IRQCHIP_NUM_SOURCES,
> + &s->memmap[TT_ATL_MAPLIC],
> + &s->memmap[TT_ATL_SAPLIC],
> + &s->memmap[TT_ATL_MIMSIC],
> + &s->memmap[TT_ATL_SIMSIC],
> + 0, base_hartid, hart_count,
> + TT_IRQCHIP_NUM_MSIS,
> + TT_IRQCHIP_NUM_PRIO_BITS);
> +
> + riscv_aclint_mtimer_create(s->memmap[TT_ATL_ACLINT].base,
> + TT_ACLINT_MTIME_SIZE,
> + base_hartid, hart_count,
> + TT_ACLINT_MTIMECMP,
> + TT_ACLINT_MTIME,
> + TT_ACLINT_TIMEBASE_FREQ, true);
> +
> + /* DDR */
> +
> + /* The high address covers all of RAM, the low address just the first 2GB */
> + lo_ram_size = s->memmap[TT_ATL_DDR_LO].size;
> + hi_ram_size = s->memmap[TT_ATL_DDR_HI].size;
> + if (machine->ram_size > hi_ram_size) {
> + char *sz = size_to_str(hi_ram_size);
> + error_report("RAM size is too large, maximum is %s", sz);
> + g_free(sz);
> + exit(EXIT_FAILURE);
> + }
> +
> + memory_region_init_alias(ram_lo, OBJECT(machine), "ram.low", machine->ram,
> + 0, lo_ram_size);
> + memory_region_init_alias(ram_hi, OBJECT(machine), "ram.high", machine->ram,
> + 0, hi_ram_size);
> + memory_region_add_subregion(system_memory,
> + s->memmap[TT_ATL_DDR_LO].base, ram_lo);
> + memory_region_add_subregion(system_memory,
> + s->memmap[TT_ATL_DDR_HI].base, ram_hi);
> +
> + /* Boot ROM */
> + memory_region_init_rom(bootrom, NULL, "tt-atlantis.bootrom",
> + s->memmap[TT_ATL_BOOTROM].size, &error_fatal);
> + memory_region_add_subregion(system_memory, s->memmap[TT_ATL_BOOTROM].base,
> + bootrom);
> +
> + /*
> + * Init fw_cfg. Must be done before riscv_load_fdt, otherwise the
> + * device tree cannot be altered and we get FDT_ERR_NOSPACE.
> + */
> + s->fw_cfg = create_fw_cfg(&s->memmap[TT_ATL_FW_CFG], machine->smp.cpus);
> + rom_set_fw(s->fw_cfg);
> +
> + /* Reboot and exit */
> + create_reboot_device(&s->memmap[TT_ATL_SYSCON]);
> +
> + /* UART */
> + serial_mm_init(system_memory, s->memmap[TT_ATL_UART0].base, 2,
> + qdev_get_gpio_in(s->irqchip, TT_ATL_UART0_IRQ),
> + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN);
> +
> + /* Load or create device tree */
> + if (machine->dtb) {
> + machine->fdt = load_device_tree(machine->dtb, &s->fdt_size);
> + if (!machine->fdt) {
> + error_report("load_device_tree() failed");
> + exit(1);
> + }
> + } else {
> + create_fdt(s);
> + }
> +
> + s->machine_done.notify = tt_atlantis_machine_done;
> + qemu_add_machine_init_done_notifier(&s->machine_done);
> +}
> +
> +static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
> +{
> + MachineClass *mc = MACHINE_CLASS(oc);
> +
> + mc->desc = "Tenstorrent Atlantis RISC-V SoC";
> + mc->init = tt_atlantis_machine_init;
> + mc->max_cpus = 8;
> + mc->default_cpus = 8;
> + mc->default_ram_size = 2 * GiB;
> + mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON;
> + mc->block_default_type = IF_VIRTIO;
> + mc->no_cdrom = 1;
> + mc->default_ram_id = "tt_atlantis.ram";
> +}
> +
> +static const TypeInfo tt_atlantis_types[] = {
> + {
> + .name = MACHINE_TYPE_NAME("tt-atlantis"),
> + .parent = TYPE_MACHINE,
> + .class_init = tt_atlantis_machine_class_init,
> + .instance_size = sizeof(TTAtlantisState),
> + },
> +};
> +
> +DEFINE_TYPES(tt_atlantis_types)
> diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> index 0222c93f878b..0601ae1a7494 100644
> --- a/hw/riscv/Kconfig
> +++ b/hw/riscv/Kconfig
> @@ -120,6 +120,21 @@ config SPIKE
> select RISCV_ACLINT
> select SIFIVE_PLIC
>
> +config TENSTORRENT
> + bool
> + default y
> + depends on RISCV64
> + imply PCI_DEVICES
> + imply TEST_DEVICES
> + select DEVICE_TREE
> + select RISCV_NUMA
Do you actually need NUMA? You have a numa include as well which I
don't think is required.
Alistair
> + select PVPANIC_MMIO
> + select SERIAL_MM
> + select RISCV_ACLINT
> + select RISCV_APLIC
> + select RISCV_IMSIC
> + select FW_CFG_DMA
> +
> config XIANGSHAN_KUNMINGHU
> bool
> default y
> diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build
> index e53c180d0d10..026e79591f4b 100644
> --- a/hw/riscv/meson.build
> +++ b/hw/riscv/meson.build
> @@ -9,6 +9,7 @@ riscv_ss.add(when: 'CONFIG_SIFIVE_E', if_true: files('sifive_e.c'))
> riscv_ss.add(when: 'CONFIG_SIFIVE_U', if_true: files('sifive_u.c'))
> riscv_ss.add(when: 'CONFIG_SPIKE', if_true: files('spike.c'))
> riscv_ss.add(when: 'CONFIG_MICROCHIP_PFSOC', if_true: files('microchip_pfsoc.c'))
> +riscv_ss.add(when: 'CONFIG_TENSTORRENT', if_true: files('tt_atlantis.c'))
> riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c'))
> riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files(
> 'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c'))
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 10/13] hw/riscv/atlantis: Add PCIe controller
2026-04-25 13:17 ` [PATCH v4 10/13] hw/riscv/atlantis: Add PCIe controller Joel Stanley
@ 2026-04-30 0:04 ` Alistair Francis
2026-05-05 1:38 ` Nicholas Piggin
0 siblings, 1 reply; 44+ messages in thread
From: Alistair Francis @ 2026-04-30 0:04 UTC (permalink / raw)
To: Joel Stanley
Cc: Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Nicholas Piggin, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
On Sat, Apr 25, 2026 at 11:21 PM Joel Stanley <joel@jms.id.au> wrote:
>
> From: Nicholas Piggin <npiggin@gmail.com>
>
> Atlantis has 3 DesignWare PCIe host controllers, however the boot firmware
> has not been tested with the QEMU DesignWare model so a generic ECAM
> controller is used for the interim. The system bus aperture and PCIe IO
I don't follow.
The actual hardware has a different PCIe controller? This is user
visible so you can't easily change it once it's merged. Generally user
visible changes go through a deprecation process, we have some
exceptions for soft cores which change all the time. But ideally this
should model the hardware from the start.
Also if it isn't that should be noted in the board documentation.
A quick grep shows support for a DesignWare PCIe Host, why not use that?
Alistair
> mappings match the hardware configuration. Only a single PCIe controller is
> implemented, because the gpex model does not support multiple controllers.
>
> Booting Linux v6.19:
>
> pci-host-generic 1110000000.pci: host bridge /soc/pci@1110000000 ranges:
> pci-host-generic 1110000000.pci: IO 0x30011000000..0x300110fffff -> 0x0000000000
> pci-host-generic 1110000000.pci: MEM 0x30080000000..0x300ffffffff -> 0x0080000000
> pci-host-generic 1110000000.pci: MEM 0x30100000000..0x3ff7fffffff -> 0x30100000000
> pci-host-generic 1110000000.pci: ECAM at [mem 0x1110000000-0x111fffffff] for [bus 00-ff]
> pci-host-generic 1110000000.pci: PCI host bridge to bus 0000:00
> pci_bus 0000:00: root bus resource [bus 00-ff]
> pci_bus 0000:00: root bus resource [io 0x0000-0xfffff]
> pci_bus 0000:00: root bus resource [mem 0x30080000000-0x300ffffffff] (bus address [0x80000000-0xffffffff])
> pci_bus 0000:00: root bus resource [mem 0x30100000000-0x3ff7fffffff pref]
> pci 0000:00:00.0: [1b36:0008] type 00 class 0x060000 conventional PCI endpoint
> pci 0000:00:01.0: [1af4:1001] type 00 class 0x010000 conventional PCI endpoint
> pci 0000:00:01.0: BAR 0 [io 0x0000-0x007f]
> pci 0000:00:01.0: BAR 1 [mem 0x00000000-0x00000fff]
> pci 0000:00:01.0: BAR 4 [mem 0x00000000-0x00003fff 64bit pref]
> pci 0000:00:01.0: BAR 4 [mem 0x30100000000-0x30100003fff 64bit pref]: assigned
> pci 0000:00:01.0: BAR 1 [mem 0x30080000000-0x30080000fff]: assigned
> pci 0000:00:01.0: BAR 0 [io 0x0080-0x00ff]: assigned
> pci_bus 0000:00: resource 4 [io 0x0000-0xfffff]
> pci_bus 0000:00: resource 5 [mem 0x30080000000-0x300ffffffff]
> pci_bus 0000:00: resource 6 [mem 0x30100000000-0x3ff7fffffff pref]
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> ---
> v4:
> - Remove select PCI
> - Rework addressing, use a table for describing the map
> - Remove traces of controller 2 and 3; they will be implemented in
> future patches
> v3: Avoid leaks in the dt string allocation
> ---
> include/hw/riscv/tt_atlantis.h | 12 ++
> hw/riscv/tt_atlantis.c | 234 ++++++++++++++++++++++++++++++++-
> hw/riscv/Kconfig | 1 +
> 3 files changed, 246 insertions(+), 1 deletion(-)
>
> diff --git a/include/hw/riscv/tt_atlantis.h b/include/hw/riscv/tt_atlantis.h
> index 9fe50d9c9040..070f53eeb450 100644
> --- a/include/hw/riscv/tt_atlantis.h
> +++ b/include/hw/riscv/tt_atlantis.h
> @@ -17,6 +17,13 @@
> #define TYPE_TT_ATLANTIS_MACHINE MACHINE_TYPE_NAME("tt-atlantis")
> OBJECT_DECLARE_SIMPLE_TYPE(TTAtlantisState, TT_ATLANTIS_MACHINE)
>
> +typedef struct {
> + hwaddr pcie_addr; /* Device side address */
> + hwaddr window_offset; /* Host side offset */
> + hwaddr size;
> + uint32_t flags;
> +} PciMapEntry;
> +
> struct TTAtlantisState {
> /*< private >*/
> MachineState parent;
> @@ -25,9 +32,11 @@ struct TTAtlantisState {
> Notifier machine_done;
> FWCfgState *fw_cfg;
> const MemMapEntry *memmap;
> + const PciMapEntry *pcimap;
>
> RISCVHartArrayState soc;
> DeviceState *irqchip;
> + GPEXHost gpex_host;
>
> int fdt_size;
> };
> @@ -39,6 +48,7 @@ enum {
> TT_ATL_UART2_IRQ = 40,
> TT_ATL_UART3_IRQ = 41,
> TT_ATL_UART4_IRQ = 42,
> + TT_ATL_PCIE0_INTA_IRQ = 96,
> };
>
> enum {
> @@ -50,6 +60,8 @@ enum {
> TT_ATL_I2C0,
> TT_ATL_MAPLIC,
> TT_ATL_MIMSIC,
> + TT_ATL_PCIE_ECAM0,
> + TT_ATL_PCIE_MMIO0,
> TT_ATL_SAPLIC,
> TT_ATL_SIMSIC,
> TT_ATL_SYSCON,
> diff --git a/hw/riscv/tt_atlantis.c b/hw/riscv/tt_atlantis.c
> index 5b66d7e95bb9..1eb473907ad7 100644
> --- a/hw/riscv/tt_atlantis.c
> +++ b/hw/riscv/tt_atlantis.c
> @@ -27,6 +27,7 @@
> #include "hw/intc/riscv_aclint.h"
> #include "hw/intc/riscv_aplic.h"
> #include "hw/misc/pvpanic.h"
> +#include "hw/pci-host/gpex.h"
>
> #include "system/system.h"
> #include "system/device_tree.h"
> @@ -51,6 +52,13 @@
> #define TT_ACLINT_MTIMECMP 0x8000
> #define TT_ACLINT_TIMEBASE_FREQ 1000000000
>
> +
> +enum {
> + PCIE0_PIO,
> + PCIE0_MMIO_32,
> + PCIE0_MMIO_64,
> +};
> +
> static const MemMapEntry tt_atlantis_memmap[] = {
> /* Keep sorted with :'<,'>!sort -g -k 4 */
> [TT_ATL_DDR_LO] = { 0x00000000, 0x80000000 },
> @@ -66,6 +74,19 @@ static const MemMapEntry tt_atlantis_memmap[] = {
> [TT_ATL_MAPLIC] = { 0xcc000000, 0x4000000 },
> [TT_ATL_SAPLIC] = { 0xe8000000, 0x4000000 },
> [TT_ATL_DDR_HI] = { 0x100000000, 0x1000000000 },
> + [TT_ATL_PCIE_ECAM0] = { 0x01110000000, 0x10000000 },
> + [TT_ATL_PCIE_MMIO0] = { 0x30000000000, 0x10000000000 },
> +};
> +
> +static const PciMapEntry tt_atlantis_pci_map[] = {
> + /* pcie_addr window_offset size flags */
> + { 0x0, 0x11000000, 1 * MiB, FDT_PCI_RANGE_IOPORT |
> + FDT_PCI_RANGE_RELOCATABLE },
> + { 0x80000000, 0x80000000, 2 * GiB, FDT_PCI_RANGE_MMIO |
> + FDT_PCI_RANGE_RELOCATABLE },
> + { 0x30100000000, 0x100000000, 1018 * GiB, FDT_PCI_RANGE_MMIO_64BIT |
> + FDT_PCI_RANGE_RELOCATABLE |
> + FDT_PCI_RANGE_PREFETCHABLE },
> };
>
> static uint32_t next_phandle(void)
> @@ -74,6 +95,59 @@ static uint32_t next_phandle(void)
> return phandle++;
> }
>
> +static void create_pcie_irq_map(void *fdt, char *nodename, int legacy_irq,
> + uint32_t irqchip_phandle)
> +{
> + int pin, dev;
> + uint32_t irq_map_stride = 0;
> + uint32_t full_irq_map[PCI_NUM_PINS * PCI_NUM_PINS *
> + FDT_MAX_INT_MAP_WIDTH] = {};
> + uint32_t *irq_map = full_irq_map;
> +
> + /*
> + * This code creates a standard swizzle of interrupts such that
> + * each device's first interrupt is based on it's PCI_SLOT number.
> + * (See pci_swizzle_map_irq_fn())
> + *
> + * We only need one entry per interrupt in the table (not one per
> + * possible slot) seeing the interrupt-map-mask will allow the table
> + * to wrap to any number of devices.
> + */
> + for (dev = 0; dev < PCI_NUM_PINS; dev++) {
> + int devfn = dev * 0x8;
> +
> + for (pin = 0; pin < PCI_NUM_PINS; pin++) {
> + int irq_nr = legacy_irq + ((pin + PCI_SLOT(devfn)) % PCI_NUM_PINS);
> + int i = 0;
> +
> + /* Fill PCI address cells */
> + irq_map[i] = cpu_to_be32(devfn << 8);
> + i += FDT_PCI_ADDR_CELLS;
> +
> + /* Fill PCI Interrupt cells */
> + irq_map[i] = cpu_to_be32(pin + 1);
> + i += FDT_PCI_INT_CELLS;
> +
> + /* Fill interrupt controller phandle and cells */
> + irq_map[i++] = cpu_to_be32(irqchip_phandle);
> + irq_map[i++] = cpu_to_be32(irq_nr);
> + irq_map[i++] = cpu_to_be32(0x4);
> +
> + if (!irq_map_stride) {
> + irq_map_stride = i;
> + }
> + irq_map += irq_map_stride;
> + }
> + }
> +
> + qemu_fdt_setprop(fdt, nodename, "interrupt-map", full_irq_map,
> + PCI_NUM_PINS * PCI_NUM_PINS *
> + irq_map_stride * sizeof(uint32_t));
> +
> + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask",
> + 0x1800, 0, 0, 0x7);
> +}
> +
> static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
> {
> uint32_t cpu_phandle;
> @@ -314,6 +388,52 @@ static void create_fdt_cpu(TTAtlantisState *s, const MemMapEntry *memmap,
> IRQ_S_EXT, s->soc.num_harts);
> }
>
> +static void create_fdt_pcie(void *fdt,
> + const MemMapEntry *ecam_mem,
> + const MemMapEntry *pcie_mem,
> + const PciMapEntry *pio,
> + const PciMapEntry *mmio32,
> + const PciMapEntry *mmio64,
> + int legacy_irq,
> + uint32_t aplic_s_phandle,
> + uint32_t imsic_s_phandle)
> +{
> + g_autofree char *name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX,
> + ecam_mem->base);
> +
> + qemu_fdt_setprop_cell(fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS);
> + qemu_fdt_setprop_cell(fdt, name, "#interrupt-cells", FDT_PCI_INT_CELLS);
> + qemu_fdt_setprop_cell(fdt, name, "#size-cells", 0x2);
> + qemu_fdt_setprop_string(fdt, name, "compatible", "pci-host-ecam-generic");
> + qemu_fdt_setprop_string(fdt, name, "device_type", "pci");
> + qemu_fdt_setprop_cells(fdt, name, "bus-range", 0,
> + ecam_mem->size / PCIE_MMCFG_SIZE_MIN - 1);
> + qemu_fdt_setprop(fdt, name, "dma-coherent", NULL, 0);
> + qemu_fdt_setprop_cell(fdt, name, "msi-parent", imsic_s_phandle);
> +
> + qemu_fdt_setprop_sized_cells(fdt, name, "reg",
> + 2, ecam_mem->base,
> + 2, ecam_mem->size);
> +
> + qemu_fdt_setprop_sized_cells(fdt, name, "ranges",
> + 1, pio->flags,
> + 2, pio->pcie_addr,
> + 2, pio->window_offset + pcie_mem->base,
> + 2, pio->size,
> +
> + 1, mmio32->flags,
> + 2, mmio32->pcie_addr,
> + 2, mmio32->window_offset + pcie_mem->base,
> + 2, mmio32->size,
> +
> + 1, mmio64->flags,
> + 2, mmio64->pcie_addr,
> + 2, mmio64->window_offset + pcie_mem->base,
> + 2, mmio64->size);
> +
> + create_pcie_irq_map(fdt, name, legacy_irq, aplic_s_phandle);
> +}
> +
> static void create_fdt_reset(void *fdt, const MemMapEntry *mem)
> {
> uint32_t syscon_phandle = next_phandle();
> @@ -379,6 +499,15 @@ static void finalize_fdt(TTAtlantisState *s)
> * aplic_s_phandle);
> */
>
> + create_fdt_pcie(fdt,
> + &s->memmap[TT_ATL_PCIE_ECAM0],
> + &s->memmap[TT_ATL_PCIE_MMIO0],
> + &s->pcimap[PCIE0_PIO],
> + &s->pcimap[PCIE0_MMIO_32],
> + &s->pcimap[PCIE0_MMIO_64],
> + TT_ATL_PCIE0_INTA_IRQ,
> + aplic_s_phandle, imsic_s_phandle);
> +
> create_fdt_reset(fdt, &s->memmap[TT_ATL_SYSCON]);
>
> create_fdt_uart(fdt, &s->memmap[TT_ATL_UART0], TT_ATL_UART0_IRQ,
> @@ -389,7 +518,7 @@ static void create_fdt(TTAtlantisState *s)
> {
> MachineState *ms = MACHINE(s);
> uint8_t rng_seed[32];
> - g_autofree char *name = NULL;
> + char *name;
> void *fdt;
>
> fdt = create_device_tree(&s->fdt_size);
> @@ -411,6 +540,15 @@ static void create_fdt(TTAtlantisState *s)
> qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2);
> qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2);
>
> + /*
> + * The "/soc/pci@..." node is needed for PCIE hotplugs
> + * that might happen before finalize_fdt().
> + */
> + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIX,
> + s->memmap[TT_ATL_PCIE_ECAM0].base);
> + qemu_fdt_add_subnode(fdt, name);
> + g_free(name);
> +
> qemu_fdt_add_subnode(fdt, "/chosen");
>
> /* Pass seed to RNG */
> @@ -423,6 +561,95 @@ static void create_fdt(TTAtlantisState *s)
> create_fdt_pmu(s);
> }
>
> +static void gpex_pcie_init_one(TTAtlantisState *s, GPEXHost *gpex_host,
> + MemoryRegion *mr,
> + const MemMapEntry *ecam_mem,
> + const MemMapEntry *pcie_mem,
> + const PciMapEntry *mem_pio,
> + const PciMapEntry *mem_mmio32,
> + const PciMapEntry *mem_mmio64,
> + int legacy_irq)
> +{
> + DeviceState *dev;
> + Object *obj;
> + MemoryRegion *ecam_alias, *ecam_reg;
> + MemoryRegion *mmio32_alias, *mmio64_alias, *mmio_reg;
> + hwaddr ecam_base = ecam_mem->base;
> + hwaddr ecam_size = ecam_mem->size;
> + hwaddr pio_base = mem_pio->window_offset + pcie_mem->base;
> + hwaddr pio_size = mem_pio->size;
> + hwaddr mmio32_base = mem_mmio32->window_offset + pcie_mem->base;
> + hwaddr mmio32_size = mem_mmio32->size;
> + hwaddr mmio64_base = mem_mmio64->window_offset + pcie_mem->base;
> + hwaddr mmio64_size = mem_mmio64->size;
> + qemu_irq irq;
> + char name[16];
> + int i;
> +
> + snprintf(name, sizeof(name), "pcie");
> + object_initialize_child(OBJECT(s), name, gpex_host, TYPE_GPEX_HOST);
> + dev = DEVICE(gpex_host);
> + obj = OBJECT(dev);
> +
> + object_property_set_uint(obj, PCI_HOST_ECAM_BASE, ecam_base, &error_abort);
> + object_property_set_int(obj, PCI_HOST_ECAM_SIZE, ecam_size, &error_abort);
> + object_property_set_uint(obj, PCI_HOST_BELOW_4G_MMIO_BASE, mmio32_base,
> + &error_abort);
> + object_property_set_int(obj, PCI_HOST_BELOW_4G_MMIO_SIZE, mmio32_size,
> + &error_abort);
> + object_property_set_uint(obj, PCI_HOST_ABOVE_4G_MMIO_BASE, mmio64_base,
> + &error_abort);
> + object_property_set_int(obj, PCI_HOST_ABOVE_4G_MMIO_SIZE, mmio64_size,
> + &error_abort);
> + object_property_set_uint(obj, PCI_HOST_PIO_BASE, pio_base, &error_abort);
> + object_property_set_int(obj, PCI_HOST_PIO_SIZE, pio_size, &error_abort);
> +
> + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
> +
> + ecam_alias = g_new0(MemoryRegion, 1);
> + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0);
> + snprintf(name, sizeof(name), "pcie.ecam");
> + memory_region_init_alias(ecam_alias, obj, name,
> + ecam_reg, 0, ecam_size);
> + memory_region_add_subregion(mr, ecam_base, ecam_alias);
> +
> + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1);
> +
> + mmio32_alias = g_new0(MemoryRegion, 1);
> + snprintf(name, sizeof(name), "pcie.mmio32");
> + memory_region_init_alias(mmio32_alias, obj, name,
> + mmio_reg, mmio32_base & 0xffffffffUL, mmio32_size);
> + memory_region_add_subregion(mr, mmio32_base, mmio32_alias);
> +
> + mmio64_alias = g_new0(MemoryRegion, 1);
> + snprintf(name, sizeof(name), "pcie.mmio64");
> + memory_region_init_alias(mmio64_alias, obj, name,
> + mmio_reg, mmio64_base, mmio64_size);
> + memory_region_add_subregion(mr, mmio64_base, mmio64_alias);
> +
> + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base);
> +
> + for (i = 0; i < PCI_NUM_PINS; i++) {
> + irq = qdev_get_gpio_in(s->irqchip, legacy_irq + i);
> +
> + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq);
> + gpex_set_irq_num(GPEX_HOST(dev), i, legacy_irq + i);
> + }
> +
> + gpex_host->gpex_cfg.bus = PCI_HOST_BRIDGE(dev)->bus;
> +}
> +
> +static void gpex_pcie_init(TTAtlantisState *s, MemoryRegion *mr)
> +{
> + gpex_pcie_init_one(s, &s->gpex_host, mr,
> + &s->memmap[TT_ATL_PCIE_ECAM0],
> + &s->memmap[TT_ATL_PCIE_MMIO0],
> + &s->pcimap[PCIE0_PIO],
> + &s->pcimap[PCIE0_MMIO_32],
> + &s->pcimap[PCIE0_MMIO_64],
> + TT_ATL_PCIE0_INTA_IRQ);
> +}
> +
> static DeviceState *create_reboot_device(const MemMapEntry *mem)
> {
> DeviceState *dev = qdev_new(TYPE_PVPANIC_MMIO_DEVICE);
> @@ -517,6 +744,7 @@ static void tt_atlantis_machine_init(MachineState *machine)
> int base_hartid = 0;
>
> s->memmap = tt_atlantis_memmap;
> + s->pcimap = tt_atlantis_pci_map;
>
> object_initialize_child(OBJECT(machine), "soc", &s->soc,
> TYPE_RISCV_HART_ARRAY);
> @@ -582,6 +810,9 @@ static void tt_atlantis_machine_init(MachineState *machine)
> s->fw_cfg = create_fw_cfg(&s->memmap[TT_ATL_FW_CFG], machine->smp.cpus);
> rom_set_fw(s->fw_cfg);
>
> + /* PCIe */
> + gpex_pcie_init(s, system_memory);
> +
> /* Reboot and exit */
> create_reboot_device(&s->memmap[TT_ATL_SYSCON]);
>
> @@ -617,6 +848,7 @@ static void tt_atlantis_machine_class_init(ObjectClass *oc, const void *data)
> mc->default_cpu_type = TYPE_RISCV_CPU_TT_ASCALON;
> mc->block_default_type = IF_VIRTIO;
> mc->no_cdrom = 1;
> + mc->pci_allow_0_address = true;
> mc->default_ram_id = "tt_atlantis.ram";
> }
>
> diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> index 0601ae1a7494..f13a05c7b1d1 100644
> --- a/hw/riscv/Kconfig
> +++ b/hw/riscv/Kconfig
> @@ -129,6 +129,7 @@ config TENSTORRENT
> select DEVICE_TREE
> select RISCV_NUMA
> select PVPANIC_MMIO
> + select PCI_EXPRESS_GENERIC_BRIDGE
> select SERIAL_MM
> select RISCV_ACLINT
> select RISCV_APLIC
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 01/13] hw/i2c: Add designware i2c controller
2026-04-25 13:17 ` [PATCH v4 01/13] hw/i2c: Add designware i2c controller Joel Stanley
@ 2026-04-30 3:53 ` Alistair Francis
2026-05-05 6:20 ` Nicholas Piggin
2026-05-05 7:57 ` Philippe Mathieu-Daudé
1 sibling, 1 reply; 44+ messages in thread
From: Alistair Francis @ 2026-04-30 3:53 UTC (permalink / raw)
To: Joel Stanley
Cc: Alistair Francis, Daniel Henrique Barboza, Chao Liu, Chris Rauer,
Michael Ellerman, Nicholas Piggin, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel,
Hao Wu
On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
>
> From: Chris Rauer <crauer@google.com>
>
> In the past this model has been submitted for use with the arm virt
> machine, however in this case it will be used by the upcoming
> Tenstorrent Atlantis RISC-V machine.
>
> This is a re-submission of the model with Chris' permission, with a
> light touch of updates to make it build with qemu master.
>
> Reviewed-by: Hao Wu <wuhaotsh@google.com>
> Signed-off-by: Chris Rauer <crauer@google.com>
> Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com
> [jms: rebase and minor build fixes for class_init and reset callback]
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> ---
> v4: Remove unused random header
> v2: Add trace event for read and write, document Alano and myself as
> reviewers.
> ---
> MAINTAINERS | 8 +
> include/hw/i2c/designware_i2c.h | 101 ++++
> hw/i2c/designware_i2c.c | 817 ++++++++++++++++++++++++++++++++
> hw/i2c/Kconfig | 4 +
> hw/i2c/meson.build | 1 +
> hw/i2c/trace-events | 4 +
> 6 files changed, 935 insertions(+)
> create mode 100644 include/hw/i2c/designware_i2c.h
> create mode 100644 hw/i2c/designware_i2c.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index aa4267b15806..e1942a86eba5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2716,6 +2716,14 @@ S: Orphan
> F: hw/gpio/pcf8574.c
> F: include/gpio/pcf8574.h
>
> +DesignWare I2C
> +M: Chris Rauer <crauer@google.com>
> +R: Alano Song <alanosong@163.com>
> +R: Joel Stanley <joel@jms.id.au>
> +S: Maintained
> +F: hw/i2c/designware_i2c.c
> +F: include/hw/i2c/designware_i2c.h
> +
> Generic Loader
> M: Alistair Francis <alistair@alistair23.me>
> S: Maintained
> diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h
> new file mode 100644
> index 000000000000..0d8f904f51b7
> --- /dev/null
> +++ b/include/hw/i2c/designware_i2c.h
> @@ -0,0 +1,101 @@
> +/*
> + * DesignWare I2C Module.
> + *
> + * Copyright 2021 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +#ifndef DESIGNWARE_I2C_H
> +#define DESIGNWARE_I2C_H
> +
> +#include "hw/i2c/i2c.h"
> +#include "hw/core/irq.h"
> +#include "hw/core/sysbus.h"
> +
> +/* Size of the FIFO buffers. */
> +#define DESIGNWARE_I2C_RX_FIFO_SIZE 16
> +#define DESIGNWARE_I2C_TX_FIFO_SIZE 16
> +
> +typedef enum DesignWareI2CStatus {
> + DW_I2C_STATUS_IDLE,
> + DW_I2C_STATUS_SENDING_ADDRESS,
> + DW_I2C_STATUS_SENDING,
> + DW_I2C_STATUS_RECEIVING,
> +} DesignWareI2CStatus;
> +
> +/*
> + * struct DesignWareI2CState - DesignWare I2C device state.
> + * @bus: The underlying I2C Bus
> + * @irq: GIC interrupt line to fire on events
> + * @ic_con: : I2C control register
> + * @ic_tar: I2C target address register
> + * @ic_sar: I2C slave address register
> + * @ic_ss_scl_hcnt: Standard speed i2c clock scl high count register
> + * @ic_ss_scl_lcnt: Standard speed i2c clock scl low count register
> + * @ic_fs_scl_hcnt: Fast mode or fast mode plus i2c clock scl high count
> + * register
> + * @ic_fs_scl_lcnt:Fast mode or fast mode plus i2c clock scl low count
> + * register
> + * @ic_intr_mask: I2C Interrupt Mask Register
> + * @ic_raw_intr_stat: I2C raw interrupt status register
> + * @ic_rx_tl: I2C receive FIFO threshold register
> + * @ic_tx_tl: I2C transmit FIFO threshold register
> + * @ic_enable: I2C enable register
> + * @ic_status: I2C status register
> + * @ic_txflr: I2C transmit fifo level register
> + * @ic_rxflr: I2C receive fifo level register
> + * @ic_sda_hold: I2C SDA hold time length register
> + * @ic_tx_abrt_source: The I2C transmit abort source register
> + * @ic_sda_setup: I2C SDA setup register
> + * @ic_enable_status: I2C enable status register
> + * @ic_fs_spklen: I2C SS, FS or FM+ spike suppression limit
> + * @ic_comp_param_1: Component parameter register
> + * @ic_comp_version: I2C component version register
> + * @ic_comp_type: I2C component type register
> + * @rx_fifo: The FIFO buffer for receiving in FIFO mode.
> + * @rx_cur: The current position of rx_fifo.
> + * @status: The current status of the SMBus.
> + */
> +typedef struct DesignWareI2CState {
> + SysBusDevice parent;
> +
> + MemoryRegion iomem;
> +
> + I2CBus *bus;
> + qemu_irq irq;
> +
> + uint32_t ic_con;
> + uint32_t ic_tar;
> + uint32_t ic_sar;
> + uint32_t ic_ss_scl_hcnt;
> + uint32_t ic_ss_scl_lcnt;
> + uint32_t ic_fs_scl_hcnt;
> + uint32_t ic_fs_scl_lcnt;
> + uint32_t ic_intr_mask;
> + uint32_t ic_raw_intr_stat;
> + uint32_t ic_rx_tl;
> + uint32_t ic_tx_tl;
> + uint32_t ic_enable;
> + uint32_t ic_status;
> + uint32_t ic_txflr;
> + uint32_t ic_rxflr;
> + uint32_t ic_sda_hold;
> + uint32_t ic_tx_abrt_source;
> + uint32_t ic_sda_setup;
> + uint32_t ic_enable_status;
> + uint32_t ic_fs_spklen;
> + uint32_t ic_comp_param_1;
> + uint32_t ic_comp_version;
> + uint32_t ic_comp_type;
> +
> + uint8_t rx_fifo[DESIGNWARE_I2C_RX_FIFO_SIZE];
> + uint8_t rx_cur;
> +
> + DesignWareI2CStatus status;
> +} DesignWareI2CState;
> +
> +#define TYPE_DESIGNWARE_I2C "designware-i2c"
> +#define DESIGNWARE_I2C(obj) OBJECT_CHECK(DesignWareI2CState, (obj), \
> + TYPE_DESIGNWARE_I2C)
> +
> +#endif /* DESIGNWARE_I2C_H */
> diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c
> new file mode 100644
> index 000000000000..7d8b1c13533e
> --- /dev/null
> +++ b/hw/i2c/designware_i2c.c
> @@ -0,0 +1,817 @@
> +/*
> + * DesignWare I2C Module.
> + *
> + * Copyright 2021 Google LLC
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +
> +#include "hw/i2c/designware_i2c.h"
> +#include "migration/vmstate.h"
> +#include "qemu/bitops.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/units.h"
> +#include "trace.h"
> +
> +enum DesignWareI2CRegister {
> + DW_IC_CON = 0x00,
> + DW_IC_TAR = 0x04,
> + DW_IC_SAR = 0x08,
> + DW_IC_DATA_CMD = 0x10,
> + DW_IC_SS_SCL_HCNT = 0x14,
> + DW_IC_SS_SCL_LCNT = 0x18,
> + DW_IC_FS_SCL_HCNT = 0x1c,
> + DW_IC_FS_SCL_LCNT = 0x20,
> + DW_IC_INTR_STAT = 0x2c,
> + DW_IC_INTR_MASK = 0x30,
> + DW_IC_RAW_INTR_STAT = 0x34,
> + DW_IC_RX_TL = 0x38,
> + DW_IC_TX_TL = 0x3c,
> + DW_IC_CLR_INTR = 0x40,
> + DW_IC_CLR_RX_UNDER = 0x44,
> + DW_IC_CLR_RX_OVER = 0x48,
> + DW_IC_CLR_TX_OVER = 0x4c,
> + DW_IC_CLR_RD_REQ = 0x50,
> + DW_IC_CLR_TX_ABRT = 0x54,
> + DW_IC_CLR_RX_DONE = 0x58,
> + DW_IC_CLR_ACTIVITY = 0x5c,
> + DW_IC_CLR_STOP_DET = 0x60,
> + DW_IC_CLR_START_DET = 0x64,
> + DW_IC_CLR_GEN_CALL = 0x68,
> + DW_IC_ENABLE = 0x6c,
> + DW_IC_STATUS = 0x70,
> + DW_IC_TXFLR = 0x74,
> + DW_IC_RXFLR = 0x78,
> + DW_IC_SDA_HOLD = 0x7c,
> + DW_IC_TX_ABRT_SOURCE = 0x80,
> + DW_IC_SLV_DATA_NACK_ONLY = 0x84,
> + DW_IC_DMA_CR = 0x88,
> + DW_IC_DMA_TDLR = 0x8c,
> + DW_IC_DMA_RDLR = 0x90,
> + DW_IC_SDA_SETUP = 0x94,
> + DW_IC_ACK_GENERAL_CALL = 0x98,
> + DW_IC_ENABLE_STATUS = 0x9c,
> + DW_IC_FS_SPKLEN = 0xa0,
> + DW_IC_CLR_RESTART_DET = 0xa8,
> + DW_IC_COMP_PARAM_1 = 0xf4,
> + DW_IC_COMP_VERSION = 0xf8,
> + DW_IC_COMP_TYPE = 0xfc,
> +};
> +
> +/* DW_IC_CON fields */
> +#define DW_IC_CON_STOP_DET_IF_MASTER_ACTIV BIT(10)
> +#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9)
> +#define DW_IC_CON_TX_EMPTY_CTRL BIT(8)
> +#define DW_IC_CON_STOP_IF_ADDRESSED BIT(7)
> +#define DW_IC_CON_SLAVE_DISABLE BIT(6)
> +#define DW_IC_CON_IC_RESTART_EN BIT(5)
> +#define DW_IC_CON_10BITADDR_MASTER BIT(4)
> +#define DW_IC_CON_10BITADDR_SLAVE BIT(3)
> +#define DW_IC_CON_SPEED(rv) extract32((rv), 1, 2)
> +#define DW_IC_CON_MASTER_MODE BIT(0)
> +
> +/* DW_IC_TAR fields */
> +#define DW_IC_TAR_IC_10BITADDR_MASTER BIT(12)
> +#define DW_IC_TAR_SPECIAL BIT(11)
> +#define DW_IC_TAR_GC_OR_START BIT(10)
> +#define DW_IC_TAR_ADDRESS(rv) extract32((rv), 0, 10)
> +
> +/* DW_IC_DATA_CMD fields */
> +#define DW_IC_DATA_CMD_RESTART BIT(10)
> +#define DW_IC_DATA_CMD_STOP BIT(9)
> +#define DW_IC_DATA_CMD_CMD BIT(8)
> +#define DW_IC_DATA_CMD_DAT(rv) extract32((rv), 0, 8)
> +
> +/* DW_IC_INTR_STAT/INTR_MASK/RAW_INTR_STAT fields */
> +#define DW_IC_INTR_RESTART_DET BIT(12)
> +#define DW_IC_INTR_GEN_CALL BIT(11)
> +#define DW_IC_INTR_START_DET BIT(10)
> +#define DW_IC_INTR_STOP_DET BIT(9)
> +#define DW_IC_INTR_ACTIVITY BIT(8)
> +#define DW_IC_INTR_RX_DONE BIT(7)
> +#define DW_IC_INTR_TX_ABRT BIT(6)
> +#define DW_IC_INTR_RD_REQ BIT(5)
> +#define DW_IC_INTR_TX_EMPTY BIT(4) /* Hardware clear only. */
> +#define DW_IC_INTR_TX_OVER BIT(3)
> +#define DW_IC_INTR_RX_FULL BIT(2) /* Hardware clear only. */
> +#define DW_IC_INTR_RX_OVER BIT(1)
> +#define DW_IC_INTR_RX_UNDER BIT(0)
> +
> +/* DW_IC_ENABLE fields */
> +#define DW_IC_ENABLE_TX_CMD_BLOCK BIT(2)
> +#define DW_IC_ENABLE_ABORT BIT(1)
> +#define DW_IC_ENABLE_ENABLE BIT(0)
> +
> +/* DW_IC_STATUS fields */
> +#define DW_IC_STATUS_SLV_ACTIVITY BIT(6)
> +#define DW_IC_STATUS_MST_ACTIVITY BIT(5)
> +#define DW_IC_STATUS_RFF BIT(4)
> +#define DW_IC_STATUS_RFNE BIT(3)
> +#define DW_IC_STATUS_TFE BIT(2)
> +#define DW_IC_STATUS_TFNF BIT(1)
> +#define DW_IC_STATUS_ACTIVITY BIT(0)
> +
> +/* DW_IC_TX_ABRT_SOURCE fields */
> +#define DW_IC_TX_TX_FLUSH_CNT extract32((rv), 23, 9)
> +#define DW_IC_TX_ABRT_USER_ABRT BIT(16)
> +#define DW_IC_TX_ABRT_SLVRD_INTX BIT(15)
> +#define DW_IC_TX_ABRT_SLV_ARBLOST BIT(14)
> +#define DW_IC_TX_ABRT_SLVFLUSH_TXFIFO BIT(13)
> +#define DW_IC_TX_ARB_LOST BIT(12)
> +#define DW_IC_TX_ABRT_MASTER_DIS BIT(11)
> +#define DW_IC_TX_ABRT_10B_RD_NORSTRT BIT(10)
> +#define DW_IC_TX_ABRT_SBYTE_NORSTRT BIT(9)
> +#define DW_IC_TX_ABRT_HS_NORSTRT BIT(8)
> +#define DW_IC_TX_ABRT_SBYTE_ACKDET BIT(7)
> +#define DW_IC_TX_ABRT_HS_ACKDET BIT(6)
> +#define DW_IC_TX_ABRT_GCALL_READ BIT(5)
> +#define DW_IC_TX_ABRT_GCALL_NOACK BIT(4)
> +#define DW_IC_TX_ABRT_TXDATA_NOACK BIT(3)
> +#define DW_IC_TX_ABRT_10ADDR2_NOACK BIT(2)
> +#define DW_IC_TX_ABRT_10ADDR1_NOACK BIT(1)
> +#define DW_IC_TX_ABRT_7B_ADDR_NOACK BIT(0)
> +
> +
> +/* IC_ENABLE_STATUS fields */
> +#define DW_IC_ENABLE_STATUS_SLV_RX_DATA_LOST BIT(2)
> +#define DW_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY BIT(1)
> +#define DW_IC_ENABLE_STATUS_IC_EN BIT(0)
> +
> +/* Masks for writable registers. */
> +#define DW_IC_CON_MASK 0x000003ff
> +#define DW_IC_TAR_MASK 0x00000fff
> +#define DW_IC_SAR_MASK 0x000003ff
> +#define DW_IC_SS_SCL_HCNT_MASK 0x0000ffff
> +#define DW_IC_SS_SCL_LCNT_MASK 0x0000ffff
> +#define DW_IC_FS_SCL_HCNT_MASK 0x0000ffff
> +#define DW_IC_FS_SCL_LCNT_MASK 0x0000ffff
> +#define DW_IC_INTR_MASK_MASK 0x00001fff
> +#define DW_IC_ENABLE_MASK 0x00000007
> +#define DW_IC_SDA_HOLD_MASK 0x00ffffff
> +#define DW_IC_SDA_SETUP_MASK 0x000000ff
> +#define DW_IC_FS_SPKLEN_MASK 0x000000ff
> +
> +/* Reset values */
> +#define DW_IC_CON_INIT_VAL 0x7d
> +#define DW_IC_TAR_INIT_VAL 0x1055
> +#define DW_IC_SAR_INIT_VAL 0x55
> +#define DW_IC_SS_SCL_HCNT_INIT_VAL 0x190
> +#define DW_IC_SS_SCL_LCNT_INIT_VAL 0x1d6
> +#define DW_IC_FS_SCL_HCNT_INIT_VAL 0x3c
> +#define DW_IC_FS_SCL_LCNT_INIT_VAL 0x82
> +#define DW_IC_INTR_MASK_INIT_VAL 0x8ff
> +#define DW_IC_STATUS_INIT_VAL 0x6
> +#define DW_IC_SDA_HOLD_INIT_VAL 0x1
> +#define DW_IC_SDA_SETUP_INIT_VAL 0x64
> +#define DW_IC_FS_SPKLEN_INIT_VAL 0x2
> +
> +#define DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS BIT(7)
> +#define DW_IC_COMP_PARAM_1_HAS_DMA 0 /* bit 6 - DMA disabled. */
> +#define DW_IC_COMP_PARAM_1_INTR_IO BIT(5)
> +#define DW_IC_COMP_PARAM_1_HC_COUNT_VAL 0 /* bit 4 - disabled */
> +#define DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE (BIT(2) | BIT(3))
> +#define DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 BIT(1) /* bits 0, 1 */
> +#define DW_IC_COMP_PARAM_1_INIT_VAL \
> + (DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 | \
> + DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE | \
> + DW_IC_COMP_PARAM_1_HC_COUNT_VAL | \
> + DW_IC_COMP_PARAM_1_INTR_IO | \
> + DW_IC_COMP_PARAM_1_HAS_DMA | \
> + DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS | \
> + ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1) << 8) | \
> + ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1) << 16))
> +#define DW_IC_COMP_VERSION_INIT_VAL 0x3132302a
> +#define DW_IC_COMP_TYPE_INIT_VAL 0x44570140
I would prefer these to sue the QEMU register API
> +
> +static void dw_i2c_update_irq(DesignWareI2CState *s)
> +{
> + int level;
> + uint32_t intr = s->ic_raw_intr_stat & s->ic_intr_mask;
> +
> + level = !!((intr & DW_IC_INTR_RX_UNDER) |
> + (intr & DW_IC_INTR_RX_OVER) |
> + (intr & DW_IC_INTR_RX_FULL) |
> + (intr & DW_IC_INTR_TX_OVER) |
> + (intr & DW_IC_INTR_TX_EMPTY) |
> + (intr & DW_IC_INTR_RD_REQ) |
> + (intr & DW_IC_INTR_TX_ABRT) |
> + (intr & DW_IC_INTR_RX_DONE) |
> + (intr & DW_IC_INTR_ACTIVITY) |
> + (intr & DW_IC_INTR_STOP_DET) |
> + (intr & DW_IC_INTR_START_DET) |
> + (intr & DW_IC_INTR_GEN_CALL) |
> + (intr & DW_IC_INTR_RESTART_DET)
> + );
> + qemu_set_irq(s->irq, level);
> +}
> +
> +static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s)
> +{
> + uint32_t value = s->rx_fifo[s->rx_cur];
> +
> + if (s->status != DW_I2C_STATUS_RECEIVING) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: Attempted to read from RX fifo when not in receive "
> + "state.\n", DEVICE(s)->canonical_path);
> + if (s->status != DW_I2C_STATUS_IDLE) {
> + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
> + dw_i2c_update_irq(s);
> + }
> + return 0;
> + }
> +
> + s->rx_cur = (s->rx_cur + 1) % DESIGNWARE_I2C_RX_FIFO_SIZE;
> +
> + if (s->ic_rxflr > 0) {
> + s->ic_rxflr--;
> + } else {
> + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
> + dw_i2c_update_irq(s);
> + return 0;
> + }
> +
> + if (s->ic_rxflr <= s->ic_rx_tl) {
> + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL;
> + dw_i2c_update_irq(s);
> + }
> +
> + return value;
> +}
> +
> +static uint64_t dw_i2c_read(void *opaque, hwaddr offset, unsigned size)
> +{
> + uint64_t value = 0;
> +
> + DesignWareI2CState *s = opaque;
> +
> + switch (offset) {
> + case DW_IC_CON:
> + value = s->ic_con;
> + break;
> + case DW_IC_TAR:
> + value = s->ic_tar;
> + break;
> + case DW_IC_SAR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_sar\n",
> + DEVICE(s)->canonical_path);
> + value = s->ic_sar;
> + break;
> + case DW_IC_DATA_CMD:
> + value = dw_i2c_read_ic_data_cmd(s);
> + break;
> + case DW_IC_SS_SCL_HCNT:
> + value = s->ic_ss_scl_hcnt;
> + break;
> + case DW_IC_SS_SCL_LCNT:
> + value = s->ic_ss_scl_lcnt;
> + break;
> + case DW_IC_FS_SCL_HCNT:
> + value = s->ic_fs_scl_hcnt;
> + break;
> + case DW_IC_FS_SCL_LCNT:
> + value = s->ic_fs_scl_lcnt;
> + break;
> + case DW_IC_INTR_STAT:
> + value = s->ic_raw_intr_stat & s->ic_intr_mask;
> + break;
> + case DW_IC_INTR_MASK:
> + value = s->ic_intr_mask;
> + break;
> + case DW_IC_RAW_INTR_STAT:
> + value = s->ic_raw_intr_stat;
> + break;
> + case DW_IC_RX_TL:
> + value = s->ic_rx_tl;
> + break;
> + case DW_IC_TX_TL:
> + value = s->ic_tx_tl;
> + break;
> + case DW_IC_CLR_INTR:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_GEN_CALL |
> + DW_IC_INTR_RESTART_DET |
> + DW_IC_INTR_START_DET |
> + DW_IC_INTR_STOP_DET |
> + DW_IC_INTR_ACTIVITY |
> + DW_IC_INTR_RX_DONE |
> + DW_IC_INTR_TX_ABRT |
> + DW_IC_INTR_RD_REQ |
> + DW_IC_INTR_TX_OVER |
> + DW_IC_INTR_RX_OVER |
> + DW_IC_INTR_RX_UNDER);
> + s->ic_tx_abrt_source = 0;
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_RX_UNDER:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_UNDER);
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_RX_OVER:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_OVER);
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_TX_OVER:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_TX_OVER);
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_RD_REQ:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RD_REQ);
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_TX_ABRT:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_TX_ABRT);
> + s->ic_tx_abrt_source = 0;
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_RX_DONE:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RX_DONE);
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_ACTIVITY:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_ACTIVITY);
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_STOP_DET:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_STOP_DET);
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_START_DET:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_START_DET);
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_CLR_GEN_CALL:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_GEN_CALL);
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_ENABLE:
> + value = s->ic_enable;
> + break;
> + case DW_IC_STATUS:
> + value = s->ic_status;
> + break;
> + case DW_IC_TXFLR:
> + value = s->ic_txflr;
> + break;
> + case DW_IC_RXFLR:
> + value = s->ic_rxflr;
> + break;
> + case DW_IC_SDA_HOLD:
> + value = s->ic_sda_hold;
> + break;
> + case DW_IC_TX_ABRT_SOURCE:
> + value = s->ic_tx_abrt_source;
> + break;
> + case DW_IC_SLV_DATA_NACK_ONLY:
> + qemu_log_mask(LOG_UNIMP,
> + "%s: unsupported read - ic_slv_data_nack_only\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_DMA_CR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_cr\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_DMA_TDLR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_tdlr\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_DMA_RDLR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_dma_rdlr\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_SDA_SETUP:
> + value = s->ic_sda_setup;
> + break;
> + case DW_IC_ACK_GENERAL_CALL:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported read - ic_ack_general_call\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_ENABLE_STATUS:
> + value = s->ic_enable_status;
> + break;
> + case DW_IC_FS_SPKLEN:
> + value = s->ic_fs_spklen;
> + break;
> + case DW_IC_CLR_RESTART_DET:
> + s->ic_raw_intr_stat &= ~(DW_IC_INTR_RESTART_DET);
> + break;
> + case DW_IC_COMP_PARAM_1:
> + value = s->ic_comp_param_1;
> + break;
> + case DW_IC_COMP_VERSION:
> + value = s->ic_comp_version;
> + break;
> + case DW_IC_COMP_TYPE:
> + value = s->ic_comp_type;
> + break;
> +
> + /* This register is invalid at this point. */
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: read from invalid offset 0x%" HWADDR_PRIx "\n",
> + DEVICE(s)->canonical_path, offset);
> + break;
> + }
> +
> + trace_dw_i2c_read(DEVICE(s)->canonical_path, offset, value);
> +
> + return value;
> +}
> +
> +static void dw_i2c_write_ic_con(DesignWareI2CState *s, uint32_t value)
> +{
> + if (value & DW_IC_CON_RX_FIFO_FULL_HLD_CTRL) {
> + qemu_log_mask(LOG_UNIMP,
> + "%s: unsupported ic_con flag - RX_FIFO_FULL_HLD_CTRL\n",
> + DEVICE(s)->canonical_path);
> + }
> +
> + if (!(s->ic_enable & DW_IC_ENABLE_ENABLE)) {
> + s->ic_con = value & DW_IC_CON_MASK;
> + } else {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: invalid setting to ic_con %d when ic_enable[0]==1\n",
> + DEVICE(s)->canonical_path, value);
> + }
> +}
> +
> +static void dw_i2c_reset_to_idle(DesignWareI2CState *s)
> +{
> + s->ic_enable_status &= ~DW_IC_ENABLE_STATUS_IC_EN;
> + s->ic_raw_intr_stat &= ~DW_IC_INTR_TX_EMPTY;
> + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL;
> + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
> + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_OVER;
> + s->ic_rxflr = 0;
> + s->ic_status &= ~DW_IC_STATUS_ACTIVITY;
> + s->status = DW_I2C_STATUS_IDLE;
> + dw_i2c_update_irq(s);
> +}
> +
> +static void dw_ic_tx_abort(DesignWareI2CState *s, uint32_t src)
> +{
> + s->ic_tx_abrt_source |= src;
> + s->ic_raw_intr_stat |= DW_IC_INTR_TX_ABRT;
> + dw_i2c_reset_to_idle(s);
> + dw_i2c_update_irq(s);
> +}
> +
> +static void dw_i2c_write_ic_data_cmd(DesignWareI2CState *s, uint32_t value)
> +{
> + int recv = !!(value & DW_IC_DATA_CMD_CMD);
> +
> + if (s->status == DW_I2C_STATUS_IDLE ||
> + s->ic_raw_intr_stat & DW_IC_INTR_TX_ABRT) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: Attempted to write to TX fifo when it is held in "
> + "reset.\n", DEVICE(s)->canonical_path);
> + return;
> + }
> +
> + /* Send the address if it hasn't been sent yet. */
> + if (s->status == DW_I2C_STATUS_SENDING_ADDRESS) {
> + int rv = i2c_start_transfer(s->bus, DW_IC_TAR_ADDRESS(s->ic_tar), recv);
> + if (rv) {
> + dw_ic_tx_abort(s, DW_IC_TX_ABRT_7B_ADDR_NOACK);
> + return;
> + }
> + s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING;
> + }
> +
> + /* Send data */
> + if (!recv) {
> + int rv = i2c_send(s->bus, DW_IC_DATA_CMD_DAT(value));
> + if (rv) {
> + i2c_end_transfer(s->bus);
> + dw_ic_tx_abort(s, DW_IC_TX_ABRT_TXDATA_NOACK);
> + return;
> + }
> + dw_i2c_update_irq(s);
> + }
> +
> + /* Restart command */
> + if (value & DW_IC_DATA_CMD_RESTART && s->ic_con & DW_IC_CON_IC_RESTART_EN) {
> + s->ic_raw_intr_stat |= DW_IC_INTR_RESTART_DET |
> + DW_IC_INTR_START_DET |
> + DW_IC_INTR_ACTIVITY;
> + s->ic_status |= DW_IC_STATUS_ACTIVITY;
> + dw_i2c_update_irq(s);
> +
> + if (i2c_start_transfer(s->bus, DW_IC_TAR_ADDRESS(s->ic_tar), recv)) {
> + dw_ic_tx_abort(s, DW_IC_TX_ABRT_7B_ADDR_NOACK);
> + return;
> + }
> +
> + s->status = recv ? DW_I2C_STATUS_RECEIVING : DW_I2C_STATUS_SENDING;
> + }
> +
> + /* Receive data */
> + if (recv) {
> + uint8_t pos = (s->rx_cur + s->ic_rxflr) % DESIGNWARE_I2C_RX_FIFO_SIZE;
> +
> + if (s->ic_rxflr < DESIGNWARE_I2C_RX_FIFO_SIZE) {
> + s->rx_fifo[pos] = i2c_recv(s->bus);
> + s->ic_rxflr++;
QEMU also has the Fifo8 which would handle the FIFO for you, I would
prefer that as well.
> + } else {
> + s->ic_raw_intr_stat |= DW_IC_INTR_RX_OVER;
> + dw_i2c_update_irq(s);
> + }
> +
> + if (s->ic_rxflr > s->ic_rx_tl) {
> + s->ic_raw_intr_stat |= DW_IC_INTR_RX_FULL;
> + dw_i2c_update_irq(s);
> + }
> + if (value & DW_IC_DATA_CMD_STOP) {
> + i2c_nack(s->bus);
> + }
> + }
> +
> + /* Stop command */
> + if (value & DW_IC_DATA_CMD_STOP) {
> + s->ic_raw_intr_stat |= DW_IC_INTR_STOP_DET;
> + s->ic_status &= ~DW_IC_STATUS_ACTIVITY;
> + s->ic_raw_intr_stat &= ~DW_IC_INTR_TX_EMPTY;
> + i2c_end_transfer(s->bus);
> + dw_i2c_update_irq(s);
> + }
> +}
> +
> +static void dw_i2c_write_ic_enable(DesignWareI2CState *s, uint32_t value)
> +{
> + if (value & DW_IC_ENABLE_ENABLE && !(s->ic_con & DW_IC_CON_SLAVE_DISABLE)) {
> + qemu_log_mask(LOG_UNIMP,
> + "%s: Designware I2C slave mode is not supported.\n",
> + DEVICE(s)->canonical_path);
> + return;
> + }
> +
> + s->ic_enable = value & DW_IC_ENABLE_MASK;
> +
> + if (value & DW_IC_ENABLE_ABORT || value & DW_IC_ENABLE_TX_CMD_BLOCK) {
> + dw_ic_tx_abort(s, DW_IC_TX_ABRT_USER_ABRT);
> + return;
> + }
> +
> + if (value & DW_IC_ENABLE_ENABLE) {
> + s->ic_enable_status |= DW_IC_ENABLE_STATUS_IC_EN;
> + s->ic_status |= DW_IC_STATUS_ACTIVITY;
> + s->ic_raw_intr_stat |= DW_IC_INTR_ACTIVITY |
> + DW_IC_INTR_START_DET |
> + DW_IC_INTR_TX_EMPTY;
> + s->status = DW_I2C_STATUS_SENDING_ADDRESS;
> + dw_i2c_update_irq(s);
> + } else if ((value & DW_IC_ENABLE_ENABLE) == 0) {
> + dw_i2c_reset_to_idle(s);
> + }
> +
> +}
> +
> +static void dw_i2c_write_ic_rx_tl(DesignWareI2CState *s, uint32_t value)
> +{
> + /* Note that a value of 0 for ic_rx_tl indicates a threashold of 1. */
> + if (value > DESIGNWARE_I2C_RX_FIFO_SIZE - 1) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: invalid setting to ic_rx_tl %d\n",
> + DEVICE(s)->canonical_path, value);
> + s->ic_rx_tl = DESIGNWARE_I2C_RX_FIFO_SIZE - 1;
> + } else {
> + s->ic_rx_tl = value;
> + }
> +
> + if (s->ic_rxflr > s->ic_rx_tl && s->ic_enable & DW_IC_ENABLE_ENABLE) {
> + s->ic_raw_intr_stat |= DW_IC_INTR_RX_FULL;
> + } else {
> + s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_FULL;
> + }
> + dw_i2c_update_irq(s);
> +}
> +
> +static void dw_i2c_write_ic_tx_tl(DesignWareI2CState *s, uint32_t value)
> +{
> + /*
> + * Note that a value of 0 for ic_tx_tl indicates a threashold of 1.
> + * However, the tx threshold is not used in the model because commands are
> + * always sent out as soon as they are written.
> + */
> + if (value > DESIGNWARE_I2C_TX_FIFO_SIZE - 1) {
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: invalid setting to ic_tx_tl %d\n",
> + DEVICE(s)->canonical_path, value);
> + s->ic_tx_tl = DESIGNWARE_I2C_TX_FIFO_SIZE - 1;
> + } else {
> + s->ic_tx_tl = value;
> + }
> +}
> +
> +static void dw_i2c_write(void *opaque, hwaddr offset, uint64_t value,
> + unsigned size)
> +{
> + DesignWareI2CState *s = opaque;
> +
> + trace_dw_i2c_write(DEVICE(s)->canonical_path, offset, value);
> +
> + /* The order of the registers are their order in memory. */
> + switch (offset) {
> + case DW_IC_CON:
> + dw_i2c_write_ic_con(s, value);
> + break;
> + case DW_IC_TAR:
> + s->ic_tar = value & DW_IC_TAR_MASK;
> + break;
> + case DW_IC_SAR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_sar\n",
> + DEVICE(s)->canonical_path);
> + s->ic_sar = value & DW_IC_SAR_MASK;
> + break;
> + case DW_IC_DATA_CMD:
> + dw_i2c_write_ic_data_cmd(s, value);
> + break;
> + case DW_IC_SS_SCL_HCNT:
> + s->ic_ss_scl_hcnt = value & DW_IC_SS_SCL_HCNT_MASK;
> + break;
> + case DW_IC_SS_SCL_LCNT:
> + s->ic_ss_scl_lcnt = value & DW_IC_SS_SCL_LCNT_MASK;
> + break;
> + case DW_IC_FS_SCL_HCNT:
> + s->ic_fs_scl_hcnt = value & DW_IC_FS_SCL_HCNT_MASK;
> + break;
> + case DW_IC_FS_SCL_LCNT:
> + s->ic_fs_scl_lcnt = value & DW_IC_FS_SCL_LCNT_MASK;
> + break;
> + case DW_IC_INTR_MASK:
> + s->ic_intr_mask = value & DW_IC_INTR_MASK_MASK;
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_RX_TL:
> + dw_i2c_write_ic_rx_tl(s, value);
> + break;
> + case DW_IC_TX_TL:
> + dw_i2c_write_ic_tx_tl(s, value);
> + break;
> + case DW_IC_ENABLE:
> + dw_i2c_write_ic_enable(s, value);
> + break;
> + case DW_IC_SDA_HOLD:
> + s->ic_sda_hold = value & DW_IC_SDA_HOLD_MASK;
> + break;
> + case DW_IC_SLV_DATA_NACK_ONLY:
> + qemu_log_mask(LOG_UNIMP,
> + "%s: unsupported write - ic_slv_data_nack_only\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_DMA_CR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_cr\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_DMA_TDLR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_tdlr\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_DMA_RDLR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_rdlr\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_SDA_SETUP:
> + s->ic_sda_setup = value & DW_IC_SDA_SETUP_MASK;
> + break;
> + case DW_IC_ACK_GENERAL_CALL:
> + qemu_log_mask(LOG_UNIMP,
> + "%s: unsupported write - ic_ack_general_call\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_FS_SPKLEN:
> + s->ic_fs_spklen = value & DW_IC_FS_SPKLEN_MASK;
> + break;
> +
> + /* This register is invalid at this point. */
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: write to invalid offset or readonly register 0x%"
> + HWADDR_PRIx "\n",
> + DEVICE(s)->canonical_path, offset);
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps designware_i2c_ops = {
> + .read = dw_i2c_read,
> + .write = dw_i2c_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4,
> + .unaligned = false,
> + },
> +};
> +
> +static void designware_i2c_enter_reset(Object *obj, ResetType type)
> +{
> + DesignWareI2CState *s = DESIGNWARE_I2C(obj);
> +
> + s->ic_con = DW_IC_CON_INIT_VAL;
> + s->ic_tar = DW_IC_TAR_INIT_VAL;
> + s->ic_sar = DW_IC_SAR_INIT_VAL;
> + s->ic_ss_scl_hcnt = DW_IC_SS_SCL_HCNT_INIT_VAL;
> + s->ic_ss_scl_lcnt = DW_IC_SS_SCL_LCNT_INIT_VAL;
> + s->ic_fs_scl_hcnt = DW_IC_FS_SCL_HCNT_INIT_VAL;
> + s->ic_fs_scl_lcnt = DW_IC_FS_SCL_LCNT_INIT_VAL;
> + s->ic_intr_mask = DW_IC_INTR_MASK_INIT_VAL;
> + s->ic_raw_intr_stat = 0;
> + s->ic_rx_tl = 0;
> + s->ic_tx_tl = 0;
> + s->ic_enable = 0;
> + s->ic_status = DW_IC_STATUS_INIT_VAL;
> + s->ic_txflr = 0;
> + s->ic_rxflr = 0;
> + s->ic_sda_hold = DW_IC_SDA_HOLD_INIT_VAL;
> + s->ic_tx_abrt_source = 0;
> + s->ic_sda_setup = DW_IC_SDA_SETUP_INIT_VAL;
> + s->ic_enable_status = 0;
> + s->ic_fs_spklen = DW_IC_FS_SPKLEN_INIT_VAL;
> + s->ic_comp_param_1 = DW_IC_COMP_PARAM_1_INIT_VAL;
> + s->ic_comp_version = DW_IC_COMP_VERSION_INIT_VAL;
> + s->ic_comp_type = DW_IC_COMP_TYPE_INIT_VAL;
> +
> + s->rx_cur = 0;
> + s->status = DW_I2C_STATUS_IDLE;
> +}
> +
> +static void designware_i2c_hold_reset(Object *obj, ResetType type)
> +{
> + DesignWareI2CState *s = DESIGNWARE_I2C(obj);
> +
> + qemu_irq_lower(s->irq);
> +}
> +
> +static const VMStateDescription vmstate_designware_i2c = {
> + .name = TYPE_DESIGNWARE_I2C,
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(ic_con, DesignWareI2CState),
> + VMSTATE_UINT32(ic_tar, DesignWareI2CState),
> + VMSTATE_UINT32(ic_sar, DesignWareI2CState),
> + VMSTATE_UINT32(ic_ss_scl_hcnt, DesignWareI2CState),
> + VMSTATE_UINT32(ic_ss_scl_lcnt, DesignWareI2CState),
> + VMSTATE_UINT32(ic_fs_scl_hcnt, DesignWareI2CState),
> + VMSTATE_UINT32(ic_fs_scl_lcnt, DesignWareI2CState),
> + VMSTATE_UINT32(ic_intr_mask, DesignWareI2CState),
> + VMSTATE_UINT32(ic_raw_intr_stat, DesignWareI2CState),
> + VMSTATE_UINT32(ic_rx_tl, DesignWareI2CState),
> + VMSTATE_UINT32(ic_tx_tl, DesignWareI2CState),
> + VMSTATE_UINT32(ic_enable, DesignWareI2CState),
> + VMSTATE_UINT32(ic_status, DesignWareI2CState),
> + VMSTATE_UINT32(ic_txflr, DesignWareI2CState),
> + VMSTATE_UINT32(ic_rxflr, DesignWareI2CState),
> + VMSTATE_UINT32(ic_sda_hold, DesignWareI2CState),
> + VMSTATE_UINT32(ic_tx_abrt_source, DesignWareI2CState),
> + VMSTATE_UINT32(ic_sda_setup, DesignWareI2CState),
> + VMSTATE_UINT32(ic_enable_status, DesignWareI2CState),
> + VMSTATE_UINT32(ic_fs_spklen, DesignWareI2CState),
> + VMSTATE_UINT32(ic_comp_param_1, DesignWareI2CState),
> + VMSTATE_UINT32(ic_comp_version, DesignWareI2CState),
> + VMSTATE_UINT32(ic_comp_type, DesignWareI2CState),
> + VMSTATE_UINT32(status, DesignWareI2CState),
> + VMSTATE_UINT8_ARRAY(rx_fifo, DesignWareI2CState,
> + DESIGNWARE_I2C_RX_FIFO_SIZE),
> + VMSTATE_UINT8(rx_cur, DesignWareI2CState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static void designware_i2c_smbus_init(Object *obj)
> +{
> + DesignWareI2CState *s = DESIGNWARE_I2C(obj);
> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +
> + sysbus_init_irq(sbd, &s->irq);
> +
> + memory_region_init_io(&s->iomem, obj, &designware_i2c_ops, s,
> + "regs", 4 * KiB);
> + sysbus_init_mmio(sbd, &s->iomem);
> +
> + s->bus = i2c_init_bus(DEVICE(s), "i2c-bus");
> +}
> +
> +static void designware_i2c_class_init(ObjectClass *klass, const void *data)
> +{
> + ResettableClass *rc = RESETTABLE_CLASS(klass);
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->desc = "Designware I2C";
> + dc->vmsd = &vmstate_designware_i2c;
> + rc->phases.enter = designware_i2c_enter_reset;
> + rc->phases.hold = designware_i2c_hold_reset;
> +}
> +
> +static const TypeInfo designware_i2c_types[] = {
> + {
> + .name = TYPE_DESIGNWARE_I2C,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(DesignWareI2CState),
> + .class_init = designware_i2c_class_init,
> + .instance_init = designware_i2c_smbus_init,
> + },
> +};
> +DEFINE_TYPES(designware_i2c_types);
Overall this looks ok. I don't have access to the datasheet though (I
assume that's secret) so it's just a quick read through
Acked-by: Alistair Francis <alistair.francis@wdc.com>
Alistair
> diff --git a/hw/i2c/Kconfig b/hw/i2c/Kconfig
> index 596a7a3165ad..d3f394edeb9c 100644
> --- a/hw/i2c/Kconfig
> +++ b/hw/i2c/Kconfig
> @@ -18,6 +18,10 @@ config ARM_SBCON_I2C
> bool
> select BITBANG_I2C
>
> +config DESIGNWARE_I2C
> + bool
> + select I2C
> +
> config ACPI_SMBUS
> bool
> select SMBUS
> diff --git a/hw/i2c/meson.build b/hw/i2c/meson.build
> index c459adcb596c..88aea35662dd 100644
> --- a/hw/i2c/meson.build
> +++ b/hw/i2c/meson.build
> @@ -11,6 +11,7 @@ i2c_ss.add(when: 'CONFIG_MPC_I2C', if_true: files('mpc_i2c.c'))
> i2c_ss.add(when: 'CONFIG_ALLWINNER_I2C', if_true: files('allwinner-i2c.c'))
> i2c_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('microbit_i2c.c'))
> i2c_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_smbus.c'))
> +i2c_ss.add(when: 'CONFIG_DESIGNWARE_I2C', if_true: files('designware_i2c.c'))
> i2c_ss.add(when: 'CONFIG_SMBUS_EEPROM', if_true: files('smbus_eeprom.c'))
> i2c_ss.add(when: 'CONFIG_ARM_SBCON_I2C', if_true: files('arm_sbcon_i2c.c'))
> i2c_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_i2c.c'))
> diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events
> index 1ad0e95c0e60..8a78d2d3c8de 100644
> --- a/hw/i2c/trace-events
> +++ b/hw/i2c/trace-events
> @@ -61,3 +61,7 @@ pca954x_read_data(uint8_t value) "PCA954X read data: 0x%02x"
>
> imx_i2c_read(const char *id, const char *reg, uint64_t ofs, uint64_t value) "%s:[%s (0x%" PRIx64 ")] -> 0x%02" PRIx64
> imx_i2c_write(const char *id, const char *reg, uint64_t ofs, uint64_t value) "%s:[%s (0x%" PRIx64 ")] <- 0x%02" PRIx64
> +
> +# designware_i2c.c
> +dw_i2c_read(const char *id, uint64_t ofs, uint64_t value) "%s: offset 0x%02" PRIx64 " -> value: 0x%02" PRIx64
> +dw_i2c_write(const char *id, uint64_t ofs, uint64_t value) "%s: offset: 0x%02" PRIx64 " <- value: 0x%02" PRIx64
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 03/13] hw/riscv/boot: Account for discontiguous memory when loading firmware
2026-04-29 23:34 ` Alistair Francis
@ 2026-05-04 23:45 ` Nicholas Piggin
0 siblings, 0 replies; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-04 23:45 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel
On Thu, Apr 30, 2026 at 09:34:24AM +1000, Alistair Francis wrote:
> On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> >
> > From: Nicholas Piggin <npiggin@gmail.com>
> >
> > This loads firmware into the first (low) memory range,
> > accounting for machines having discontiguous memory regions.
> >
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > Reviewed-by: Daniel Henrique Barboza <daniel.barboza@oss.qualcomm.com>
> > Signed-off-by: Joel Stanley <joel@jms.id.au>
> > ---
> > v4: Make RISCVBootInfo *info const in riscv_load_firmware
> > v3: Call riscv_boot_info_init before riscv_find_and_load_firmware in
> > sifive_u
> > ---
> > include/hw/riscv/boot.h | 5 ++++-
> > hw/riscv/boot.c | 18 ++++++++++++------
> > hw/riscv/microchip_pfsoc.c | 6 ++++--
> > hw/riscv/opentitan.c | 6 ++++--
> > hw/riscv/shakti_c.c | 6 +++++-
> > hw/riscv/sifive_u.c | 6 ++++--
> > hw/riscv/spike.c | 6 ++++--
> > hw/riscv/virt.c | 7 ++++---
> > hw/riscv/xiangshan_kmh.c | 6 +++++-
> > 9 files changed, 46 insertions(+), 20 deletions(-)
> >
> > diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
> > index 115e3222174f..b2ef64fb1d14 100644
> > --- a/include/hw/riscv/boot.h
> > +++ b/include/hw/riscv/boot.h
> > @@ -53,13 +53,16 @@ void riscv_boot_info_init_discontig_mem(RISCVBootInfo *info,
> > vaddr riscv_calc_kernel_start_addr(RISCVBootInfo *info,
> > hwaddr firmware_end_addr);
> > hwaddr riscv_find_and_load_firmware(MachineState *machine,
> > + RISCVBootInfo *info,
> > const char *default_machine_firmware,
> > hwaddr *firmware_load_addr,
> > symbol_fn_t sym_cb);
> > const char *riscv_default_firmware_name(RISCVHartArrayState *harts);
> > char *riscv_find_firmware(const char *firmware_filename,
> > const char *default_machine_firmware);
> > -hwaddr riscv_load_firmware(const char *firmware_filename,
> > +hwaddr riscv_load_firmware(MachineState *machine,
> > + const RISCVBootInfo *info,
> > + const char *firmware_filename,
> > hwaddr *firmware_load_addr,
> > symbol_fn_t sym_cb);
> > void riscv_load_kernel(MachineState *machine,
> > diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
> > index 5c9547429a36..4fbc778263cf 100644
> > --- a/hw/riscv/boot.c
> > +++ b/hw/riscv/boot.c
> > @@ -145,6 +145,7 @@ char *riscv_find_firmware(const char *firmware_filename,
> > }
> >
> > hwaddr riscv_find_and_load_firmware(MachineState *machine,
> > + RISCVBootInfo *info,
> > const char *default_machine_firmware,
> > hwaddr *firmware_load_addr,
> > symbol_fn_t sym_cb)
> > @@ -157,7 +158,8 @@ hwaddr riscv_find_and_load_firmware(MachineState *machine,
> >
> > if (firmware_filename) {
> > /* If not "none" load the firmware */
> > - firmware_end_addr = riscv_load_firmware(firmware_filename,
> > + firmware_end_addr = riscv_load_firmware(machine, info,
> > + firmware_filename,
> > firmware_load_addr, sym_cb);
> > g_free(firmware_filename);
> > }
> > @@ -165,10 +167,13 @@ hwaddr riscv_find_and_load_firmware(MachineState *machine,
> > return firmware_end_addr;
> > }
> >
> > -hwaddr riscv_load_firmware(const char *firmware_filename,
> > +hwaddr riscv_load_firmware(MachineState *machine,
> > + const RISCVBootInfo *info,
> > + const char *firmware_filename,
> > hwaddr *firmware_load_addr,
> > symbol_fn_t sym_cb)
> > {
> > + uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
> > uint64_t firmware_entry, firmware_end;
> > ssize_t firmware_size;
> >
> > @@ -197,7 +202,7 @@ hwaddr riscv_load_firmware(const char *firmware_filename,
> >
> > firmware_size = load_image_targphys_as(firmware_filename,
> > *firmware_load_addr,
> > - current_machine->ram_size, NULL,
> > + mem_size, NULL,
> > NULL);
> >
> > if (firmware_size > 0) {
> > @@ -212,7 +217,7 @@ hwaddr riscv_load_firmware(const char *firmware_filename,
> > static void riscv_load_initrd(MachineState *machine, RISCVBootInfo *info)
> > {
> > const char *filename = machine->initrd_filename;
> > - uint64_t mem_size = machine->ram_size;
> > + uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
> > void *fdt = machine->fdt;
> > hwaddr start, end;
> > ssize_t size;
> > @@ -258,6 +263,7 @@ void riscv_load_kernel(MachineState *machine,
> > bool load_initrd,
> > symbol_fn_t sym_cb)
> > {
> > + uint64_t mem_size = info->ram_low_size ?: machine->ram_size;
> > const char *kernel_filename = machine->kernel_filename;
> > ssize_t kernel_size;
> > void *fdt = machine->fdt;
> > @@ -289,7 +295,7 @@ void riscv_load_kernel(MachineState *machine,
> > }
> >
> > kernel_size = load_image_targphys_as(kernel_filename, kernel_start_addr,
> > - current_machine->ram_size, NULL, NULL);
> > + mem_size, NULL, NULL);
> > if (kernel_size > 0) {
> > info->kernel_size = kernel_size;
> > info->image_low_addr = kernel_start_addr;
> > @@ -385,7 +391,7 @@ uint64_t riscv_compute_fdt_addr(hwaddr dram_base, hwaddr dram_size,
> > dtb_start = QEMU_ALIGN_DOWN(temp - fdtsize, 2 * MiB);
> >
> > if (dtb_start_limit && (dtb_start < dtb_start_limit)) {
> > - error_report("No enough memory to place DTB after kernel/initrd");
> > + error_report("Not enough memory to place DTB after kernel/initrd");
> > exit(1);
> > }
> >
> > diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c
> > index 743f31f00578..1d1ddb05a882 100644
> > --- a/hw/riscv/microchip_pfsoc.c
> > +++ b/hw/riscv/microchip_pfsoc.c
> > @@ -618,18 +618,20 @@ static void microchip_icicle_kit_machine_init(MachineState *machine)
> > firmware_load_addr = RESET_VECTOR;
> > }
> >
> > + riscv_boot_info_init(&boot_info, &s->soc.u_cpus);
>
> We should be able to use the new riscv_boot_info_init_discontig_mem() here
Good catch, something like this.
riscv_boot_info_init_discontig_mem(&boot_info, &s->soc.u_cpus,
memmap[MICROCHIP_PFSOC_DRAM_LO].base,
mem_low_size);
I'll change that.
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 04/13] hw/riscv/boot: Provide a simple halting payload
2026-04-29 23:35 ` Alistair Francis
@ 2026-05-04 23:52 ` Nicholas Piggin
2026-05-05 8:06 ` Philippe Mathieu-Daudé
0 siblings, 1 reply; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-04 23:52 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel
On Thu, Apr 30, 2026 at 09:35:33AM +1000, Alistair Francis wrote:
> On Sat, Apr 25, 2026 at 11:21 PM Joel Stanley <joel@jms.id.au> wrote:
> >
> > From: Nicholas Piggin <npiggin@gmail.com>
> >
> > OpenSBI hangs before any console output if the domain init code sees the
> > next stage is not in an executable region.
>
> That's an OpenSBI bug though and needs to be fixed there and not just
> hacked around in QEMU.
You're right of course. OpenSBI has other no-output crash cases in early
boot code (not just this), so it would be nice to fix properly. It wasn't
trivial to fix (IIRC because serial device is not detected before a
bunch of the early failures), I have some hacks, but I need to go back
and come up with something nicer.
This is only related to Atlantis by coincidence of the physical address
map. It can be dropped out of the series.
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 07/13] target/riscv: tt-ascalon: Enable Zkr extension
2026-04-29 23:36 ` Alistair Francis
@ 2026-05-05 0:06 ` Nicholas Piggin
0 siblings, 0 replies; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-05 0:06 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel
On Thu, Apr 30, 2026 at 09:36:45AM +1000, Alistair Francis wrote:
> On Sat, Apr 25, 2026 at 11:21 PM Joel Stanley <joel@jms.id.au> wrote:
> >
> > From: Nicholas Piggin <npiggin@gmail.com>
> >
> > Ascalon supports Zkr and the SEED CSR.
>
> A datasheet would be nice to verify this
At the moment nothing public with this level of detail, I'm sorry.
> >
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > Reviewed-by: Chao Liu <chao.liu.zevorn@gmail.com>
> > Signed-off-by: Joel Stanley <joel@jms.id.au>
>
> Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Thanks for the reviews.
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-04-29 23:57 ` Alistair Francis
@ 2026-05-05 1:04 ` Nicholas Piggin
2026-05-05 4:34 ` Alistair Francis
2026-05-05 2:00 ` Nicholas Piggin
1 sibling, 1 reply; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-05 1:04 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
> On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> >
> > The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> > and CoreLab Technology. It is based on the Atlantis SoC, which includes
> > the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
>
> Can you provide a link to documentation
Also not presently.
[...]
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index e1942a86eba5..635a380945fd 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -1760,6 +1760,17 @@ F: hw/*/*sifive*.c
> > F: include/hw/*/*sifive*.h
> > F: tests/functional/test_riscv64_sifive_u.py
> >
> > +Tenstorrent Machines
> > +M: Joel Stanley <joel@jms.id.au>
> > +R: Nicholas Piggin <npiggin@gmail.com>
> > +R: Michael Ellerman <mpe@kernel.org>
> > +L: qemu-riscv@nongnu.org
> > +S: Supported
> > +F: docs/system/riscv/tt_*.rst
> > +F: hw/riscv/tt_*.c
> > +F: hw/riscv/aia.[ch]
>
> AIA?
Not sure why that is there, there's no Tenstorrent implementation detail
in there AFAIKS. Let's remove that line.
>
> > +F: include/hw/riscv/tt_*.h
> > +
> > AMD Microblaze-V Generic Board
> > M: Sai Pavan Boddu <sai.pavan.boddu@amd.com>
> > S: Maintained
> > diff --git a/docs/system/riscv/tt_atlantis.rst b/docs/system/riscv/tt_atlantis.rst
> > new file mode 100644
> > index 000000000000..640cabf7b046
> > --- /dev/null
> > +++ b/docs/system/riscv/tt_atlantis.rst
> > @@ -0,0 +1,38 @@
> > +Tenstorrent Atlantis (``tt-atlantis``)
> > +======================================
> > +
> > +The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> > +and CoreLab Technology. It is based on the Atlantis SoC, which includes
> > +the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
> > +
> > +The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant
> > +RISC-V CPU.
> > +
> > +Features
> > +--------
> > +
> > +* 8-core Ascalon-X CPU Cluster
> > +* Dual x32 LPDDR5 @ 6400 MT/s
> > +* RISC-V compliant Advanced Interrupt Architecture
> > +* PCIe Gen4
> > +* RISC-V compliant IOMMU
>
> IOMMU isn't supported (at least not in this patch), maybe this should
> list things that are supported in QEMU (and can then be expanded).
> Leave the features list to your official documentation (which is
> missing a link here)
Sounds reasonable.
[...]
> > +
> > +static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
>
> So generally we only create a device tree for virtual boards, that is
> boards that don't really exist and are just QEMU constructs.
>
> You said that this is based on the "Atlantis SoC" so I assume it's
> real hardware. In the real hardware do you expect the device tree to
> be provided by the ROM? Usually for boards users supply the device
> tree as part of the boot flow (via u-boot or something similar). In
> which case should QEMU actually generate this or can users just supply
> their own?
>
> Basically the question is, if you are trying to model the hardware, is
> generating the device tree here correct?
>
> Obviously the advantage of doing it here is then the device tree
> describes what QEMU supports, which is great. But that can be harder
> to manage when the hardware doesn't do that, as then you have
> divergence.
We went around this same discussion internally and yeah it's a valid
question, we could have explained this a bit better.
In the real (unreleased) hardware and the DT will be loaded from ROM.
I think we should do that when the model and firmware and dt is more
complete. At the moment it has been easier for development to use QEMU
generated fdt.
Is that acceptable to do that now and switch to a fdt ROM later...
[...]
> > +static void finalize_fdt(TTAtlantisState *s)
> > +{
> > + uint32_t aplic_s_phandle = next_phandle();
> > + uint32_t imsic_s_phandle = next_phandle();
> > + void *fdt = MACHINE(s)->fdt;
> > +
> > + create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
> > +
> > + /*
> > + * We want to do this, but the Linux aplic driver was broken before v6.16
> > + *
> > + * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
> > + * aplic_s_phandle);
> > + */
>
> And now we are stuck with hacks like this, which we really don't want
> to be dealing with. Maybe it's best to leave the device tree to the
> kernel.
... Despite issues like this?
[...]
> > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> > index 0222c93f878b..0601ae1a7494 100644
> > --- a/hw/riscv/Kconfig
> > +++ b/hw/riscv/Kconfig
> > @@ -120,6 +120,21 @@ config SPIKE
> > select RISCV_ACLINT
> > select SIFIVE_PLIC
> >
> > +config TENSTORRENT
> > + bool
> > + default y
> > + depends on RISCV64
> > + imply PCI_DEVICES
> > + imply TEST_DEVICES
> > + select DEVICE_TREE
> > + select RISCV_NUMA
>
> Do you actually need NUMA? You have a numa include as well which I
> don't think is required.
Hardware is not NUMA. It was probably just copied from the VIRT config.
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 10/13] hw/riscv/atlantis: Add PCIe controller
2026-04-30 0:04 ` Alistair Francis
@ 2026-05-05 1:38 ` Nicholas Piggin
0 siblings, 0 replies; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-05 1:38 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel
On Thu, Apr 30, 2026 at 10:04:16AM +1000, Alistair Francis wrote:
> On Sat, Apr 25, 2026 at 11:21 PM Joel Stanley <joel@jms.id.au> wrote:
> >
> > From: Nicholas Piggin <npiggin@gmail.com>
> >
> > Atlantis has 3 DesignWare PCIe host controllers, however the boot firmware
> > has not been tested with the QEMU DesignWare model so a generic ECAM
> > controller is used for the interim. The system bus aperture and PCIe IO
>
> I don't follow.
>
> The actual hardware has a different PCIe controller? This is user
> visible so you can't easily change it once it's merged. Generally user
> visible changes go through a deprecation process, we have some
> exceptions for soft cores which change all the time. But ideally this
> should model the hardware from the start.
>
> Also if it isn't that should be noted in the board documentation.
Yeah, this was me punting on doing the designware host to get some IOMMU
modeling and driver code tested. And generally being easier to test
other things if we have PCI up.
I hadn't really considered the stability / deprecation issue with
upstreaming it.
> A quick grep shows support for a DesignWare PCIe Host, why not use that?
We should be, I'll take another look at it. We could drop this and the
functional test that uses disk, for now.
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-04-29 23:57 ` Alistair Francis
2026-05-05 1:04 ` Nicholas Piggin
@ 2026-05-05 2:00 ` Nicholas Piggin
2026-05-05 4:36 ` Alistair Francis
1 sibling, 1 reply; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-05 2:00 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
> On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index e1942a86eba5..635a380945fd 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -1760,6 +1760,17 @@ F: hw/*/*sifive*.c
> > F: include/hw/*/*sifive*.h
> > F: tests/functional/test_riscv64_sifive_u.py
> >
> > +Tenstorrent Machines
> > +M: Joel Stanley <joel@jms.id.au>
> > +R: Nicholas Piggin <npiggin@gmail.com>
> > +R: Michael Ellerman <mpe@kernel.org>
> > +L: qemu-riscv@nongnu.org
> > +S: Supported
> > +F: docs/system/riscv/tt_*.rst
> > +F: hw/riscv/tt_*.c
> > +F: hw/riscv/aia.[ch]
>
> AIA?
Oh it must be because checkpatch complained about patch 5.
It is covered by RISC-V TCG CPUs though, so no change needed to
MAINTAINERS for AIA AFAIKS?
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-05-05 1:04 ` Nicholas Piggin
@ 2026-05-05 4:34 ` Alistair Francis
2026-05-05 6:00 ` Nicholas Piggin
0 siblings, 1 reply; 44+ messages in thread
From: Alistair Francis @ 2026-05-05 4:34 UTC (permalink / raw)
To: Nicholas Piggin
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Tue, May 5, 2026 at 11:04 AM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
> > On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> > >
> > > The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> > > and CoreLab Technology. It is based on the Atlantis SoC, which includes
> > > the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
> >
> > Can you provide a link to documentation
>
> Also not presently.
Hmmm... That's a bit annoying. Can we get some?
>
> [...]
>
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index e1942a86eba5..635a380945fd 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -1760,6 +1760,17 @@ F: hw/*/*sifive*.c
> > > F: include/hw/*/*sifive*.h
> > > F: tests/functional/test_riscv64_sifive_u.py
> > >
> > > +Tenstorrent Machines
> > > +M: Joel Stanley <joel@jms.id.au>
> > > +R: Nicholas Piggin <npiggin@gmail.com>
> > > +R: Michael Ellerman <mpe@kernel.org>
> > > +L: qemu-riscv@nongnu.org
> > > +S: Supported
> > > +F: docs/system/riscv/tt_*.rst
> > > +F: hw/riscv/tt_*.c
> > > +F: hw/riscv/aia.[ch]
> >
> > AIA?
>
> Not sure why that is there, there's no Tenstorrent implementation detail
> in there AFAIKS. Let's remove that line.
>
> >
> > > +F: include/hw/riscv/tt_*.h
> > > +
> > > AMD Microblaze-V Generic Board
> > > M: Sai Pavan Boddu <sai.pavan.boddu@amd.com>
> > > S: Maintained
> > > diff --git a/docs/system/riscv/tt_atlantis.rst b/docs/system/riscv/tt_atlantis.rst
> > > new file mode 100644
> > > index 000000000000..640cabf7b046
> > > --- /dev/null
> > > +++ b/docs/system/riscv/tt_atlantis.rst
> > > @@ -0,0 +1,38 @@
> > > +Tenstorrent Atlantis (``tt-atlantis``)
> > > +======================================
> > > +
> > > +The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> > > +and CoreLab Technology. It is based on the Atlantis SoC, which includes
> > > +the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
> > > +
> > > +The Tenstorrent Ascalon-X is a high performance 64-bit RVA23 compliant
> > > +RISC-V CPU.
> > > +
> > > +Features
> > > +--------
> > > +
> > > +* 8-core Ascalon-X CPU Cluster
> > > +* Dual x32 LPDDR5 @ 6400 MT/s
> > > +* RISC-V compliant Advanced Interrupt Architecture
> > > +* PCIe Gen4
> > > +* RISC-V compliant IOMMU
> >
> > IOMMU isn't supported (at least not in this patch), maybe this should
> > list things that are supported in QEMU (and can then be expanded).
> > Leave the features list to your official documentation (which is
> > missing a link here)
>
> Sounds reasonable.
>
> [...]
>
> > > +
> > > +static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
> >
> > So generally we only create a device tree for virtual boards, that is
> > boards that don't really exist and are just QEMU constructs.
> >
> > You said that this is based on the "Atlantis SoC" so I assume it's
> > real hardware. In the real hardware do you expect the device tree to
> > be provided by the ROM? Usually for boards users supply the device
> > tree as part of the boot flow (via u-boot or something similar). In
> > which case should QEMU actually generate this or can users just supply
> > their own?
> >
> > Basically the question is, if you are trying to model the hardware, is
> > generating the device tree here correct?
> >
> > Obviously the advantage of doing it here is then the device tree
> > describes what QEMU supports, which is great. But that can be harder
> > to manage when the hardware doesn't do that, as then you have
> > divergence.
>
> We went around this same discussion internally and yeah it's a valid
> question, we could have explained this a bit better.
>
> In the real (unreleased) hardware and the DT will be loaded from ROM.
Really? That usually doesn't go well
> I think we should do that when the model and firmware and dt is more
> complete. At the moment it has been easier for development to use QEMU
> generated fdt.
>
> Is that acceptable to do that now and switch to a fdt ROM later...
Obviously guest software can decide to use whatever it wants. But what
we don't want to do is break users. So removing the generated FDT
(which is the equivalent of ROM FDT on hardware) will cause breakages.
So we don't want to do that if we can avoid it. Also generating the
FDT is overhead for us, so we only want to if the guest software will
actually use it. Does that make sense?
Adding the device tree later is fine though, as guests don't have to use it.
>
> [...]
>
> > > +static void finalize_fdt(TTAtlantisState *s)
> > > +{
> > > + uint32_t aplic_s_phandle = next_phandle();
> > > + uint32_t imsic_s_phandle = next_phandle();
> > > + void *fdt = MACHINE(s)->fdt;
> > > +
> > > + create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
> > > +
> > > + /*
> > > + * We want to do this, but the Linux aplic driver was broken before v6.16
> > > + *
> > > + * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
> > > + * aplic_s_phandle);
> > > + */
> >
> > And now we are stuck with hacks like this, which we really don't want
> > to be dealing with. Maybe it's best to leave the device tree to the
> > kernel.
>
> ... Despite issues like this?
If the kernel generates a device tree for it to use then we don't have
mismatched problems (like this one). Generating the DT in QEMU for
real boards proves to be a challenge and this is exactly why. Now we
have to choose between pre or post 6.16 support (I assume 6.16+ works
without this, but you get the point).
Alistair
>
> [...]
>
> > > diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig
> > > index 0222c93f878b..0601ae1a7494 100644
> > > --- a/hw/riscv/Kconfig
> > > +++ b/hw/riscv/Kconfig
> > > @@ -120,6 +120,21 @@ config SPIKE
> > > select RISCV_ACLINT
> > > select SIFIVE_PLIC
> > >
> > > +config TENSTORRENT
> > > + bool
> > > + default y
> > > + depends on RISCV64
> > > + imply PCI_DEVICES
> > > + imply TEST_DEVICES
> > > + select DEVICE_TREE
> > > + select RISCV_NUMA
> >
> > Do you actually need NUMA? You have a numa include as well which I
> > don't think is required.
>
> Hardware is not NUMA. It was probably just copied from the VIRT config.
>
> Thanks,
> Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-05-05 2:00 ` Nicholas Piggin
@ 2026-05-05 4:36 ` Alistair Francis
2026-05-05 6:01 ` Nicholas Piggin
0 siblings, 1 reply; 44+ messages in thread
From: Alistair Francis @ 2026-05-05 4:36 UTC (permalink / raw)
To: Nicholas Piggin
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Tue, May 5, 2026 at 12:00 PM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
> > On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
>
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index e1942a86eba5..635a380945fd 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -1760,6 +1760,17 @@ F: hw/*/*sifive*.c
> > > F: include/hw/*/*sifive*.h
> > > F: tests/functional/test_riscv64_sifive_u.py
> > >
> > > +Tenstorrent Machines
> > > +M: Joel Stanley <joel@jms.id.au>
> > > +R: Nicholas Piggin <npiggin@gmail.com>
> > > +R: Michael Ellerman <mpe@kernel.org>
> > > +L: qemu-riscv@nongnu.org
> > > +S: Supported
> > > +F: docs/system/riscv/tt_*.rst
> > > +F: hw/riscv/tt_*.c
> > > +F: hw/riscv/aia.[ch]
> >
> > AIA?
>
> Oh it must be because checkpatch complained about patch 5.
>
> It is covered by RISC-V TCG CPUs though, so no change needed to
> MAINTAINERS for AIA AFAIKS?
If it's already covered then that should be fine. Also, if you are
covering it, MAINTAINERS should be updated with patch 5 and not here
Alistair
>
> Thanks,
> Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-05-05 4:34 ` Alistair Francis
@ 2026-05-05 6:00 ` Nicholas Piggin
2026-05-05 7:31 ` Philippe Mathieu-Daudé
2026-05-06 3:14 ` Alistair Francis
0 siblings, 2 replies; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-05 6:00 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Tue, May 05, 2026 at 02:34:50PM +1000, Alistair Francis wrote:
> On Tue, May 5, 2026 at 11:04 AM Nicholas Piggin <npiggin@gmail.com> wrote:
> >
> > On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
> > > On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> > > >
> > > > The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> > > > and CoreLab Technology. It is based on the Atlantis SoC, which includes
> > > > the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
> > >
> > > Can you provide a link to documentation
> >
> > Also not presently.
>
> Hmmm... That's a bit annoying. Can we get some?
That's the plan but I don't have a date I can give you for it, sorry.
[...]
> > > > +static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
> > >
> > > So generally we only create a device tree for virtual boards, that is
> > > boards that don't really exist and are just QEMU constructs.
> > >
> > > You said that this is based on the "Atlantis SoC" so I assume it's
> > > real hardware. In the real hardware do you expect the device tree to
> > > be provided by the ROM? Usually for boards users supply the device
> > > tree as part of the boot flow (via u-boot or something similar). In
> > > which case should QEMU actually generate this or can users just supply
> > > their own?
> > >
> > > Basically the question is, if you are trying to model the hardware, is
> > > generating the device tree here correct?
> > >
> > > Obviously the advantage of doing it here is then the device tree
> > > describes what QEMU supports, which is great. But that can be harder
> > > to manage when the hardware doesn't do that, as then you have
> > > divergence.
> >
> > We went around this same discussion internally and yeah it's a valid
> > question, we could have explained this a bit better.
> >
> > In the real (unreleased) hardware and the DT will be loaded from ROM.
>
> Really? That usually doesn't go well
Well not ROM but flash loaded by early firmware. The plan is to have an
"M mode" dtb loaded for opensbi and then later stages supply their own
dtb.
The opensbi dtb won't go away from the tt-atlantis model, whether its
generated or we change it to use a dtb for the purpose of QEMU. I guess
if the next stage does not have its own dtb it can use one supplied by
opensbi.
Does that sound right? I don't know if I have the details exactly right.
> > I think we should do that when the model and firmware and dt is more
> > complete. At the moment it has been easier for development to use QEMU
> > generated fdt.
> >
> > Is that acceptable to do that now and switch to a fdt ROM later...
>
> Obviously guest software can decide to use whatever it wants. But what
> we don't want to do is break users. So removing the generated FDT
> (which is the equivalent of ROM FDT on hardware) will cause breakages.
> So we don't want to do that if we can avoid it. Also generating the
> FDT is overhead for us, so we only want to if the guest software will
> actually use it. Does that make sense?
>
> Adding the device tree later is fine though, as guests don't have to use it.
>
> >
> > [...]
> >
> > > > +static void finalize_fdt(TTAtlantisState *s)
> > > > +{
> > > > + uint32_t aplic_s_phandle = next_phandle();
> > > > + uint32_t imsic_s_phandle = next_phandle();
> > > > + void *fdt = MACHINE(s)->fdt;
> > > > +
> > > > + create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
> > > > +
> > > > + /*
> > > > + * We want to do this, but the Linux aplic driver was broken before v6.16
> > > > + *
> > > > + * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
> > > > + * aplic_s_phandle);
> > > > + */
> > >
> > > And now we are stuck with hacks like this, which we really don't want
> > > to be dealing with. Maybe it's best to leave the device tree to the
> > > kernel.
> >
> > ... Despite issues like this?
>
> If the kernel generates a device tree for it to use then we don't have
> mismatched problems (like this one). Generating the DT in QEMU for
> real boards proves to be a challenge and this is exactly why. Now we
> have to choose between pre or post 6.16 support (I assume 6.16+ works
> without this, but you get the point).
Yeah I see do see why we'd want a Linux one, and we are currently
upstreaming dts for Linux. At the moment we don't have it so using
the firmware stage one fills the gap.
It is a little bit hacky maybe but I guess I'm not seeing where a
compatibility problem comes in versus other systems with their own
firmware DTB (that I assume a next-stage could similarly use and be
exposed to these issues if it did not supply its own).
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-05-05 4:36 ` Alistair Francis
@ 2026-05-05 6:01 ` Nicholas Piggin
0 siblings, 0 replies; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-05 6:01 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Tue, May 05, 2026 at 02:36:23PM +1000, Alistair Francis wrote:
> On Tue, May 5, 2026 at 12:00 PM Nicholas Piggin <npiggin@gmail.com> wrote:
> >
> > On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
> > > On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> >
> > > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > > index e1942a86eba5..635a380945fd 100644
> > > > --- a/MAINTAINERS
> > > > +++ b/MAINTAINERS
> > > > @@ -1760,6 +1760,17 @@ F: hw/*/*sifive*.c
> > > > F: include/hw/*/*sifive*.h
> > > > F: tests/functional/test_riscv64_sifive_u.py
> > > >
> > > > +Tenstorrent Machines
> > > > +M: Joel Stanley <joel@jms.id.au>
> > > > +R: Nicholas Piggin <npiggin@gmail.com>
> > > > +R: Michael Ellerman <mpe@kernel.org>
> > > > +L: qemu-riscv@nongnu.org
> > > > +S: Supported
> > > > +F: docs/system/riscv/tt_*.rst
> > > > +F: hw/riscv/tt_*.c
> > > > +F: hw/riscv/aia.[ch]
> > >
> > > AIA?
> >
> > Oh it must be because checkpatch complained about patch 5.
> >
> > It is covered by RISC-V TCG CPUs though, so no change needed to
> > MAINTAINERS for AIA AFAIKS?
>
> If it's already covered then that should be fine. Also, if you are
> covering it, MAINTAINERS should be updated with patch 5 and not here
Agree on both counts. I think it was in the wrong patch, but also
unnecessary. I've just dropped it.
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 01/13] hw/i2c: Add designware i2c controller
2026-04-30 3:53 ` Alistair Francis
@ 2026-05-05 6:20 ` Nicholas Piggin
2026-05-05 7:36 ` Philippe Mathieu-Daudé
0 siblings, 1 reply; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-05 6:20 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Chris Rauer, Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel, Hao Wu
On Thu, Apr 30, 2026 at 01:53:21PM +1000, Alistair Francis wrote:
> On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> >
> > From: Chris Rauer <crauer@google.com>
> >
> > In the past this model has been submitted for use with the arm virt
> > machine, however in this case it will be used by the upcoming
> > Tenstorrent Atlantis RISC-V machine.
> >
> > This is a re-submission of the model with Chris' permission, with a
> > light touch of updates to make it build with qemu master.
> >
> > Reviewed-by: Hao Wu <wuhaotsh@google.com>
> > Signed-off-by: Chris Rauer <crauer@google.com>
> > Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com
> > [jms: rebase and minor build fixes for class_init and reset callback]
> > Signed-off-by: Joel Stanley <joel@jms.id.au>
> > ---
> > v4: Remove unused random header
> > v2: Add trace event for read and write, document Alano and myself as
> > reviewers.
> > ---
> > MAINTAINERS | 8 +
> > include/hw/i2c/designware_i2c.h | 101 ++++
> > hw/i2c/designware_i2c.c | 817 ++++++++++++++++++++++++++++++++
> > hw/i2c/Kconfig | 4 +
> > hw/i2c/meson.build | 1 +
> > hw/i2c/trace-events | 4 +
> > 6 files changed, 935 insertions(+)
> > create mode 100644 include/hw/i2c/designware_i2c.h
> > create mode 100644 hw/i2c/designware_i2c.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index aa4267b15806..e1942a86eba5 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -2716,6 +2716,14 @@ S: Orphan
> > F: hw/gpio/pcf8574.c
> > F: include/gpio/pcf8574.h
> >
> > +DesignWare I2C
> > +M: Chris Rauer <crauer@google.com>
> > +R: Alano Song <alanosong@163.com>
> > +R: Joel Stanley <joel@jms.id.au>
> > +S: Maintained
> > +F: hw/i2c/designware_i2c.c
> > +F: include/hw/i2c/designware_i2c.h
> > +
> > Generic Loader
> > M: Alistair Francis <alistair@alistair23.me>
> > S: Maintained
> > diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h
> > new file mode 100644
> > index 000000000000..0d8f904f51b7
> > --- /dev/null
> > +++ b/include/hw/i2c/designware_i2c.h
> > @@ -0,0 +1,101 @@
> > +/*
> > + * DesignWare I2C Module.
> > + *
> > + * Copyright 2021 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + */
> > +#ifndef DESIGNWARE_I2C_H
> > +#define DESIGNWARE_I2C_H
> > +
> > +#include "hw/i2c/i2c.h"
> > +#include "hw/core/irq.h"
> > +#include "hw/core/sysbus.h"
> > +
> > +/* Size of the FIFO buffers. */
> > +#define DESIGNWARE_I2C_RX_FIFO_SIZE 16
> > +#define DESIGNWARE_I2C_TX_FIFO_SIZE 16
> > +
> > +typedef enum DesignWareI2CStatus {
> > + DW_I2C_STATUS_IDLE,
> > + DW_I2C_STATUS_SENDING_ADDRESS,
> > + DW_I2C_STATUS_SENDING,
> > + DW_I2C_STATUS_RECEIVING,
> > +} DesignWareI2CStatus;
> > +
> > +/*
> > + * struct DesignWareI2CState - DesignWare I2C device state.
> > + * @bus: The underlying I2C Bus
> > + * @irq: GIC interrupt line to fire on events
> > + * @ic_con: : I2C control register
> > + * @ic_tar: I2C target address register
> > + * @ic_sar: I2C slave address register
> > + * @ic_ss_scl_hcnt: Standard speed i2c clock scl high count register
> > + * @ic_ss_scl_lcnt: Standard speed i2c clock scl low count register
> > + * @ic_fs_scl_hcnt: Fast mode or fast mode plus i2c clock scl high count
> > + * register
> > + * @ic_fs_scl_lcnt:Fast mode or fast mode plus i2c clock scl low count
> > + * register
> > + * @ic_intr_mask: I2C Interrupt Mask Register
> > + * @ic_raw_intr_stat: I2C raw interrupt status register
> > + * @ic_rx_tl: I2C receive FIFO threshold register
> > + * @ic_tx_tl: I2C transmit FIFO threshold register
> > + * @ic_enable: I2C enable register
> > + * @ic_status: I2C status register
> > + * @ic_txflr: I2C transmit fifo level register
> > + * @ic_rxflr: I2C receive fifo level register
> > + * @ic_sda_hold: I2C SDA hold time length register
> > + * @ic_tx_abrt_source: The I2C transmit abort source register
> > + * @ic_sda_setup: I2C SDA setup register
> > + * @ic_enable_status: I2C enable status register
> > + * @ic_fs_spklen: I2C SS, FS or FM+ spike suppression limit
> > + * @ic_comp_param_1: Component parameter register
> > + * @ic_comp_version: I2C component version register
> > + * @ic_comp_type: I2C component type register
> > + * @rx_fifo: The FIFO buffer for receiving in FIFO mode.
> > + * @rx_cur: The current position of rx_fifo.
> > + * @status: The current status of the SMBus.
> > + */
> > +typedef struct DesignWareI2CState {
> > + SysBusDevice parent;
> > +
> > + MemoryRegion iomem;
> > +
> > + I2CBus *bus;
> > + qemu_irq irq;
> > +
> > + uint32_t ic_con;
> > + uint32_t ic_tar;
> > + uint32_t ic_sar;
> > + uint32_t ic_ss_scl_hcnt;
> > + uint32_t ic_ss_scl_lcnt;
> > + uint32_t ic_fs_scl_hcnt;
> > + uint32_t ic_fs_scl_lcnt;
> > + uint32_t ic_intr_mask;
> > + uint32_t ic_raw_intr_stat;
> > + uint32_t ic_rx_tl;
> > + uint32_t ic_tx_tl;
> > + uint32_t ic_enable;
> > + uint32_t ic_status;
> > + uint32_t ic_txflr;
> > + uint32_t ic_rxflr;
> > + uint32_t ic_sda_hold;
> > + uint32_t ic_tx_abrt_source;
> > + uint32_t ic_sda_setup;
> > + uint32_t ic_enable_status;
> > + uint32_t ic_fs_spklen;
> > + uint32_t ic_comp_param_1;
> > + uint32_t ic_comp_version;
> > + uint32_t ic_comp_type;
> > +
> > + uint8_t rx_fifo[DESIGNWARE_I2C_RX_FIFO_SIZE];
> > + uint8_t rx_cur;
> > +
> > + DesignWareI2CStatus status;
> > +} DesignWareI2CState;
> > +
> > +#define TYPE_DESIGNWARE_I2C "designware-i2c"
> > +#define DESIGNWARE_I2C(obj) OBJECT_CHECK(DesignWareI2CState, (obj), \
> > + TYPE_DESIGNWARE_I2C)
> > +
> > +#endif /* DESIGNWARE_I2C_H */
> > diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c
> > new file mode 100644
> > index 000000000000..7d8b1c13533e
> > --- /dev/null
> > +++ b/hw/i2c/designware_i2c.c
> > @@ -0,0 +1,817 @@
> > +/*
> > + * DesignWare I2C Module.
> > + *
> > + * Copyright 2021 Google LLC
> > + *
> > + * SPDX-License-Identifier: GPL-2.0-or-later
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +
> > +#include "hw/i2c/designware_i2c.h"
> > +#include "migration/vmstate.h"
> > +#include "qemu/bitops.h"
> > +#include "qemu/log.h"
> > +#include "qemu/module.h"
> > +#include "qemu/units.h"
> > +#include "trace.h"
> > +
> > +enum DesignWareI2CRegister {
> > + DW_IC_CON = 0x00,
> > + DW_IC_TAR = 0x04,
> > + DW_IC_SAR = 0x08,
> > + DW_IC_DATA_CMD = 0x10,
> > + DW_IC_SS_SCL_HCNT = 0x14,
> > + DW_IC_SS_SCL_LCNT = 0x18,
> > + DW_IC_FS_SCL_HCNT = 0x1c,
> > + DW_IC_FS_SCL_LCNT = 0x20,
> > + DW_IC_INTR_STAT = 0x2c,
> > + DW_IC_INTR_MASK = 0x30,
> > + DW_IC_RAW_INTR_STAT = 0x34,
> > + DW_IC_RX_TL = 0x38,
> > + DW_IC_TX_TL = 0x3c,
> > + DW_IC_CLR_INTR = 0x40,
> > + DW_IC_CLR_RX_UNDER = 0x44,
> > + DW_IC_CLR_RX_OVER = 0x48,
> > + DW_IC_CLR_TX_OVER = 0x4c,
> > + DW_IC_CLR_RD_REQ = 0x50,
> > + DW_IC_CLR_TX_ABRT = 0x54,
> > + DW_IC_CLR_RX_DONE = 0x58,
> > + DW_IC_CLR_ACTIVITY = 0x5c,
> > + DW_IC_CLR_STOP_DET = 0x60,
> > + DW_IC_CLR_START_DET = 0x64,
> > + DW_IC_CLR_GEN_CALL = 0x68,
> > + DW_IC_ENABLE = 0x6c,
> > + DW_IC_STATUS = 0x70,
> > + DW_IC_TXFLR = 0x74,
> > + DW_IC_RXFLR = 0x78,
> > + DW_IC_SDA_HOLD = 0x7c,
> > + DW_IC_TX_ABRT_SOURCE = 0x80,
> > + DW_IC_SLV_DATA_NACK_ONLY = 0x84,
> > + DW_IC_DMA_CR = 0x88,
> > + DW_IC_DMA_TDLR = 0x8c,
> > + DW_IC_DMA_RDLR = 0x90,
> > + DW_IC_SDA_SETUP = 0x94,
> > + DW_IC_ACK_GENERAL_CALL = 0x98,
> > + DW_IC_ENABLE_STATUS = 0x9c,
> > + DW_IC_FS_SPKLEN = 0xa0,
> > + DW_IC_CLR_RESTART_DET = 0xa8,
> > + DW_IC_COMP_PARAM_1 = 0xf4,
> > + DW_IC_COMP_VERSION = 0xf8,
> > + DW_IC_COMP_TYPE = 0xfc,
> > +};
> > +
> > +/* DW_IC_CON fields */
> > +#define DW_IC_CON_STOP_DET_IF_MASTER_ACTIV BIT(10)
> > +#define DW_IC_CON_RX_FIFO_FULL_HLD_CTRL BIT(9)
> > +#define DW_IC_CON_TX_EMPTY_CTRL BIT(8)
> > +#define DW_IC_CON_STOP_IF_ADDRESSED BIT(7)
> > +#define DW_IC_CON_SLAVE_DISABLE BIT(6)
> > +#define DW_IC_CON_IC_RESTART_EN BIT(5)
> > +#define DW_IC_CON_10BITADDR_MASTER BIT(4)
> > +#define DW_IC_CON_10BITADDR_SLAVE BIT(3)
> > +#define DW_IC_CON_SPEED(rv) extract32((rv), 1, 2)
> > +#define DW_IC_CON_MASTER_MODE BIT(0)
> > +
> > +/* DW_IC_TAR fields */
> > +#define DW_IC_TAR_IC_10BITADDR_MASTER BIT(12)
> > +#define DW_IC_TAR_SPECIAL BIT(11)
> > +#define DW_IC_TAR_GC_OR_START BIT(10)
> > +#define DW_IC_TAR_ADDRESS(rv) extract32((rv), 0, 10)
> > +
> > +/* DW_IC_DATA_CMD fields */
> > +#define DW_IC_DATA_CMD_RESTART BIT(10)
> > +#define DW_IC_DATA_CMD_STOP BIT(9)
> > +#define DW_IC_DATA_CMD_CMD BIT(8)
> > +#define DW_IC_DATA_CMD_DAT(rv) extract32((rv), 0, 8)
> > +
> > +/* DW_IC_INTR_STAT/INTR_MASK/RAW_INTR_STAT fields */
> > +#define DW_IC_INTR_RESTART_DET BIT(12)
> > +#define DW_IC_INTR_GEN_CALL BIT(11)
> > +#define DW_IC_INTR_START_DET BIT(10)
> > +#define DW_IC_INTR_STOP_DET BIT(9)
> > +#define DW_IC_INTR_ACTIVITY BIT(8)
> > +#define DW_IC_INTR_RX_DONE BIT(7)
> > +#define DW_IC_INTR_TX_ABRT BIT(6)
> > +#define DW_IC_INTR_RD_REQ BIT(5)
> > +#define DW_IC_INTR_TX_EMPTY BIT(4) /* Hardware clear only. */
> > +#define DW_IC_INTR_TX_OVER BIT(3)
> > +#define DW_IC_INTR_RX_FULL BIT(2) /* Hardware clear only. */
> > +#define DW_IC_INTR_RX_OVER BIT(1)
> > +#define DW_IC_INTR_RX_UNDER BIT(0)
> > +
> > +/* DW_IC_ENABLE fields */
> > +#define DW_IC_ENABLE_TX_CMD_BLOCK BIT(2)
> > +#define DW_IC_ENABLE_ABORT BIT(1)
> > +#define DW_IC_ENABLE_ENABLE BIT(0)
> > +
> > +/* DW_IC_STATUS fields */
> > +#define DW_IC_STATUS_SLV_ACTIVITY BIT(6)
> > +#define DW_IC_STATUS_MST_ACTIVITY BIT(5)
> > +#define DW_IC_STATUS_RFF BIT(4)
> > +#define DW_IC_STATUS_RFNE BIT(3)
> > +#define DW_IC_STATUS_TFE BIT(2)
> > +#define DW_IC_STATUS_TFNF BIT(1)
> > +#define DW_IC_STATUS_ACTIVITY BIT(0)
> > +
> > +/* DW_IC_TX_ABRT_SOURCE fields */
> > +#define DW_IC_TX_TX_FLUSH_CNT extract32((rv), 23, 9)
> > +#define DW_IC_TX_ABRT_USER_ABRT BIT(16)
> > +#define DW_IC_TX_ABRT_SLVRD_INTX BIT(15)
> > +#define DW_IC_TX_ABRT_SLV_ARBLOST BIT(14)
> > +#define DW_IC_TX_ABRT_SLVFLUSH_TXFIFO BIT(13)
> > +#define DW_IC_TX_ARB_LOST BIT(12)
> > +#define DW_IC_TX_ABRT_MASTER_DIS BIT(11)
> > +#define DW_IC_TX_ABRT_10B_RD_NORSTRT BIT(10)
> > +#define DW_IC_TX_ABRT_SBYTE_NORSTRT BIT(9)
> > +#define DW_IC_TX_ABRT_HS_NORSTRT BIT(8)
> > +#define DW_IC_TX_ABRT_SBYTE_ACKDET BIT(7)
> > +#define DW_IC_TX_ABRT_HS_ACKDET BIT(6)
> > +#define DW_IC_TX_ABRT_GCALL_READ BIT(5)
> > +#define DW_IC_TX_ABRT_GCALL_NOACK BIT(4)
> > +#define DW_IC_TX_ABRT_TXDATA_NOACK BIT(3)
> > +#define DW_IC_TX_ABRT_10ADDR2_NOACK BIT(2)
> > +#define DW_IC_TX_ABRT_10ADDR1_NOACK BIT(1)
> > +#define DW_IC_TX_ABRT_7B_ADDR_NOACK BIT(0)
> > +
> > +
> > +/* IC_ENABLE_STATUS fields */
> > +#define DW_IC_ENABLE_STATUS_SLV_RX_DATA_LOST BIT(2)
> > +#define DW_IC_ENABLE_STATUS_SLV_DISABLED_WHILE_BUSY BIT(1)
> > +#define DW_IC_ENABLE_STATUS_IC_EN BIT(0)
> > +
> > +/* Masks for writable registers. */
> > +#define DW_IC_CON_MASK 0x000003ff
> > +#define DW_IC_TAR_MASK 0x00000fff
> > +#define DW_IC_SAR_MASK 0x000003ff
> > +#define DW_IC_SS_SCL_HCNT_MASK 0x0000ffff
> > +#define DW_IC_SS_SCL_LCNT_MASK 0x0000ffff
> > +#define DW_IC_FS_SCL_HCNT_MASK 0x0000ffff
> > +#define DW_IC_FS_SCL_LCNT_MASK 0x0000ffff
> > +#define DW_IC_INTR_MASK_MASK 0x00001fff
> > +#define DW_IC_ENABLE_MASK 0x00000007
> > +#define DW_IC_SDA_HOLD_MASK 0x00ffffff
> > +#define DW_IC_SDA_SETUP_MASK 0x000000ff
> > +#define DW_IC_FS_SPKLEN_MASK 0x000000ff
> > +
> > +/* Reset values */
> > +#define DW_IC_CON_INIT_VAL 0x7d
> > +#define DW_IC_TAR_INIT_VAL 0x1055
> > +#define DW_IC_SAR_INIT_VAL 0x55
> > +#define DW_IC_SS_SCL_HCNT_INIT_VAL 0x190
> > +#define DW_IC_SS_SCL_LCNT_INIT_VAL 0x1d6
> > +#define DW_IC_FS_SCL_HCNT_INIT_VAL 0x3c
> > +#define DW_IC_FS_SCL_LCNT_INIT_VAL 0x82
> > +#define DW_IC_INTR_MASK_INIT_VAL 0x8ff
> > +#define DW_IC_STATUS_INIT_VAL 0x6
> > +#define DW_IC_SDA_HOLD_INIT_VAL 0x1
> > +#define DW_IC_SDA_SETUP_INIT_VAL 0x64
> > +#define DW_IC_FS_SPKLEN_INIT_VAL 0x2
> > +
> > +#define DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS BIT(7)
> > +#define DW_IC_COMP_PARAM_1_HAS_DMA 0 /* bit 6 - DMA disabled. */
> > +#define DW_IC_COMP_PARAM_1_INTR_IO BIT(5)
> > +#define DW_IC_COMP_PARAM_1_HC_COUNT_VAL 0 /* bit 4 - disabled */
> > +#define DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE (BIT(2) | BIT(3))
> > +#define DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 BIT(1) /* bits 0, 1 */
> > +#define DW_IC_COMP_PARAM_1_INIT_VAL \
> > + (DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 | \
> > + DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE | \
> > + DW_IC_COMP_PARAM_1_HC_COUNT_VAL | \
> > + DW_IC_COMP_PARAM_1_INTR_IO | \
> > + DW_IC_COMP_PARAM_1_HAS_DMA | \
> > + DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS | \
> > + ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1) << 8) | \
> > + ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1) << 16))
> > +#define DW_IC_COMP_VERSION_INIT_VAL 0x3132302a
> > +#define DW_IC_COMP_TYPE_INIT_VAL 0x44570140
>
> I would prefer these to sue the QEMU register API
That's probably a good idea. It's a lot of churn to change it, but
I'm having a try. Might do it as a follow on patch.
> > +
> > + /* Receive data */
> > + if (recv) {
> > + uint8_t pos = (s->rx_cur + s->ic_rxflr) % DESIGNWARE_I2C_RX_FIFO_SIZE;
> > +
> > + if (s->ic_rxflr < DESIGNWARE_I2C_RX_FIFO_SIZE) {
> > + s->rx_fifo[pos] = i2c_recv(s->bus);
> > + s->ic_rxflr++;
>
> QEMU also has the Fifo8 which would handle the FIFO for you, I would
> prefer that as well.
Yeah, that one isn't so hard and makes the code nicer. I might've found
a bug in dw_i2c_reset_to_idle() not clearing rx_cur while doing it.
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-05-05 6:00 ` Nicholas Piggin
@ 2026-05-05 7:31 ` Philippe Mathieu-Daudé
2026-05-06 3:14 ` Alistair Francis
1 sibling, 0 replies; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-05-05 7:31 UTC (permalink / raw)
To: Nicholas Piggin, Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel
On 5/5/26 08:00, Nicholas Piggin wrote:
> On Tue, May 05, 2026 at 02:34:50PM +1000, Alistair Francis wrote:
>> On Tue, May 5, 2026 at 11:04 AM Nicholas Piggin <npiggin@gmail.com> wrote:
>>>
>>> On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
>>>> On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
>>>>>
>>>>> The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
>>>>> and CoreLab Technology. It is based on the Atlantis SoC, which includes
>>>>> the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
>>>>
>>>> Can you provide a link to documentation
>>>
>>> Also not presently.
>>
>> Hmmm... That's a bit annoying. Can we get some?
That would be nice of course. I didn't mind while reviewing because both
documentation and functional test are provided.
> That's the plan but I don't have a date I can give you for it, sorry.
>
> [...]
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 01/13] hw/i2c: Add designware i2c controller
2026-05-05 6:20 ` Nicholas Piggin
@ 2026-05-05 7:36 ` Philippe Mathieu-Daudé
2026-05-06 5:58 ` Nicholas Piggin
0 siblings, 1 reply; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-05-05 7:36 UTC (permalink / raw)
To: Nicholas Piggin, Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Chris Rauer, Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel, Hao Wu
On 5/5/26 08:20, Nicholas Piggin wrote:
> On Thu, Apr 30, 2026 at 01:53:21PM +1000, Alistair Francis wrote:
>> On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
>>>
>>> From: Chris Rauer <crauer@google.com>
>>>
>>> In the past this model has been submitted for use with the arm virt
>>> machine, however in this case it will be used by the upcoming
>>> Tenstorrent Atlantis RISC-V machine.
>>>
>>> This is a re-submission of the model with Chris' permission, with a
>>> light touch of updates to make it build with qemu master.
>>>
>>> Reviewed-by: Hao Wu <wuhaotsh@google.com>
>>> Signed-off-by: Chris Rauer <crauer@google.com>
>>> Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com
>>> [jms: rebase and minor build fixes for class_init and reset callback]
>>> Signed-off-by: Joel Stanley <joel@jms.id.au>
>>> ---
>>> +#define DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS BIT(7)
>>> +#define DW_IC_COMP_PARAM_1_HAS_DMA 0 /* bit 6 - DMA disabled. */
>>> +#define DW_IC_COMP_PARAM_1_INTR_IO BIT(5)
>>> +#define DW_IC_COMP_PARAM_1_HC_COUNT_VAL 0 /* bit 4 - disabled */
>>> +#define DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE (BIT(2) | BIT(3))
>>> +#define DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 BIT(1) /* bits 0, 1 */
>>> +#define DW_IC_COMP_PARAM_1_INIT_VAL \
>>> + (DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 | \
>>> + DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE | \
>>> + DW_IC_COMP_PARAM_1_HC_COUNT_VAL | \
>>> + DW_IC_COMP_PARAM_1_INTR_IO | \
>>> + DW_IC_COMP_PARAM_1_HAS_DMA | \
>>> + DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS | \
>>> + ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1) << 8) | \
>>> + ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1) << 16))
>>> +#define DW_IC_COMP_VERSION_INIT_VAL 0x3132302a
>>> +#define DW_IC_COMP_TYPE_INIT_VAL 0x44570140
>>
>> I would prefer these to sue the QEMU register API
>
> That's probably a good idea. It's a lot of churn to change it, but
> I'm having a try. Might do it as a follow on patch.
Good suggestion but please as a follow up, this patch is already
big enough, is reviewed and tested.
>
>>> +
>>> + /* Receive data */
>>> + if (recv) {
>>> + uint8_t pos = (s->rx_cur + s->ic_rxflr) % DESIGNWARE_I2C_RX_FIFO_SIZE;
>>> +
>>> + if (s->ic_rxflr < DESIGNWARE_I2C_RX_FIFO_SIZE) {
>>> + s->rx_fifo[pos] = i2c_recv(s->bus);
>>> + s->ic_rxflr++;
>>
>> QEMU also has the Fifo8 which would handle the FIFO for you, I would
>> prefer that as well.
>
> Yeah, that one isn't so hard and makes the code nicer. I might've found
> a bug in dw_i2c_reset_to_idle() not clearing rx_cur while doing it.
>
> Thanks,
> Nick
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 01/13] hw/i2c: Add designware i2c controller
2026-04-25 13:17 ` [PATCH v4 01/13] hw/i2c: Add designware i2c controller Joel Stanley
2026-04-30 3:53 ` Alistair Francis
@ 2026-05-05 7:57 ` Philippe Mathieu-Daudé
2026-05-06 5:53 ` Nicholas Piggin
1 sibling, 1 reply; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-05-05 7:57 UTC (permalink / raw)
To: Joel Stanley, Alistair Francis, Daniel Henrique Barboza
Cc: Chao Liu, Chris Rauer, Michael Ellerman, Nicholas Piggin,
Joel Stanley, Anirudh Srinivasan, Portia Stephens, qemu-riscv,
qemu-devel, Hao Wu
On 25/4/26 15:17, Joel Stanley wrote:
> From: Chris Rauer <crauer@google.com>
>
> In the past this model has been submitted for use with the arm virt
> machine, however in this case it will be used by the upcoming
> Tenstorrent Atlantis RISC-V machine.
>
> This is a re-submission of the model with Chris' permission, with a
> light touch of updates to make it build with qemu master.
>
> Reviewed-by: Hao Wu <wuhaotsh@google.com>
> Signed-off-by: Chris Rauer <crauer@google.com>
> Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com
> [jms: rebase and minor build fixes for class_init and reset callback]
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> ---
> v4: Remove unused random header
> v2: Add trace event for read and write, document Alano and myself as
> reviewers.
> ---
> MAINTAINERS | 8 +
> include/hw/i2c/designware_i2c.h | 101 ++++
> hw/i2c/designware_i2c.c | 817 ++++++++++++++++++++++++++++++++
> hw/i2c/Kconfig | 4 +
> hw/i2c/meson.build | 1 +
> hw/i2c/trace-events | 4 +
> 6 files changed, 935 insertions(+)
> create mode 100644 include/hw/i2c/designware_i2c.h
> create mode 100644 hw/i2c/designware_i2c.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index aa4267b15806..e1942a86eba5 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -2716,6 +2716,14 @@ S: Orphan
> F: hw/gpio/pcf8574.c
> F: include/gpio/pcf8574.h
>
> +DesignWare I2C
> +M: Chris Rauer <crauer@google.com>
This was 4 years ago. Is Chris still up to this?
> +R: Alano Song <alanosong@163.com>
> +R: Joel Stanley <joel@jms.id.au>
> +S: Maintained
> +F: hw/i2c/designware_i2c.c
> +F: include/hw/i2c/designware_i2c.h
> +
> Generic Loader
> M: Alistair Francis <alistair@alistair23.me>
> S: Maintained
> diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h
> new file mode 100644
> index 000000000000..0d8f904f51b7
> --- /dev/null
> +++ b/include/hw/i2c/designware_i2c.h
> +typedef struct DesignWareI2CState {
> + SysBusDevice parent;
s/parent/parent_obj/
> +
> + MemoryRegion iomem;
> +
> + I2CBus *bus;
> + qemu_irq irq;
> +
> + uint32_t ic_con;
> + uint32_t ic_tar;
> + uint32_t ic_sar;
> + uint32_t ic_ss_scl_hcnt;
> + uint32_t ic_ss_scl_lcnt;
> + uint32_t ic_fs_scl_hcnt;
> + uint32_t ic_fs_scl_lcnt;
> + uint32_t ic_intr_mask;
> + uint32_t ic_raw_intr_stat;
> + uint32_t ic_rx_tl;
> + uint32_t ic_tx_tl;
> + uint32_t ic_enable;
> + uint32_t ic_status;
> + uint32_t ic_txflr;
> + uint32_t ic_rxflr;
> + uint32_t ic_sda_hold;
> + uint32_t ic_tx_abrt_source;
> + uint32_t ic_sda_setup;
> + uint32_t ic_enable_status;
> + uint32_t ic_fs_spklen;
> + uint32_t ic_comp_param_1;
> + uint32_t ic_comp_version;
> + uint32_t ic_comp_type;
> +
> + uint8_t rx_fifo[DESIGNWARE_I2C_RX_FIFO_SIZE];
> + uint8_t rx_cur;
> +
> + DesignWareI2CStatus status;
> +} DesignWareI2CState;
> +#endif /* DESIGNWARE_I2C_H */
> diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c
> +enum DesignWareI2CRegister {
> + DW_IC_CON = 0x00,
> + DW_IC_TAR = 0x04,
> + DW_IC_ACK_GENERAL_CALL = 0x98,
> + DW_IC_ENABLE_STATUS = 0x9c,
> + DW_IC_FS_SPKLEN = 0xa0,
> + DW_IC_CLR_RESTART_DET = 0xa8,
> + DW_IC_COMP_PARAM_1 = 0xf4,
> + DW_IC_COMP_VERSION = 0xf8,
> + DW_IC_COMP_TYPE = 0xfc,
> +};
> +static void dw_i2c_write(void *opaque, hwaddr offset, uint64_t value,
> + unsigned size)
> +{
> + DesignWareI2CState *s = opaque;
> +
> + trace_dw_i2c_write(DEVICE(s)->canonical_path, offset, value);
> +
> + /* The order of the registers are their order in memory. */
> + switch (offset) {
> + case DW_IC_CON:
> + dw_i2c_write_ic_con(s, value);
> + break;
> + case DW_IC_TAR:
> + s->ic_tar = value & DW_IC_TAR_MASK;
> + break;
> + case DW_IC_SAR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_sar\n",
> + DEVICE(s)->canonical_path);
> + s->ic_sar = value & DW_IC_SAR_MASK;
Unsupported but updated...
> + break;
> + case DW_IC_DATA_CMD:
> + dw_i2c_write_ic_data_cmd(s, value);
> + break;
> + case DW_IC_SS_SCL_HCNT:
> + s->ic_ss_scl_hcnt = value & DW_IC_SS_SCL_HCNT_MASK;
> + break;
> + case DW_IC_SS_SCL_LCNT:
> + s->ic_ss_scl_lcnt = value & DW_IC_SS_SCL_LCNT_MASK;
> + break;
> + case DW_IC_FS_SCL_HCNT:
> + s->ic_fs_scl_hcnt = value & DW_IC_FS_SCL_HCNT_MASK;
> + break;
> + case DW_IC_FS_SCL_LCNT:
> + s->ic_fs_scl_lcnt = value & DW_IC_FS_SCL_LCNT_MASK;
> + break;
> + case DW_IC_INTR_MASK:
> + s->ic_intr_mask = value & DW_IC_INTR_MASK_MASK;
> + dw_i2c_update_irq(s);
> + break;
> + case DW_IC_RX_TL:
> + dw_i2c_write_ic_rx_tl(s, value);
> + break;
> + case DW_IC_TX_TL:
> + dw_i2c_write_ic_tx_tl(s, value);
> + break;
> + case DW_IC_ENABLE:
> + dw_i2c_write_ic_enable(s, value);
> + break;
> + case DW_IC_SDA_HOLD:
> + s->ic_sda_hold = value & DW_IC_SDA_HOLD_MASK;
> + break;
> + case DW_IC_SLV_DATA_NACK_ONLY:
> + qemu_log_mask(LOG_UNIMP,
> + "%s: unsupported write - ic_slv_data_nack_only\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_DMA_CR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_cr\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_DMA_TDLR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_tdlr\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_DMA_RDLR:
> + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_dma_rdlr\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_SDA_SETUP:
> + s->ic_sda_setup = value & DW_IC_SDA_SETUP_MASK;
> + break;
> + case DW_IC_ACK_GENERAL_CALL:
> + qemu_log_mask(LOG_UNIMP,
> + "%s: unsupported write - ic_ack_general_call\n",
> + DEVICE(s)->canonical_path);
> + break;
> + case DW_IC_FS_SPKLEN:
> + s->ic_fs_spklen = value & DW_IC_FS_SPKLEN_MASK;
> + break;
> +
> + /* This register is invalid at this point. */
> + default:
> + qemu_log_mask(LOG_GUEST_ERROR,
> + "%s: write to invalid offset or readonly register 0x%"
> + HWADDR_PRIx "\n",
> + DEVICE(s)->canonical_path, offset);
> + break;
> + }
> +}
> +
> +static const MemoryRegionOps designware_i2c_ops = {
> + .read = dw_i2c_read,
> + .write = dw_i2c_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
.impl = { 4, 4 }
> + .valid = {
> + .min_access_size = 4,
> + .max_access_size = 4,
> + .unaligned = false,
> + },
> +};
> +
> +static void designware_i2c_enter_reset(Object *obj, ResetType type)
> +{
> + DesignWareI2CState *s = DESIGNWARE_I2C(obj);
> +
> + s->ic_con = DW_IC_CON_INIT_VAL;
> + s->ic_tar = DW_IC_TAR_INIT_VAL;
> + s->ic_sar = DW_IC_SAR_INIT_VAL;
> + s->ic_ss_scl_hcnt = DW_IC_SS_SCL_HCNT_INIT_VAL;
> + s->ic_ss_scl_lcnt = DW_IC_SS_SCL_LCNT_INIT_VAL;
> + s->ic_fs_scl_hcnt = DW_IC_FS_SCL_HCNT_INIT_VAL;
> + s->ic_fs_scl_lcnt = DW_IC_FS_SCL_LCNT_INIT_VAL;
> + s->ic_intr_mask = DW_IC_INTR_MASK_INIT_VAL;
> + s->ic_raw_intr_stat = 0;
> + s->ic_rx_tl = 0;
> + s->ic_tx_tl = 0;
> + s->ic_enable = 0;
> + s->ic_status = DW_IC_STATUS_INIT_VAL;
> + s->ic_txflr = 0;
> + s->ic_rxflr = 0;
> + s->ic_sda_hold = DW_IC_SDA_HOLD_INIT_VAL;
> + s->ic_tx_abrt_source = 0;
> + s->ic_sda_setup = DW_IC_SDA_SETUP_INIT_VAL;
> + s->ic_enable_status = 0;
> + s->ic_fs_spklen = DW_IC_FS_SPKLEN_INIT_VAL;
> + s->ic_comp_param_1 = DW_IC_COMP_PARAM_1_INIT_VAL;
> + s->ic_comp_version = DW_IC_COMP_VERSION_INIT_VAL;
> + s->ic_comp_type = DW_IC_COMP_TYPE_INIT_VAL;
Preferably initialize the IC_COMP registers once in the
designware_i2c_realize() handler.
> + s->rx_cur = 0;
> + s->status = DW_I2C_STATUS_IDLE;
> +}
> +
> +static void designware_i2c_hold_reset(Object *obj, ResetType type)
> +{
> + DesignWareI2CState *s = DESIGNWARE_I2C(obj);
> +
> + qemu_irq_lower(s->irq);
> +}
> +
> +static const VMStateDescription vmstate_designware_i2c = {
> + .name = TYPE_DESIGNWARE_I2C,
> + .version_id = 0,
> + .minimum_version_id = 0,
> + .fields = (VMStateField[]) {
const!
> + VMSTATE_UINT32(ic_con, DesignWareI2CState),
> + VMSTATE_UINT32(ic_tar, DesignWareI2CState),
> + VMSTATE_UINT32(ic_sar, DesignWareI2CState),
> + VMSTATE_UINT32(ic_ss_scl_hcnt, DesignWareI2CState),
> + VMSTATE_UINT32(ic_ss_scl_lcnt, DesignWareI2CState),
> + VMSTATE_UINT32(ic_fs_scl_hcnt, DesignWareI2CState),
> + VMSTATE_UINT32(ic_fs_scl_lcnt, DesignWareI2CState),
> + VMSTATE_UINT32(ic_intr_mask, DesignWareI2CState),
> + VMSTATE_UINT32(ic_raw_intr_stat, DesignWareI2CState),
> + VMSTATE_UINT32(ic_rx_tl, DesignWareI2CState),
> + VMSTATE_UINT32(ic_tx_tl, DesignWareI2CState),
> + VMSTATE_UINT32(ic_enable, DesignWareI2CState),
> + VMSTATE_UINT32(ic_status, DesignWareI2CState),
> + VMSTATE_UINT32(ic_txflr, DesignWareI2CState),
> + VMSTATE_UINT32(ic_rxflr, DesignWareI2CState),
> + VMSTATE_UINT32(ic_sda_hold, DesignWareI2CState),
> + VMSTATE_UINT32(ic_tx_abrt_source, DesignWareI2CState),
> + VMSTATE_UINT32(ic_sda_setup, DesignWareI2CState),
> + VMSTATE_UINT32(ic_enable_status, DesignWareI2CState),
> + VMSTATE_UINT32(ic_fs_spklen, DesignWareI2CState),
> + VMSTATE_UINT32(ic_comp_param_1, DesignWareI2CState),
> + VMSTATE_UINT32(ic_comp_version, DesignWareI2CState),
> + VMSTATE_UINT32(ic_comp_type, DesignWareI2CState),
> + VMSTATE_UINT32(status, DesignWareI2CState),
> + VMSTATE_UINT8_ARRAY(rx_fifo, DesignWareI2CState,
> + DESIGNWARE_I2C_RX_FIFO_SIZE),
> + VMSTATE_UINT8(rx_cur, DesignWareI2CState),
> + VMSTATE_END_OF_LIST(),
> + },
> +};
> +
> +static void designware_i2c_smbus_init(Object *obj)
> +{
> + DesignWareI2CState *s = DESIGNWARE_I2C(obj);
> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +
> + sysbus_init_irq(sbd, &s->irq);
> +
> + memory_region_init_io(&s->iomem, obj, &designware_i2c_ops, s,
> + "regs", 4 * KiB);
I note the current registers only describe a 1KiB region.
> + sysbus_init_mmio(sbd, &s->iomem);
> +
> + s->bus = i2c_init_bus(DEVICE(s), "i2c-bus");
> +}
> +
> +static void designware_i2c_class_init(ObjectClass *klass, const void *data)
> +{
> + ResettableClass *rc = RESETTABLE_CLASS(klass);
> + DeviceClass *dc = DEVICE_CLASS(klass);
> +
> + dc->desc = "Designware I2C";
> + dc->vmsd = &vmstate_designware_i2c;
> + rc->phases.enter = designware_i2c_enter_reset;
> + rc->phases.hold = designware_i2c_hold_reset;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> +}
Since I couldn't look at the datasheet values, for overall modelling:
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 04/13] hw/riscv/boot: Provide a simple halting payload
2026-05-04 23:52 ` Nicholas Piggin
@ 2026-05-05 8:06 ` Philippe Mathieu-Daudé
2026-05-07 1:53 ` Nicholas Piggin
0 siblings, 1 reply; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-05-05 8:06 UTC (permalink / raw)
To: Nicholas Piggin, Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel
On 5/5/26 01:52, Nicholas Piggin wrote:
> On Thu, Apr 30, 2026 at 09:35:33AM +1000, Alistair Francis wrote:
>> On Sat, Apr 25, 2026 at 11:21 PM Joel Stanley <joel@jms.id.au> wrote:
>>>
>>> From: Nicholas Piggin <npiggin@gmail.com>
>>>
>>> OpenSBI hangs before any console output if the domain init code sees the
>>> next stage is not in an executable region.
>>
>> That's an OpenSBI bug though and needs to be fixed there and not just
>> hacked around in QEMU.
>
> You're right of course. OpenSBI has other no-output crash cases in early
> boot code (not just this), so it would be nice to fix properly. It wasn't
> trivial to fix (IIRC because serial device is not detected before a
> bunch of the early failures), I have some hacks, but I need to go back
> and come up with something nicer.
>
> This is only related to Atlantis by coincidence of the physical address
> map. It can be dropped out of the series.
Meanwhile add it in hw/riscv/tt_atlantis.c:
static void
tt_atlantis_setup_opensbi_halting_payload_fixup(RISCVBootInfo *info,
hwaddr addr)
{ ... }
and call in tt_atlantis_machine_init()!
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-05-05 6:00 ` Nicholas Piggin
2026-05-05 7:31 ` Philippe Mathieu-Daudé
@ 2026-05-06 3:14 ` Alistair Francis
2026-05-07 1:50 ` Nicholas Piggin
1 sibling, 1 reply; 44+ messages in thread
From: Alistair Francis @ 2026-05-06 3:14 UTC (permalink / raw)
To: Nicholas Piggin
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Tue, May 5, 2026 at 4:00 PM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> On Tue, May 05, 2026 at 02:34:50PM +1000, Alistair Francis wrote:
> > On Tue, May 5, 2026 at 11:04 AM Nicholas Piggin <npiggin@gmail.com> wrote:
> > >
> > > On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
> > > > On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> > > > >
> > > > > The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> > > > > and CoreLab Technology. It is based on the Atlantis SoC, which includes
> > > > > the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
> > > >
> > > > Can you provide a link to documentation
> > >
> > > Also not presently.
> >
> > Hmmm... That's a bit annoying. Can we get some?
>
> That's the plan but I don't have a date I can give you for it, sorry.
>
> [...]
>
> > > > > +static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
> > > >
> > > > So generally we only create a device tree for virtual boards, that is
> > > > boards that don't really exist and are just QEMU constructs.
> > > >
> > > > You said that this is based on the "Atlantis SoC" so I assume it's
> > > > real hardware. In the real hardware do you expect the device tree to
> > > > be provided by the ROM? Usually for boards users supply the device
> > > > tree as part of the boot flow (via u-boot or something similar). In
> > > > which case should QEMU actually generate this or can users just supply
> > > > their own?
> > > >
> > > > Basically the question is, if you are trying to model the hardware, is
> > > > generating the device tree here correct?
> > > >
> > > > Obviously the advantage of doing it here is then the device tree
> > > > describes what QEMU supports, which is great. But that can be harder
> > > > to manage when the hardware doesn't do that, as then you have
> > > > divergence.
> > >
> > > We went around this same discussion internally and yeah it's a valid
> > > question, we could have explained this a bit better.
> > >
> > > In the real (unreleased) hardware and the DT will be loaded from ROM.
> >
> > Really? That usually doesn't go well
>
> Well not ROM but flash loaded by early firmware. The plan is to have an
Fair, when I say ROM I guess I mean ROM or rarely changed firmware,
not specifically only ROM.
> "M mode" dtb loaded for opensbi and then later stages supply their own
> dtb.
Ok, so the "hardware" will provide a DTB to OpenSBI. In which case
this is the right approach for QEMU.
Can you document that in the commit message?
>
> The opensbi dtb won't go away from the tt-atlantis model, whether its
> generated or we change it to use a dtb for the purpose of QEMU. I guess
You can ask the user to supply a DTB, which is passed to OpenSBI (and
then other layers).
But if you expect your hardware to provide a DTB we should match that.
> if the next stage does not have its own dtb it can use one supplied by
> opensbi.
>
> Does that sound right? I don't know if I have the details exactly right.
I don't know either! You don't have public documentation :)
>
> > > I think we should do that when the model and firmware and dt is more
> > > complete. At the moment it has been easier for development to use QEMU
> > > generated fdt.
> > >
> > > Is that acceptable to do that now and switch to a fdt ROM later...
> >
> > Obviously guest software can decide to use whatever it wants. But what
> > we don't want to do is break users. So removing the generated FDT
> > (which is the equivalent of ROM FDT on hardware) will cause breakages.
> > So we don't want to do that if we can avoid it. Also generating the
> > FDT is overhead for us, so we only want to if the guest software will
> > actually use it. Does that make sense?
> >
> > Adding the device tree later is fine though, as guests don't have to use it.
> >
> > >
> > > [...]
> > >
> > > > > +static void finalize_fdt(TTAtlantisState *s)
> > > > > +{
> > > > > + uint32_t aplic_s_phandle = next_phandle();
> > > > > + uint32_t imsic_s_phandle = next_phandle();
> > > > > + void *fdt = MACHINE(s)->fdt;
> > > > > +
> > > > > + create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
> > > > > +
> > > > > + /*
> > > > > + * We want to do this, but the Linux aplic driver was broken before v6.16
> > > > > + *
> > > > > + * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
> > > > > + * aplic_s_phandle);
> > > > > + */
> > > >
> > > > And now we are stuck with hacks like this, which we really don't want
> > > > to be dealing with. Maybe it's best to leave the device tree to the
> > > > kernel.
> > >
> > > ... Despite issues like this?
> >
> > If the kernel generates a device tree for it to use then we don't have
> > mismatched problems (like this one). Generating the DT in QEMU for
> > real boards proves to be a challenge and this is exactly why. Now we
> > have to choose between pre or post 6.16 support (I assume 6.16+ works
> > without this, but you get the point).
>
> Yeah I see do see why we'd want a Linux one, and we are currently
> upstreaming dts for Linux. At the moment we don't have it so using
> the firmware stage one fills the gap.
>
> It is a little bit hacky maybe but I guess I'm not seeing where a
> compatibility problem comes in versus other systems with their own
> firmware DTB (that I assume a next-stage could similarly use and be
> exposed to these issues if it did not supply its own).
The difference is I don't have to worry or maintain changing firmware :P
Basically QEMU providing an FDT increases the machine complexity and
maintenance overhead. Admittingly it has probably improved now that
RISC-V has stabalised a bit, but it used to be much more of a hassle
to deal with.
So we want a reason to do that, as once it's merged we are mostly
stuck with it. Adding it just to simplify development now isn't a good
reason. Adding it because that's what the real hardware does is a good
reason though.
Alistair
>
> Thanks,
> Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 01/13] hw/i2c: Add designware i2c controller
2026-05-05 7:57 ` Philippe Mathieu-Daudé
@ 2026-05-06 5:53 ` Nicholas Piggin
2026-05-06 8:55 ` Philippe Mathieu-Daudé
0 siblings, 1 reply; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-06 5:53 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Chris Rauer, Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel, Hao Wu
On Tue, May 05, 2026 at 09:57:20AM +0200, Philippe Mathieu-Daudé wrote:
> On 25/4/26 15:17, Joel Stanley wrote:
> > From: Chris Rauer <crauer@google.com>
> >
> > In the past this model has been submitted for use with the arm virt
> > machine, however in this case it will be used by the upcoming
> > Tenstorrent Atlantis RISC-V machine.
> >
> > This is a re-submission of the model with Chris' permission, with a
> > light touch of updates to make it build with qemu master.
> >
> > Reviewed-by: Hao Wu <wuhaotsh@google.com>
> > Signed-off-by: Chris Rauer <crauer@google.com>
> > Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com
> > [jms: rebase and minor build fixes for class_init and reset callback]
> > Signed-off-by: Joel Stanley <joel@jms.id.au>
> > ---
> > v4: Remove unused random header
> > v2: Add trace event for read and write, document Alano and myself as
> > reviewers.
> > ---
> > MAINTAINERS | 8 +
> > include/hw/i2c/designware_i2c.h | 101 ++++
> > hw/i2c/designware_i2c.c | 817 ++++++++++++++++++++++++++++++++
> > hw/i2c/Kconfig | 4 +
> > hw/i2c/meson.build | 1 +
> > hw/i2c/trace-events | 4 +
> > 6 files changed, 935 insertions(+)
> > create mode 100644 include/hw/i2c/designware_i2c.h
> > create mode 100644 hw/i2c/designware_i2c.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index aa4267b15806..e1942a86eba5 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -2716,6 +2716,14 @@ S: Orphan
> > F: hw/gpio/pcf8574.c
> > F: include/gpio/pcf8574.h
> > +DesignWare I2C
> > +M: Chris Rauer <crauer@google.com>
>
> This was 4 years ago. Is Chris still up to this?
Joel is away for a few weeks, but AFAIK he did ping Chris and got
ack to upstream his patch.
> > + case DW_IC_SAR:
> > + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_sar\n",
> > + DEVICE(s)->canonical_path);
> > + s->ic_sar = value & DW_IC_SAR_MASK;
>
> Unsupported but updated...
Yeah, not sure what the best course of action is, maybe device driver
was working better if the value got saved?
> > + s->ic_comp_param_1 = DW_IC_COMP_PARAM_1_INIT_VAL;
> > + s->ic_comp_version = DW_IC_COMP_VERSION_INIT_VAL;
> > + s->ic_comp_type = DW_IC_COMP_TYPE_INIT_VAL;
>
> Preferably initialize the IC_COMP registers once in the
> designware_i2c_realize() handler.
This I didn't change yet because I started register API converstion and
that all changes.
> > +static void designware_i2c_smbus_init(Object *obj)
> > +{
> > + DesignWareI2CState *s = DESIGNWARE_I2C(obj);
> > + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> > +
> > + sysbus_init_irq(sbd, &s->irq);
> > +
> > + memory_region_init_io(&s->iomem, obj, &designware_i2c_ops, s,
> > + "regs", 4 * KiB);
>
> I note the current registers only describe a 1KiB region.
256 bytes even. It might have 4K alignment in data sheet.
> > +}
> Since I couldn't look at the datasheet values, for overall modelling:
> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Thanks!
I have an incremental patch here that incorporates review comments and
also a couple of possible issues I found - RX underflow bit was never
being set AFAIKS it should be set here, and one of the INTR bit clear
registers was not recalculating irqs.
I will fold this in before next repost if there are no objections.
Thanks,
Nick
---
diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c
index 7d8b1c1353..1e5505f571 100644
--- a/hw/i2c/designware_i2c.c
+++ b/hw/i2c/designware_i2c.c
@@ -217,7 +217,7 @@ static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s)
"%s: Attempted to read from RX fifo when not in receive "
"state.\n", DEVICE(s)->canonical_path);
if (s->status != DW_I2C_STATUS_IDLE) {
- s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
+ s->ic_raw_intr_stat |= DW_IC_INTR_RX_UNDER;
dw_i2c_update_irq(s);
}
return 0;
@@ -228,7 +228,7 @@ static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s)
if (s->ic_rxflr > 0) {
s->ic_rxflr--;
} else {
- s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
+ s->ic_raw_intr_stat |= DW_IC_INTR_RX_UNDER;
dw_i2c_update_irq(s);
return 0;
}
@@ -395,6 +395,7 @@ static uint64_t dw_i2c_read(void *opaque, hwaddr offset, unsigned size)
break;
case DW_IC_CLR_RESTART_DET:
s->ic_raw_intr_stat &= ~(DW_IC_INTR_RESTART_DET);
+ dw_i2c_update_irq(s);
break;
case DW_IC_COMP_PARAM_1:
value = s->ic_comp_param_1;
@@ -698,6 +699,10 @@ static const MemoryRegionOps designware_i2c_ops = {
.read = dw_i2c_read,
.write = dw_i2c_write,
.endianness = DEVICE_LITTLE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4,
+ },
.valid = {
.min_access_size = 4,
.max_access_size = 4,
@@ -748,7 +753,7 @@ static const VMStateDescription vmstate_designware_i2c = {
.name = TYPE_DESIGNWARE_I2C,
.version_id = 0,
.minimum_version_id = 0,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT32(ic_con, DesignWareI2CState),
VMSTATE_UINT32(ic_tar, DesignWareI2CState),
VMSTATE_UINT32(ic_sar, DesignWareI2CState),
@@ -803,6 +808,8 @@ static void designware_i2c_class_init(ObjectClass *klass, const void *data)
dc->vmsd = &vmstate_designware_i2c;
rc->phases.enter = designware_i2c_enter_reset;
rc->phases.hold = designware_i2c_hold_reset;
+
+ set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
static const TypeInfo designware_i2c_types[] = {
diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h
index 0d8f904f51..71d74c9141 100644
--- a/include/hw/i2c/designware_i2c.h
+++ b/include/hw/i2c/designware_i2c.h
@@ -57,7 +57,7 @@ typedef enum DesignWareI2CStatus {
* @status: The current status of the SMBus.
*/
typedef struct DesignWareI2CState {
- SysBusDevice parent;
+ SysBusDevice parent_obj;
MemoryRegion iomem;
^ permalink raw reply related [flat|nested] 44+ messages in thread
* Re: [PATCH v4 01/13] hw/i2c: Add designware i2c controller
2026-05-05 7:36 ` Philippe Mathieu-Daudé
@ 2026-05-06 5:58 ` Nicholas Piggin
2026-05-06 8:59 ` Philippe Mathieu-Daudé
0 siblings, 1 reply; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-06 5:58 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: Alistair Francis, Joel Stanley, Alistair Francis,
Daniel Henrique Barboza, Chao Liu, Chris Rauer, Michael Ellerman,
Joel Stanley, Anirudh Srinivasan, Portia Stephens, qemu-riscv,
qemu-devel, Hao Wu
On Tue, May 05, 2026 at 09:36:57AM +0200, Philippe Mathieu-Daudé wrote:
> On 5/5/26 08:20, Nicholas Piggin wrote:
> > On Thu, Apr 30, 2026 at 01:53:21PM +1000, Alistair Francis wrote:
> > > On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> > > >
> > > > From: Chris Rauer <crauer@google.com>
> > > >
> > > > In the past this model has been submitted for use with the arm virt
> > > > machine, however in this case it will be used by the upcoming
> > > > Tenstorrent Atlantis RISC-V machine.
> > > >
> > > > This is a re-submission of the model with Chris' permission, with a
> > > > light touch of updates to make it build with qemu master.
> > > >
> > > > Reviewed-by: Hao Wu <wuhaotsh@google.com>
> > > > Signed-off-by: Chris Rauer <crauer@google.com>
> > > > Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com
> > > > [jms: rebase and minor build fixes for class_init and reset callback]
> > > > Signed-off-by: Joel Stanley <joel@jms.id.au>
> > > > ---
>
>
> > > > +#define DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS BIT(7)
> > > > +#define DW_IC_COMP_PARAM_1_HAS_DMA 0 /* bit 6 - DMA disabled. */
> > > > +#define DW_IC_COMP_PARAM_1_INTR_IO BIT(5)
> > > > +#define DW_IC_COMP_PARAM_1_HC_COUNT_VAL 0 /* bit 4 - disabled */
> > > > +#define DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE (BIT(2) | BIT(3))
> > > > +#define DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 BIT(1) /* bits 0, 1 */
> > > > +#define DW_IC_COMP_PARAM_1_INIT_VAL \
> > > > + (DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 | \
> > > > + DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE | \
> > > > + DW_IC_COMP_PARAM_1_HC_COUNT_VAL | \
> > > > + DW_IC_COMP_PARAM_1_INTR_IO | \
> > > > + DW_IC_COMP_PARAM_1_HAS_DMA | \
> > > > + DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS | \
> > > > + ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1) << 8) | \
> > > > + ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1) << 16))
> > > > +#define DW_IC_COMP_VERSION_INIT_VAL 0x3132302a
> > > > +#define DW_IC_COMP_TYPE_INIT_VAL 0x44570140
> > >
> > > I would prefer these to sue the QEMU register API
> >
> > That's probably a good idea. It's a lot of churn to change it, but
> > I'm having a try. Might do it as a follow on patch.
>
> Good suggestion but please as a follow up, this patch is already
> big enough, is reviewed and tested.
I agree... however I implemented it and it is a large amount of chrun
which is also not easy to review. Also it breaks migration
compatibility. So I don't want to get this patch bogged down but if we
agree to take the register API conversion then maybe we should squash
before upstreaming?
Not sure. I will post the patch as a separate optional addition to the
series to begin with.
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 01/13] hw/i2c: Add designware i2c controller
2026-05-06 5:53 ` Nicholas Piggin
@ 2026-05-06 8:55 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-05-06 8:55 UTC (permalink / raw)
To: Nicholas Piggin
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Chris Rauer, Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel, Hao Wu
On 6/5/26 07:53, Nicholas Piggin wrote:
> On Tue, May 05, 2026 at 09:57:20AM +0200, Philippe Mathieu-Daudé wrote:
>> On 25/4/26 15:17, Joel Stanley wrote:
>>> From: Chris Rauer <crauer@google.com>
>>>
>>> In the past this model has been submitted for use with the arm virt
>>> machine, however in this case it will be used by the upcoming
>>> Tenstorrent Atlantis RISC-V machine.
>>>
>>> This is a re-submission of the model with Chris' permission, with a
>>> light touch of updates to make it build with qemu master.
>>>
>>> Reviewed-by: Hao Wu <wuhaotsh@google.com>
>>> Signed-off-by: Chris Rauer <crauer@google.com>
>>> Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com
>>> [jms: rebase and minor build fixes for class_init and reset callback]
>>> Signed-off-by: Joel Stanley <joel@jms.id.au>
>>> ---
>>> v4: Remove unused random header
>>> v2: Add trace event for read and write, document Alano and myself as
>>> reviewers.
>>> ---
>>> MAINTAINERS | 8 +
>>> include/hw/i2c/designware_i2c.h | 101 ++++
>>> hw/i2c/designware_i2c.c | 817 ++++++++++++++++++++++++++++++++
>>> hw/i2c/Kconfig | 4 +
>>> hw/i2c/meson.build | 1 +
>>> hw/i2c/trace-events | 4 +
>>> 6 files changed, 935 insertions(+)
>>> create mode 100644 include/hw/i2c/designware_i2c.h
>>> create mode 100644 hw/i2c/designware_i2c.c
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index aa4267b15806..e1942a86eba5 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -2716,6 +2716,14 @@ S: Orphan
>>> F: hw/gpio/pcf8574.c
>>> F: include/gpio/pcf8574.h
>>> +DesignWare I2C
>>> +M: Chris Rauer <crauer@google.com>
>>
>> This was 4 years ago. Is Chris still up to this?
>
> Joel is away for a few weeks, but AFAIK he did ping Chris and got
> ack to upstream his patch.
>
>>> + case DW_IC_SAR:
>>> + qemu_log_mask(LOG_UNIMP, "%s: unsupported write - ic_sar\n",
>>> + DEVICE(s)->canonical_path);
>>> + s->ic_sar = value & DW_IC_SAR_MASK;
>>
>> Unsupported but updated...
>
> Yeah, not sure what the best course of action is, maybe device driver
> was working better if the value got saved?
>
>>> + s->ic_comp_param_1 = DW_IC_COMP_PARAM_1_INIT_VAL;
>>> + s->ic_comp_version = DW_IC_COMP_VERSION_INIT_VAL;
>>> + s->ic_comp_type = DW_IC_COMP_TYPE_INIT_VAL;
>>
>> Preferably initialize the IC_COMP registers once in the
>> designware_i2c_realize() handler.
>
> This I didn't change yet because I started register API converstion and
> that all changes.
>
>>> +static void designware_i2c_smbus_init(Object *obj)
>>> +{
>>> + DesignWareI2CState *s = DESIGNWARE_I2C(obj);
>>> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
>>> +
>>> + sysbus_init_irq(sbd, &s->irq);
>>> +
>>> + memory_region_init_io(&s->iomem, obj, &designware_i2c_ops, s,
>>> + "regs", 4 * KiB);
>>
>> I note the current registers only describe a 1KiB region.
>
> 256 bytes even. It might have 4K alignment in data sheet.
>
>>> +}
>> Since I couldn't look at the datasheet values, for overall modelling:
>> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
>
> Thanks!
>
> I have an incremental patch here that incorporates review comments and
> also a couple of possible issues I found - RX underflow bit was never
> being set AFAIKS it should be set here, and one of the INTR bit clear
> registers was not recalculating irqs.
>
> I will fold this in before next repost if there are no objections.
Fine by me!
>
> Thanks,
> Nick
>
> ---
> diff --git a/hw/i2c/designware_i2c.c b/hw/i2c/designware_i2c.c
> index 7d8b1c1353..1e5505f571 100644
> --- a/hw/i2c/designware_i2c.c
> +++ b/hw/i2c/designware_i2c.c
> @@ -217,7 +217,7 @@ static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s)
> "%s: Attempted to read from RX fifo when not in receive "
> "state.\n", DEVICE(s)->canonical_path);
> if (s->status != DW_I2C_STATUS_IDLE) {
> - s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
> + s->ic_raw_intr_stat |= DW_IC_INTR_RX_UNDER;
> dw_i2c_update_irq(s);
> }
> return 0;
> @@ -228,7 +228,7 @@ static uint32_t dw_i2c_read_ic_data_cmd(DesignWareI2CState *s)
> if (s->ic_rxflr > 0) {
> s->ic_rxflr--;
> } else {
> - s->ic_raw_intr_stat &= ~DW_IC_INTR_RX_UNDER;
> + s->ic_raw_intr_stat |= DW_IC_INTR_RX_UNDER;
> dw_i2c_update_irq(s);
> return 0;
> }
> @@ -395,6 +395,7 @@ static uint64_t dw_i2c_read(void *opaque, hwaddr offset, unsigned size)
> break;
> case DW_IC_CLR_RESTART_DET:
> s->ic_raw_intr_stat &= ~(DW_IC_INTR_RESTART_DET);
> + dw_i2c_update_irq(s);
> break;
> case DW_IC_COMP_PARAM_1:
> value = s->ic_comp_param_1;
> @@ -698,6 +699,10 @@ static const MemoryRegionOps designware_i2c_ops = {
> .read = dw_i2c_read,
> .write = dw_i2c_write,
> .endianness = DEVICE_LITTLE_ENDIAN,
> + .impl = {
> + .min_access_size = 4,
> + .max_access_size = 4,
> + },
> .valid = {
> .min_access_size = 4,
> .max_access_size = 4,
> @@ -748,7 +753,7 @@ static const VMStateDescription vmstate_designware_i2c = {
> .name = TYPE_DESIGNWARE_I2C,
> .version_id = 0,
> .minimum_version_id = 0,
> - .fields = (VMStateField[]) {
> + .fields = (const VMStateField[]) {
> VMSTATE_UINT32(ic_con, DesignWareI2CState),
> VMSTATE_UINT32(ic_tar, DesignWareI2CState),
> VMSTATE_UINT32(ic_sar, DesignWareI2CState),
> @@ -803,6 +808,8 @@ static void designware_i2c_class_init(ObjectClass *klass, const void *data)
> dc->vmsd = &vmstate_designware_i2c;
> rc->phases.enter = designware_i2c_enter_reset;
> rc->phases.hold = designware_i2c_hold_reset;
> +
> + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
> }
>
> static const TypeInfo designware_i2c_types[] = {
> diff --git a/include/hw/i2c/designware_i2c.h b/include/hw/i2c/designware_i2c.h
> index 0d8f904f51..71d74c9141 100644
> --- a/include/hw/i2c/designware_i2c.h
> +++ b/include/hw/i2c/designware_i2c.h
> @@ -57,7 +57,7 @@ typedef enum DesignWareI2CStatus {
> * @status: The current status of the SMBus.
> */
> typedef struct DesignWareI2CState {
> - SysBusDevice parent;
> + SysBusDevice parent_obj;
>
> MemoryRegion iomem;
>
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 01/13] hw/i2c: Add designware i2c controller
2026-05-06 5:58 ` Nicholas Piggin
@ 2026-05-06 8:59 ` Philippe Mathieu-Daudé
0 siblings, 0 replies; 44+ messages in thread
From: Philippe Mathieu-Daudé @ 2026-05-06 8:59 UTC (permalink / raw)
To: Nicholas Piggin
Cc: Alistair Francis, Joel Stanley, Alistair Francis,
Daniel Henrique Barboza, Chao Liu, Chris Rauer, Michael Ellerman,
Joel Stanley, Anirudh Srinivasan, Portia Stephens, qemu-riscv,
qemu-devel, Hao Wu
On 6/5/26 07:58, Nicholas Piggin wrote:
> On Tue, May 05, 2026 at 09:36:57AM +0200, Philippe Mathieu-Daudé wrote:
>> On 5/5/26 08:20, Nicholas Piggin wrote:
>>> On Thu, Apr 30, 2026 at 01:53:21PM +1000, Alistair Francis wrote:
>>>> On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
>>>>>
>>>>> From: Chris Rauer <crauer@google.com>
>>>>>
>>>>> In the past this model has been submitted for use with the arm virt
>>>>> machine, however in this case it will be used by the upcoming
>>>>> Tenstorrent Atlantis RISC-V machine.
>>>>>
>>>>> This is a re-submission of the model with Chris' permission, with a
>>>>> light touch of updates to make it build with qemu master.
>>>>>
>>>>> Reviewed-by: Hao Wu <wuhaotsh@google.com>
>>>>> Signed-off-by: Chris Rauer <crauer@google.com>
>>>>> Link: https://lore.kernel.org/qemu-devel/20220110214755.810343-2-venture@google.com
>>>>> [jms: rebase and minor build fixes for class_init and reset callback]
>>>>> Signed-off-by: Joel Stanley <joel@jms.id.au>
>>>>> ---
>>
>>
>>>>> +#define DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS BIT(7)
>>>>> +#define DW_IC_COMP_PARAM_1_HAS_DMA 0 /* bit 6 - DMA disabled. */
>>>>> +#define DW_IC_COMP_PARAM_1_INTR_IO BIT(5)
>>>>> +#define DW_IC_COMP_PARAM_1_HC_COUNT_VAL 0 /* bit 4 - disabled */
>>>>> +#define DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE (BIT(2) | BIT(3))
>>>>> +#define DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 BIT(1) /* bits 0, 1 */
>>>>> +#define DW_IC_COMP_PARAM_1_INIT_VAL \
>>>>> + (DW_IC_COMP_PARAM_1_APB_DATA_WIDTH_32 | \
>>>>> + DW_IC_COMP_PARAM_1_HIGH_SPEED_MODE | \
>>>>> + DW_IC_COMP_PARAM_1_HC_COUNT_VAL | \
>>>>> + DW_IC_COMP_PARAM_1_INTR_IO | \
>>>>> + DW_IC_COMP_PARAM_1_HAS_DMA | \
>>>>> + DW_IC_COMP_PARAM_1_HAS_ENCODED_PARAMS | \
>>>>> + ((DESIGNWARE_I2C_RX_FIFO_SIZE - 1) << 8) | \
>>>>> + ((DESIGNWARE_I2C_TX_FIFO_SIZE - 1) << 16))
>>>>> +#define DW_IC_COMP_VERSION_INIT_VAL 0x3132302a
>>>>> +#define DW_IC_COMP_TYPE_INIT_VAL 0x44570140
>>>>
>>>> I would prefer these to sue the QEMU register API
>>>
>>> That's probably a good idea. It's a lot of churn to change it, but
>>> I'm having a try. Might do it as a follow on patch.
>>
>> Good suggestion but please as a follow up, this patch is already
>> big enough, is reviewed and tested.
>
> I agree... however I implemented it and it is a large amount of chrun
> which is also not easy to review. Also it breaks migration
> compatibility. So I don't want to get this patch bogged down but if we
> agree to take the register API conversion then maybe we should squash
> before upstreaming?
OK.
> Not sure. I will post the patch as a separate optional addition to the
> series to begin with.
Yeah, you can respin this series without i2c at all, then follow up
with this patch, #12 and #13 (integrate i2c bus & few peripherals to
the Atlantis machine).
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-05-06 3:14 ` Alistair Francis
@ 2026-05-07 1:50 ` Nicholas Piggin
2026-05-07 2:53 ` Alistair Francis
0 siblings, 1 reply; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-07 1:50 UTC (permalink / raw)
To: Alistair Francis
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Wed, May 06, 2026 at 01:14:09PM +1000, Alistair Francis wrote:
> On Tue, May 5, 2026 at 4:00 PM Nicholas Piggin <npiggin@gmail.com> wrote:
> >
> > On Tue, May 05, 2026 at 02:34:50PM +1000, Alistair Francis wrote:
> > > On Tue, May 5, 2026 at 11:04 AM Nicholas Piggin <npiggin@gmail.com> wrote:
> > > >
> > > > On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
> > > > > On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> > > > > >
> > > > > > The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> > > > > > and CoreLab Technology. It is based on the Atlantis SoC, which includes
> > > > > > the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
> > > > >
> > > > > Can you provide a link to documentation
> > > >
> > > > Also not presently.
> > >
> > > Hmmm... That's a bit annoying. Can we get some?
> >
> > That's the plan but I don't have a date I can give you for it, sorry.
> >
> > [...]
> >
> > > > > > +static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
> > > > >
> > > > > So generally we only create a device tree for virtual boards, that is
> > > > > boards that don't really exist and are just QEMU constructs.
> > > > >
> > > > > You said that this is based on the "Atlantis SoC" so I assume it's
> > > > > real hardware. In the real hardware do you expect the device tree to
> > > > > be provided by the ROM? Usually for boards users supply the device
> > > > > tree as part of the boot flow (via u-boot or something similar). In
> > > > > which case should QEMU actually generate this or can users just supply
> > > > > their own?
> > > > >
> > > > > Basically the question is, if you are trying to model the hardware, is
> > > > > generating the device tree here correct?
> > > > >
> > > > > Obviously the advantage of doing it here is then the device tree
> > > > > describes what QEMU supports, which is great. But that can be harder
> > > > > to manage when the hardware doesn't do that, as then you have
> > > > > divergence.
> > > >
> > > > We went around this same discussion internally and yeah it's a valid
> > > > question, we could have explained this a bit better.
> > > >
> > > > In the real (unreleased) hardware and the DT will be loaded from ROM.
> > >
> > > Really? That usually doesn't go well
> >
> > Well not ROM but flash loaded by early firmware. The plan is to have an
>
> Fair, when I say ROM I guess I mean ROM or rarely changed firmware,
> not specifically only ROM.
>
> > "M mode" dtb loaded for opensbi and then later stages supply their own
> > dtb.
>
> Ok, so the "hardware" will provide a DTB to OpenSBI. In which case
> this is the right approach for QEMU.
>
> Can you document that in the commit message?
Will do.
> > The opensbi dtb won't go away from the tt-atlantis model, whether its
> > generated or we change it to use a dtb for the purpose of QEMU. I guess
>
> You can ask the user to supply a DTB, which is passed to OpenSBI (and
> then other layers).
>
> But if you expect your hardware to provide a DTB we should match that.
That's what we are aiming for, we just don't have the DTB or enough of
the machine modeled with QEMU yet.
> > if the next stage does not have its own dtb it can use one supplied by
> > opensbi.
> >
> > Does that sound right? I don't know if I have the details exactly right.
>
> I don't know either! You don't have public documentation :)
Hah, touche. I meant, does that sound sane? Joel isn't around to correct
me! We will ship a machine DTB for OpenSBI, I don't know about the
mechanism u-boot or Linux use to supply their own DTB vs using the one
from the previous stage. I suppose that's not the domain of the QEMU model.
> >
> > > > I think we should do that when the model and firmware and dt is more
> > > > complete. At the moment it has been easier for development to use QEMU
> > > > generated fdt.
> > > >
> > > > Is that acceptable to do that now and switch to a fdt ROM later...
> > >
> > > Obviously guest software can decide to use whatever it wants. But what
> > > we don't want to do is break users. So removing the generated FDT
> > > (which is the equivalent of ROM FDT on hardware) will cause breakages.
> > > So we don't want to do that if we can avoid it. Also generating the
> > > FDT is overhead for us, so we only want to if the guest software will
> > > actually use it. Does that make sense?
> > >
> > > Adding the device tree later is fine though, as guests don't have to use it.
> > >
> > > >
> > > > [...]
> > > >
> > > > > > +static void finalize_fdt(TTAtlantisState *s)
> > > > > > +{
> > > > > > + uint32_t aplic_s_phandle = next_phandle();
> > > > > > + uint32_t imsic_s_phandle = next_phandle();
> > > > > > + void *fdt = MACHINE(s)->fdt;
> > > > > > +
> > > > > > + create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
> > > > > > +
> > > > > > + /*
> > > > > > + * We want to do this, but the Linux aplic driver was broken before v6.16
> > > > > > + *
> > > > > > + * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
> > > > > > + * aplic_s_phandle);
> > > > > > + */
> > > > >
> > > > > And now we are stuck with hacks like this, which we really don't want
> > > > > to be dealing with. Maybe it's best to leave the device tree to the
> > > > > kernel.
> > > >
> > > > ... Despite issues like this?
> > >
> > > If the kernel generates a device tree for it to use then we don't have
> > > mismatched problems (like this one). Generating the DT in QEMU for
> > > real boards proves to be a challenge and this is exactly why. Now we
> > > have to choose between pre or post 6.16 support (I assume 6.16+ works
> > > without this, but you get the point).
> >
> > Yeah I see do see why we'd want a Linux one, and we are currently
> > upstreaming dts for Linux. At the moment we don't have it so using
> > the firmware stage one fills the gap.
> >
> > It is a little bit hacky maybe but I guess I'm not seeing where a
> > compatibility problem comes in versus other systems with their own
> > firmware DTB (that I assume a next-stage could similarly use and be
> > exposed to these issues if it did not supply its own).
>
> The difference is I don't have to worry or maintain changing firmware :P
>
> Basically QEMU providing an FDT increases the machine complexity and
> maintenance overhead. Admittingly it has probably improved now that
> RISC-V has stabalised a bit, but it used to be much more of a hassle
> to deal with.
That makes sense. My powerpc experience was after its teething problems
so DT there was fairly mature, and the machines I worked on were virtual
first so generating DT in QEMU is the normal thing to do. I see where
you are coming from a bit better now.
> So we want a reason to do that, as once it's merged we are mostly
> stuck with it. Adding it just to simplify development now isn't a good
> reason. Adding it because that's what the real hardware does is a good
> reason though.
Makes sense. I guess adding to the device tree should be less
problematic, so we're okay to upstream pieces that are pretty stable?
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 04/13] hw/riscv/boot: Provide a simple halting payload
2026-05-05 8:06 ` Philippe Mathieu-Daudé
@ 2026-05-07 1:53 ` Nicholas Piggin
0 siblings, 0 replies; 44+ messages in thread
From: Nicholas Piggin @ 2026-05-07 1:53 UTC (permalink / raw)
To: Philippe Mathieu-Daudé
Cc: Alistair Francis, Joel Stanley, Alistair Francis,
Daniel Henrique Barboza, Chao Liu, Michael Ellerman, Joel Stanley,
Anirudh Srinivasan, Portia Stephens, qemu-riscv, qemu-devel
On Tue, May 05, 2026 at 10:06:08AM +0200, Philippe Mathieu-Daudé wrote:
> On 5/5/26 01:52, Nicholas Piggin wrote:
> > On Thu, Apr 30, 2026 at 09:35:33AM +1000, Alistair Francis wrote:
> > > On Sat, Apr 25, 2026 at 11:21 PM Joel Stanley <joel@jms.id.au> wrote:
> > > >
> > > > From: Nicholas Piggin <npiggin@gmail.com>
> > > >
> > > > OpenSBI hangs before any console output if the domain init code sees the
> > > > next stage is not in an executable region.
> > >
> > > That's an OpenSBI bug though and needs to be fixed there and not just
> > > hacked around in QEMU.
> >
> > You're right of course. OpenSBI has other no-output crash cases in early
> > boot code (not just this), so it would be nice to fix properly. It wasn't
> > trivial to fix (IIRC because serial device is not detected before a
> > bunch of the early failures), I have some hacks, but I need to go back
> > and come up with something nicer.
> >
> > This is only related to Atlantis by coincidence of the physical address
> > map. It can be dropped out of the series.
>
> Meanwhile add it in hw/riscv/tt_atlantis.c:
>
> static void
> tt_atlantis_setup_opensbi_halting_payload_fixup(RISCVBootInfo *info,
> hwaddr addr)
> { ... }
>
> and call in tt_atlantis_machine_init()!
I was going to drop it entirely but that might be better if Alistair
doesn't mind. It means we can keep the opensbi functional test. It's
still a hack just the same, but at least not touching greater-riscv.
Thanks,
Nick
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine
2026-05-07 1:50 ` Nicholas Piggin
@ 2026-05-07 2:53 ` Alistair Francis
0 siblings, 0 replies; 44+ messages in thread
From: Alistair Francis @ 2026-05-07 2:53 UTC (permalink / raw)
To: Nicholas Piggin
Cc: Joel Stanley, Alistair Francis, Daniel Henrique Barboza, Chao Liu,
Michael Ellerman, Joel Stanley, Anirudh Srinivasan,
Portia Stephens, qemu-riscv, qemu-devel,
Philippe Mathieu-Daudé
On Thu, May 7, 2026 at 11:50 AM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> On Wed, May 06, 2026 at 01:14:09PM +1000, Alistair Francis wrote:
> > On Tue, May 5, 2026 at 4:00 PM Nicholas Piggin <npiggin@gmail.com> wrote:
> > >
> > > On Tue, May 05, 2026 at 02:34:50PM +1000, Alistair Francis wrote:
> > > > On Tue, May 5, 2026 at 11:04 AM Nicholas Piggin <npiggin@gmail.com> wrote:
> > > > >
> > > > > On Thu, Apr 30, 2026 at 09:57:34AM +1000, Alistair Francis wrote:
> > > > > > On Sat, Apr 25, 2026 at 11:20 PM Joel Stanley <joel@jms.id.au> wrote:
> > > > > > >
> > > > > > > The Tenstorrent Atlantis platform is a collaboration between Tenstorrent
> > > > > > > and CoreLab Technology. It is based on the Atlantis SoC, which includes
> > > > > > > the Ascalon-X CPU and other IP from Tenstorrent and CoreLab Technology.
> > > > > >
> > > > > > Can you provide a link to documentation
> > > > >
> > > > > Also not presently.
> > > >
> > > > Hmmm... That's a bit annoying. Can we get some?
> > >
> > > That's the plan but I don't have a date I can give you for it, sorry.
> > >
> > > [...]
> > >
> > > > > > > +static void create_fdt_cpus(TTAtlantisState *s, uint32_t *intc_phandles)
> > > > > >
> > > > > > So generally we only create a device tree for virtual boards, that is
> > > > > > boards that don't really exist and are just QEMU constructs.
> > > > > >
> > > > > > You said that this is based on the "Atlantis SoC" so I assume it's
> > > > > > real hardware. In the real hardware do you expect the device tree to
> > > > > > be provided by the ROM? Usually for boards users supply the device
> > > > > > tree as part of the boot flow (via u-boot or something similar). In
> > > > > > which case should QEMU actually generate this or can users just supply
> > > > > > their own?
> > > > > >
> > > > > > Basically the question is, if you are trying to model the hardware, is
> > > > > > generating the device tree here correct?
> > > > > >
> > > > > > Obviously the advantage of doing it here is then the device tree
> > > > > > describes what QEMU supports, which is great. But that can be harder
> > > > > > to manage when the hardware doesn't do that, as then you have
> > > > > > divergence.
> > > > >
> > > > > We went around this same discussion internally and yeah it's a valid
> > > > > question, we could have explained this a bit better.
> > > > >
> > > > > In the real (unreleased) hardware and the DT will be loaded from ROM.
> > > >
> > > > Really? That usually doesn't go well
> > >
> > > Well not ROM but flash loaded by early firmware. The plan is to have an
> >
> > Fair, when I say ROM I guess I mean ROM or rarely changed firmware,
> > not specifically only ROM.
> >
> > > "M mode" dtb loaded for opensbi and then later stages supply their own
> > > dtb.
> >
> > Ok, so the "hardware" will provide a DTB to OpenSBI. In which case
> > this is the right approach for QEMU.
> >
> > Can you document that in the commit message?
>
> Will do.
>
> > > The opensbi dtb won't go away from the tt-atlantis model, whether its
> > > generated or we change it to use a dtb for the purpose of QEMU. I guess
> >
> > You can ask the user to supply a DTB, which is passed to OpenSBI (and
> > then other layers).
> >
> > But if you expect your hardware to provide a DTB we should match that.
>
> That's what we are aiming for, we just don't have the DTB or enough of
> the machine modeled with QEMU yet.
>
> > > if the next stage does not have its own dtb it can use one supplied by
> > > opensbi.
> > >
> > > Does that sound right? I don't know if I have the details exactly right.
> >
> > I don't know either! You don't have public documentation :)
>
> Hah, touche. I meant, does that sound sane? Joel isn't around to correct
Generally software doesn't use the device tree from the "hardware".
The original SiFive Unleashed did, and everyone stopped using it as it
was a major headache.
Instead software usually provides a device tree, often just built from
the Linux source that is being run. That's why we support
`FW_FDT_PATH` in OpenSBI for example [1], to avoid having the
"hardware" provide something.
Which is why I flagged this originally and there is no documentation
to check. But if it matches the hardware (again hardware or early
firmware that you aren't running on QEMU) then that's fine.
1: https://github.com/riscv-software-src/opensbi/blob/2257e9957103aac7df8089a59b9d4bdda7c592ce/docs/platform/sifive_fu540.md?plain=1#L38
> me! We will ship a machine DTB for OpenSBI, I don't know about the
> mechanism u-boot or Linux use to supply their own DTB vs using the one
> from the previous stage. I suppose that's not the domain of the QEMU model.
>
> > >
> > > > > I think we should do that when the model and firmware and dt is more
> > > > > complete. At the moment it has been easier for development to use QEMU
> > > > > generated fdt.
> > > > >
> > > > > Is that acceptable to do that now and switch to a fdt ROM later...
> > > >
> > > > Obviously guest software can decide to use whatever it wants. But what
> > > > we don't want to do is break users. So removing the generated FDT
> > > > (which is the equivalent of ROM FDT on hardware) will cause breakages.
> > > > So we don't want to do that if we can avoid it. Also generating the
> > > > FDT is overhead for us, so we only want to if the guest software will
> > > > actually use it. Does that make sense?
> > > >
> > > > Adding the device tree later is fine though, as guests don't have to use it.
> > > >
> > > > >
> > > > > [...]
> > > > >
> > > > > > > +static void finalize_fdt(TTAtlantisState *s)
> > > > > > > +{
> > > > > > > + uint32_t aplic_s_phandle = next_phandle();
> > > > > > > + uint32_t imsic_s_phandle = next_phandle();
> > > > > > > + void *fdt = MACHINE(s)->fdt;
> > > > > > > +
> > > > > > > + create_fdt_cpu(s, s->memmap, aplic_s_phandle, imsic_s_phandle);
> > > > > > > +
> > > > > > > + /*
> > > > > > > + * We want to do this, but the Linux aplic driver was broken before v6.16
> > > > > > > + *
> > > > > > > + * qemu_fdt_setprop_cell(MACHINE(s)->fdt, "/soc", "interrupt-parent",
> > > > > > > + * aplic_s_phandle);
> > > > > > > + */
> > > > > >
> > > > > > And now we are stuck with hacks like this, which we really don't want
> > > > > > to be dealing with. Maybe it's best to leave the device tree to the
> > > > > > kernel.
> > > > >
> > > > > ... Despite issues like this?
> > > >
> > > > If the kernel generates a device tree for it to use then we don't have
> > > > mismatched problems (like this one). Generating the DT in QEMU for
> > > > real boards proves to be a challenge and this is exactly why. Now we
> > > > have to choose between pre or post 6.16 support (I assume 6.16+ works
> > > > without this, but you get the point).
> > >
> > > Yeah I see do see why we'd want a Linux one, and we are currently
> > > upstreaming dts for Linux. At the moment we don't have it so using
> > > the firmware stage one fills the gap.
> > >
> > > It is a little bit hacky maybe but I guess I'm not seeing where a
> > > compatibility problem comes in versus other systems with their own
> > > firmware DTB (that I assume a next-stage could similarly use and be
> > > exposed to these issues if it did not supply its own).
> >
> > The difference is I don't have to worry or maintain changing firmware :P
> >
> > Basically QEMU providing an FDT increases the machine complexity and
> > maintenance overhead. Admittingly it has probably improved now that
> > RISC-V has stabalised a bit, but it used to be much more of a hassle
> > to deal with.
>
> That makes sense. My powerpc experience was after its teething problems
> so DT there was fairly mature, and the machines I worked on were virtual
> first so generating DT in QEMU is the normal thing to do. I see where
> you are coming from a bit better now.
Yeah, virt machines have to generate the DT.
In the early RISC-V days in order to support kernels 5.y we had to
break support for 5.x due to device tree issues. It was a huge pain.
It does seem a lot better now, but you also have compat issues here,
so it's not all fixed.
>
> > So we want a reason to do that, as once it's merged we are mostly
> > stuck with it. Adding it just to simplify development now isn't a good
> > reason. Adding it because that's what the real hardware does is a good
> > reason though.
>
> Makes sense. I guess adding to the device tree should be less
> problematic, so we're okay to upstream pieces that are pretty stable?
Adding stuff is fine, that won't break users. We just don't want to
take things away if we can avoid it.
Alistair
^ permalink raw reply [flat|nested] 44+ messages in thread
end of thread, other threads:[~2026-05-07 2:55 UTC | newest]
Thread overview: 44+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-25 13:17 [PATCH v4 00/13] hw/riscv: Add the Tenstorrent Atlantis machine Joel Stanley
2026-04-25 13:17 ` [PATCH v4 01/13] hw/i2c: Add designware i2c controller Joel Stanley
2026-04-30 3:53 ` Alistair Francis
2026-05-05 6:20 ` Nicholas Piggin
2026-05-05 7:36 ` Philippe Mathieu-Daudé
2026-05-06 5:58 ` Nicholas Piggin
2026-05-06 8:59 ` Philippe Mathieu-Daudé
2026-05-05 7:57 ` Philippe Mathieu-Daudé
2026-05-06 5:53 ` Nicholas Piggin
2026-05-06 8:55 ` Philippe Mathieu-Daudé
2026-04-25 13:17 ` [PATCH v4 02/13] hw/riscv/boot: Describe discontiguous memory in boot_info Joel Stanley
2026-04-25 13:17 ` [PATCH v4 03/13] hw/riscv/boot: Account for discontiguous memory when loading firmware Joel Stanley
2026-04-29 23:34 ` Alistair Francis
2026-05-04 23:45 ` Nicholas Piggin
2026-04-25 13:17 ` [PATCH v4 04/13] hw/riscv/boot: Provide a simple halting payload Joel Stanley
2026-04-29 23:35 ` Alistair Francis
2026-05-04 23:52 ` Nicholas Piggin
2026-05-05 8:06 ` Philippe Mathieu-Daudé
2026-05-07 1:53 ` Nicholas Piggin
2026-04-25 13:17 ` [PATCH v4 05/13] hw/riscv/virt: Move AIA initialisation to helper file Joel Stanley
2026-04-25 13:17 ` [PATCH v4 06/13] hw/riscv/aia: Provide number of irq sources Joel Stanley
2026-04-25 13:17 ` [PATCH v4 07/13] target/riscv: tt-ascalon: Enable Zkr extension Joel Stanley
2026-04-29 23:36 ` Alistair Francis
2026-05-05 0:06 ` Nicholas Piggin
2026-04-25 13:17 ` [PATCH v4 08/13] target/riscv: tt-ascalon: Enable Svadu by removing Svade Joel Stanley
2026-04-29 23:41 ` Alistair Francis
2026-04-25 13:17 ` [PATCH v4 09/13] hw/riscv: Add Tenstorrent Atlantis machine Joel Stanley
2026-04-29 23:57 ` Alistair Francis
2026-05-05 1:04 ` Nicholas Piggin
2026-05-05 4:34 ` Alistair Francis
2026-05-05 6:00 ` Nicholas Piggin
2026-05-05 7:31 ` Philippe Mathieu-Daudé
2026-05-06 3:14 ` Alistair Francis
2026-05-07 1:50 ` Nicholas Piggin
2026-05-07 2:53 ` Alistair Francis
2026-05-05 2:00 ` Nicholas Piggin
2026-05-05 4:36 ` Alistair Francis
2026-05-05 6:01 ` Nicholas Piggin
2026-04-25 13:17 ` [PATCH v4 10/13] hw/riscv/atlantis: Add PCIe controller Joel Stanley
2026-04-30 0:04 ` Alistair Francis
2026-05-05 1:38 ` Nicholas Piggin
2026-04-25 13:17 ` [PATCH v4 11/13] tests/functional/riscv64: Add tt-atlantis tests Joel Stanley
2026-04-25 13:17 ` [PATCH v4 12/13] hw/riscv/atlantis: Integrate i2c buses Joel Stanley
2026-04-25 13:17 ` [PATCH v4 13/13] hw/riscv/atlantis: Add some i2c peripherals Joel Stanley
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.