Add IOMUXC General Purpose Registers (GPR) IP and wire it into the i.MX8MP SoC 
model.
The GPR block provides system-level configuration registers on the i.MX8MP SoC.
It provides general purpose control bits and Cortex-M7 CPUWAIT gate needed for 
AMP boot

Signed-off-by: Gaurav Sharma <[email protected]>
---
 docs/system/arm/imx8m.rst    |   1 +
 hw/arm/Kconfig               |   1 +
 hw/arm/fsl-imx8mp.c          |  10 +++
 hw/misc/Kconfig              |   3 +
 hw/misc/imx8mp_gpr.c         | 129 +++++++++++++++++++++++++++++++++++
 hw/misc/meson.build          |   1 +
 include/hw/arm/fsl-imx8mp.h  |   2 +
 include/hw/misc/imx8mp_gpr.h |  56 +++++++++++++++
 8 files changed, 203 insertions(+)
 create mode 100644 hw/misc/imx8mp_gpr.c
 create mode 100644 include/hw/misc/imx8mp_gpr.h

diff --git a/docs/system/arm/imx8m.rst b/docs/system/arm/imx8m.rst
index 00325c2413..1e110bc810 100644
--- a/docs/system/arm/imx8m.rst
+++ b/docs/system/arm/imx8m.rst
@@ -26,6 +26,7 @@ following devices:
  * Secure Non-Volatile Storage (SNVS) including an RTC
  * Clock Tree
  * General Power Controller (GPC)
+ * General Purpose Register (GPR)
 
 Boot options
 ------------
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 83293ff8a7..9e4b71264a 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -601,6 +601,7 @@ config FSL_IMX8MP
     select FSL_IMX8MP_ANALOG
     select FSL_IMX8MP_CCM
     select FSL_IMX8MP_GPC
+    select FSL_IMX8MP_GPR
     select IMX
     select IMX_FEC
     select IMX_I2C
diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c
index d68d816bf1..c4aafbdc1c 100644
--- a/hw/arm/fsl-imx8mp.c
+++ b/hw/arm/fsl-imx8mp.c
@@ -206,6 +206,8 @@ static void fsl_imx8mp_init(Object *obj)
 
     object_initialize_child(obj, "gpc", &s->gpc, TYPE_IMX8MP_GPC);
 
+    object_initialize_child(obj, "gpr", &s->gpr, TYPE_IMX8MP_GPR);
+
     for (i = 0; i < FSL_IMX8MP_NUM_UARTS; i++) {
         g_autofree char *name = g_strdup_printf("uart%d", i + 1);
         object_initialize_child(obj, name, &s->uart[i], TYPE_IMX_SERIAL);
@@ -431,6 +433,13 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error 
**errp)
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpc), 0,
                     fsl_imx8mp_memmap[FSL_IMX8MP_GPC].addr);
 
+    /* GPR */
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpr), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0,
+                    fsl_imx8mp_memmap[FSL_IMX8MP_IOMUXC_GPR].addr);
+
     /* GPTs */
     object_property_set_int(OBJECT(&s->gpt5_gpt6_irq), "num-lines", 2,
                             &error_abort);
@@ -701,6 +710,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error 
**errp)
         case FSL_IMX8MP_ECSPI1 ... FSL_IMX8MP_ECSPI3:
         case FSL_IMX8MP_ENET1:
         case FSL_IMX8MP_I2C1 ... FSL_IMX8MP_I2C6:
+        case FSL_IMX8MP_IOMUXC_GPR:
         case FSL_IMX8MP_OCRAM:
         case FSL_IMX8MP_PCIE1:
         case FSL_IMX8MP_PCIE_PHY1:
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index e707a40f8c..7464788d82 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -104,6 +104,9 @@ config FSL_IMX8MP_CCM
 config FSL_IMX8MP_GPC
     bool
 
+config FSL_IMX8MP_GPR
+    bool
+
 config STM32_RCC
     bool
 
