qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine
       [not found] <20250510042043.2056265-1-jcksn@duck.com>
@ 2025-05-10  4:20 ` jcksn
  2025-06-13 15:01   ` Peter Maydell
  2025-05-10  4:20 ` [PATCH 2/6] MAX78000: ICC Implementation jcksn
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 12+ messages in thread
From: jcksn @ 2025-05-10  4:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: alistair

This patch adds support for the MAX78000FTHR machine.

The MAX78000FTHR contains a MAX78000 and a RISC-V core. This patch
implements only the MAX78000, which is Cortex-M4 based.
Details can be found at:
https://www.analog.com/media/en/technical-documentation/user-guides/max78000-user-guide.pdf

Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
 hw/arm/Kconfig                |  10 ++
 hw/arm/max78000_soc.c         | 184 ++++++++++++++++++++++++++++++++++
 hw/arm/max78000fthr.c         |  50 +++++++++
 hw/arm/meson.build            |   2 +
 include/hw/arm/max78000_soc.h |  36 +++++++
 5 files changed, 282 insertions(+)
 create mode 100644 hw/arm/max78000_soc.c
 create mode 100644 hw/arm/max78000fthr.c
 create mode 100644 include/hw/arm/max78000_soc.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index a55b44d7bd..1c365d1115 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -95,6 +95,12 @@ config INTEGRATOR
     select PL181 # display
     select SMC91C111
 
+config MAX78000FTHR
+    bool
+    default y
+    depends on TCG && ARM
+    select MAX78000_SOC
+
 config MPS3R
     bool
     default y
@@ -358,6 +364,10 @@ config ALLWINNER_R40
     select USB_EHCI_SYSBUS
     select SD
 
+config MAX78000_SOC
+    bool
+    select ARM_V7M
+
 config RASPI
     bool
     default y
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
new file mode 100644
index 0000000000..64578438bd
--- /dev/null
+++ b/hw/arm/max78000_soc.c
@@ -0,0 +1,184 @@
+/*
+ * MAX78000 SOC
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Implementation based on stm32f205
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "system/address-spaces.h"
+#include "system/system.h"
+#include "hw/arm/max78000_soc.h"
+#include "hw/qdev-clock.h"
+#include "hw/misc/unimp.h"
+
+static void max78000_soc_initfn(Object *obj)
+{
+    MAX78000State *s = MAX78000_SOC(obj);
+
+    object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
+
+    s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
+    s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
+}
+
+static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
+{
+    MAX78000State *s = MAX78000_SOC(dev_soc);
+    MemoryRegion *system_memory = get_system_memory();
+    DeviceState *armv7m;
+    Error *err = NULL;
+
+    /*
+     * We use s->refclk internally and only define it with qdev_init_clock_in()
+     * so it is correctly parented and not leaked on an init/deinit; it is not
+     * intended as an externally exposed clock.
+     */
+    if (clock_has_source(s->refclk)) {
+        error_setg(errp, "refclk clock must not be wired up by the board code");
+        return;
+    }
+
+    if (!clock_has_source(s->sysclk)) {
+        error_setg(errp, "sysclk clock must be wired up by the board code");
+        return;
+    }
+
+    /*
+     * TODO: ideally we should model the SoC RCC and its ability to
+     * change the sysclk frequency and define different sysclk sources.
+     */
+
+    /* The refclk always runs at frequency HCLK / 8 */
+    clock_set_mul_div(s->refclk, 8, 1);
+    clock_set_source(s->refclk, s->sysclk);
+
+    memory_region_init_rom(&s->flash, OBJECT(dev_soc), "MAX78000.flash",
+                           FLASH_SIZE, &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
+
+    memory_region_init_ram(&s->sram, NULL, "MAX78000.sram", SRAM_SIZE,
+                           &err);
+    if (err != NULL) {
+        error_propagate(errp, err);
+        return;
+    }
+    memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
+
+    armv7m = DEVICE(&s->armv7m);
+    qdev_prop_set_uint32(armv7m, "num-irq", 134);
+    qdev_prop_set_uint8(armv7m, "num-prio-bits", 3);
+    qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
+    qdev_prop_set_bit(armv7m, "enable-bitband", true);
+    qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
+    qdev_connect_clock_in(armv7m, "refclk", s->refclk);
+    object_property_set_link(OBJECT(&s->armv7m), "memory",
+                             OBJECT(system_memory), &error_abort);
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
+        return;
+    }
+
+    create_unimplemented_device("globalControl",    0x40000000, 0x400);
+    create_unimplemented_device("systemInterface",  0x40000400, 0x400);
+    create_unimplemented_device("functionControl",  0x40000800, 0x3400);
+    create_unimplemented_device("watchdogTimer0",   0x40003000, 0x400);
+    create_unimplemented_device("dynamicVoltScale", 0x40003c00, 0x800);
+    create_unimplemented_device("SIMO",             0x40004400, 0x400);
+    create_unimplemented_device("trimSystemInit",   0x40005400, 0x400);
+    create_unimplemented_device("generalCtrlFunc",  0x40005800, 0xc00);
+    create_unimplemented_device("wakeupTimer",      0x40006400, 0x400);
+    create_unimplemented_device("powerSequencer",   0x40006800, 0x400);
+    create_unimplemented_device("miscControl",      0x40006c00, 0x800);
+
+    create_unimplemented_device("aes",              0x40007400, 0x400);
+    create_unimplemented_device("aesKey",           0x40007800, 0x800);
+
+    create_unimplemented_device("gpio0",            0x40008000, 0x1000);
+    create_unimplemented_device("gpio1",            0x40009000, 0x1000);
+
+    create_unimplemented_device("parallelCamInter", 0x4000e000, 0x1000);
+    create_unimplemented_device("CRC",              0x4000f000, 0x1000);
+
+    create_unimplemented_device("timer0",           0x40010000, 0x1000);
+    create_unimplemented_device("timer1",           0x40011000, 0x1000);
+    create_unimplemented_device("timer2",           0x40012000, 0x1000);
+    create_unimplemented_device("timer3",           0x40013000, 0x1000);
+
+    create_unimplemented_device("i2c0",             0x4001d000, 0x1000);
+    create_unimplemented_device("i2c1",             0x4001e000, 0x1000);
+    create_unimplemented_device("i2c2",             0x4001f000, 0x1000);
+
+    create_unimplemented_device("standardDMA",      0x40028000, 0x1000);
+    create_unimplemented_device("flashController0", 0x40029000, 0x400);
+
+    create_unimplemented_device("icc0",             0x4002a000, 0x800);
+    create_unimplemented_device("icc1",             0x4002a800, 0x800);
+
+    create_unimplemented_device("adc",              0x40034000, 0x1000);
+    create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0);
+    create_unimplemented_device("oneWireMaster",    0x4003d000, 0x1000);
+    create_unimplemented_device("semaphore",        0x4003e000, 0x1000);
+
+    create_unimplemented_device("uart0",            0x40042000, 0x1000);
+    create_unimplemented_device("uart1",            0x40043000, 0x1000);
+    create_unimplemented_device("uart2",            0x40044000, 0x1000);
+
+    create_unimplemented_device("spi1",             0x40046000, 0x2000);
+    create_unimplemented_device("trng",             0x4004d000, 0x1000);
+    create_unimplemented_device("i2s",              0x40060000, 0x1000);
+    create_unimplemented_device("lowPowerControl",  0x40080000, 0x400);
+    create_unimplemented_device("gpio2",            0x40080400, 0x200);
+    create_unimplemented_device("lowPowWatchdogTi", 0x40080800, 0x400);
+
+    create_unimplemented_device("lowPowerTimer5",   0x40081000, 0x400);
+    create_unimplemented_device("lowPowerUART0",    0x40081400, 0x400);
+    create_unimplemented_device("lowPowerCompar",   0x40088000, 0x400);
+
+    create_unimplemented_device("spi0",             0x400be000, 0x400);
+
+    /*
+     * The MAX78000 user guide's base address map lists the CNN TX FIFO as
+     * beginning at 0x400c0400 and ending at 0x400c0400. Given that CNN_FIFO
+     * is listed as having data accessible up to offset 0x1000, the user
+     * guide is likely incorrect.
+     */
+    create_unimplemented_device("cnnTxFIFO",        0x400c0400, 0x2000);
+
+    create_unimplemented_device("cnnGlobalControl", 0x50000000, 0x10000);
+    create_unimplemented_device("cnnx16quad0",      0x50100000, 0x40000);
+    create_unimplemented_device("cnnx16quad1",      0x50500000, 0x40000);
+    create_unimplemented_device("cnnx16quad2",      0x50900000, 0x40000);
+    create_unimplemented_device("cnnx16quad3",      0x50d00000, 0x40000);
+
+}
+
+static void max78000_soc_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = max78000_soc_realize;
+}
+
+static const TypeInfo max78000_soc_info = {
+    .name          = TYPE_MAX78000_SOC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(MAX78000State),
+    .instance_init = max78000_soc_initfn,
+    .class_init    = max78000_soc_class_init,
+};
+
+static void max78000_soc_types(void)
+{
+    type_register_static(&max78000_soc_info);
+}
+
+type_init(max78000_soc_types)
diff --git a/hw/arm/max78000fthr.c b/hw/arm/max78000fthr.c
new file mode 100644
index 0000000000..c4f6b5b1b0
--- /dev/null
+++ b/hw/arm/max78000fthr.c
@@ -0,0 +1,50 @@
+/*
+ * MAX78000FTHR Evaluation Board
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "hw/boards.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-clock.h"
+#include "qemu/error-report.h"
+#include "hw/arm/max78000_soc.h"
+#include "hw/arm/boot.h"
+
+/* 60MHz is the default, but other clocks can be selected. */
+#define SYSCLK_FRQ 60000000ULL
+static void max78000_init(MachineState *machine)
+{
+    DeviceState *dev;
+    Clock *sysclk;
+
+    sysclk = clock_new(OBJECT(machine), "SYSCLK");
+    clock_set_hz(sysclk, SYSCLK_FRQ);
+
+    dev = qdev_new(TYPE_MAX78000_SOC);
+    object_property_add_child(OBJECT(machine), "soc", OBJECT(dev));
+    qdev_connect_clock_in(dev, "sysclk", sysclk);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
+
+    armv7m_load_kernel(ARM_CPU(first_cpu),
+                       machine->kernel_filename,
+                       0x00000000, FLASH_SIZE);
+}
+
+static void max78000_machine_init(MachineClass *mc)
+{
+    static const char * const valid_cpu_types[] = {
+        ARM_CPU_TYPE_NAME("cortex-m4"),
+        NULL
+    };
+
+    mc->desc = "MAX78000FTHR Board (Cortex-M4 / (Unimplemented) RISC-V)";
+    mc->init = max78000_init;
+    mc->valid_cpu_types = valid_cpu_types;
+}
+
+DEFINE_MACHINE("max78000fthr", max78000_machine_init)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index 5098795f61..31f40c5bd4 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -27,6 +27,7 @@ arm_common_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c'))
 arm_common_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c'))
 arm_common_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c'))
 arm_common_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c'))
+arm_common_ss.add(when: 'CONFIG_MAX78000_SOC', if_true: files('max78000_soc.c'))
 arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c'))
 arm_common_ss.add(when: ['CONFIG_RASPI', 'TARGET_AARCH64'], if_true: files('bcm2838.c', 'raspi4b.c'))
 arm_common_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c'))
@@ -71,6 +72,7 @@ arm_ss.add(when: 'CONFIG_XEN', if_true: files(
 arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c'))
 arm_common_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c'))
 arm_common_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c'))
+arm_common_ss.add(when: 'CONFIG_MAX78000FTHR', if_true: files('max78000fthr.c'))
 arm_common_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c'))
 arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c'))
 arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c'))
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
new file mode 100644
index 0000000000..0e6def295d
--- /dev/null
+++ b/include/hw/arm/max78000_soc.h
@@ -0,0 +1,36 @@
+/*
+ * MAX78000 SOC
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_ARM_MAX78000_SOC_H
+#define HW_ARM_MAX78000_SOC_H
+
+#include "hw/or-irq.h"
+#include "hw/arm/armv7m.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_SOC "max78000-soc"
+OBJECT_DECLARE_SIMPLE_TYPE(MAX78000State, MAX78000_SOC)
+
+#define FLASH_BASE_ADDRESS 0x10000000
+#define FLASH_SIZE (512 * 1024)
+#define SRAM_BASE_ADDRESS 0x20000000
+#define SRAM_SIZE (128 * 1024)
+
+struct MAX78000State {
+    SysBusDevice parent_obj;
+
+    ARMv7MState armv7m;
+
+    MemoryRegion sram;
+    MemoryRegion flash;
+
+    Clock *sysclk;
+    Clock *refclk;
+};
+
+#endif
-- 
2.34.1



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

* [PATCH 2/6] MAX78000: ICC Implementation
       [not found] <20250510042043.2056265-1-jcksn@duck.com>
  2025-05-10  4:20 ` [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine jcksn
@ 2025-05-10  4:20 ` jcksn
  2025-06-13 15:26   ` Peter Maydell
  2025-05-10  4:20 ` [PATCH 3/6] MAX78000 UART implementation jcksn
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 12+ messages in thread
From: jcksn @ 2025-05-10  4:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: alistair

This patch implements the Instruction Cache Controller for the MAX78000 SOC

Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
 hw/arm/Kconfig                 |  1 +
 hw/arm/max78000_soc.c          | 20 ++++++--
 hw/misc/Kconfig                |  3 ++
 hw/misc/max78000_icc.c         | 89 ++++++++++++++++++++++++++++++++++
 hw/misc/meson.build            |  1 +
 include/hw/arm/max78000_soc.h  |  6 +++
 include/hw/misc/max78000_icc.h | 34 +++++++++++++
 7 files changed, 150 insertions(+), 4 deletions(-)
 create mode 100644 hw/misc/max78000_icc.c
 create mode 100644 include/hw/misc/max78000_icc.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 1c365d1115..3f23af3244 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -367,6 +367,7 @@ config ALLWINNER_R40
 config MAX78000_SOC
     bool
     select ARM_V7M
+    select MAX78000_ICC
 
 config RASPI
     bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 64578438bd..4d598bddd4 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -16,12 +16,19 @@
 #include "hw/qdev-clock.h"
 #include "hw/misc/unimp.h"
 
+static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
+
 static void max78000_soc_initfn(Object *obj)
 {
     MAX78000State *s = MAX78000_SOC(obj);
+    int i;
 
     object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
 
+    for (i = 0; i < MAX78000_NUM_ICC; i++) {
+        object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
+    }
+
     s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
     s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
 }
@@ -30,8 +37,9 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
 {
     MAX78000State *s = MAX78000_SOC(dev_soc);
     MemoryRegion *system_memory = get_system_memory();
-    DeviceState *armv7m;
+    DeviceState *dev, *armv7m;
     Error *err = NULL;
+    int i;
 
     /*
      * We use s->refclk internally and only define it with qdev_init_clock_in()
@@ -87,6 +95,13 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
         return;
     }
 
+    for (i = 0; i < MAX78000_NUM_ICC; i++) {
+        dev = DEVICE(&(s->icc[i]));
+        sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
+        sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]);
+    }
+
+
     create_unimplemented_device("globalControl",    0x40000000, 0x400);
     create_unimplemented_device("systemInterface",  0x40000400, 0x400);
     create_unimplemented_device("functionControl",  0x40000800, 0x3400);
@@ -120,9 +135,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("standardDMA",      0x40028000, 0x1000);
     create_unimplemented_device("flashController0", 0x40029000, 0x400);
 
-    create_unimplemented_device("icc0",             0x4002a000, 0x800);
-    create_unimplemented_device("icc1",             0x4002a800, 0x800);
-
     create_unimplemented_device("adc",              0x40034000, 0x1000);
     create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0);
     create_unimplemented_device("oneWireMaster",    0x4003d000, 0x1000);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index ec0fa5aa9f..781bcf74cc 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -47,6 +47,9 @@ config A9SCU
 config ARM11SCU
     bool
 
+config MAX78000_ICC
+    bool
+
 config MOS6522
     bool
 
diff --git a/hw/misc/max78000_icc.c b/hw/misc/max78000_icc.c
new file mode 100644
index 0000000000..3eacf6bd1b
--- /dev/null
+++ b/hw/misc/max78000_icc.c
@@ -0,0 +1,89 @@
+/*
+ * MAX78000 Instruction Cache
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/max78000_icc.h"
+
+
+static uint64_t max78000_icc_read(void *opaque, hwaddr addr,
+                                    unsigned int size)
+{
+    Max78000IccState *s = opaque;
+    switch (addr) {
+        case ICC_INFO:{
+            return s->info;
+        }
+        case ICC_SZ:{
+            return s->sz;
+        }
+        case ICC_CTRL:{
+            return s->ctrl;
+        }
+        case ICC_INVALIDATE:{
+            return s->invalidate;
+        }
+        default:{
+            return 0;
+        }
+    }
+}
+
+static void max78000_icc_write(void *opaque, hwaddr addr,
+                    uint64_t val64, unsigned int size)
+{
+    Max78000IccState *s = opaque;
+
+    switch (addr) {
+        case ICC_CTRL:{
+        s->ctrl = 0x10000 + (val64 & 1);
+        break;
+        }
+        case ICC_INVALIDATE:{
+        s->ctrl = s->ctrl | 0x80;
+        }
+    }
+}
+
+static const MemoryRegionOps stm32f4xx_exti_ops = {
+    .read = max78000_icc_read,
+    .write = max78000_icc_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void max78000_icc_init(Object *obj)
+{
+    Max78000IccState *s = MAX78000_ICC(obj);
+    s->info = 0;
+    s->sz = 0x10000010;
+    s->ctrl = 0x10000;
+    s->invalidate = 0;
+
+
+    memory_region_init_io(&s->mmio, obj, &stm32f4xx_exti_ops, s,
+                        TYPE_MAX78000_ICC, 0x800);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static const TypeInfo max78000_icc_info = {
+    .name          = TYPE_MAX78000_ICC,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Max78000IccState),
+    .instance_init = max78000_icc_init,
+};
+
+static void max78000_icc_register_types(void)
+{
+    type_register_static(&max78000_icc_info);
+}
+
+type_init(max78000_icc_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 6d47de482c..a21a994ff8 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
   'imx_ccm.c',
   'imx_rngc.c',
 ))
+system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c'))
 system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
   'npcm_clk.c',
   'npcm_gcr.c',
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 0e6def295d..207a4da68a 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -11,6 +11,7 @@
 
 #include "hw/or-irq.h"
 #include "hw/arm/armv7m.h"
+#include "hw/misc/max78000_icc.h"
 #include "qom/object.h"
 
 #define TYPE_MAX78000_SOC "max78000-soc"
@@ -21,6 +22,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(MAX78000State, MAX78000_SOC)
 #define SRAM_BASE_ADDRESS 0x20000000
 #define SRAM_SIZE (128 * 1024)
 
+/* The MAX78k has 2 instruction caches; only icc0 matters, icc1 is for RISC */
+#define MAX78000_NUM_ICC 2
+
 struct MAX78000State {
     SysBusDevice parent_obj;
 
@@ -29,6 +33,8 @@ struct MAX78000State {
     MemoryRegion sram;
     MemoryRegion flash;
 
+    Max78000IccState icc[MAX78000_NUM_ICC];
+
     Clock *sysclk;
     Clock *refclk;
 };
diff --git a/include/hw/misc/max78000_icc.h b/include/hw/misc/max78000_icc.h
new file mode 100644
index 0000000000..fe9228e25d
--- /dev/null
+++ b/include/hw/misc/max78000_icc.h
@@ -0,0 +1,34 @@
+/*
+ * MAX78000 Instruction Cache
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MAX78000_ICC_H
+#define HW_MAX78000_ICC_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_ICC "max78000-icc"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000IccState, MAX78000_ICC)
+
+#define ICC_INFO       0x0
+#define ICC_SZ         0x4
+#define ICC_CTRL       0x100
+#define ICC_INVALIDATE 0x700
+
+struct Max78000IccState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+
+    uint32_t info;
+    uint32_t sz;
+    uint32_t ctrl;
+    uint32_t invalidate;
+};
+
+#endif
-- 
2.34.1



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

* [PATCH 3/6] MAX78000 UART implementation
       [not found] <20250510042043.2056265-1-jcksn@duck.com>
  2025-05-10  4:20 ` [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine jcksn
  2025-05-10  4:20 ` [PATCH 2/6] MAX78000: ICC Implementation jcksn
@ 2025-05-10  4:20 ` jcksn
  2025-06-13 16:03   ` Peter Maydell
  2025-05-10  4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 12+ messages in thread
From: jcksn @ 2025-05-10  4:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: alistair

This patch implements UART support for the MAX78000 SOC

Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
 hw/arm/Kconfig                  |   1 +
 hw/arm/max78000_soc.c           |  27 +++-
 hw/char/Kconfig                 |   3 +
 hw/char/max78000_uart.c         | 263 ++++++++++++++++++++++++++++++++
 hw/char/meson.build             |   1 +
 include/hw/arm/max78000_soc.h   |   3 +
 include/hw/char/max78000_uart.h |  77 ++++++++++
 7 files changed, 370 insertions(+), 5 deletions(-)
 create mode 100644 hw/char/max78000_uart.c
 create mode 100644 include/hw/char/max78000_uart.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 3f23af3244..59450dc3cb 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -368,6 +368,7 @@ config MAX78000_SOC
     bool
     select ARM_V7M
     select MAX78000_ICC
+    select MAX78000_UART
 
 config RASPI
     bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 4d598bddd4..6334d8b49b 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -16,7 +16,11 @@
 #include "hw/qdev-clock.h"
 #include "hw/misc/unimp.h"
 
-static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
+static const uint32_t max78000_icc_addr[] =  {0x4002a000, 0x4002a800};
+static const uint32_t max78000_uart_addr[] = {0x40042000, 0x40043000,
+                                              0x40044000};
+
+static const int max78000_uart_irq[] = {30, 31, 50};
 
 static void max78000_soc_initfn(Object *obj)
 {
@@ -29,6 +33,10 @@ static void max78000_soc_initfn(Object *obj)
         object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
     }
 
+    for (i = 0; i < MAX78000_NUM_UART; i++) {
+        object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_MAX78000_UART);
+    }
+
     s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
     s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
 }
@@ -38,6 +46,7 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
     MAX78000State *s = MAX78000_SOC(dev_soc);
     MemoryRegion *system_memory = get_system_memory();
     DeviceState *dev, *armv7m;
+    SysBusDevice *busdev;
     Error *err = NULL;
     int i;
 
@@ -101,6 +110,18 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
         sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]);
     }
 
+    for (i = 0; i < MAX78000_NUM_UART; i++) {
+        dev = DEVICE(&(s->uart[i]));
+        qdev_prop_set_chr(dev, "chardev", serial_hd(i));
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
+            return;
+        }
+        busdev = SYS_BUS_DEVICE(dev);
+        sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
+        sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
+                                                       max78000_uart_irq[i]));
+    }
+
 
     create_unimplemented_device("globalControl",    0x40000000, 0x400);
     create_unimplemented_device("systemInterface",  0x40000400, 0x400);
@@ -140,10 +161,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("oneWireMaster",    0x4003d000, 0x1000);
     create_unimplemented_device("semaphore",        0x4003e000, 0x1000);
 
-    create_unimplemented_device("uart0",            0x40042000, 0x1000);
-    create_unimplemented_device("uart1",            0x40043000, 0x1000);
-    create_unimplemented_device("uart2",            0x40044000, 0x1000);
-
     create_unimplemented_device("spi1",             0x40046000, 0x2000);
     create_unimplemented_device("trng",             0x4004d000, 0x1000);
     create_unimplemented_device("i2s",              0x40060000, 0x1000);
diff --git a/hw/char/Kconfig b/hw/char/Kconfig
index 9d517f3e28..020c0a84bb 100644
--- a/hw/char/Kconfig
+++ b/hw/char/Kconfig
@@ -48,6 +48,9 @@ config VIRTIO_SERIAL
     default y
     depends on VIRTIO
 
+config MAX78000_UART
+    bool
+
 config STM32F2XX_USART
     bool
 
diff --git a/hw/char/max78000_uart.c b/hw/char/max78000_uart.c
new file mode 100644
index 0000000000..edd39c5a8b
--- /dev/null
+++ b/hw/char/max78000_uart.c
@@ -0,0 +1,263 @@
+/*
+ * MAX78000 UART
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/char/max78000_uart.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "trace.h"
+
+static int max78000_uart_can_receive(void *opaque)
+{
+    Max78000UartState *s = opaque;
+    if (!(s->ctrl & UART_BCLKEN)) {
+        return 0;
+    }
+    return fifo8_num_free(&s->rx_fifo);
+}
+
+static void max78000_update_irq(Max78000UartState *s)
+{
+    int interrupt_level = 0;
+    uint32_t rx_threshold = s->ctrl & 0xf;
+
+    /*
+     * Because tx is synchronous and we should have no frame errors, the only
+     * possible interrupt is receive fifo threshold
+     */
+    if ((s->int_en & UART_RX_THD) && fifo8_num_used(&s->rx_fifo) >= rx_threshold) {
+        interrupt_level = 1;
+        s->int_fl = s->int_fl & UART_RX_THD;
+    } else{
+        s->int_fl = s->int_fl & ~UART_RX_THD;
+    }
+    qemu_set_irq(s->irq, interrupt_level);
+}
+
+static void max78000_uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+    Max78000UartState *s = opaque;
+
+    if (size <= fifo8_num_free(&s->rx_fifo)) {
+        fifo8_push_all(&s->rx_fifo, buf, size);
+    } else{
+        fifo8_push_all(&s->rx_fifo, buf, fifo8_num_free(&s->rx_fifo));
+        printf("rx_fifo overrun!\n");
+    }
+
+    max78000_update_irq(s);
+}
+
+static void max78000_uart_reset_hold(Object *obj, ResetType type)
+{
+    Max78000UartState *s = MAX78000_UART(obj);
+
+    s->ctrl = 0;
+    s->status = UART_TX_EM | UART_RX_EM;
+    s->int_en = 0;
+    s->int_fl = 0;
+    s->osr = 0;
+    s->txpeek = 0;
+    s->pnr = UART_RTS;
+    s->fifo = 0;
+    s->dma = 0;
+    s->wken = 0;
+    s->wkfl = 0;
+    fifo8_reset(&s->rx_fifo);
+}
+
+static uint64_t max78000_uart_read(void *opaque, hwaddr addr,
+                                       unsigned int size)
+{
+    Max78000UartState *s = opaque;
+    uint64_t retvalue = 0;
+
+    switch (addr) {
+    case UART_CTRL:
+        retvalue = s->ctrl;
+        break;
+    case UART_STATUS:
+        retvalue = (fifo8_num_used(&s->rx_fifo) > 7 ?
+                    8 : (fifo8_num_used(&s->rx_fifo)) << UART_RX_LVL) |
+                    UART_TX_EM |
+                    (fifo8_is_empty(&s->rx_fifo) ? UART_RX_EM : 0);
+        break;
+    case UART_INT_EN:
+        retvalue = s->int_en;
+        break;
+    case UART_INT_FL:
+        retvalue = s->int_fl;
+        break;
+    case UART_CLKDIV:
+        retvalue = s->clkdiv;
+        break;
+    case UART_OSR:
+        retvalue = s->osr;
+        break;
+    case UART_TXPEEK:
+        if (!fifo8_is_empty(&s->rx_fifo)) {
+            retvalue = fifo8_peek(&s->rx_fifo);
+        }
+        break;
+    case UART_PNR:
+        retvalue = s->pnr;
+        break;
+    case UART_FIFO:
+        if (!fifo8_is_empty(&s->rx_fifo)) {
+            retvalue = fifo8_pop(&s->rx_fifo);
+            max78000_update_irq(s);
+        }
+        break;
+    case UART_DMA:
+        /* DMA not implemented */
+        retvalue = s->dma;
+        break;
+    case UART_WKEN:
+        retvalue = s->wken;
+        break;
+    case UART_WKFL:
+        retvalue = s->wkfl;
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr);
+        break;
+    }
+
+    return retvalue;
+}
+
+static void max78000_uart_write(void *opaque, hwaddr addr,
+                                  uint64_t val64, unsigned int size)
+{
+    Max78000UartState *s = opaque;
+
+    uint32_t value = val64;
+    uint8_t data;
+
+    switch (addr) {
+    case UART_CTRL:
+        if (value & UART_FLUSH_RX) {
+            fifo8_reset(&s->rx_fifo);
+        }
+        if (value & UART_BCLKEN) {
+            value = value | UART_BCLKRDY;
+        }
+        s->ctrl = value & ~(UART_FLUSH_RX | UART_FLUSH_TX);
+
+        /*
+         * Software can manage UART flow control manually by setting hfc_en
+         * in UART_CTRL. This would require emulating uart at a lower level,
+         * and is currently unimplemented.
+         */
+
+        return;
+    case UART_STATUS:
+        /* UART_STATUS is read only */
+        return;
+    case UART_INT_EN:
+        s->int_en = value;
+        return;
+    case UART_INT_FL:
+        s->int_fl = value;
+        return;
+    case UART_CLKDIV:
+        s->clkdiv = value;
+        return;
+    case UART_OSR:
+        s->osr = value;
+        return;
+    case UART_TXPEEK:
+        /* UART_TXPEEK is read only */
+        return;
+    case UART_PNR:
+        s->pnr = value;
+        return;
+    case UART_FIFO:
+        data = value & 0xff;
+        /*
+         * XXX this blocks entire thread. Rewrite to use
+         * qemu_chr_fe_write and background I/O callbacks
+         */
+        qemu_chr_fe_write_all(&s->chr, &data, 1);
+        return;
+    case UART_DMA:
+        /* DMA not implemented */
+        s->dma = value;
+        return;
+    case UART_WKEN:
+        s->wken = value;
+        return;
+    case UART_WKFL:
+        s->wkfl = value;
+        return;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+            HWADDR_PRIx "\n", TYPE_MAX78000_UART, __func__, addr);
+    }
+}
+
+static const MemoryRegionOps max78000_uart_ops = {
+    .read = max78000_uart_read,
+    .write = max78000_uart_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const Property max78000_uart_properties[] = {
+    DEFINE_PROP_CHR("chardev", Max78000UartState, chr),
+};
+
+static void max78000_uart_init(Object *obj)
+{
+    Max78000UartState *s = MAX78000_UART(obj);
+    fifo8_create(&s->rx_fifo, 8);
+
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+    memory_region_init_io(&s->mmio, obj, &max78000_uart_ops, s,
+                          TYPE_MAX78000_UART, 0x400);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static void max78000_uart_realize(DeviceState *dev, Error **errp)
+{
+    Max78000UartState *s = MAX78000_UART(dev);
+
+    qemu_chr_fe_set_handlers(&s->chr, max78000_uart_can_receive,
+                             max78000_uart_receive, NULL, NULL,
+                             s, NULL, true);
+}
+
+static void max78000_uart_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    rc->phases.hold = max78000_uart_reset_hold;
+
+    device_class_set_props(dc, max78000_uart_properties);
+    dc->realize = max78000_uart_realize;
+}
+
+static const TypeInfo max78000_uart_info = {
+    .name          = TYPE_MAX78000_UART,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Max78000UartState),
+    .instance_init = max78000_uart_init,
+    .class_init    = max78000_uart_class_init,
+};
+
+static void max78000_uart_register_types(void)
+{
+    type_register_static(&max78000_uart_info);
+}
+
+type_init(max78000_uart_register_types)
diff --git a/hw/char/meson.build b/hw/char/meson.build
index 4e439da8b9..a9e1dc26c0 100644
--- a/hw/char/meson.build
+++ b/hw/char/meson.build
@@ -26,6 +26,7 @@ system_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c'))
 system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c'))
 system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-uart.c'))
 system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c'))
+system_ss.add(when: 'CONFIG_MAX78000_UART', if_true: files('max78000_uart.c'))
 system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c'))
 system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c'))
 system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c'))
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 207a4da68a..1ee280b894 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -12,6 +12,7 @@
 #include "hw/or-irq.h"
 #include "hw/arm/armv7m.h"
 #include "hw/misc/max78000_icc.h"
+#include "hw/char/max78000_uart.h"
 #include "qom/object.h"
 
 #define TYPE_MAX78000_SOC "max78000-soc"
@@ -24,6 +25,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(MAX78000State, MAX78000_SOC)
 
 /* The MAX78k has 2 instruction caches; only icc0 matters, icc1 is for RISC */
 #define MAX78000_NUM_ICC 2
+#define MAX78000_NUM_UART 3
 
 struct MAX78000State {
     SysBusDevice parent_obj;
@@ -34,6 +36,7 @@ struct MAX78000State {
     MemoryRegion flash;
 
     Max78000IccState icc[MAX78000_NUM_ICC];
+    Max78000UartState uart[MAX78000_NUM_UART];
 
     Clock *sysclk;
     Clock *refclk;
diff --git a/include/hw/char/max78000_uart.h b/include/hw/char/max78000_uart.h
new file mode 100644
index 0000000000..0d39a2dd59
--- /dev/null
+++ b/include/hw/char/max78000_uart.h
@@ -0,0 +1,77 @@
+/*
+ * MAX78000 UART
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_MAX78000_UART_H
+#define HW_MAX78000_UART_H
+
+#include "hw/sysbus.h"
+#include "chardev/char-fe.h"
+#include "qemu/fifo8.h"
+#include "qom/object.h"
+
+#define UART_CTRL       0x0
+#define UART_STATUS     0x4
+#define UART_INT_EN     0x8
+#define UART_INT_FL     0xc
+#define UART_CLKDIV     0x10
+#define UART_OSR        0x14
+#define UART_TXPEEK     0x18
+#define UART_PNR        0x1c
+#define UART_FIFO       0x20
+#define UART_DMA        0x30
+#define UART_WKEN       0x34
+#define UART_WKFL       0x38
+
+/* CTRL */
+#define UART_CTF_DIS    (1 << 7)
+#define UART_FLUSH_TX   (1 << 8)
+#define UART_FLUSH_RX   (1 << 9)
+#define UART_BCLKEN     (1 << 15)
+#define UART_BCLKRDY    (1 << 19)
+
+/* STATUS */
+#define UART_RX_LVL     8
+#define UART_TX_EM      (1 << 6)
+#define UART_RX_FULL    (1 << 5)
+#define UART_RX_EM      (1 << 4)
+
+/* PNR (Pin Control Register) */
+#define UART_CTS        1
+#define UART_RTS        (1 << 1)
+
+/* INT_EN / INT_FL */
+#define UART_RX_THD     (1 << 4)
+
+#define UART_RXBUFLEN   0x100
+#define TYPE_MAX78000_UART "max78000-uart"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000UartState, MAX78000_UART)
+
+struct Max78000UartState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+
+    uint32_t ctrl;
+    uint32_t status;
+    uint32_t int_en;
+    uint32_t int_fl;
+    uint32_t clkdiv;
+    uint32_t osr;
+    uint32_t txpeek;
+    uint32_t pnr;
+    uint32_t fifo;
+    uint32_t dma;
+    uint32_t wken;
+    uint32_t wkfl;
+
+    Fifo8 rx_fifo;
+
+    CharBackend chr;
+    qemu_irq irq;
+};
+#endif /* HW_STM32F2XX_USART_H */
-- 
2.34.1



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

* [PATCH 4/6] MAX78000 GCR implementation
       [not found] <20250510042043.2056265-1-jcksn@duck.com>
                   ` (2 preceding siblings ...)
  2025-05-10  4:20 ` [PATCH 3/6] MAX78000 UART implementation jcksn
@ 2025-05-10  4:20 ` jcksn
  2025-06-16  5:13   ` Philippe Mathieu-Daudé
  2025-06-16  9:47   ` Peter Maydell
  2025-05-10  4:20 ` [PATCH 5/6] MAX78000 TRNG Implementation jcksn
  2025-05-10  4:20 ` [PATCH 6/6] MAX78000 AES implementation jcksn
  5 siblings, 2 replies; 12+ messages in thread
From: jcksn @ 2025-05-10  4:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: alistair

This patch implements the Global Control Register for the MAX78000 SOC

Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
 hw/arm/Kconfig                 |   1 +
 hw/arm/max78000_soc.c          |   9 +-
 hw/misc/Kconfig                |   3 +
 hw/misc/max78000_gcr.c         | 285 +++++++++++++++++++++++++++++++++
 hw/misc/meson.build            |   1 +
 include/hw/arm/max78000_soc.h  |   2 +
 include/hw/misc/max78000_gcr.h | 122 ++++++++++++++
 7 files changed, 422 insertions(+), 1 deletion(-)
 create mode 100644 hw/misc/max78000_gcr.c
 create mode 100644 include/hw/misc/max78000_gcr.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 59450dc3cb..211b201629 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -369,6 +369,7 @@ config MAX78000_SOC
     select ARM_V7M
     select MAX78000_ICC
     select MAX78000_UART
+    select MAX78000_GCR
 
 config RASPI
     bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 6334d8b49b..7a012c6ef7 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -29,6 +29,8 @@ static void max78000_soc_initfn(Object *obj)
 
     object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
 
+    object_initialize_child(obj, "gcr", &s->gcr, TYPE_MAX78000_GCR);
+
     for (i = 0; i < MAX78000_NUM_ICC; i++) {
         object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
     }
@@ -104,6 +106,10 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
         return;
     }
 
+    dev = DEVICE(&s->gcr);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40000000);
+
     for (i = 0; i < MAX78000_NUM_ICC; i++) {
         dev = DEVICE(&(s->icc[i]));
         sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
@@ -116,6 +122,8 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
         if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
             return;
         }
