[RFC PATCH v2 06/14] hw/misc: add TI K3 CTRL_MMR, GTC, DDRSS, SDHCI PHY and TRNG models

Wadim Mueller <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel
Message-ID <[email protected]>
Add the small K3 support blocks the boot chain touches:

 - ti-k3-ctrlmmr: control module registers, including the DEVSTAT boot
   pins, which the ROM and SPL use to select the boot device.
 - ti-k3-gtc: global timebase counter, read by TF-A.
 - ti-k3-ddrss: DDR subsystem stub that reports a successful DRAM init
   (including BIST/ECC priming status) to the R5 SPL.
 - ti-k3-sdhci-phy: PHY control/calibration registers of the K3 SDHCI
   wrapper, reporting calibration as done.
 - ti-k3-trng: TRNG stub used by OP-TEE.

These are register-level stubs: they implement the handshakes that
firmware polls for, not the underlying hardware behaviour.

Signed-off-by: Wadim Mueller <[email protected]>
---
 hw/misc/Kconfig                   |  15 ++++
 hw/misc/meson.build               |   6 ++
 hw/misc/ti-k3-ctrlmmr.c           | 103 ++++++++++++++++++++++
 hw/misc/ti-k3-ddrss.c             |  98 +++++++++++++++++++++
 hw/misc/ti-k3-gtc.c               | 100 +++++++++++++++++++++
 hw/misc/ti-k3-sdhci-phy.c         |  91 +++++++++++++++++++
 hw/misc/ti-k3-trng.c              | 139 ++++++++++++++++++++++++++++++
 hw/misc/trace-events              |   4 +
 include/hw/misc/ti-k3-ctrlmmr.h   |  26 ++++++
 include/hw/misc/ti-k3-ddrss.h     |  26 ++++++
 include/hw/misc/ti-k3-gtc.h       |  23 +++++
 include/hw/misc/ti-k3-sdhci-phy.h |  26 ++++++
 include/hw/misc/ti-k3-trng.h      |  35 ++++++++
 13 files changed, 692 insertions(+)
 create mode 100644 hw/misc/ti-k3-ctrlmmr.c
 create mode 100644 hw/misc/ti-k3-ddrss.c
 create mode 100644 hw/misc/ti-k3-gtc.c
 create mode 100644 hw/misc/ti-k3-sdhci-phy.c
 create mode 100644 hw/misc/ti-k3-trng.c
 create mode 100644 include/hw/misc/ti-k3-ctrlmmr.h
 create mode 100644 include/hw/misc/ti-k3-ddrss.h
 create mode 100644 include/hw/misc/ti-k3-gtc.h
 create mode 100644 include/hw/misc/ti-k3-sdhci-phy.h
 create mode 100644 include/hw/misc/ti-k3-trng.h

diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index b8860dd3e7..3e499a902d 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -143,6 +143,21 @@ config MPS2_SCC
     bool
     select LED
 
+config TI_K3_CTRLMMR
+    bool
+
+config TI_K3_GTC
+    bool
+
+config TI_K3_DDRSS
+    bool
+
+config TI_K3_SDHCI_PHY
+    bool
+
+config TI_K3_TRNG
+    bool
+
 config TZ_MPC
     bool
 
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index e86d9ad6b3..e973d7e8a4 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -96,6 +96,7 @@ system_ss.add(when: 'CONFIG_RASPI', if_true: files(
   'bcm2835_cprman.c',
   'bcm2835_powermgt.c',
 ))
+
 system_ss.add(when: 'CONFIG_SLAVIO', if_true: files('slavio_misc.c'))
 system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq_slcr.c'))
 system_ss.add(when: 'CONFIG_XLNX_ZYNQ_DDRC', if_true: files('xlnx-zynq-ddrc.c'))
@@ -122,6 +123,11 @@ system_ss.add(when: 'CONFIG_STM32L4X5_RCC', if_true: files('stm32l4x5_rcc.c'))
 system_ss.add(when: 'CONFIG_MPS2_FPGAIO', if_true: files('mps2-fpgaio.c'))
 system_ss.add(when: 'CONFIG_MPS2_SCC', if_true: files('mps2-scc.c'))
 
