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

Reply via email to