+        dev->id = g_strdup_printf("uart%d", i);
+
         busdev = SYS_BUS_DEVICE(dev);
         sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
         sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
@@ -123,7 +131,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
     }
 
 
-    create_unimplemented_device("globalControl",    0x40000000, 0x400);
     create_unimplemented_device("systemInterface",  0x40000400, 0x400);
     create_unimplemented_device("functionControl",  0x40000800, 0x3400);
     create_unimplemented_device("watchdogTimer0",   0x40003000, 0x400);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 781bcf74cc..fde2266b8f 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -47,6 +47,9 @@ config A9SCU
 config ARM11SCU
     bool
 
+config MAX78000_GCR
+    bool
+
 config MAX78000_ICC
     bool
 
diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
new file mode 100644
index 0000000000..657b7fc490
--- /dev/null
+++ b/hw/misc/max78000_gcr.c
@@ -0,0 +1,285 @@
+/*
+ * MAX78000 Global Control Registers
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "system/runstate.h"
+#include "migration/vmstate.h"
+#include "hw/misc/max78000_gcr.h"
+
+static void max78000_gcr_reset(DeviceState *dev)
+{
+    Max78000GcrState *s = MAX78000_GCR(dev);
+    s->sysctrl = 0x21002;
+    s->rst0 = 0;
+    /* All clocks are always ready */
+    s->clkctrl = 0x3e140008;
+    s->pm = 0x3f000;
+    s->pclkdiv = 0;
+    s->pclkdis0 = 0xffffffff;
+    s->memctrl = 0x5;
+    s->memz = 0;
+    s->sysst = 0;
+    s->rst1 = 0;
+    s->pckdis1 = 0xffffffff;
+    s->eventen = 0;
+    s->revision = 0xa1;
+    s->sysie = 0;
+    s->eccerr = 0;
+    s->ecced = 0;
+    s->eccie = 0;
+    s->eccaddr = 0;
+}
+
+static uint64_t max78000_gcr_read(void *opaque, hwaddr addr,
+                                     unsigned int size)
+{
+    Max78000GcrState *s = opaque;
+
+    switch (addr) {
+        case SYSCTRL:{
+            return s->sysctrl;
+        }
+        case RST0:{
+            return s->rst0;
+        }
+        case CLKCTRL:{
+            return s->clkctrl;
+        }
+        case PM:{
+            return s->pm;
+        }
+        case PCLKDIV:{
+            return s->pclkdiv;
+        }
+        case PCLKDIS0:{
+            return s->pclkdis0;
+        }
+        case MEMCTRL:{
+            return s->memctrl;
+        }
+        case MEMZ:{
+            return s->memz;
+        }
+        case SYSST:{
+            return s->sysst;
+        }
+        case RST1:{
+            return s->rst1;
+        }
+        case PCKDIS1:{
+            return s->pckdis1;
+        }
+        case EVENTEN:{
+            return s->eventen;
+        }
+        case REVISION:{
+            return s->revision;
+        }
+        case SYSIE:{
+            return s->sysie;
+        }
+        case ECCERR:{
+            return s->eccerr;
+        }
+        case ECCED:{
+            return s->ecced;
+        }
+        case ECCIE:{
+            return s->eccie;
+        }
+        case ECCADDR:{
+            return s->eccaddr;
+        }
+        default:{
+            return 0;
+        }
+    }
+}
+
+static void max78000_gcr_reset_device(const char *device_name)
+{
+    DeviceState *dev = qdev_find_recursive(sysbus_get_default(), device_name);
+    if (dev) {
+        device_cold_reset(dev);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR, "no device %s for reset\n", device_name);
+    }
+}
+
+static void max78000_gcr_write(void *opaque, hwaddr addr,
+                       uint64_t val64, unsigned int size)
+{
+    Max78000GcrState *s = opaque;
+    uint32_t val = val64;
+    uint8_t zero[0xc000] = {0};
+    switch (addr) {
+        case SYSCTRL:{
+            /* Checksum calculations always pass immediately */
+            s->sysctrl = (val & 0x30000) | 0x1002;
+            break;
+        }
+        case RST0:{
+            if (val & SYSTEM_RESET) {
+                qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+            }
+            if (val & PERIPHERAL_RESET) {
+                /*
+                 * Peripheral reset resets all peripherals. The CPU
+                 * retains its state. The GPIO, watchdog timers, AoD,
+                 * RAM retention, and general control registers (GCR),
+                 * including the clock configuration, are unaffected.
+                 */
+                val = UART2_RESET | UART1_RESET | UART0_RESET |
+                      ADC_RESET | CNN_RESET | TRNG_RESET |
+                      RTC_RESET | I2C0_RESET | SPI1_RESET |
+                      TMR3_RESET | TMR2_RESET | TMR1_RESET |
+                      TMR0_RESET | WDT0_RESET | DMA_RESET;
+            }
+            if (val & SOFT_RESET) {
+                /* Soft reset also resets GPIO */
+                val = UART2_RESET | UART1_RESET | UART0_RESET |
+                      ADC_RESET | CNN_RESET | TRNG_RESET |
+                      RTC_RESET | I2C0_RESET | SPI1_RESET |
+                      TMR3_RESET | TMR2_RESET | TMR1_RESET |
+                      TMR0_RESET | GPIO1_RESET | GPIO0_RESET |
+                      DMA_RESET;
+            }
+            if (val & UART2_RESET) {
+                max78000_gcr_reset_device("uart2");
+            }
+            if (val & UART1_RESET) {
+                max78000_gcr_reset_device("uart1");
+            }
+            if (val & UART0_RESET) {
+                max78000_gcr_reset_device("uart0");
+            }
+            /* TODO: As other devices are implemented, add them here */
+            break;
+        }
+        case CLKCTRL:{
+            s->clkctrl = val | SYSCLK_RDY;
+            break;
+        }
+        case PM:{
+            s->pm = val;
+            break;
+        }
+        case PCLKDIV:{
+            s->pclkdiv = val;
+            break;
+        }
+        case PCLKDIS0:{
+            s->pclkdis0 = val;
+            break;
+        }
+        case MEMCTRL:{
+            s->memctrl = val;
+            break;
+        }
+        case MEMZ:{
+            if (val & ram0) {
+                cpu_physical_memory_write(SYSRAM0_START, zero, 0x8000);
+            }
+            if (val & ram1) {
+                cpu_physical_memory_write(SYSRAM1_START, zero, 0x8000);
+            }
+            if (val & ram2) {
+                cpu_physical_memory_write(SYSRAM2_START, zero, 0xC000);
+            }
+            if (val & ram3) {
+                cpu_physical_memory_write(SYSRAM3_START, zero, 0x4000);
+            }
+            break;
+        }
+        case SYSST:{
+            s->sysst = val;
+            break;
+        }
+        case RST1:{
+            /* TODO: As other devices are implemented, add them here */
+            s->rst1 = val;
+            break;
+        }
+        case PCKDIS1:{
+            s->pckdis1 = val;
+            break;
+        }
+        case EVENTEN:{
+            s->eventen = val;
+            break;
+        }
+        case REVISION:{
+            s->revision = val;
+            break;
+        }
+        case SYSIE:{
+            s->sysie = val;
+            break;
+        }
+        case ECCERR:{
+            s->eccerr = val;
+            break;
+        }
+        case ECCED:{
+            s->ecced = val;
+            break;
+        }
+        case ECCIE:{
+            s->eccie = val;
+            break;
+        }
+        case ECCADDR:{
+            s->eccaddr = val;
+            break;
+        }
+        default:{
+            break;
+        }
+    }
+}
+
+static const MemoryRegionOps max78000_gcr_ops = {
+    .read = max78000_gcr_read,
+    .write = max78000_gcr_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void max78000_gcr_init(Object *obj)
+{
+    Max78000GcrState *s = MAX78000_GCR(obj);
+
+    memory_region_init_io(&s->mmio, obj, &max78000_gcr_ops, s,
+                          TYPE_MAX78000_GCR, 0x400);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static void max78000_gcr_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    device_class_set_legacy_reset(dc, max78000_gcr_reset);
+}
+
+static const TypeInfo max78000_gcr_info = {
+    .name          = TYPE_MAX78000_GCR,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Max78000GcrState),
+    .instance_init = max78000_gcr_init,
+    .class_init     = max78000_gcr_class_init,
+};
+
+static void max78000_gcr_register_types(void)
+{
+    type_register_static(&max78000_gcr_info);
+}
+
+type_init(max78000_gcr_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index a21a994ff8..283d06dad4 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
   'imx_ccm.c',
   'imx_rngc.c',
 ))