+system_ss.add(when: 'CONFIG_TI_K3_CTRLMMR', if_true: files('ti-k3-ctrlmmr.c'))
+system_ss.add(when: 'CONFIG_TI_K3_GTC', if_true: files('ti-k3-gtc.c'))
+system_ss.add(when: 'CONFIG_TI_K3_DDRSS', if_true: files('ti-k3-ddrss.c'))
+system_ss.add(when: 'CONFIG_TI_K3_SDHCI_PHY', if_true: files('ti-k3-sdhci-phy.c'))
+system_ss.add(when: 'CONFIG_TI_K3_TRNG', if_true: files('ti-k3-trng.c'))
 system_ss.add(when: 'CONFIG_TZ_MPC', if_true: files('tz-mpc.c'))
 system_ss.add(when: 'CONFIG_TZ_MSC', if_true: files('tz-msc.c'))
 system_ss.add(when: 'CONFIG_TZ_PPC', if_true: files('tz-ppc.c'))
diff --git a/hw/misc/ti-k3-ctrlmmr.c b/hw/misc/ti-k3-ctrlmmr.c
new file mode 100644
index 0000000000..5925573075
--- /dev/null
+++ b/hw/misc/ti-k3-ctrlmmr.c
@@ -0,0 +1,103 @@
+/*
+ * TI K3 CTRL_MMR stub
+ *
+ * Minimal AM64x control MMR model: selected boot, reset-source and
+ * security-status registers are configurable; other reads return zero,
+ * writes are ignored.
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/misc/ti-k3-ctrlmmr.h"
+
+#define CTRLMMR_MAIN_DEVSTAT 0x30
+#define CTRLMMR_MCU_RST_SRC  0x18178
+/*
+ * K3_SEC_MGR_SYS_STATUS lives at offset 0x100 in the sec-ctrlmmr window.
+ * It reports the device type, e.g. GP or HS.
+ */
+#define CTRLMMR_SEC_MGR_SYS_STATUS 0x100
+#define CTRLMMR_SIZE 0x20000 /* partitions 0-7 */
+
+static uint64_t ti_k3_ctrlmmr_read(void *opaque, hwaddr addr, unsigned size)
+{
+    TIK3CtrlMmrState *s = TI_K3_CTRLMMR(opaque);
+
+    if (addr == CTRLMMR_MAIN_DEVSTAT) {
+        return s->devstat;
+    }
+    if (addr == CTRLMMR_MCU_RST_SRC) {
+        return s->rst_src;
+    }
+    if (addr == CTRLMMR_SEC_MGR_SYS_STATUS) {
+        return s->sec_mgr_sys_status;
+    }
+    qemu_log_mask(LOG_UNIMP,
+                  "%s: unimplemented read @0x%" HWADDR_PRIx "\n",
+                  __func__, addr);
+    return 0;
+}
+
+static void ti_k3_ctrlmmr_write(void *opaque, hwaddr addr, uint64_t val,
+                                unsigned size)
+{
+    /* lock-kick and pinmux writes are accepted and ignored. */
+}
+
+static const MemoryRegionOps ti_k3_ctrlmmr_ops = {
+    .read = ti_k3_ctrlmmr_read,
+    .write = ti_k3_ctrlmmr_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+};
+
+static void ti_k3_ctrlmmr_init(Object *obj)
+{
+    TIK3CtrlMmrState *s = TI_K3_CTRLMMR(obj);
+
+    memory_region_init_io(&s->iomem, obj, &ti_k3_ctrlmmr_ops, s,
+                          TYPE_TI_K3_CTRLMMR, CTRLMMR_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+}
+
+static const Property ti_k3_ctrlmmr_properties[] = {
+    /* Default primary bootmode is eMMC (0x9 << 3). */
+    DEFINE_PROP_UINT32("devstat", TIK3CtrlMmrState, devstat, 0x48),
+    /*
+     * Report warm reset in MCU_RST_SRC. Cold/POR cause bits are not
+     * modelled by this reset-source stub.
+     */
+    DEFINE_PROP_UINT32("rst-src", TIK3CtrlMmrState, rst_src, 0x1),
+    /*
+     * Default to SYS_STATUS_DEV_TYPE_GP (0x3), matching a model without
+     * security manager and certificate authentication.
+     */
+    DEFINE_PROP_UINT32("sec-mgr-sys-status", TIK3CtrlMmrState,
+                       sec_mgr_sys_status, 0x3),
+};
+
+static void ti_k3_ctrlmmr_class_init(ObjectClass *klass, const void *data)
+{
+    device_class_set_props(DEVICE_CLASS(klass), ti_k3_ctrlmmr_properties);
+}
+
+static const TypeInfo ti_k3_ctrlmmr_info = {
+    .name = TYPE_TI_K3_CTRLMMR,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(TIK3CtrlMmrState),
+    .instance_init = ti_k3_ctrlmmr_init,
+    .class_init = ti_k3_ctrlmmr_class_init,
+};
+
+static void ti_k3_ctrlmmr_register_types(void)
+{
+    type_register_static(&ti_k3_ctrlmmr_info);
+}
+
+type_init(ti_k3_ctrlmmr_register_types)
diff --git a/hw/misc/ti-k3-ddrss.c b/hw/misc/ti-k3-ddrss.c
new file mode 100644
index 0000000000..661b88b02c
--- /dev/null
+++ b/hw/misc/ti-k3-ddrss.c
@@ -0,0 +1,98 @@
+/*
+ * TI K3 DDRSS register-file stub (AM64x, DDR4 flavour)
+ *
+ * RAM-backed DENALI CTL/PI/PHY register file. Writes are stored and read
+ * back unchanged. Init-done and optional ECC BIST_DONE status bits are ORed
+ * into reads, so the DDR bring-up handshake completes immediately.
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/misc/ti-k3-ddrss.h"
+
+/* Read-side OR masks: {offset, bits}. */
+static const struct {
+    hwaddr offset;
+    uint32_t bits;
+} ddrss_status_bits[] = {
+    { 0x214C, 1u << 0 },   /* DENALI_PI_83:  PI init done       */
+    { 0x0538, 1u << 13 },  /* DENALI_CTL_334: MASTER, MC_INIT grp */
+    { 0x0558, 1u << 25 },  /* DENALI_CTL_342: INT_STATUS_INIT bit1 */
+    /*
+     * ECC BIST_DONE is exposed in both places: INT_STATUS_MASTER bit 8 and
+     * INT_STATUS_BIST bit 0, resp. raw bit 16.
+     */
+    { 0x0538, 1u << 8 },   /* DENALI_CTL_334: MASTER, BIST grp     */
+    { 0x0554, 1u << 16 },  /* DENALI_CTL_341: BIST fld, BIST_DONE  */
+};
+
+static uint64_t ti_k3_ddrss_read(void *opaque, hwaddr addr, unsigned size)
+{
+    TIK3DdrssState *s = TI_K3_DDRSS(opaque);
+    uint32_t val = s->regs[addr >> 2];
+
+    for (size_t i = 0; i < ARRAY_SIZE(ddrss_status_bits); i++) {
+        if (addr == ddrss_status_bits[i].offset) {
+            val |= ddrss_status_bits[i].bits;
+        }
+    }
+    return val;
+}
+
+static void ti_k3_ddrss_write(void *opaque, hwaddr addr, uint64_t val,
+                              unsigned size)
+{
+    TIK3DdrssState *s = TI_K3_DDRSS(opaque);
+
+    s->regs[addr >> 2] = val;
+}
+
+static const MemoryRegionOps ti_k3_ddrss_ops = {
+    .read = ti_k3_ddrss_read,
+    .write = ti_k3_ddrss_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+};
+
+static void ti_k3_ddrss_reset(DeviceState *dev)
+{
+    TIK3DdrssState *s = TI_K3_DDRSS(dev);
+
+    memset(s->regs, 0, sizeof(s->regs));
+}
+
+static void ti_k3_ddrss_init(Object *obj)
+{
+    TIK3DdrssState *s = TI_K3_DDRSS(obj);
+
+    memory_region_init_io(&s->iomem, obj, &ti_k3_ddrss_ops, s,
+                          TYPE_TI_K3_DDRSS, TI_K3_DDRSS_CFG_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+}
+
+static void ti_k3_ddrss_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    device_class_set_legacy_reset(dc, ti_k3_ddrss_reset);
+}
+
+static const TypeInfo ti_k3_ddrss_info = {
+    .name = TYPE_TI_K3_DDRSS,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(TIK3DdrssState),
+    .instance_init = ti_k3_ddrss_init,
+    .class_init = ti_k3_ddrss_class_init,
+};
+
+static void ti_k3_ddrss_register_types(void)
+{
+    type_register_static(&ti_k3_ddrss_info);
+}
+
+type_init(ti_k3_ddrss_register_types)
diff --git a/hw/misc/ti-k3-gtc.c b/hw/misc/ti-k3-gtc.c
new file mode 100644
index 0000000000..b51baddc71
--- /dev/null
+++ b/hw/misc/ti-k3-gtc.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * TI K3 Global Timebase Counter (GTC) register stub.
+ *
+ * The GTC distributes a system counter and its nominal frequency to the ARM
+ * generic timers. Only CNTCR and CNTFID0 are implemented; the counter itself
+ * is not modelled. Reset reports an enabled 200 MHz counter, the AM64x
+ * default.
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/misc/ti-k3-gtc.h"
+
+#define TI_K3_GTC_SIZE       0x1000
+
+#define GTC_CNTCR            0x000   /* control; bit0 = EN */
+#define GTC_CNTFID0          0x020   /* frequency id 0 (Hz) */
+
+static uint64_t ti_k3_gtc_read(void *opaque, hwaddr addr, unsigned size)
+{
+    TIK3GtcState *s = TI_K3_GTC(opaque);
+
+    switch (addr) {
+    case GTC_CNTCR:
+        return s->cntcr;
+    case GTC_CNTFID0:
+        return s->cntfid0;
+    default:
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: unimplemented read @0x%" HWADDR_PRIx "\n",
+                      __func__, addr);
+        return 0;
+    }
+}
+
+static void ti_k3_gtc_write(void *opaque, hwaddr addr, uint64_t val,
+                            unsigned size)
+{
+    TIK3GtcState *s = TI_K3_GTC(opaque);
+
+    switch (addr) {
+    case GTC_CNTCR:
+        s->cntcr = (uint32_t)val;
+        break;
+    case GTC_CNTFID0:
+        s->cntfid0 = (uint32_t)val;
+        break;
+    default:
+        qemu_log_mask(LOG_UNIMP,
+                      "%s: unimplemented write @0x%" HWADDR_PRIx "\n",
+                      __func__, addr);
+        break;
+    }
+}
+
+static const MemoryRegionOps ti_k3_gtc_ops = {
+    .read = ti_k3_gtc_read,
+    .write = ti_k3_gtc_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 1,
+    .valid.max_access_size = 4,
+};
+
+static void ti_k3_gtc_init(Object *obj)
+{
+    TIK3GtcState *s = TI_K3_GTC(obj);
+
+    memory_region_init_io(&s->iomem, obj, &ti_k3_gtc_ops, s,
+                          TYPE_TI_K3_GTC, TI_K3_GTC_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+}
+
+static const Property ti_k3_gtc_properties[] = {
+    DEFINE_PROP_UINT32("cntcr", TIK3GtcState, cntcr, 0x1),
+    DEFINE_PROP_UINT32("cntfid0", TIK3GtcState, cntfid0, 200000000),
+};
+
+static void ti_k3_gtc_class_init(ObjectClass *klass, const void *data)
+{
+    device_class_set_props(DEVICE_CLASS(klass), ti_k3_gtc_properties);
+}
+
+static const TypeInfo ti_k3_gtc_info = {
+    .name = TYPE_TI_K3_GTC,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(TIK3GtcState),
+    .instance_init = ti_k3_gtc_init,
+    .class_init = ti_k3_gtc_class_init,
+};
+
+static void ti_k3_gtc_register_types(void)
+{
+    type_register_static(&ti_k3_gtc_info);
+}
+
+type_init(ti_k3_gtc_register_types)
diff --git a/hw/misc/ti-k3-sdhci-phy.c b/hw/misc/ti-k3-sdhci-phy.c
new file mode 100644
index 0000000000..10b1f56032
--- /dev/null
+++ b/hw/misc/ti-k3-sdhci-phy.c
@@ -0,0 +1,91 @@
+/*
+ * TI K3 SDHCI PHY register-file stub (AM64x)
+ *
+ * RAM-backed SDHCI companion PHY window. Drive and delay registers are stored
+ * as written. PHY_STAT1 reports CALDONE (bit 1) and DLLRDY (bit 0) set, so
+ * calibration resp. DLL lock complete immediately.
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "hw/misc/ti-k3-sdhci-phy.h"
+
+/* Read-side OR masks: {offset, bits}. */
+static const struct {
+    hwaddr offset;
+    uint32_t bits;
+} sdhci_phy_status_bits[] = {
+    /* PHY_STAT1: CALDONE (bit 1) | DLLRDY (bit 0) */
+    { 0x130, 0x3 },
+};
+
+static uint64_t ti_k3_sdhci_phy_read(void *opaque, hwaddr addr, unsigned size)
+{
+    TIK3SdhciPhyState *s = TI_K3_SDHCI_PHY(opaque);
+    uint32_t val = s->regs[addr >> 2];
+
+    for (size_t i = 0; i < ARRAY_SIZE(sdhci_phy_status_bits); i++) {
+        if (addr == sdhci_phy_status_bits[i].offset) {
+            val |= sdhci_phy_status_bits[i].bits;
+        }
+    }
+    return val;
+}
+
+static void ti_k3_sdhci_phy_write(void *opaque, hwaddr addr, uint64_t val,
+                                  unsigned size)
+{
+    TIK3SdhciPhyState *s = TI_K3_SDHCI_PHY(opaque);
+
+    s->regs[addr >> 2] = val;
+}
+
+static const MemoryRegionOps ti_k3_sdhci_phy_ops = {
+    .read = ti_k3_sdhci_phy_read,
+    .write = ti_k3_sdhci_phy_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid.min_access_size = 4,
+    .valid.max_access_size = 4,
+};
+
+static void ti_k3_sdhci_phy_reset(DeviceState *dev)
+{
+    TIK3SdhciPhyState *s = TI_K3_SDHCI_PHY(dev);
+
+    memset(s->regs, 0, sizeof(s->regs));
+}
+
+static void ti_k3_sdhci_phy_init(Object *obj)
+{
+    TIK3SdhciPhyState *s = TI_K3_SDHCI_PHY(obj);
+
+    memory_region_init_io(&s->iomem, obj, &ti_k3_sdhci_phy_ops, s,
+                          TYPE_TI_K3_SDHCI_PHY, TI_K3_SDHCI_PHY_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+}
+
+static void ti_k3_sdhci_phy_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    device_class_set_legacy_reset(dc, ti_k3_sdhci_phy_reset);
+}
+
+static const TypeInfo ti_k3_sdhci_phy_info = {
+    .name = TYPE_TI_K3_SDHCI_PHY,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(TIK3SdhciPhyState),
+    .instance_init = ti_k3_sdhci_phy_init,
+    .class_init = ti_k3_sdhci_phy_class_init,
+};
+
+static void ti_k3_sdhci_phy_register_types(void)
+{
+    type_register_static(&ti_k3_sdhci_phy_info);
+}
+
+type_init(ti_k3_sdhci_phy_register_types)
diff --git a/hw/misc/ti-k3-trng.c b/hw/misc/ti-k3-trng.c
new file mode 100644
index 0000000000..14fb2d077c
--- /dev/null
+++ b/hw/misc/ti-k3-trng.c
@@ -0,0 +1,139 @@
+/*
+ * TI K3 SA2UL TRNG (EIP-76) stub (AM64x)
+ *
+ * Minimal EIP-76 register subset. STATUS reports RNG_READY, INTACK writes
+ * are accepted, OUTPUT_0..3 return two cached 64-bit pairs, and configuration
+ * registers are RAM-backed.
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include "qemu/osdep.h"
+#include "hw/misc/ti-k3-trng.h"
+#include "trace.h"
+
+#define RNG_OUTPUT_0    0x00
+#define RNG_OUTPUT_1    0x04
+#define RNG_OUTPUT_2    0x08
+#define RNG_OUTPUT_3    0x0C
+#define RNG_STATUS      0x10
+#define RNG_READY       (1u << 0)
+#define RNG_CONTROL     0x14
+
+/* Nonzero xorshift64 state reaches never zero. */
+static uint64_t ti_k3_trng_next(TIK3TrngState *s)
+{
+    s->rng_state ^= s->rng_state << 13;
+    s->rng_state ^= s->rng_state >> 7;
+    s->rng_state ^= s->rng_state << 17;
+    return s->rng_state;
+}
+
+static uint64_t ti_k3_trng_read(void *opaque, hwaddr addr, unsigned size)
+{
+    TIK3TrngState *s = TI_K3_TRNG(opaque);
+    uint32_t val;
+
+    switch (addr) {
+    case RNG_OUTPUT_0:
+        s->pair_a = ti_k3_trng_next(s);
+        val = (uint32_t)s->pair_a;
+        break;
+    case RNG_OUTPUT_1:
+        val = (uint32_t)(s->pair_a >> 32);
+        break;
+    case RNG_OUTPUT_2:
+        s->pair_b = ti_k3_trng_next(s);
+        val = (uint32_t)s->pair_b;
+        break;
+    case RNG_OUTPUT_3:
+        val = (uint32_t)(s->pair_b >> 32);
+        break;
+    case RNG_STATUS:
+        /* Always ready, never in shutdown. */
+        val = RNG_READY;
+        break;
+    default:
+        val = s->regs[addr >> 2];
+        break;
+    }
+
+    trace_ti_k3_trng_read(addr, val);
+    return val;
+}
+
+static void ti_k3_trng_write(void *opaque, hwaddr addr, uint64_t value,
+                             unsigned size)
+{
+    TIK3TrngState *s = TI_K3_TRNG(opaque);
+
+    trace_ti_k3_trng_write(addr, value);
+
+    switch (addr) {
+    case RNG_OUTPUT_0:
+    case RNG_OUTPUT_1:
+    case RNG_OUTPUT_2:
+    case RNG_OUTPUT_3:
+        /* Output registers are read-only; use RAZ/WI. */
+        break;
+    case RNG_STATUS:
+        /* STATUS is synthesized on each read, so INTACK has no state. */
+        break;
+    default:
+        s->regs[addr >> 2] = (uint32_t)value;
+        break;
+    }
+}
+
+static const MemoryRegionOps ti_k3_trng_ops = {
+    .read = ti_k3_trng_read,
+    .write = ti_k3_trng_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void ti_k3_trng_reset(DeviceState *dev)
+{
+    TIK3TrngState *s = TI_K3_TRNG(dev);
+
+    memset(s->regs, 0, sizeof(s->regs));
+    s->rng_state = 0x9e3779b97f4a7c15ULL;
+    s->pair_a = 0;
+    s->pair_b = 0;
+}
+
+static void ti_k3_trng_init(Object *obj)
+{
+    TIK3TrngState *s = TI_K3_TRNG(obj);
+
+    memory_region_init_io(&s->iomem, obj, &ti_k3_trng_ops, s,
+                          TYPE_TI_K3_TRNG, TI_K3_TRNG_REGS_SIZE);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+}
+
+static void ti_k3_trng_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    device_class_set_legacy_reset(dc, ti_k3_trng_reset);
+}
+
+static const TypeInfo ti_k3_trng_info = {
+    .name = TYPE_TI_K3_TRNG,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(TIK3TrngState),
+    .instance_init = ti_k3_trng_init,
+    .class_init = ti_k3_trng_class_init,
+};
+
+static void ti_k3_trng_register_types(void)
+{
+    type_register_static(&ti_k3_trng_info);
+}
+
+type_init(ti_k3_trng_register_types)
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index c9a868b3ef..b32ce80ea9 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -442,3 +442,7 @@ iommu_testdev_dma_read(uint64_t gva, uint32_t len) "gva=0x%" PRIx64 " len=%u"
 iommu_testdev_dma_verify(uint32_t expected, uint32_t actual) "expected=0x%x actual=0x%x"
 iommu_testdev_dma_result(uint32_t result) "DMA completed result=0x%x"
 iommu_testdev_dma_armed(bool armed) "armed=%d"
+
+# ti-k3-trng.c
+ti_k3_trng_read(uint64_t addr, uint32_t val) "offset 0x%" PRIx64 " -> 0x%08x"
+ti_k3_trng_write(uint64_t addr, uint64_t val) "offset 0x%" PRIx64 " <- 0x%" PRIx64
diff --git a/include/hw/misc/ti-k3-ctrlmmr.h b/include/hw/misc/ti-k3-ctrlmmr.h
new file mode 100644
index 0000000000..01fb71bc74
--- /dev/null
+++ b/include/hw/misc/ti-k3-ctrlmmr.h
@@ -0,0 +1,26 @@
+/*
+ * TI K3 CTRL_MMR stub
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MISC_TI_K3_CTRLMMR_H
+#define HW_MISC_TI_K3_CTRLMMR_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_TI_K3_CTRLMMR "ti-k3-ctrlmmr"
+OBJECT_DECLARE_SIMPLE_TYPE(TIK3CtrlMmrState, TI_K3_CTRLMMR)
+
+struct TIK3CtrlMmrState {
+    SysBusDevice parent_obj;
+    MemoryRegion iomem;
+    uint32_t devstat;
+    uint32_t rst_src;
+    uint32_t sec_mgr_sys_status;
+};
+
+#endif
diff --git a/include/hw/misc/ti-k3-ddrss.h b/include/hw/misc/ti-k3-ddrss.h
new file mode 100644
index 0000000000..ed0f54e5c4
--- /dev/null
+++ b/include/hw/misc/ti-k3-ddrss.h
@@ -0,0 +1,26 @@
+/*
+ * TI K3 DDRSS register-file stub (AM64x)
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MISC_TI_K3_DDRSS_H
+#define HW_MISC_TI_K3_DDRSS_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_TI_K3_DDRSS "ti-k3-ddrss"
+OBJECT_DECLARE_SIMPLE_TYPE(TIK3DdrssState, TI_K3_DDRSS)
+
+#define TI_K3_DDRSS_CFG_SIZE 0x8000
+
+struct TIK3DdrssState {
+    SysBusDevice parent_obj;
+    MemoryRegion iomem;
+    uint32_t regs[TI_K3_DDRSS_CFG_SIZE / 4];
+};
+
+#endif
diff --git a/include/hw/misc/ti-k3-gtc.h b/include/hw/misc/ti-k3-gtc.h
new file mode 100644
index 0000000000..a2a22afbf6
--- /dev/null
+++ b/include/hw/misc/ti-k3-gtc.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ */
+#ifndef HW_MISC_TI_K3_GTC_H
+#define HW_MISC_TI_K3_GTC_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_TI_K3_GTC "ti-k3-gtc"
+OBJECT_DECLARE_SIMPLE_TYPE(TIK3GtcState, TI_K3_GTC)
+
+struct TIK3GtcState {
+    SysBusDevice parent_obj;
+    MemoryRegion iomem;
+
+    uint32_t cntcr;    /* GTC control: bit0 EN. Read back enabled. */
+    uint32_t cntfid0;  /* GTC frequency id 0, in Hz. */
+};
+
+#endif /* HW_MISC_TI_K3_GTC_H */
diff --git a/include/hw/misc/ti-k3-sdhci-phy.h b/include/hw/misc/ti-k3-sdhci-phy.h
new file mode 100644
index 0000000000..2a7296fc6f
--- /dev/null
+++ b/include/hw/misc/ti-k3-sdhci-phy.h
@@ -0,0 +1,26 @@
+/*
+ * TI K3 SDHCI PHY register-file stub (AM64x)
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MISC_TI_K3_SDHCI_PHY_H
+#define HW_MISC_TI_K3_SDHCI_PHY_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_TI_K3_SDHCI_PHY "ti-k3-sdhci-phy"
+OBJECT_DECLARE_SIMPLE_TYPE(TIK3SdhciPhyState, TI_K3_SDHCI_PHY)
+
+#define TI_K3_SDHCI_PHY_SIZE 0x400
+
+struct TIK3SdhciPhyState {
+    SysBusDevice parent_obj;
+    MemoryRegion iomem;
+    uint32_t regs[TI_K3_SDHCI_PHY_SIZE / 4];
+};
+
+#endif
diff --git a/include/hw/misc/ti-k3-trng.h b/include/hw/misc/ti-k3-trng.h
new file mode 100644
index 0000000000..b9c33b589f
--- /dev/null
+++ b/include/hw/misc/ti-k3-trng.h
@@ -0,0 +1,35 @@
+/*
+ * TI K3 SA2UL TRNG (EIP-76) stub (AM64x)
+ *
+ * Copyright (c) 2026 CMBLU Energy AG
+ * Author: Wadim Mueller <[email protected]>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MISC_TI_K3_TRNG_H
+#define HW_MISC_TI_K3_TRNG_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_TI_K3_TRNG "ti-k3-trng"
+OBJECT_DECLARE_SIMPLE_TYPE(TIK3TrngState, TI_K3_TRNG)
+
+/* EIP-76 register window: offsets 0x00..0x7c. */
+#define TI_K3_TRNG_REGS_SIZE 0x80
+
+struct TIK3TrngState {
+    SysBusDevice parent_obj;
+    MemoryRegion iomem;
+
+    /* Nonzero xorshift64 generator state. */
+    uint64_t rng_state;
+    /* Cached pairs for OUTPUT_0/1 and OUTPUT_2/3. */
+    uint64_t pair_a;
+    uint64_t pair_b;
+
+    /* RAM-backed registers except OUTPUT_x and STATUS. */
+    uint32_t regs[TI_K3_TRNG_REGS_SIZE / 4];
+};
+
+#endif
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.