diff --git a/hw/misc/imx8mp_gpr.c b/hw/misc/imx8mp_gpr.c
new file mode 100644
index 0000000000..04d11cc1f9
--- /dev/null
+++ b/hw/misc/imx8mp_gpr.c
@@ -0,0 +1,129 @@
+/*
+ * i.MX 8M Plus IOMUXC GPR
+ *
+ * Copyright (c) 2026, NXP Semiconductors
+ * Author: Gaurav Sharma <[email protected]>
+
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/misc/imx8mp_gpr.h"
+#include "hw/core/irq.h"
+#include "migration/vmstate.h"
+
+#define IMX8MP_GPR22_OFF     0x58
+#define IMX8MP_GPR22_CM7_CPUWAIT_BIT  (1u << 0)
+
+static inline void imx8mp_gpr_update_cm7_run(IMX8MPGPRState *s, uint32_t gpr22)
+{
+    /* CPUWAIT=0 => run, CPUWAIT=1 => stop */
+    bool run = ((gpr22 & IMX8MP_GPR22_CM7_CPUWAIT_BIT) == 0);
+    qemu_set_irq(s->cm7_run_irq, run);
+}
+
+static uint64_t imx8mp_gpr_read(void *opaque, hwaddr offset, unsigned size)
+{
+    IMX8MPGPRState *s = opaque;
+
+    return s->gpr[offset >> 2];
+}
+
+static void imx8mp_gpr_write(void *opaque, hwaddr offset,
+                              uint64_t value, unsigned size)
+{
+    IMX8MPGPRState *s = opaque;
+
+    s->gpr[offset >> 2] = (uint32_t)value;
+
+    /* Watch GPR22 bit0 (CM7_CPUWAIT) transitions */
+    if (offset == IMX8MP_GPR22_OFF) {
+        imx8mp_gpr_update_cm7_run(s, s->gpr[IMX8MP_GPR22_OFF >> 2]);
+    }
+
+}
+
+static const MemoryRegionOps imx8mp_gpr_ops = {
+    .read = imx8mp_gpr_read,
+    .write = imx8mp_gpr_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    .unaligned = false,
+    },
+};
+
+static void imx8mp_gpr_reset(DeviceState *dev)
+{
+    IMX8MPGPRState *s = IMX8MP_GPR(dev);
+    memset(s->gpr, 0, sizeof(s->gpr));
+
+    s->gpr[IOMUXC_GPR_GPR0] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR1] = 0x00010000;
+    s->gpr[IOMUXC_GPR_GPR2] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR3] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR4] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR5] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR6] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR7] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR8] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR9] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR10] = 0x00000008;
+    s->gpr[IOMUXC_GPR_GPR11] = 0x00000200;
+    s->gpr[IOMUXC_GPR_GPR12] = 0x00004000;
+    s->gpr[IOMUXC_GPR_GPR13] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR14] = 0x03494000;
+    s->gpr[IOMUXC_GPR_GPR15] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR16] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR17] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR18] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR19] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR20] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR21] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR22] = 0x00000001;
+    s->gpr[IOMUXC_GPR_GPR23] = 0x00000000;
+    s->gpr[IOMUXC_GPR_GPR24] = 0x00000000;
+
+    /* Drive cm7_run_irq output to match reset value of CPUWAIT */
+    imx8mp_gpr_update_cm7_run(s, s->gpr[IMX8MP_GPR22_OFF >> 2]);
+}
+
+static const VMStateDescription imx8mp_gpr_vmstate = {
+    .name = TYPE_IMX8MP_GPR,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(gpr, IMX8MPGPRState, IOMUXC_GPR_MAX),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void imx8mp_gpr_init(Object *obj)
+{
+    IMX8MPGPRState *s = IMX8MP_GPR(obj);
+    memory_region_init_io(&s->mmio, obj, &imx8mp_gpr_ops, s,
+                          TYPE_IMX8MP_GPR, sizeof(s->gpr));
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->cm7_run_irq);
+}
+
+static void imx8mp_gpr_class_init(ObjectClass *oc, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+    device_class_set_legacy_reset(dc, imx8mp_gpr_reset);
+    dc->vmsd = &imx8mp_gpr_vmstate;
+    dc->desc = "i.MX8MP IOMUXC GPR";
+}
+
+static const TypeInfo imx8mp_gpr_info[] = {
+    {
+        .name = TYPE_IMX8MP_GPR,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(IMX8MPGPRState),
+        .instance_init = imx8mp_gpr_init,
+        .class_init = imx8mp_gpr_class_init,
+    }
+};
+
+DEFINE_TYPES(imx8mp_gpr_info);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 512a539c6a..67dd1a98e5 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -60,6 +60,7 @@ system_ss.add(when: 'CONFIG_EXYNOS4', if_true: 
files('exynos4210_pmu.c', 'exynos
 system_ss.add(when: 'CONFIG_FSL_IMX8MP_ANALOG', if_true: 
files('imx8mp_analog.c'))
 system_ss.add(when: 'CONFIG_FSL_IMX8MP_CCM', if_true: files('imx8mp_ccm.c'))
 system_ss.add(when: 'CONFIG_FSL_IMX8MP_GPC', if_true: files('imx8mp_gpc.c'))
+system_ss.add(when: 'CONFIG_FSL_IMX8MP_GPR', if_true: files('imx8mp_gpr.c'))
 system_ss.add(when: 'CONFIG_IMX', if_true: files(
   'imx25_ccm.c',
   'imx31_ccm.c',
diff --git a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h
index 51630cfd3b..b3aca1f692 100644
--- a/include/hw/arm/fsl-imx8mp.h
+++ b/include/hw/arm/fsl-imx8mp.h
@@ -18,6 +18,7 @@
 #include "hw/misc/imx8mp_analog.h"
 #include "hw/misc/imx8mp_ccm.h"
 #include "hw/misc/imx8mp_gpc.h"
+#include "hw/misc/imx8mp_gpr.h"
 #include "hw/net/imx_fec.h"
 #include "hw/core/or-irq.h"
 #include "hw/pci-host/designware.h"
@@ -56,6 +57,7 @@ struct FslImx8mpState {
     ARMCPU             cpu[FSL_IMX8MP_NUM_CPUS];
     GICv3State         gic;
     IMX8MPGPCState     gpc;
+    IMX8MPGPRState     gpr;
     IMXGPTState        gpt[FSL_IMX8MP_NUM_GPTS];
     IMXGPIOState       gpio[FSL_IMX8MP_NUM_GPIOS];
     IMX8MPCCMState     ccm;
diff --git a/include/hw/misc/imx8mp_gpr.h b/include/hw/misc/imx8mp_gpr.h
new file mode 100644
index 0000000000..4a0c7d8583
--- /dev/null
+++ b/include/hw/misc/imx8mp_gpr.h
@@ -0,0 +1,56 @@
+/*
+ * i.MX 8M Plus IOMUXC GPR
+ *
+ * Copyright (c) 2026, NXP Semiconductors
+ * Author: Gaurav Sharma <[email protected]>
+
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+
+#ifndef IMX8MP_GPR_H
+#define IMX8MP_GPR_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_IMX8MP_GPR "imx8mp.gpr"
+OBJECT_DECLARE_SIMPLE_TYPE(IMX8MPGPRState, IMX8MP_GPR)
+
+enum IMX8MPGPRRegisters {
+    IOMUXC_GPR_GPR0  = 0x000 / 4,
+    IOMUXC_GPR_GPR1  = 0x004 / 4,
+    IOMUXC_GPR_GPR2  = 0x008 / 4,
+    IOMUXC_GPR_GPR3  = 0x00C / 4,
+    IOMUXC_GPR_GPR4  = 0x010 / 4,
+    IOMUXC_GPR_GPR5  = 0x014 / 4,
+    IOMUXC_GPR_GPR6  = 0x018 / 4,
+    IOMUXC_GPR_GPR7  = 0x01C / 4,
+    IOMUXC_GPR_GPR8  = 0x020 / 4,
+    IOMUXC_GPR_GPR9  = 0x024 / 4,
+    IOMUXC_GPR_GPR10 = 0x028 / 4,
+    IOMUXC_GPR_GPR11 = 0x02C / 4,
+    IOMUXC_GPR_GPR12 = 0x030 / 4,
+    IOMUXC_GPR_GPR13 = 0x034 / 4,
+    IOMUXC_GPR_GPR14 = 0x038 / 4,
+    IOMUXC_GPR_GPR15 = 0x03C / 4,
+    IOMUXC_GPR_GPR16 = 0x040 / 4,
+    IOMUXC_GPR_GPR17 = 0x044 / 4,
+    IOMUXC_GPR_GPR18 = 0x048 / 4,
+    IOMUXC_GPR_GPR19 = 0x04C / 4,
+    IOMUXC_GPR_GPR20 = 0x050 / 4,
+    IOMUXC_GPR_GPR21 = 0x054 / 4,
+    IOMUXC_GPR_GPR22 = 0x058 / 4,
+    IOMUXC_GPR_GPR23 = 0x05C / 4,
+    IOMUXC_GPR_GPR24 = 0x060 / 4,
+    IOMUXC_GPR_MAX,
+};
+
+struct IMX8MPGPRState {
+    SysBusDevice parent_obj;
+    MemoryRegion mmio;
+    qemu_irq cm7_run_irq;
+    uint32_t gpr[IOMUXC_GPR_MAX];
+};
+
+#endif /* IMX8MP_GPR_H */
-- 
2.34.1


Reply via email to