+system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c'))
 system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c'))
 system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
   'npcm_clk.c',
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 1ee280b894..8b9e3052cd 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -11,6 +11,7 @@
 
 #include "hw/or-irq.h"
 #include "hw/arm/armv7m.h"
+#include "hw/misc/max78000_gcr.h"
 #include "hw/misc/max78000_icc.h"
 #include "hw/char/max78000_uart.h"
 #include "qom/object.h"
@@ -35,6 +36,7 @@ struct MAX78000State {
     MemoryRegion sram;
     MemoryRegion flash;
 
+    Max78000GcrState gcr;
     Max78000IccState icc[MAX78000_NUM_ICC];
     Max78000UartState uart[MAX78000_NUM_UART];
 
diff --git a/include/hw/misc/max78000_gcr.h b/include/hw/misc/max78000_gcr.h
new file mode 100644
index 0000000000..128f9b8a99
--- /dev/null
+++ b/include/hw/misc/max78000_gcr.h
@@ -0,0 +1,122 @@
+/*
+ * MAX78000 Global Control Register
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MAX78000_GCR_H
+#define HW_MAX78000_GCR_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_GCR "max78000-gcr"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000GcrState, MAX78000_GCR)
+
+#define SYSCTRL     0x0
+#define RST0        0x4
+#define CLKCTRL     0x8
+#define PM          0xc
+#define PCLKDIV     0x18
+#define PCLKDIS0    0x24
+#define MEMCTRL     0x28
+#define MEMZ        0x2c
+#define SYSST       0x40
+#define RST1        0x44
+#define PCKDIS1     0x48
+#define EVENTEN     0x4c
+#define REVISION    0x50
+#define SYSIE       0x54
+#define ECCERR      0x64
+#define ECCED       0x68
+#define ECCIE       0x6c
+#define ECCADDR     0x70
+
+/* RST0 */
+#define SYSTEM_RESET (1 << 31)
+#define PERIPHERAL_RESET (1 << 30)
+#define SOFT_RESET (1 << 29)
+#define UART2_RESET (1 << 28)
+
+#define ADC_RESET (1 << 26)
+#define CNN_RESET (1 << 25)
+#define TRNG_RESET (1 << 24)
+
+#define RTC_RESET (1 << 17)
+#define I2C0_RESET (1 << 16)
+
+#define SPI1_RESET (1 << 13)
+#define UART1_RESET (1 << 12)
+#define UART0_RESET (1 << 11)
+
+#define TMR3_RESET (1 << 8)
+#define TMR2_RESET (1 << 7)
+#define TMR1_RESET (1 << 6)
+#define TMR0_RESET (1 << 5)
+
+#define GPIO1_RESET (1 << 3)
+#define GPIO0_RESET (1 << 2)
+#define WDT0_RESET (1 << 1)
+#define DMA_RESET (1 << 0)
+
+/* CLKCTRL */
+#define SYSCLK_RDY (1 << 13)
+
+/* MEMZ */
+#define ram0 (1 << 0)
+#define ram1 (1 << 1)
+#define ram2 (1 << 2)
+#define ram3 (1 << 3)
+
+/* RST1 */
+#define CPU1_RESET (1 << 31)
+
+#define SIMO_RESET (1 << 25)
+#define DVS_RESET (1 << 24)
+
+#define I2C2_RESET (1 << 20)
+#define I2S_RESET (1 << 19)
+
+#define SMPHR_RESET (1 << 16)
+
+#define SPI0_RESET (1 << 11)
+#define AES_RESET (1 << 10)
+#define CRC_RESET (1 << 9)
+
+#define PT_RESET (1 << 1)
+#define I2C1_RESET (1 << 0)
+
+
+#define SYSRAM0_START 0x20000000
+#define SYSRAM1_START 0x20008000
+#define SYSRAM2_START 0x20010000
+#define SYSRAM3_START 0x2001C000
+
+struct Max78000GcrState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+
+    uint32_t sysctrl;
+    uint32_t rst0;
+    uint32_t clkctrl;
+    uint32_t pm;
+    uint32_t pclkdiv;
+    uint32_t pclkdis0;
+    uint32_t memctrl;
+    uint32_t memz;
+    uint32_t sysst;
+    uint32_t rst1;
+    uint32_t pckdis1;
+    uint32_t eventen;
+    uint32_t revision;
+    uint32_t sysie;
+    uint32_t eccerr;
+    uint32_t ecced;
+    uint32_t eccie;
+    uint32_t eccaddr;
+
+};
+
+#endif
-- 
2.34.1



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

