On Sat, 10 May 2025 at 07:57, <jc...@duck.com> wrote: > > This patch implements the Global Control Register for the MAX78000 SOC > > Signed-off-by: Jackson Donaldson <jc...@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 <jc...@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