* [PATCH 5/6] MAX78000 TRNG Implementation
       [not found] <20250510042043.2056265-1-jcksn@duck.com>
                   ` (3 preceding siblings ...)
  2025-05-10  4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
@ 2025-05-10  4:20 ` jcksn
  2025-06-16  9:56   ` Peter Maydell
  2025-05-10  4:20 ` [PATCH 6/6] MAX78000 AES implementation jcksn
  5 siblings, 1 reply; 12+ messages in thread
From: jcksn @ 2025-05-10  4:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: alistair

This patch implements the True Random Number Generator for the MAX78000 SOC

Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
 hw/arm/Kconfig                  |   1 +
 hw/arm/max78000_soc.c           |   9 ++-
 hw/misc/Kconfig                 |   3 +
 hw/misc/max78000_gcr.c          |   3 +
 hw/misc/max78000_trng.c         | 115 ++++++++++++++++++++++++++++++++
 hw/misc/meson.build             |   1 +
 include/hw/arm/max78000_soc.h   |   2 +
 include/hw/misc/max78000_trng.h |  35 ++++++++++
 8 files changed, 168 insertions(+), 1 deletion(-)
 create mode 100644 hw/misc/max78000_trng.c
 create mode 100644 include/hw/misc/max78000_trng.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 211b201629..c13ddc980a 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -370,6 +370,7 @@ config MAX78000_SOC
     select MAX78000_ICC
     select MAX78000_UART
     select MAX78000_GCR
+    select MAX78000_TRNG
 
 config RASPI
     bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 7a012c6ef7..6809d4e249 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -39,6 +39,8 @@ static void max78000_soc_initfn(Object *obj)
         object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_MAX78000_UART);
     }
 
+    object_initialize_child(obj, "trng", &s->trng, TYPE_MAX78000_TRNG);
+
     s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
     s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
 }
@@ -130,6 +132,11 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
                                                        max78000_uart_irq[i]));
     }
 
+    dev = DEVICE(&s->trng);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x4004d000);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 4));
+    dev->id = g_strdup("trng");
 
     create_unimplemented_device("systemInterface",  0x40000400, 0x400);
     create_unimplemented_device("functionControl",  0x40000800, 0x3400);
@@ -169,7 +176,7 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("semaphore",        0x4003e000, 0x1000);
 
     create_unimplemented_device("spi1",             0x40046000, 0x2000);
-    create_unimplemented_device("trng",             0x4004d000, 0x1000);
+
     create_unimplemented_device("i2s",              0x40060000, 0x1000);
     create_unimplemented_device("lowPowerControl",  0x40080000, 0x400);
     create_unimplemented_device("gpio2",            0x40080400, 0x200);
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index fde2266b8f..dd6a6e54da 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -53,6 +53,9 @@ config MAX78000_GCR
 config MAX78000_ICC
     bool
 
+config MAX78000_TRNG
+    bool
+
 config MOS6522
     bool
 
diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
index 657b7fc490..a469d7a489 100644
--- a/hw/misc/max78000_gcr.c
+++ b/hw/misc/max78000_gcr.c
@@ -161,6 +161,9 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
             if (val & UART0_RESET) {
                 max78000_gcr_reset_device("uart0");
             }
+            if (val & TRNG_RESET) {
+                max78000_gcr_reset_device("trng");
+            }
             /* TODO: As other devices are implemented, add them here */
             break;
         }
diff --git a/hw/misc/max78000_trng.c b/hw/misc/max78000_trng.c
new file mode 100644
index 0000000000..bdfaa75bd4
--- /dev/null
+++ b/hw/misc/max78000_trng.c
@@ -0,0 +1,115 @@
+/*
+ * MAX78000 True Random Number Generator
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/max78000_trng.h"
+#include "crypto/random.h"
+
+static uint64_t max78000_trng_read(void *opaque, hwaddr addr,
+                                    unsigned int size)
+{
+    uint8_t buf[4];
+    Error *errp = NULL;
+
+    Max78000TrngState *s = opaque;
+    switch (addr) {
+        case CTRL:{
+            return s->ctrl;
+        }
+        case STATUS:{
+            return 1;
+        }
+        case DATA:{
+            qcrypto_random_bytes(buf, 4, &errp);
+            return *(uint32_t *)buf;
+        }
+        default:{
+            qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                HWADDR_PRIx "\n", TYPE_MAX78000_TRNG, __func__, addr);
+            break;
+        }
+    }
+    return 0;
+}
+
+static void max78000_trng_write(void *opaque, hwaddr addr,
+                    uint64_t val64, unsigned int size)
+{
+    Max78000TrngState *s = opaque;
+    uint32_t val = val64;
+    switch (addr) {
+        case CTRL:{
+            /* TODO: implement AES keygen */
+            s->ctrl = val;
+            if (val & RND_IE) {
+                qemu_set_irq(s->irq, 1);
+            } else{
+                qemu_set_irq(s->irq, 0);
+            }
+            break;
+        }
+        default:{
+            qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                HWADDR_PRIx "\n", TYPE_MAX78000_TRNG, __func__, addr);
+            break;
+        }
+
+    }
+}
+
+static void max78000_trng_reset_hold(Object *obj, ResetType type)
+{
+    Max78000TrngState *s = MAX78000_TRNG(obj);
+    s->ctrl = 0;
+    s->status = 0;
+    s->data = 0;
+}
+
+static const MemoryRegionOps max78000_trng_ops = {
+    .read = max78000_trng_read,
+    .write = max78000_trng_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void max78000_trng_init(Object *obj)
+{
+    Max78000TrngState *s = MAX78000_TRNG(obj);
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+    memory_region_init_io(&s->mmio, obj, &max78000_trng_ops, s,
+                        TYPE_MAX78000_TRNG, 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static void max78000_trng_class_init(ObjectClass *klass, const void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    rc->phases.hold = max78000_trng_reset_hold;
+
+}
+
+static const TypeInfo max78000_trng_info = {
+    .name          = TYPE_MAX78000_TRNG,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Max78000TrngState),
+    .instance_init = max78000_trng_init,
+    .class_init    = max78000_trng_class_init,
+};
+
+static void max78000_trng_register_types(void)
+{
+    type_register_static(&max78000_trng_info);
+}
+
+type_init(max78000_trng_register_types)
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 283d06dad4..c7c57d924b 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -72,6 +72,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
 ))
 system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c'))
 system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c'))
+system_ss.add(when: 'CONFIG_MAX78000_TRNG', if_true: files('max78000_trng.c'))
 system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
   'npcm_clk.c',
   'npcm_gcr.c',
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 8b9e3052cd..936825399c 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -14,6 +14,7 @@
 #include "hw/misc/max78000_gcr.h"
 #include "hw/misc/max78000_icc.h"
 #include "hw/char/max78000_uart.h"
+#include "hw/misc/max78000_trng.h"
 #include "qom/object.h"
 
 #define TYPE_MAX78000_SOC "max78000-soc"
@@ -39,6 +40,7 @@ struct MAX78000State {
     Max78000GcrState gcr;
     Max78000IccState icc[MAX78000_NUM_ICC];
     Max78000UartState uart[MAX78000_NUM_UART];
+    Max78000TrngState trng;
 
     Clock *sysclk;
     Clock *refclk;
diff --git a/include/hw/misc/max78000_trng.h b/include/hw/misc/max78000_trng.h
new file mode 100644
index 0000000000..c5a8129b6a
--- /dev/null
+++ b/include/hw/misc/max78000_trng.h
@@ -0,0 +1,35 @@
+/*
+ * MAX78000 True Random Number Generator
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MAX78000_TRNG_H
+#define HW_MAX78000_TRNG_H
+
+#include "hw/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_TRNG "max78000-trng"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000TrngState, MAX78000_TRNG)
+
+#define CTRL 0
+#define STATUS 4
+#define DATA 8
+
+#define RND_IE (1 << 1)
+
+struct Max78000TrngState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+
+    uint32_t ctrl;
+    uint32_t status;
+    uint32_t data;
+
+    qemu_irq irq;
+};
+
+#endif
-- 
2.34.1



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

* [PATCH 6/6] MAX78000 AES implementation
       [not found] <20250510042043.2056265-1-jcksn@duck.com>
                   ` (4 preceding siblings ...)
  2025-05-10  4:20 ` [PATCH 5/6] MAX78000 TRNG Implementation jcksn
@ 2025-05-10  4:20 ` jcksn
  5 siblings, 0 replies; 12+ messages in thread
From: jcksn @ 2025-05-10  4:20 UTC (permalink / raw)
  To: qemu-devel; +Cc: alistair

This commit implements AES encryption/decryption functionality
for the MAX78000 SOC

Signed-off-by: Jackson Donaldson <jcksn@duck.com>
---
 hw/arm/Kconfig                 |   1 +
 hw/arm/max78000_soc.c          |  12 +-
 hw/misc/Kconfig                |   3 +
 hw/misc/max78000_aes.c         | 215 +++++++++++++++++++++++++++++++++
 hw/misc/max78000_gcr.c         |   9 +-
 hw/misc/meson.build            |   1 +
 include/hw/arm/max78000_soc.h  |   2 +
 include/hw/misc/max78000_aes.h |  68 +++++++++++
 include/hw/misc/max78000_gcr.h |  19 +++
 9 files changed, 326 insertions(+), 4 deletions(-)
 create mode 100644 hw/misc/max78000_aes.c
 create mode 100644 include/hw/misc/max78000_aes.h

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index c13ddc980a..5d38aa0dc3 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -371,6 +371,7 @@ config MAX78000_SOC
     select MAX78000_UART
     select MAX78000_GCR
     select MAX78000_TRNG
+    select MAX78000_AES
 
 config RASPI
     bool
diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
index 6809d4e249..4b9682afce 100644
--- a/hw/arm/max78000_soc.c
+++ b/hw/arm/max78000_soc.c
@@ -41,6 +41,8 @@ static void max78000_soc_initfn(Object *obj)
 
     object_initialize_child(obj, "trng", &s->trng, TYPE_MAX78000_TRNG);
 
+    object_initialize_child(obj, "aes", &s->aes, TYPE_MAX78000_AES);
+
     s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
     s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
 }
@@ -138,6 +140,13 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
     sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 4));
     dev->id = g_strdup("trng");
 
+    dev = DEVICE(&s->aes);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
+    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40007400);
+    sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 5));
+    dev->id = g_strdup("aes");
+
+
     create_unimplemented_device("systemInterface",  0x40000400, 0x400);
     create_unimplemented_device("functionControl",  0x40000800, 0x3400);
     create_unimplemented_device("watchdogTimer0",   0x40003000, 0x400);
@@ -149,9 +158,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
     create_unimplemented_device("powerSequencer",   0x40006800, 0x400);
     create_unimplemented_device("miscControl",      0x40006c00, 0x800);
 
-    create_unimplemented_device("aes",              0x40007400, 0x400);
-    create_unimplemented_device("aesKey",           0x40007800, 0x800);
-
     create_unimplemented_device("gpio0",            0x40008000, 0x1000);
     create_unimplemented_device("gpio1",            0x40009000, 0x1000);
 
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index dd6a6e54da..c27285b47a 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -47,6 +47,9 @@ config A9SCU
 config ARM11SCU
     bool
 
+config MAX78000_AES
+    bool
+
 config MAX78000_GCR
     bool
 
diff --git a/hw/misc/max78000_aes.c b/hw/misc/max78000_aes.c
new file mode 100644
index 0000000000..9de230fb07
--- /dev/null
+++ b/hw/misc/max78000_aes.c
@@ -0,0 +1,215 @@
+/*
+ * MAX78000 AES
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "trace.h"
+#include "hw/irq.h"
+#include "migration/vmstate.h"
+#include "hw/misc/max78000_aes.h"
+#include "crypto/aes.h"
+
+static void max78000_aes_set_status(Max78000AesState *s)
+{
+    s->status = 0;
+    if (s->result_index >= 16) {
+        s->status |= OUTPUT_FULL;
+    }
+    if (s->result_index == 0) {
+        s->status |= OUTPUT_EMPTY;
+    }
+    if (s->data_index >= 16) {
+        s->status |= INPUT_FULL;
+    }
+    if (s->data_index == 0) {
+        s->status |= INPUT_EMPTY;
+    }
+}
+
+static uint64_t max78000_aes_read(void *opaque, hwaddr addr,
+                                    unsigned int size)
+{
+    Max78000AesState *s = opaque;
+    switch (addr) {
+        case CTRL:{
+            return s->ctrl;
+        }
+        case STATUS:{
+            return s->status;
+        }
+        case INTFL:{
+            return s->intfl;
+        }
+        case INTEN:{
+            return s->inten;
+        }
+        case FIFO:{
+            if (s->result_index >= 4) {
+                s->intfl &= ~DONE;
+                s->result_index -= 4;
+                max78000_aes_set_status(s);
+                return (s->result[s->result_index] << 24) +
+                    (s->result[s->result_index + 1] << 16) +
+                    (s->result[s->result_index + 2] << 8) +
+                    s->result[s->result_index + 3];
+            } else{
+                return 0;
+            }
+        }
+        default:{
+            qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                HWADDR_PRIx "\n", TYPE_MAX78000_AES, __func__, addr);
+            break;
+        }
+    }
+    return 0;
+}
+
+static void max78000_aes_do_crypto(Max78000AesState *s)
+{
+    int keylen = 256;
+    uint8_t *keydata = s->key;
+    if ((s->ctrl & KEY_SIZE) == 0) {
+        keylen = 128;
+        keydata += 16;
+    } else if ((s->ctrl & KEY_SIZE) == 1 << 6) {
+        keylen = 192;
+        keydata += 8;
+    }
+
+    AES_KEY key;
+    if ((s->ctrl & TYPE) == 0) {
+        AES_set_encrypt_key(keydata, keylen, &key);
+        AES_set_decrypt_key(keydata, keylen, &s->internal_key);
+        AES_encrypt(s->data, s->result, &key);
+        s->result_index = 16;
+    } else if ((s->ctrl & TYPE) == 1 << 8) {
+        AES_set_decrypt_key(keydata, keylen, &key);
+        AES_set_decrypt_key(keydata, keylen, &s->internal_key);
+        AES_decrypt(s->data, s->result, &key);
+        s->result_index = 16;
+    } else{
+        AES_decrypt(s->data, s->result, &s->internal_key);
+        s->result_index = 16;
+    }
+    s->intfl |= DONE;
+}
+
+static void max78000_aes_write(void *opaque, hwaddr addr,
+                    uint64_t val64, unsigned int size)
+{
+    Max78000AesState *s = opaque;
+    uint32_t val = val64;
+    int i;
+    switch (addr) {
+        case CTRL:{
+            if (val & OUTPUT_FLUSH) {
+                s->result_index = 0;
+                val &= ~OUTPUT_FLUSH;
+            }
+            if (val & INPUT_FLUSH) {
+                s->data_index = 0;
+                val &= ~INPUT_FLUSH;
+            }
+            if (val & START) {
+                max78000_aes_do_crypto(s);
+            }
+
+            /* Hardware appears to stay enabled even if 0 written */
+            s->ctrl = val | (s->ctrl & AES_EN);
+            break;
+        }
+
+        case FIFO:{
+            for (i = 0; i < 4; i++) {
+                s->data[(12 - s->data_index) + i] =
+                            (val >> ((3 - i) * 8)) & 0xff;
+            }
+            s->data_index += 4;
+            if (s->data_index >= 16) {
+                s->data_index = 0;
+                max78000_aes_do_crypto(s);
+            }
+            break;
+        }
+        case KEY_BASE ... KEY_END - 4:{
+            for (i = 0; i < 4; i++) {
+                s->key[(KEY_END - KEY_BASE - 4) - (addr - KEY_BASE) + i] =
+                            (val >> ((3 - i) * 8)) & 0xff;
+            }
+            break;
+        }
+
+        default:{
+            qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad register at offset 0x%"
+                HWADDR_PRIx "\n", TYPE_MAX78000_AES, __func__, addr);
+            break;
+        }
+
+    }
+    max78000_aes_set_status(s);
+}
+
+static void max78000_aes_reset_hold(Object *obj, ResetType type)
+{
+    Max78000AesState *s = MAX78000_AES(obj);
+    s->ctrl = 0;
+    s->status = 0;
+    s->intfl = 0;
+    s->inten = 0;
+
+    s->data_index = 0;
+    s->result_index = 0;
+
+    memset(s->data, 0, sizeof(s->data));
+    memset(s->key, 0, sizeof(s->key));
+    memset(s->result, 0, sizeof(s->result));
+    memset(&s->internal_key, 0, sizeof(s->internal_key));
+
+
+}
+
+static const MemoryRegionOps max78000_aes_ops = {
+    .read = max78000_aes_read,
+    .write = max78000_aes_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static void max78000_aes_init(Object *obj)
+{
+    Max78000AesState *s = MAX78000_AES(obj);
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+    memory_region_init_io(&s->mmio, obj, &max78000_aes_ops, s,
+                        TYPE_MAX78000_AES, 0xc00);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+}
+
+static void max78000_aes_class_init(ObjectClass *klass, const void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    rc->phases.hold = max78000_aes_reset_hold;
+
+}
+
+static const TypeInfo max78000_aes_info = {
+    .name          = TYPE_MAX78000_AES,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(Max78000AesState),
+    .instance_init = max78000_aes_init,
+    .class_init    = max78000_aes_class_init,
+};
+
+static void max78000_aes_register_types(void)
+{
+    type_register_static(&max78000_aes_info);
+}
+
+type_init(max78000_aes_register_types)
diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
index a469d7a489..c8e2279b00 100644
--- a/hw/misc/max78000_gcr.c
+++ b/hw/misc/max78000_gcr.c
@@ -142,6 +142,8 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
                       RTC_RESET | I2C0_RESET | SPI1_RESET |
                       TMR3_RESET | TMR2_RESET | TMR1_RESET |
                       TMR0_RESET | WDT0_RESET | DMA_RESET;
+
+                max78000_gcr_reset_device("aes");
             }
             if (val & SOFT_RESET) {
                 /* Soft reset also resets GPIO */
@@ -151,6 +153,7 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
                       TMR3_RESET | TMR2_RESET | TMR1_RESET |
                       TMR0_RESET | GPIO1_RESET | GPIO0_RESET |
                       DMA_RESET;
+                max78000_gcr_reset_device("aes");
             }
             if (val & UART2_RESET) {
                 max78000_gcr_reset_device("uart2");
@@ -164,6 +167,7 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
             if (val & TRNG_RESET) {
                 max78000_gcr_reset_device("trng");
             }
+
             /* TODO: As other devices are implemented, add them here */
             break;
         }
@@ -208,7 +212,10 @@ static void max78000_gcr_write(void *opaque, hwaddr addr,
         }
         case RST1:{
             /* TODO: As other devices are implemented, add them here */
-            s->rst1 = val;
+            if (val & AES_RESET) {
+                max78000_gcr_reset_device("aes");
+            }
+
             break;
         }
         case PCKDIS1:{
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index c7c57d924b..b1d8d8e5d2 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files(
   'imx_ccm.c',
   'imx_rngc.c',
 ))
+system_ss.add(when: 'CONFIG_MAX78000_AES', if_true: files('max78000_aes.c'))
 system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c'))
 system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c'))
 system_ss.add(when: 'CONFIG_MAX78000_TRNG', if_true: files('max78000_trng.c'))
diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h
index 936825399c..69284abf9f 100644
--- a/include/hw/arm/max78000_soc.h
+++ b/include/hw/arm/max78000_soc.h
@@ -11,6 +11,7 @@
 
 #include "hw/or-irq.h"
 #include "hw/arm/armv7m.h"
+#include "hw/misc/max78000_aes.h"
 #include "hw/misc/max78000_gcr.h"
 #include "hw/misc/max78000_icc.h"
 #include "hw/char/max78000_uart.h"
@@ -41,6 +42,7 @@ struct MAX78000State {
     Max78000IccState icc[MAX78000_NUM_ICC];
     Max78000UartState uart[MAX78000_NUM_UART];
     Max78000TrngState trng;
+    Max78000AesState aes;
 
     Clock *sysclk;
     Clock *refclk;
diff --git a/include/hw/misc/max78000_aes.h b/include/hw/misc/max78000_aes.h
new file mode 100644
index 0000000000..407c45ef61
--- /dev/null
+++ b/include/hw/misc/max78000_aes.h
@@ -0,0 +1,68 @@
+/*
+ * MAX78000 AES
+ *
+ * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef HW_MAX78000_AES_H
+#define HW_MAX78000_AES_H
+
+#include "hw/sysbus.h"
+#include "crypto/aes.h"
+#include "qom/object.h"
+
+#define TYPE_MAX78000_AES "max78000-aes"
+OBJECT_DECLARE_SIMPLE_TYPE(Max78000AesState, MAX78000_AES)
+
+#define CTRL 0
+#define STATUS 4
+#define INTFL 8
+#define INTEN 0xc
+#define FIFO 0x10
+
+#define KEY_BASE 0x400
+#define KEY_END 0x420
+
+/* CTRL */
+#define TYPE (1 << 9 | 1 << 8)
+#define KEY_SIZE (1 << 7 | 1 << 6)
+#define OUTPUT_FLUSH (1 << 5)
+#define INPUT_FLUSH (1 << 4)
+#define START (1 << 3)
+
+#define AES_EN (1 << 0)
+
+/* STATUS */
+#define OUTPUT_FULL (1 << 4)
+#define OUTPUT_EMPTY (1 << 3)
+#define INPUT_FULL (1 << 2)
+#define INPUT_EMPTY (1 << 1)
+#define BUSY (1 << 0)
+
+/* INTFL*/
+#define DONE (1 << 0)
+
+struct Max78000AesState {
+    SysBusDevice parent_obj;
+
+    MemoryRegion mmio;
+
+    uint32_t ctrl;
+    uint32_t status;
+    uint32_t intfl;
+    uint32_t inten;
+    uint32_t data_index;
+    uint8_t data[16];
+
+    uint8_t key[32];
+    AES_KEY internal_key;
+
+    uint32_t result_index;
+    uint8_t result[16];
+
+
+    qemu_irq irq;
+};
+
+#endif
diff --git a/include/hw/misc/max78000_gcr.h b/include/hw/misc/max78000_gcr.h
index 128f9b8a99..b6abaa25bc 100644
--- a/include/hw/misc/max78000_gcr.h
+++ b/include/hw/misc/max78000_gcr.h
@@ -88,6 +88,25 @@ OBJECT_DECLARE_SIMPLE_TYPE(Max78000GcrState, MAX78000_GCR)
 #define I2C1_RESET (1 << 0)
 
 
+/* RST1 */
+#define CPU1_RESET (1 << 31)
+
+#define SIMO_RESET (1 << 25)
+#define DVS_RESET (1 << 24)
+
+#define I2C2_RESET (1 << 20)
+#define I2S_RESET (1 << 19)
+
+#define SMPHR_RESET (1 << 16)
+
+#define SPI0_RESET (1 << 11)
+#define AES_RESET (1 << 10)
+#define CRC_RESET (1 << 9)
+
+#define PT_RESET (1 << 1)
+#define I2C1_RESET (1 << 0)
+
+
 #define SYSRAM0_START 0x20000000
 #define SYSRAM1_START 0x20008000
 #define SYSRAM2_START 0x20010000
-- 
2.34.1



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

* Re: [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine
  2025-05-10  4:20 ` [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine jcksn
@ 2025-06-13 15:01   ` Peter Maydell
  0 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-13 15:01 UTC (permalink / raw)
  To: jcksn; +Cc: qemu-devel, alistair

On Sat, 10 May 2025 at 07:57, <jcksn@duck.com> wrote:
>
> This patch adds support for the MAX78000FTHR machine.
>
> The MAX78000FTHR contains a MAX78000 and a RISC-V core. This patch
> implements only the MAX78000, which is Cortex-M4 based.
> Details can be found at:
> https://www.analog.com/media/en/technical-documentation/user-guides/max78000-user-guide.pdf
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>

Hi; thanks for this patchset, and sorry it's taken me so
long to get to reviewing it. Mostly it looks in good
shape, but I have some review comments, which I'll give
below (and on some of the other patches).

A minor note on email patchseries formatting: something odd
seems to have happened with your cover letter. Patches in a
series are supposed to be followups to the cover letter, but
in this case each patch email has a header
 In-reply-to: 20250510042043.2056265-1-jcksn@duck.com
but that doesn't match the message-id of the cover letter mail
(which was
 16B81215-B460-4A49-910E-E5FB479837C8.1@smtp-inbound1.duck.com
).

It looks like something has rewritten the message-ID header
between when git generated it and it got to the mailing list.
I don't know if that's something you can fix at your end.
(Otherwise I guess you could work around it by sending the cover
letter first, finding out what message-ID it got given, and then
generating the patch emails with the --in-reply-to=<identifier>
option of git send-email so they come out as replies to the
cover letter. But that seems a bit of a faff :-/ )

> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> new file mode 100644
> index 0000000000..64578438bd
> --- /dev/null
> +++ b/hw/arm/max78000_soc.c
> @@ -0,0 +1,184 @@
> +/*
> + * MAX78000 SOC
> + *
> + * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * Implementation based on stm32f205

This comment is a good place to add the URL of the user guide.

> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "system/address-spaces.h"
> +#include "system/system.h"
> +#include "hw/arm/max78000_soc.h"
> +#include "hw/qdev-clock.h"
> +#include "hw/misc/unimp.h"
> +
> +static void max78000_soc_initfn(Object *obj)
> +{
> +    MAX78000State *s = MAX78000_SOC(obj);
> +
> +    object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
> +
> +    s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
> +    s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
> +}
> +
> +static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
> +{
> +    MAX78000State *s = MAX78000_SOC(dev_soc);
> +    MemoryRegion *system_memory = get_system_memory();
> +    DeviceState *armv7m;
> +    Error *err = NULL;
> +
> +    /*
> +     * We use s->refclk internally and only define it with qdev_init_clock_in()
> +     * so it is correctly parented and not leaked on an init/deinit; it is not
> +     * intended as an externally exposed clock.
> +     */
> +    if (clock_has_source(s->refclk)) {
> +        error_setg(errp, "refclk clock must not be wired up by the board code");
> +        return;
> +    }
> +
> +    if (!clock_has_source(s->sysclk)) {
> +        error_setg(errp, "sysclk clock must be wired up by the board code");
> +        return;
> +    }
> +
> +    /*
> +     * TODO: ideally we should model the SoC RCC and its ability to
> +     * change the sysclk frequency and define different sysclk sources.
> +     */

The MAX78000 doesn't have an RCC...

> +
> +    /* The refclk always runs at frequency HCLK / 8 */

Is this actually true for the MAX78000? I couldn't figure out
from the datasheet what it does for the systick refclk.

> +    clock_set_mul_div(s->refclk, 8, 1);
> +    clock_set_source(s->refclk, s->sysclk);
> +
> +    memory_region_init_rom(&s->flash, OBJECT(dev_soc), "MAX78000.flash",
> +                           FLASH_SIZE, &err);
> +    if (err != NULL) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +
> +    memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash);
> +
> +    memory_region_init_ram(&s->sram, NULL, "MAX78000.sram", SRAM_SIZE,
> +                           &err);
> +    if (err != NULL) {
> +        error_propagate(errp, err);
> +        return;
> +    }
> +    memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram);
> +
> +    armv7m = DEVICE(&s->armv7m);
> +    qdev_prop_set_uint32(armv7m, "num-irq", 134);

The datasheet seems a little confused here, because
it says there are 134 interrupts including the 15 system
exceptions, but the interrupt vector table it lists then
only goes from 0 to 119 despite including the system exceptions.
So if we believe table 5-1 then there are 104 external
interrupts (numbered from 16 to 119 in that table).

If we believe the "total number of entries including the
15 system exceptions in 134" text above the table, then
there are 120 external exceptions, numbered 16 to 135,
of which 120 to 135 don't appear in the table and would
presumably be unused.

The num-irq property is the number of external interrupts
(not including the CPU-internal ones). So 134 seems definitely
the wrong value, and the correct number is probably either
104 or 120.

If you have the real hardware and the enthusiasm then you
can check by writing to the NVIC_ISERn register bank until
you find the point where you can't write 1 bits and read
them back any more (i.e. until you find the RAZ/WI bits
corresponding to "interrupt number not implemented").
Otherwise, it's not a big deal if we don't actually
get this value correct, but it would in that case be worth
a comment noting that the datasheet is unclear and we've
made our best guess at the value.

> +    qdev_prop_set_uint8(armv7m, "num-prio-bits", 3);
> +    qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4"));
> +    qdev_prop_set_bit(armv7m, "enable-bitband", true);
> +    qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk);
> +    qdev_connect_clock_in(armv7m, "refclk", s->refclk);
> +    object_property_set_link(OBJECT(&s->armv7m), "memory",
> +                             OBJECT(system_memory), &error_abort);
> +    if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) {
> +        return;
> +    }
> +
> +    create_unimplemented_device("globalControl",    0x40000000, 0x400);
> +    create_unimplemented_device("systemInterface",  0x40000400, 0x400);
> +    create_unimplemented_device("functionControl",  0x40000800, 0x3400);

Table 3-3 says the end address is 0x4000_0bff, so the size should be 0x400,
not 0x3400.

> +    create_unimplemented_device("watchdogTimer0",   0x40003000, 0x400);
> +    create_unimplemented_device("dynamicVoltScale", 0x40003c00, 0x800);

Size doesn't match listed end address in table 3-3 ?

> +    create_unimplemented_device("SIMO",             0x40004400, 0x400);
> +    create_unimplemented_device("trimSystemInit",   0x40005400, 0x400);
> +    create_unimplemented_device("generalCtrlFunc",  0x40005800, 0xc00);

Ditto

> +    create_unimplemented_device("wakeupTimer",      0x40006400, 0x400);
> +    create_unimplemented_device("powerSequencer",   0x40006800, 0x400);
> +    create_unimplemented_device("miscControl",      0x40006c00, 0x800);

Ditto. I stopped checking the sizes here but you should probably
look at the rest.

> +
> +    create_unimplemented_device("aes",              0x40007400, 0x400);
> +    create_unimplemented_device("aesKey",           0x40007800, 0x800);
> +
> +    create_unimplemented_device("gpio0",            0x40008000, 0x1000);
> +    create_unimplemented_device("gpio1",            0x40009000, 0x1000);
> +
> +    create_unimplemented_device("parallelCamInter", 0x4000e000, 0x1000);
> +    create_unimplemented_device("CRC",              0x4000f000, 0x1000);
> +
> +    create_unimplemented_device("timer0",           0x40010000, 0x1000);
> +    create_unimplemented_device("timer1",           0x40011000, 0x1000);
> +    create_unimplemented_device("timer2",           0x40012000, 0x1000);
> +    create_unimplemented_device("timer3",           0x40013000, 0x1000);
> +
> +    create_unimplemented_device("i2c0",             0x4001d000, 0x1000);
> +    create_unimplemented_device("i2c1",             0x4001e000, 0x1000);
> +    create_unimplemented_device("i2c2",             0x4001f000, 0x1000);
> +
> +    create_unimplemented_device("standardDMA",      0x40028000, 0x1000);
> +    create_unimplemented_device("flashController0", 0x40029000, 0x400);
> +
> +    create_unimplemented_device("icc0",             0x4002a000, 0x800);
> +    create_unimplemented_device("icc1",             0x4002a800, 0x800);
> +
> +    create_unimplemented_device("adc",              0x40034000, 0x1000);
> +    create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0);
> +    create_unimplemented_device("oneWireMaster",    0x4003d000, 0x1000);
> +    create_unimplemented_device("semaphore",        0x4003e000, 0x1000);
> +
> +    create_unimplemented_device("uart0",            0x40042000, 0x1000);
> +    create_unimplemented_device("uart1",            0x40043000, 0x1000);
> +    create_unimplemented_device("uart2",            0x40044000, 0x1000);
> +
> +    create_unimplemented_device("spi1",             0x40046000, 0x2000);
> +    create_unimplemented_device("trng",             0x4004d000, 0x1000);
> +    create_unimplemented_device("i2s",              0x40060000, 0x1000);
> +    create_unimplemented_device("lowPowerControl",  0x40080000, 0x400);
> +    create_unimplemented_device("gpio2",            0x40080400, 0x200);
> +    create_unimplemented_device("lowPowWatchdogTi", 0x40080800, 0x400);

You don't need to just cut off the name string like this, there's
no fixed length to it. "Ti" is rather non-obvious (and the point
of these strings is to show them to the user if the guest tries
to access the missing device). Either write Timer or else just say
"lowPowerWatchdog". Similarly with some of the other names.

> +
> +    create_unimplemented_device("lowPowerTimer5",   0x40081000, 0x400);
> +    create_unimplemented_device("lowPowerUART0",    0x40081400, 0x400);
> +    create_unimplemented_device("lowPowerCompar",   0x40088000, 0x400);
> +
> +    create_unimplemented_device("spi0",             0x400be000, 0x400);
> +
> +    /*
> +     * The MAX78000 user guide's base address map lists the CNN TX FIFO as
> +     * beginning at 0x400c0400 and ending at 0x400c0400. Given that CNN_FIFO
> +     * is listed as having data accessible up to offset 0x1000, the user
> +     * guide is likely incorrect.
> +     */
> +    create_unimplemented_device("cnnTxFIFO",        0x400c0400, 0x2000);
> +
> +    create_unimplemented_device("cnnGlobalControl", 0x50000000, 0x10000);
> +    create_unimplemented_device("cnnx16quad0",      0x50100000, 0x40000);
> +    create_unimplemented_device("cnnx16quad1",      0x50500000, 0x40000);
> +    create_unimplemented_device("cnnx16quad2",      0x50900000, 0x40000);
> +    create_unimplemented_device("cnnx16quad3",      0x50d00000, 0x40000);
> +
> +}

thanks
-- PMM


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

* Re: [PATCH 2/6] MAX78000: ICC Implementation
  2025-05-10  4:20 ` [PATCH 2/6] MAX78000: ICC Implementation jcksn
@ 2025-06-13 15:26   ` Peter Maydell
  0 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-13 15:26 UTC (permalink / raw)
  To: jcksn; +Cc: qemu-devel, alistair

On Sat, 10 May 2025 at 07:58, <jcksn@duck.com> wrote:
>
> This patch implements the Instruction Cache Controller for the MAX78000 SOC
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
>  hw/arm/Kconfig                 |  1 +
>  hw/arm/max78000_soc.c          | 20 ++++++--
>  hw/misc/Kconfig                |  3 ++
>  hw/misc/max78000_icc.c         | 89 ++++++++++++++++++++++++++++++++++
>  hw/misc/meson.build            |  1 +
>  include/hw/arm/max78000_soc.h  |  6 +++
>  include/hw/misc/max78000_icc.h | 34 +++++++++++++
>  7 files changed, 150 insertions(+), 4 deletions(-)
>  create mode 100644 hw/misc/max78000_icc.c
>  create mode 100644 include/hw/misc/max78000_icc.h

We generally prefer to split into two patches,
one for "just add the new device" and then a separate
one for "instantiate the new device in the SoC".
(This device is quite small, but for larger ones it
helps to keep the patchsize down.)

> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 1c365d1115..3f23af3244 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -367,6 +367,7 @@ config ALLWINNER_R40
>  config MAX78000_SOC
>      bool
>      select ARM_V7M
> +    select MAX78000_ICC
>
>  config RASPI
>      bool
> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> index 64578438bd..4d598bddd4 100644
> --- a/hw/arm/max78000_soc.c
> +++ b/hw/arm/max78000_soc.c
> @@ -16,12 +16,19 @@
>  #include "hw/qdev-clock.h"
>  #include "hw/misc/unimp.h"
>
> +static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
> +
>  static void max78000_soc_initfn(Object *obj)
>  {
>      MAX78000State *s = MAX78000_SOC(obj);
> +    int i;
>
>      object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
>
> +    for (i = 0; i < MAX78000_NUM_ICC; i++) {
> +        object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
> +    }
> +
>      s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
>      s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
>  }
> @@ -30,8 +37,9 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>  {
>      MAX78000State *s = MAX78000_SOC(dev_soc);
>      MemoryRegion *system_memory = get_system_memory();
> -    DeviceState *armv7m;
> +    DeviceState *dev, *armv7m;
>      Error *err = NULL;
> +    int i;
>
>      /*
>       * We use s->refclk internally and only define it with qdev_init_clock_in()
> @@ -87,6 +95,13 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>          return;
>      }
>
> +    for (i = 0; i < MAX78000_NUM_ICC; i++) {
> +        dev = DEVICE(&(s->icc[i]));
> +        sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);

You want just sysbus_realize() here. There are two patterns:
 - when creating a device via qdev_new() or something similar,
   realize it with a *_realize_and_unref() function
 - when initializing an object in place with object_initialize_child(),
   realize it with a *_realize() function

(This is because object_initialize_child() has already arranged
for the only reference to the child device to be held by the
parent via the child<> property, so we don't want to deref it
again.)

> +        sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]);
> +    }
> +
> +
>      create_unimplemented_device("globalControl",    0x40000000, 0x400);
>      create_unimplemented_device("systemInterface",  0x40000400, 0x400);
>      create_unimplemented_device("functionControl",  0x40000800, 0x3400);
> @@ -120,9 +135,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>      create_unimplemented_device("standardDMA",      0x40028000, 0x1000);
>      create_unimplemented_device("flashController0", 0x40029000, 0x400);
>
> -    create_unimplemented_device("icc0",             0x4002a000, 0x800);
> -    create_unimplemented_device("icc1",             0x4002a800, 0x800);
> -
>      create_unimplemented_device("adc",              0x40034000, 0x1000);
>      create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0);
>      create_unimplemented_device("oneWireMaster",    0x4003d000, 0x1000);
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index ec0fa5aa9f..781bcf74cc 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -47,6 +47,9 @@ config A9SCU
>  config ARM11SCU
>      bool
>
> +config MAX78000_ICC
> +    bool
> +
>  config MOS6522
>      bool
>
> diff --git a/hw/misc/max78000_icc.c b/hw/misc/max78000_icc.c
> new file mode 100644
> index 0000000000..3eacf6bd1b
> --- /dev/null
> +++ b/hw/misc/max78000_icc.c
> @@ -0,0 +1,89 @@
> +/*
> + * MAX78000 Instruction Cache
> + *
> + * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "trace.h"
> +#include "hw/irq.h"
> +#include "migration/vmstate.h"
> +#include "hw/misc/max78000_icc.h"
> +
> +
> +static uint64_t max78000_icc_read(void *opaque, hwaddr addr,
> +                                    unsigned int size)
> +{
> +    Max78000IccState *s = opaque;
> +    switch (addr) {
> +        case ICC_INFO:{
> +            return s->info;
> +        }

Your switch case indentation seems a bit odd. Our usual
style puts the "case" at the same indent level as "switch".

Also, you don't need braces on cases unless they are
required to define the scope of a local variable.
So none of these need them.

> +        case ICC_SZ:{
> +            return s->sz;
> +        }
> +        case ICC_CTRL:{
> +            return s->ctrl;
> +        }
> +        case ICC_INVALIDATE:{
> +            return s->invalidate;
> +        }

ICCn_INVALIDATE is write-only; it doesn't need a state
field in the device state struct. It doesn't need a
case in the read function, we can let that drop into
the default case.

> +        default:{
> +            return 0;

For "there is no register at this offset", use:

    default:
        qemu_log_mask(LOG_GUEST_ERROR,
                      "%s: bad offset 0x%" HWADDR_PRIx "\n",
                      __func__, addr);
        return 0;

(Similarly for write.)

> +        }
> +    }
> +}
> +
> +static void max78000_icc_write(void *opaque, hwaddr addr,
> +                    uint64_t val64, unsigned int size)
> +{
> +    Max78000IccState *s = opaque;
> +
> +    switch (addr) {
> +        case ICC_CTRL:{
> +        s->ctrl = 0x10000 + (val64 & 1);

I prefer "|" over "+" for this kind of thing, as we are
assembling bit fields, not doing arithmetic.

> +        break;
> +        }
> +        case ICC_INVALIDATE:{
> +        s->ctrl = s->ctrl | 0x80;

What is this doing? The datasheet says bit 7 in ICCn_CTRL
is reserved.

> +        }

A default case logging the guest error would be good.

> +    }
> +}
> +
> +static const MemoryRegionOps stm32f4xx_exti_ops = {
> +    .read = max78000_icc_read,
> +    .write = max78000_icc_write,

I think we should also specify
       .valid.min_access_size = 4,
       .valid.max_access_size = 4,

which will mean the guest gets an exception if it tries
to do a byte read to these 32-bit registers.  Otherwise
we would have to put in extra complexity to handle the
byte and halfword access cases. (QEMU can try to
synthesize these for you but its default is not
necessarily what you need for all devices.)

> +    .endianness = DEVICE_NATIVE_ENDIAN,

We're trying to reduce the use of DEVICE_NATIVE_ENDIAN,
so use DEVICE_LITTLE_ENDIAN here, please. (For an Arm
machine type, the two are the same thing.)

> +};
> +
> +static void max78000_icc_init(Object *obj)
> +{
> +    Max78000IccState *s = MAX78000_ICC(obj);
> +    s->info = 0;
> +    s->sz = 0x10000010;
> +    s->ctrl = 0x10000;
> +    s->invalidate = 0;

These look like reset values. Reset of registers needs to
be done in a reset method, not in init.

> +
> +
> +    memory_region_init_io(&s->mmio, obj, &stm32f4xx_exti_ops, s,
> +                        TYPE_MAX78000_ICC, 0x800);
> +    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
> +
> +}

You also need to support migration, which means having
a VMStateDescription that defines what the guest-visible
state of your device is, and setting dc->vmsd to point to it
in your device's class init. For more info see
https://www.qemu.org/docs/master/devel/migration/main.html
and look at other devices in the source tree for examples.

thanks
-- PMM


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

* Re: [PATCH 3/6] MAX78000 UART implementation
  2025-05-10  4:20 ` [PATCH 3/6] MAX78000 UART implementation jcksn
@ 2025-06-13 16:03   ` Peter Maydell
  0 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-13 16:03 UTC (permalink / raw)
  To: jcksn; +Cc: qemu-devel, alistair

On Sat, 10 May 2025 at 07:57, <jcksn@duck.com> wrote:
>
> This patch implements UART support for the MAX78000 SOC
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
>  hw/arm/Kconfig                  |   1 +
>  hw/arm/max78000_soc.c           |  27 +++-
>  hw/char/Kconfig                 |   3 +
>  hw/char/max78000_uart.c         | 263 ++++++++++++++++++++++++++++++++
>  hw/char/meson.build             |   1 +
>  include/hw/arm/max78000_soc.h   |   3 +
>  include/hw/char/max78000_uart.h |  77 ++++++++++
>  7 files changed, 370 insertions(+), 5 deletions(-)
>  create mode 100644 hw/char/max78000_uart.c
>  create mode 100644 include/hw/char/max78000_uart.h

Some of the comments I had on patch 2 apply also to this
and the other devices:
 * separate patches for "new device" and "wire up new
   device into the SoC"
 * missing migration state support
 * use DEVICE_LITTLE_ENDIAN
 * set .impl.min_access_size and .impl_max_access_size
   appropriately for the device
 * make sure you've implemented a reset method (this device
   has one but some of the later ones don't)
 * fix case indent and excess braces if necessary
 * make sure you're not using the _realize_and_unref functions


> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 3f23af3244..59450dc3cb 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -368,6 +368,7 @@ config MAX78000_SOC
>      bool
>      select ARM_V7M
>      select MAX78000_ICC
> +    select MAX78000_UART
>
>  config RASPI
>      bool
> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> index 4d598bddd4..6334d8b49b 100644
> --- a/hw/arm/max78000_soc.c
> +++ b/hw/arm/max78000_soc.c
> @@ -16,7 +16,11 @@
>  #include "hw/qdev-clock.h"
>  #include "hw/misc/unimp.h"
>
> -static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800};
> +static const uint32_t max78000_icc_addr[] =  {0x4002a000, 0x4002a800};
> +static const uint32_t max78000_uart_addr[] = {0x40042000, 0x40043000,
> +                                              0x40044000};
> +
> +static const int max78000_uart_irq[] = {30, 31, 50};

The GPIO inputs to the ARM7M object are only the external
interrupt lines, so GPIO 0 is the first external interrupt,
which is 16 in the datasheet's Table 3-3. So you need to
subtract 16 from all the numbers in the table to get
the right GPIO index. The UARTs here are at 14, 15, 34.

>
>  static void max78000_soc_initfn(Object *obj)
>  {
> @@ -29,6 +33,10 @@ static void max78000_soc_initfn(Object *obj)
>          object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
>      }
>
> +    for (i = 0; i < MAX78000_NUM_UART; i++) {
> +        object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_MAX78000_UART);

I didn't notice this for the icc patch, but rather than using the
same string for each UART, you can give them unique names with
           g_autofree char *name = g_strdup_printf("uart%d", i);
           object_initialize_child(obj, name, ...)

See eg hw/arm/stm32l4x5_soc.c for examples.

> +    }
> +
>      s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0);
>      s->refclk = qdev_init_clock_in(DEVICE(s), "refclk", NULL, NULL, 0);
>  }
> @@ -38,6 +46,7 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>      MAX78000State *s = MAX78000_SOC(dev_soc);
>      MemoryRegion *system_memory = get_system_memory();
>      DeviceState *dev, *armv7m;
> +    SysBusDevice *busdev;
>      Error *err = NULL;
>      int i;
>
> @@ -101,6 +110,18 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>          sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]);
>      }
>
> +    for (i = 0; i < MAX78000_NUM_UART; i++) {
> +        dev = DEVICE(&(s->uart[i]));
> +        qdev_prop_set_chr(dev, "chardev", serial_hd(i));
> +        if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
> +            return;
> +        }
> +        busdev = SYS_BUS_DEVICE(dev);
> +        sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
> +        sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
> +                                                       max78000_uart_irq[i]));
> +    }
> +
>
>      create_unimplemented_device("globalControl",    0x40000000, 0x400);
>      create_unimplemented_device("systemInterface",  0x40000400, 0x400);
> @@ -140,10 +161,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>      create_unimplemented_device("oneWireMaster",    0x4003d000, 0x1000);
>      create_unimplemented_device("semaphore",        0x4003e000, 0x1000);
>
> -    create_unimplemented_device("uart0",            0x40042000, 0x1000);
> -    create_unimplemented_device("uart1",            0x40043000, 0x1000);
> -    create_unimplemented_device("uart2",            0x40044000, 0x1000);
> -
>      create_unimplemented_device("spi1",             0x40046000, 0x2000);
>      create_unimplemented_device("trng",             0x4004d000, 0x1000);
>      create_unimplemented_device("i2s",              0x40060000, 0x1000);
> diff --git a/hw/char/Kconfig b/hw/char/Kconfig
> index 9d517f3e28..020c0a84bb 100644
> --- a/hw/char/Kconfig
> +++ b/hw/char/Kconfig
> @@ -48,6 +48,9 @@ config VIRTIO_SERIAL
>      default y
>      depends on VIRTIO
>
> +config MAX78000_UART
> +    bool
> +
>  config STM32F2XX_USART
>      bool
>
> diff --git a/hw/char/max78000_uart.c b/hw/char/max78000_uart.c
> new file mode 100644
> index 0000000000..edd39c5a8b
> --- /dev/null
> +++ b/hw/char/max78000_uart.c
> @@ -0,0 +1,263 @@
> +/*
> + * MAX78000 UART
> + *
> + * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/char/max78000_uart.h"
> +#include "hw/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/qdev-properties-system.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "trace.h"
> +
> +static int max78000_uart_can_receive(void *opaque)
> +{
> +    Max78000UartState *s = opaque;
> +    if (!(s->ctrl & UART_BCLKEN)) {
> +        return 0;
> +    }
> +    return fifo8_num_free(&s->rx_fifo);
> +}
> +
> +static void max78000_update_irq(Max78000UartState *s)
> +{
> +    int interrupt_level = 0;
> +    uint32_t rx_threshold = s->ctrl & 0xf;
> +
> +    /*
> +     * Because tx is synchronous and we should have no frame errors, the only
> +     * possible interrupt is receive fifo threshold
> +     */
> +    if ((s->int_en & UART_RX_THD) && fifo8_num_used(&s->rx_fifo) >= rx_threshold) {
> +        interrupt_level = 1;
> +        s->int_fl = s->int_fl & UART_RX_THD;
> +    } else{
> +        s->int_fl = s->int_fl & ~UART_RX_THD;
> +    }

This looks a little odd. The usual pattern for interrupt
status bits is:
 * when we notice the condition (in this case, when we receive
   a char and it puts us above the rx threshold), set the
   bit in the int_fl register
 * option A: devices where the flag bit tracks the underlying
   condition:
     - when we notice that the condition is no longer true (in this
       case, when the guest reads a char from the rx fifo and it
       puts us below the rx threshold again), clear the int_fl bit,
 * option B: devices where the flag bit latches and the guest must
   explicitly clear it:
     - no action when the condition is no longer true

   The MAX78000 UART is an "option B" device -- see section 12.5.
 * in the update_irq() function, set the interrupt if
   the int_fl bit and the int_en bit are set, otherwise clear it.
   If the bit definitions in the two registers line up, this is
   as simple as
    bool interrupt_level = s->int_fl & s->int_en;
   If they don't line up then it needs a bit more manual work.

The code you have at the moment doesn't implement the "int_fl
bits latch and must be explicitly cleared by software" behaviour.

> +    qemu_set_irq(s->irq, interrupt_level);
> +}
> +
> +static void max78000_uart_receive(void *opaque, const uint8_t *buf, int size)
> +{
> +    Max78000UartState *s = opaque;
> +
> +    if (size <= fifo8_num_free(&s->rx_fifo)) {
> +        fifo8_push_all(&s->rx_fifo, buf, size);
> +    } else{
> +        fifo8_push_all(&s->rx_fifo, buf, fifo8_num_free(&s->rx_fifo));
> +        printf("rx_fifo overrun!\n");

This should be a "can't happen" condition -- your receive
method will never be passed more bytes of data than you
said you could handle in can_receive.

Don't printf() in device code. Your options are:
 * if it's something that the device spec says is
   forbidden but a badly behaved guest can trigger,
   use qemu_log(LOG_GUEST_ERROR, ...)
 * if it's something that the device should implement but
   we don't, use qemu_log(LOG_UNIMP, ...)
 * if it's something that can't happen unless there's a bug
   in QEMU, use some kind of assert()
 * if it's nice-to-have information for debugging or for
   a user to see what the device is doing, use a tracepoint
   (trace_* functions, see docs/devel/tracing.rst)


From your register write function:

> +    case UART_INT_FL:
> +        s->int_fl = value;
> +        return;

This register's bits are W1C, i.e. write-1-to-clear.

thanks
-- PMM


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

* Re: [PATCH 4/6] MAX78000 GCR implementation
  2025-05-10  4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
@ 2025-06-16  5:13   ` Philippe Mathieu-Daudé
  2025-06-16  9:47   ` Peter Maydell
  1 sibling, 0 replies; 12+ messages in thread
From: Philippe Mathieu-Daudé @ 2025-06-16  5:13 UTC (permalink / raw)
  To: jcksn, qemu-devel; +Cc: alistair

Hi,

On 10/5/25 06:20, jcksn@duck.com wrote:
> This patch implements the Global Control Register for the MAX78000 SOC
> 
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
>   hw/arm/Kconfig                 |   1 +
>   hw/arm/max78000_soc.c          |   9 +-
>   hw/misc/Kconfig                |   3 +
>   hw/misc/max78000_gcr.c         | 285 +++++++++++++++++++++++++++++++++
>   hw/misc/meson.build            |   1 +
>   include/hw/arm/max78000_soc.h  |   2 +
>   include/hw/misc/max78000_gcr.h | 122 ++++++++++++++
>   7 files changed, 422 insertions(+), 1 deletion(-)
>   create mode 100644 hw/misc/max78000_gcr.c
>   create mode 100644 include/hw/misc/max78000_gcr.h


> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> index 6334d8b49b..7a012c6ef7 100644
> --- a/hw/arm/max78000_soc.c
> +++ b/hw/arm/max78000_soc.c
> @@ -29,6 +29,8 @@ static void max78000_soc_initfn(Object *obj)
>   
>       object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
>   
> +    object_initialize_child(obj, "gcr", &s->gcr, TYPE_MAX78000_GCR);
> +
>       for (i = 0; i < MAX78000_NUM_ICC; i++) {
>           object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
>       }
> @@ -104,6 +106,10 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>           return;
>       }
>   
> +    dev = DEVICE(&s->gcr);
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40000000);
> +
>       for (i = 0; i < MAX78000_NUM_ICC; i++) {
>           dev = DEVICE(&(s->icc[i]));
>           sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
> @@ -116,6 +122,8 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>           if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
>               return;
>           }
> +        dev->id = g_strdup_printf("uart%d", i);

This line belongs to the previous patch (UART implementation).

>           busdev = SYS_BUS_DEVICE(dev);
>           sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
>           sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
> @@ -123,7 +131,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)



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

* Re: [PATCH 4/6] MAX78000 GCR implementation
  2025-05-10  4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
  2025-06-16  5:13   ` Philippe Mathieu-Daudé
@ 2025-06-16  9:47   ` Peter Maydell
  1 sibling, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-16  9:47 UTC (permalink / raw)
  To: jcksn; +Cc: qemu-devel, Alistair Francis

On Sat, 10 May 2025 at 07:57, <jcksn@duck.com> wrote:
>
> This patch implements the Global Control Register for the MAX78000 SOC
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
>  hw/arm/Kconfig                 |   1 +
>  hw/arm/max78000_soc.c          |   9 +-
>  hw/misc/Kconfig                |   3 +
>  hw/misc/max78000_gcr.c         | 285 +++++++++++++++++++++++++++++++++
>  hw/misc/meson.build            |   1 +
>  include/hw/arm/max78000_soc.h  |   2 +
>  include/hw/misc/max78000_gcr.h | 122 ++++++++++++++
>  7 files changed, 422 insertions(+), 1 deletion(-)
>  create mode 100644 hw/misc/max78000_gcr.c
>  create mode 100644 include/hw/misc/max78000_gcr.h
>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 59450dc3cb..211b201629 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -369,6 +369,7 @@ config MAX78000_SOC
>      select ARM_V7M
>      select MAX78000_ICC
>      select MAX78000_UART
> +    select MAX78000_GCR
>
>  config RASPI
>      bool
> diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c
> index 6334d8b49b..7a012c6ef7 100644
> --- a/hw/arm/max78000_soc.c
> +++ b/hw/arm/max78000_soc.c
> @@ -29,6 +29,8 @@ static void max78000_soc_initfn(Object *obj)
>
>      object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
>
> +    object_initialize_child(obj, "gcr", &s->gcr, TYPE_MAX78000_GCR);
> +
>      for (i = 0; i < MAX78000_NUM_ICC; i++) {
>          object_initialize_child(obj, "icc[*]", &s->icc[i], TYPE_MAX78000_ICC);
>      }
> @@ -104,6 +106,10 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>          return;
>      }
>
> +    dev = DEVICE(&s->gcr);
> +    sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40000000);
> +
>      for (i = 0; i < MAX78000_NUM_ICC; i++) {
>          dev = DEVICE(&(s->icc[i]));
>          sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp);
> @@ -116,6 +122,8 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>          if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
>              return;
>          }
> +        dev->id = g_strdup_printf("uart%d", i);
> +
>          busdev = SYS_BUS_DEVICE(dev);
>          sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]);
>          sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m,
> @@ -123,7 +131,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp)
>      }
>
>
> -    create_unimplemented_device("globalControl",    0x40000000, 0x400);
>      create_unimplemented_device("systemInterface",  0x40000400, 0x400);
>      create_unimplemented_device("functionControl",  0x40000800, 0x3400);
>      create_unimplemented_device("watchdogTimer0",   0x40003000, 0x400);
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
> index 781bcf74cc..fde2266b8f 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -47,6 +47,9 @@ config A9SCU
>  config ARM11SCU
>      bool
>
> +config MAX78000_GCR
> +    bool
> +
>  config MAX78000_ICC
>      bool
>
> diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c
> new file mode 100644
> index 0000000000..657b7fc490
> --- /dev/null
> +++ b/hw/misc/max78000_gcr.c
> @@ -0,0 +1,285 @@
> +/*
> + * MAX78000 Global Control Registers
> + *
> + * Copyright (c) 2025 Jackson Donaldson <jcksn@duck.com>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qemu/log.h"
> +#include "trace.h"
> +#include "hw/irq.h"
> +#include "system/runstate.h"
> +#include "migration/vmstate.h"
> +#include "hw/misc/max78000_gcr.h"
> +
> +static void max78000_gcr_reset(DeviceState *dev)
> +{
> +    Max78000GcrState *s = MAX78000_GCR(dev);
> +    s->sysctrl = 0x21002;
> +    s->rst0 = 0;
> +    /* All clocks are always ready */
> +    s->clkctrl = 0x3e140008;
> +    s->pm = 0x3f000;
> +    s->pclkdiv = 0;
> +    s->pclkdis0 = 0xffffffff;
> +    s->memctrl = 0x5;
> +    s->memz = 0;
> +    s->sysst = 0;
> +    s->rst1 = 0;
> +    s->pckdis1 = 0xffffffff;
> +    s->eventen = 0;
> +    s->revision = 0xa1;
> +    s->sysie = 0;
> +    s->eccerr = 0;
> +    s->ecced = 0;
> +    s->eccie = 0;
> +    s->eccaddr = 0;
> +}
> +
> +static uint64_t max78000_gcr_read(void *opaque, hwaddr addr,
> +                                     unsigned int size)
> +{
> +    Max78000GcrState *s = opaque;
> +
> +    switch (addr) {
> +        case SYSCTRL:{
> +            return s->sysctrl;
> +        }
> +        case RST0:{
> +            return s->rst0;
> +        }
> +        case CLKCTRL:{
> +            return s->clkctrl;
> +        }
> +        case PM:{
> +            return s->pm;
> +        }
> +        case PCLKDIV:{
> +            return s->pclkdiv;
> +        }
> +        case PCLKDIS0:{
> +            return s->pclkdis0;
> +        }
> +        case MEMCTRL:{
> +            return s->memctrl;
> +        }
> +        case MEMZ:{
> +            return s->memz;
> +        }
> +        case SYSST:{
> +            return s->sysst;
> +        }
> +        case RST1:{
> +            return s->rst1;
> +        }
> +        case PCKDIS1:{
> +            return s->pckdis1;
> +        }
> +        case EVENTEN:{
> +            return s->eventen;
> +        }
> +        case REVISION:{
> +            return s->revision;
> +        }
> +        case SYSIE:{
> +            return s->sysie;
> +        }
> +        case ECCERR:{
> +            return s->eccerr;
> +        }
> +        case ECCED:{
> +            return s->ecced;
> +        }
> +        case ECCIE:{
> +            return s->eccie;
> +        }
> +        case ECCADDR:{
> +            return s->eccaddr;
> +        }
> +        default:{
> +            return 0;
> +        }
> +    }
> +}
> +
> +static void max78000_gcr_reset_device(const char *device_name)
> +{
> +    DeviceState *dev = qdev_find_recursive(sysbus_get_default(), device_name);
> +    if (dev) {
> +        device_cold_reset(dev);
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR, "no device %s for reset\n", device_name);
> +    }
> +}

This device shouldn't be finding the devices it needs to reset
by looking them up by name. I don't think we really have any
pre-existing examples of reset controllers in the tree, so
here's the off-the-top-of-my-head suggestion:
 * have this device define link properties with DEFINE_PROP_LINK
   for the devices it needs to reset
 * have the SoC set those link properties when it creates this
   device
 * then in the code that does the reset you have a direct pointer
   to the DeviceState* that you want to reset

> +static void max78000_gcr_write(void *opaque, hwaddr addr,
> +                       uint64_t val64, unsigned int size)
> +{
> +    Max78000GcrState *s = opaque;
> +    uint32_t val = val64;
> +    uint8_t zero[0xc000] = {0};
> +    switch (addr) {
> +        case SYSCTRL:{
> +            /* Checksum calculations always pass immediately */
> +            s->sysctrl = (val & 0x30000) | 0x1002;
> +            break;
> +        }
> +        case RST0:{
> +            if (val & SYSTEM_RESET) {
> +                qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> +            }
> +            if (val & PERIPHERAL_RESET) {
> +                /*
> +                 * Peripheral reset resets all peripherals. The CPU
> +                 * retains its state. The GPIO, watchdog timers, AoD,
> +                 * RAM retention, and general control registers (GCR),
> +                 * including the clock configuration, are unaffected.
> +                 */
> +                val = UART2_RESET | UART1_RESET | UART0_RESET |
> +                      ADC_RESET | CNN_RESET | TRNG_RESET |
> +                      RTC_RESET | I2C0_RESET | SPI1_RESET |
> +                      TMR3_RESET | TMR2_RESET | TMR1_RESET |
> +                      TMR0_RESET | WDT0_RESET | DMA_RESET;
> +            }
> +            if (val & SOFT_RESET) {
> +                /* Soft reset also resets GPIO */
> +                val = UART2_RESET | UART1_RESET | UART0_RESET |
> +                      ADC_RESET | CNN_RESET | TRNG_RESET |
> +                      RTC_RESET | I2C0_RESET | SPI1_RESET |
> +                      TMR3_RESET | TMR2_RESET | TMR1_RESET |
> +                      TMR0_RESET | GPIO1_RESET | GPIO0_RESET |
> +                      DMA_RESET;
> +            }
> +            if (val & UART2_RESET) {
> +                max78000_gcr_reset_device("uart2");
> +            }
> +            if (val & UART1_RESET) {
> +                max78000_gcr_reset_device("uart1");
> +            }
> +            if (val & UART0_RESET) {
> +                max78000_gcr_reset_device("uart0");
> +            }
> +            /* TODO: As other devices are implemented, add them here */
> +            break;
> +        }
> +        case CLKCTRL:{
> +            s->clkctrl = val | SYSCLK_RDY;
> +            break;
> +        }
> +        case PM:{
> +            s->pm = val;
> +            break;
> +        }
> +        case PCLKDIV:{
> +            s->pclkdiv = val;
> +            break;
> +        }
> +        case PCLKDIS0:{
> +            s->pclkdis0 = val;
> +            break;
> +        }
> +        case MEMCTRL:{
> +            s->memctrl = val;
> +            break;
> +        }
> +        case MEMZ:{
> +            if (val & ram0) {
> +                cpu_physical_memory_write(SYSRAM0_START, zero, 0x8000);
> +            }
> +            if (val & ram1) {
> +                cpu_physical_memory_write(SYSRAM1_START, zero, 0x8000);
> +            }
> +            if (val & ram2) {
> +                cpu_physical_memory_write(SYSRAM2_START, zero, 0xC000);
> +            }
> +            if (val & ram3) {
> +                cpu_physical_memory_write(SYSRAM3_START, zero, 0x4000);
> +            }

New devices shouldn't use cpu_physical_memory_write(), please.
Take a link property to a MemoryRegion, which you then convert
into an AddressSpace you can pass to address_space_write().

> +            break;
> +        }

thanks
-- PMM


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

* Re: [PATCH 5/6] MAX78000 TRNG Implementation
  2025-05-10  4:20 ` [PATCH 5/6] MAX78000 TRNG Implementation jcksn
@ 2025-06-16  9:56   ` Peter Maydell
  0 siblings, 0 replies; 12+ messages in thread
From: Peter Maydell @ 2025-06-16  9:56 UTC (permalink / raw)
  To: jcksn; +Cc: qemu-devel, alistair

On Sat, 10 May 2025 at 07:57, <jcksn@duck.com> wrote:
>
> This patch implements the True Random Number Generator for the MAX78000 SOC
>
> Signed-off-by: Jackson Donaldson <jcksn@duck.com>
> ---
>  hw/arm/Kconfig                  |   1 +
>  hw/arm/max78000_soc.c           |   9 ++-
>  hw/misc/Kconfig                 |   3 +
>  hw/misc/max78000_gcr.c          |   3 +
>  hw/misc/max78000_trng.c         | 115 ++++++++++++++++++++++++++++++++
>  hw/misc/meson.build             |   1 +
>  include/hw/arm/max78000_soc.h   |   2 +
>  include/hw/misc/max78000_trng.h |  35 ++++++++++
>  8 files changed, 168 insertions(+), 1 deletion(-)
>  create mode 100644 hw/misc/max78000_trng.c
>  create mode 100644 include/hw/misc/max78000_trng.h
> +static uint64_t max78000_trng_read(void *opaque, hwaddr addr,
> +                                    unsigned int size)
> +{
> +    uint8_t buf[4];
> +    Error *errp = NULL;
> +
> +    Max78000TrngState *s = opaque;
> +    switch (addr) {
> +        case CTRL:{
> +            return s->ctrl;
> +        }
> +        case STATUS:{
> +            return 1;
> +        }
> +        case DATA:{
> +            qcrypto_random_bytes(buf, 4, &errp);
> +            return *(uint32_t *)buf;
> +        }

No other code in hw/ calls qcrypto_random_bytes(). This kind
of "almost no other use of this function like this" is usually
a hint that it's not a good idea in new code. You want
qemu_guest_getrandom_nofail().

Also, instead of using a uint8_t buf[], use a
uint32_t and pass the address of that to the function:

     uint32_t data;
     qemu_guest_getrandom_nofail(&data, sizeof(data));
     return data;

That avoids the need for a cast.


Your interrupt generation code in this device doesn't look
right: the interrupt is supposed to be generated when each
new random number is ready, so in our "generation takes
zero time" model a read from DATA should provoke a new
interrupt immediately (assuming the interrupt is enabled):
you need to simulate the ready status bit going low and
then high again.

See also my comments on an earlier patch about the usual
logic being to have an update function which does
"set interrupt to (condition && enabled)".

-- PMM


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

end of thread, other threads:[~2025-06-16  9:57 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20250510042043.2056265-1-jcksn@duck.com>
2025-05-10  4:20 ` [PATCH 1/6] MAX78000: Add MAX78000FTHR Machine jcksn
2025-06-13 15:01   ` Peter Maydell
2025-05-10  4:20 ` [PATCH 2/6] MAX78000: ICC Implementation jcksn
2025-06-13 15:26   ` Peter Maydell
2025-05-10  4:20 ` [PATCH 3/6] MAX78000 UART implementation jcksn
2025-06-13 16:03   ` Peter Maydell
2025-05-10  4:20 ` [PATCH 4/6] MAX78000 GCR implementation jcksn
2025-06-16  5:13   ` Philippe Mathieu-Daudé
2025-06-16  9:47   ` Peter Maydell
2025-05-10  4:20 ` [PATCH 5/6] MAX78000 TRNG Implementation jcksn
2025-06-16  9:56   ` Peter Maydell
2025-05-10  4:20 ` [PATCH 6/6] MAX78000 AES implementation jcksn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).