NXP Public
> -----Original Message-----
> From: Gaurav Sharma <[email protected]>
> Sent: 20 September 2026 17:25
> To: [email protected]
> Cc: [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; Gaurav Sharma
> <[email protected]>
> Subject: [PATCHv3 03/10] hw/misc: Add SRC (System Reset Controller) to
> i.MX8MP
>
> From: Bernhard Beschow <[email protected]>
>
> Add emulation of the i.MX8MP System Reset Controller.
> The SRC manages reset control for various subsystems on the i.MX8MP SoC.
> SRC registers are used to start/stop/poll the M7 core, hence needed for AMP
> boot.
>
> Signed-off-by: Bernhard Beschow <[email protected]>
> Signed-off-by: Gaurav Sharma <[email protected]>
> ---
> docs/system/arm/imx8m.rst | 5 +-
> hw/arm/Kconfig | 1 +
> hw/arm/fsl-imx8mp.c | 10 +
> hw/misc/Kconfig | 3 +
> hw/misc/imx8mp_src.c | 411 +++++++++++++++++++++++++++++++++++
> hw/misc/meson.build | 1 +
> hw/misc/trace-events | 5 +
> include/hw/arm/fsl-imx8mp.h | 2 +
> include/hw/misc/imx8mp_src.h | 52 +++++
> 9 files changed, 488 insertions(+), 2 deletions(-) create mode 100644
> hw/misc/imx8mp_src.c create mode 100644 include/hw/misc/imx8mp_src.h
>
> diff --git a/docs/system/arm/imx8m.rst b/docs/system/arm/imx8m.rst index
> 7813469154..38f4e31443 100644
> --- a/docs/system/arm/imx8m.rst
> +++ b/docs/system/arm/imx8m.rst
> @@ -26,8 +26,9 @@ following devices:
> * 6 General Purpose Timers
> * Secure Non-Volatile Storage (SNVS) including an RTC
> * Clock Tree
> - * General Power Controller (GPC)
> - * General Purpose Register (GPR)
> + * General Power Controller
> + * General Purpose Register
> + * System Reset Controller
>
> Boot options
> ------------
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 7b7378110c..2ab57b7c53
> 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -596,6 +596,7 @@ config FSL_IMX8MP
> select FSL_IMX8MP_CCM
> select FSL_IMX8MP_GPC
> select FSL_IMX8MP_GPR
> + select FSL_IMX8MP_SRC
> select IMX
> select IMX_FEC
> select IMX_I2C
> diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index
> 0c61582a27..274b44794a 100644
> --- a/hw/arm/fsl-imx8mp.c
> +++ b/hw/arm/fsl-imx8mp.c
> @@ -208,6 +208,8 @@ static void fsl_imx8mp_init(Object *obj)
>
> object_initialize_child(obj, "gpr", &s->gpr, TYPE_IMX8MP_GPR);
>
> + object_initialize_child(obj, "src", &s->src, TYPE_IMX8MP_SRC);
> +
> 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)
> qdev_get_gpio_in(gicdev, serial_table[i].irq));
> }
>
> + /* SRC */
> + if (!sysbus_realize(SYS_BUS_DEVICE(&s->src), errp)) {
> + return;
> + }
> + sysbus_mmio_map(SYS_BUS_DEVICE(&s->src), 0,
> + fsl_imx8mp_memmap[FSL_IMX8MP_SRC].addr);
> +
> /* GPC */
> if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpc), errp)) {
> return;
> @@ -750,6 +759,7 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error
> **errp)
> case FSL_IMX8MP_PCIE_PHY1:
> case FSL_IMX8MP_RAM:
> case FSL_IMX8MP_SNVS_HP:
> + case FSL_IMX8MP_SRC:
> case FSL_IMX8MP_UART1 ... FSL_IMX8MP_UART4:
> case FSL_IMX8MP_USB1 ... FSL_IMX8MP_USB2:
> case FSL_IMX8MP_USDHC1 ... FSL_IMX8MP_USDHC3:
> diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 5c2945200e..d9e904ae2f
> 100644
> --- a/hw/misc/Kconfig
> +++ b/hw/misc/Kconfig
> @@ -107,6 +107,9 @@ config FSL_IMX8MP_GPC config FSL_IMX8MP_GPR
> bool
>
> +config FSL_IMX8MP_SRC
> + bool
> +
> config STM32_RCC
> bool
>
> diff --git a/hw/misc/imx8mp_src.c b/hw/misc/imx8mp_src.c new file mode
> 100644 index 0000000000..ee540d83a6
> --- /dev/null
> +++ b/hw/misc/imx8mp_src.c
> @@ -0,0 +1,411 @@
> +/*
> + * i.MX 8M Plus System Reset Controller
> + *
> + * Copyright (c) 2025 Bernhard Beschow <[email protected]>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#include "qemu/osdep.h"
> +#include "hw/misc/imx8mp_src.h"
> +#include "hw/misc/imx8mp_gpr.h"
> +#include "hw/core/qdev-properties.h"
> +#include "hw/core/resettable.h"
> +#include "hw/core/registerfields.h"
> +#include "qapi/error.h"
> +#include "migration/vmstate.h"
> +#include "qemu/log.h"
> +#include "system/address-spaces.h"
> +#include "target/arm/arm-powerctl.h"
> +#include "target/arm/cpu.h"
> +#include "trace.h"
> +
> +REG32(SRC_SCR, 0x0000)
> +
> +REG32(SRC_A53RCR0, 0x0004)
> +
> +REG32(SRC_A53RCR1, 0x0008)
> +
> +REG32(SRC_M7RCR, 0x000c)
> +REG32(SRC_SUPERMIX_RCR, 0x0018)
> +REG32(SRC_AUDIOMIX_RCR, 0x001c)
> +REG32(SRC_USBPHY1_RCR, 0x0020)
> +REG32(SRC_USBPHY2_RCR, 0x0024)
> +REG32(SRC_MLMIX_RCR, 0x0028)
> +REG32(SRC_PCIEPHY_RCR, 0x002c)
> +REG32(SRC_HDMI_RCR, 0x0030)
> +REG32(SRC_MEDIA_RCR, 0x0034)
> +REG32(SRC_GPU2D_RCR, 0x0038)
> +REG32(SRC_GPU3D_RCR, 0x003c)
> +REG32(SRC_GPU_RCR, 0x0040)
> +REG32(SRC_VPU_RCR, 0x0044)
> +REG32(SRC_VPU_G1_RCR, 0x0048)
> +REG32(SRC_VPU_G2_RCR, 0x004c)
> +REG32(SRC_VPUVC8KE_RCR, 0x0050)
> +REG32(SRC_NOC_RCR, 0x0054)
> +REG32(SRC_SBMR1, 0x0058)
> +REG32(SRC_SRSR, 0x005c)
> +REG32(SRC_SISR, 0x0068)
> +REG32(SRC_SIMR, 0x006c)
> +
> +REG32(SRC_SBMR2, 0x0070)
> +
> +REG32(SRC_GPR1, 0x0074)
> +REG32(SRC_GPR2, 0x0078)
> +REG32(SRC_GPR3, 0x007c)
> +REG32(SRC_GPR4, 0x0080)
> +REG32(SRC_GPR5, 0x0084)
> +REG32(SRC_GPR6, 0x0088)
> +REG32(SRC_GPR7, 0x008c)
> +REG32(SRC_GPR8, 0x0090)
> +REG32(SRC_GPR9, 0x0094)
> +REG32(SRC_GPR10, 0x0098)
> +REG32(SRC_DDRC_RCR, 0x1000)
> +REG32(SRC_HDMIPHY_RCR, 0x1008)
> +REG32(SRC_MIPIPHY1_RCR, 0x100c)
> +REG32(SRC_MIPIPHY2_RCR, 0x1010)
> +REG32(SRC_HSIO_RCR, 0x1014)
> +REG32(SRC_MEDIAISPDWP_RCR, 0x1018)
> +
> +static const char *fsl_imx8mp_src_reg_name(uint32_t reg) {
> + switch (reg) {
> + case R_SRC_SCR:
> + return " (SRC_SCR)";
> + case R_SRC_A53RCR0:
> + return " (SRC_A53RCR0)";
> + case R_SRC_A53RCR1:
> + return " (SRC_A53RCR1)";
> + case R_SRC_M7RCR:
> + return " (SRC_M7RCR)";
> + case R_SRC_SUPERMIX_RCR:
> + return " (SRC_SUPERMIX_RCR)";
> + case R_SRC_AUDIOMIX_RCR:
> + return " (SRC_AUDIOMIX_RCR)";
> + case R_SRC_USBPHY1_RCR:
> + return " (SRC_USBPHY1_RCR)";
> + case R_SRC_USBPHY2_RCR:
> + return " (SRC_USBPHY2_RCR)";
> + case R_SRC_MLMIX_RCR:
> + return " (SRC_MLMIX_RCR)";
> + case R_SRC_PCIEPHY_RCR:
> + return " (SRC_PCIEPHY_RCR)";
> + case R_SRC_HDMI_RCR:
> + return " (SRC_HDMI_RCR)";
> + case R_SRC_MEDIA_RCR:
> + return " (SRC_MEDIA_RCR)";
> + case R_SRC_GPU2D_RCR:
> + return " (SRC_GPU2D_RCR)";
> + case R_SRC_GPU3D_RCR:
> + return " (SRC_GPU3D_RCR)";
> + case R_SRC_GPU_RCR:
> + return " (SRC_GPU_RCR)";
> + case R_SRC_VPU_RCR:
> + return " (SRC_VPU_RCR)";
> + case R_SRC_VPU_G1_RCR:
> + return " (SRC_VPU_G1_RCR)";
> + case R_SRC_VPU_G2_RCR:
> + return " (SRC_VPU_G2_RCR)";
> + case R_SRC_VPUVC8KE_RCR:
> + return " (SRC_VPUVC8KE_RCR)";
> + case R_SRC_NOC_RCR:
> + return " (SRC_NOC_RCR)";
> + case R_SRC_SBMR1:
> + return " (SRC_SBMR1)";
> + case R_SRC_SRSR:
> + return " (SRC_SRSR)";
> + case R_SRC_SISR:
> + return " (SRC_SISR)";
> + case R_SRC_SIMR:
> + return " (SRC_SIMR)";
> + case R_SRC_SBMR2:
> + return " (SRC_SBMR2)";
> + case R_SRC_GPR1:
> + return " (SRC_GPR1)";
> + case R_SRC_GPR2:
> + return " (SRC_GPR2)";
> + case R_SRC_GPR3:
> + return " (SRC_GPR3)";
> + case R_SRC_GPR4:
> + return " (SRC_GPR4)";
> + case R_SRC_GPR5:
> + return " (SRC_GPR5)";
> + case R_SRC_GPR6:
> + return " (SRC_GPR6)";
> + case R_SRC_GPR7:
> + return " (SRC_GPR7)";
> + case R_SRC_GPR8:
> + return " (SRC_GPR8)";
> + case R_SRC_GPR9:
> + return " (SRC_GPR9)";
> + case R_SRC_GPR10:
> + return " (SRC_GPR10)";
> + case R_SRC_DDRC_RCR:
> + return " (SRC_DDRC_RCR)";
> + case R_SRC_HDMIPHY_RCR:
> + return " (SRC_HDMIPHY_RCR)";
> + case R_SRC_MIPIPHY1_RCR:
> + return " (SRC_MIPIPHY1_RCR)";
> + case R_SRC_MIPIPHY2_RCR:
> + return " (SRC_MIPIPHY2_RCR)";
> + case R_SRC_HSIO_RCR:
> + return " (SRC_HSIO_RCR)";
> + case R_SRC_MEDIAISPDWP_RCR:
> + return " (SRC_MEDIAISPDWP_RCR)";
> + default:
> + return " (reserved)";
> + }
> +}
> +
> +static uint32_t imx8mp_src_cm7_vbase(FslImx8mpSrcState *s) {
> + uint32_t gpr6 = s->gpr ? s->gpr->gpr[IOMUXC_GPR_GPR6] : 0;
> + uint32_t vbase = gpr6 & 0xffffff80;
> +
> + if (vbase == 0) {
> + uint32_t pc_le = 0, pc;
> +
> + address_space_read(&address_space_memory,
> + IMX8MP_ITCM_BASE + 4,
> + MEMTXATTRS_UNSPECIFIED, &pc_le, sizeof(pc_le));
> + pc = le32_to_cpu(pc_le);
> + if (pc != 0 && pc != UINT32_MAX && (pc & 1)) {
> + vbase = 0; /* valid Thumb vector — boot from ITCM alias at 0x0 */
> + } else {
> + vbase = s->cm7_vector_base;
> + }
> + }
> + return vbase;
> +}
We don't need this. I will remove it in v4 :-
After sending v3, I investigated the ITCM probe in imx8mp_src_cm7_vbase()
further by testing on real i.MX8MP hardware and found it should be removed.
The correct behaviour is: GPR_M7_INITVTOR (IOMUXC_GPR_GPR6[31:7]) is wired
directly to the M7 VTOR at reset. The SoC hardware loads this register into the
M7's Vector Table Offset Register before the M7 executes its first instruction.
On the MMIO path (fsl,imx8mp-cm7-mmio), the driver never writes GPR6.
VTOR=0x0 always, and the M7 always boots from ITCM. Firmware that
wants to execute from DDR must plant its reset vector in ITCM via a paddr=0x0
ELF segment. This is exactly what NXP BSP rpmsg firmware does — verified
by inspecting the ELF program headers:
LOAD: paddr=0x00000000 filesz=0x8 → MSP=0x80400000 PC=0x800004dd
remoteproc writes this 8-byte segment to ITCM (via ATT entry
{0x0, 0x7e0000, 0x20000, ATT_OWN|ATT_IOMEM}) before releasing CPUWAIT.
So, it will be simplified to just do a "return gpr6 & 0xffffff80" after
fetching gpr6 value from
IOMUXC_GPR_GPR6
> +
> +static void imx8mp_src_cm7_reset_run(CPUState *cpu, run_on_cpu_data
> +data) {
> + FslImx8mpSrcState *s = data.host_ptr;
> + ARMCPU *m7 = ARM_CPU(cpu);
> +
> + m7->init_nsvtor = imx8mp_src_cm7_vbase(s);
> + arm_set_cpu_on_and_reset(arm_cpu_mp_affinity(m7));
> +}
> +
> +static void imx8mp_src_cm7_off(CPUState *cpu, run_on_cpu_data data) {
> + arm_set_cpu_off(arm_cpu_mp_affinity(ARM_CPU(cpu)));
> +}
> +
> +static void imx8mp_src_cm7_reset_request(FslImx8mpSrcState *s) {
> + if (!s->cm7_cpu) {
> + return;
> + }
> + if (s->cm7_cpuwait) {
> + async_run_on_cpu(CPU(s->cm7_cpu), imx8mp_src_cm7_off,
> + RUN_ON_CPU_HOST_PTR(s));
> + } else {
> + async_run_on_cpu(CPU(s->cm7_cpu), imx8mp_src_cm7_reset_run,
> + RUN_ON_CPU_HOST_PTR(s));
> + }
> +}
> +
> +/*
> + * React to a CPUWAIT level change.
> + * level=1: CPUWAIT cleared (run); level=0: CPUWAIT set (halt).
> + */
> +static void imx8mp_src_cm7_set_cpuwait(FslImx8mpSrcState *s, bool run)
> +{
> + if (!s->cm7_cpu) {
> + return;
> + }
> + if (run) {
> + CPUState *cpu = CPU(s->cm7_cpu);
> + if (cpu->halted) {
> + async_run_on_cpu(cpu, imx8mp_src_cm7_reset_run,
> + RUN_ON_CPU_HOST_PTR(s));
> + }
> + } else {
> + async_run_on_cpu(CPU(s->cm7_cpu), imx8mp_src_cm7_off,
> + RUN_ON_CPU_HOST_PTR(s));
> + }
> +}
> +
> +static uint64_t fsl_imx8mp_src_read(void *opaque, hwaddr offset,
> + unsigned size) {
> + FslImx8mpSrcState *s = opaque;
> + const uint32_t reg = offset / 4;
> + uint32_t value = 0;
> +
> + if (reg < FSL_IMX8MP_SRC_NUM_REGS) {
> + value = s->regs[reg];
> + } else {
> + qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented register at
> offset 0x%"
> + HWADDR_PRIx "\n", TYPE_IMX8MP_SRC, __func__,
> + offset);
> + }
> +
> + trace_fsl_imx8mp_src_read(offset, fsl_imx8mp_src_reg_name(reg),
> + value);
> +
> + return value;
> +}
> +
> +static void fsl_imx8mp_src_write(void *opaque, hwaddr offset, uint64_t
> value,
> + unsigned size) {
> + FslImx8mpSrcState *s = opaque;
> + const uint32_t reg = offset / 4;
> +
> + if (reg >= FSL_IMX8MP_SRC_NUM_REGS) {
> + qemu_log_mask(LOG_UNIMP, "[%s]%s: Unimplemented register at
> offset 0x%"
> + HWADDR_PRIx "\n", TYPE_IMX8MP_SRC, __func__,
> + offset);
> + goto done;
> + }
> +
> + switch (reg) {
> + case R_SRC_M7RCR: {
> + uint32_t old_value = s->regs[reg];
> + uint32_t new_value = (uint32_t)value;
> + uint32_t changed;
> + bool reset_pulse = false;
> +
> + /*
> + * SW_M7C_RST (bit1) is self-clearing on real HW: writing 1 triggers
> + * an M7 core-reset pulse and the bit reads back 0.
> + */
> + if (new_value & SRC_M7RCR_SW_M7C_RST) {
> + reset_pulse = true;
> + new_value &= ~SRC_M7RCR_SW_M7C_RST;
> + }
> +
> + s->regs[reg] = new_value;
> + changed = old_value ^ new_value;
> +
> + if (reset_pulse) {
> + /* Self-clearing core-reset pulse -> reset (honoring CPUWAIT). */
> + imx8mp_src_cm7_reset_request(s);
> + break;
> + }
> +
> + if (changed & SRC_M7RCR_SW_M7C_NON_SCLR_RST) {
> + bool held = new_value & SRC_M7RCR_SW_M7C_NON_SCLR_RST;
> + /* NON_SCLR latch: 1 = hold in reset (off); 0 = release. */
> + if (held) {
> + if (s->cm7_cpu) {
> + async_run_on_cpu(CPU(s->cm7_cpu), imx8mp_src_cm7_off,
> + RUN_ON_CPU_HOST_PTR(s));
> + }
> + } else {
> + imx8mp_src_cm7_reset_request(s);
> + }
> + }
> + break;
> + }
> + default:
> + s->regs[reg] = value;
> + break;
> + }
> +
> +done:
> +
> + trace_fsl_imx8mp_src_write(offset, fsl_imx8mp_src_reg_name(reg),
> +value); }
> +
> +static const struct MemoryRegionOps imx8mp_src_ops = {
> + .read = fsl_imx8mp_src_read,
> + .write = fsl_imx8mp_src_write,
> + .endianness = DEVICE_LITTLE_ENDIAN,
> + .valid = {
> + /*
> + * Our device would not work correctly if the guest was doing
> + * unaligned access. This might not be a limitation on the real
> + * device but in practice there is no reason for a guest to access
> + * this device unaligned.
> + */
> + .min_access_size = 4,
> + .max_access_size = 4,
> + .unaligned = false,
> + },
> +};
> +
> +static void imx8mp_src_reset_enter(Object *obj, ResetType type) {
> + FslImx8mpSrcState *s = IMX8MP_SRC(obj);
> + FslImx8mpSrcClass *sc = IMX8MP_SRC_GET_CLASS(obj);
> +
> + if (sc->parent_phases.enter) {
> + sc->parent_phases.enter(obj, type);
> + }
> +
> + memset(s->regs, 0, sizeof(s->regs));
> +
> + /*
> + * Default CM7 STOP state for Linux imx-rproc MMIO mode detection.
> + */
> + s->regs[R_SRC_M7RCR] = 0x000000A8;
> +}
> +
> +static void imx8mp_src_cm7_cpuwait_handler(void *opaque, int n, int
> +level) {
> + FslImx8mpSrcState *s = opaque;
> +
> + s->cm7_cpuwait = !level;
> + imx8mp_src_cm7_set_cpuwait(s, (bool)level); }
> +
> +static void imx8mp_src_realize(DeviceState *dev, Error **errp) {
> + FslImx8mpSrcState *s = IMX8MP_SRC(dev);
> +
> + memory_region_init_io(&s->iomem, OBJECT(dev), &imx8mp_src_ops, s,
> + TYPE_IMX8MP_SRC, 0x1000);
> + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
> + qdev_init_gpio_in_named(dev, imx8mp_src_cm7_cpuwait_handler,
> +"cm7-cpuwait", 1); }
> +
> +static const VMStateDescription imx8mp_src_vmstate = {
> + .name = TYPE_IMX8MP_SRC,
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .fields = (const VMStateField[]) {
> + VMSTATE_UINT32_ARRAY(regs, FslImx8mpSrcState,
> FSL_IMX8MP_SRC_NUM_REGS),
> + VMSTATE_END_OF_LIST()
> + },
> +};
> +
> +static const Property imx8mp_src_properties[] = {
> + DEFINE_PROP_UINT32("cm7-vector-base", FslImx8mpSrcState,
> cm7_vector_base,
> + 0x80000000),
> + DEFINE_PROP_LINK("cm7-cpu", FslImx8mpSrcState, cm7_cpu,
> + TYPE_ARM_CPU, ARMCPU *),
> + DEFINE_PROP_LINK("gpr", FslImx8mpSrcState, gpr,
> + TYPE_IMX8MP_GPR, IMX8MPGPRState *), };
> +
> +static void imx8mp_src_class_init(ObjectClass *klass, const void *data)
> +{
> + DeviceClass *dc = DEVICE_CLASS(klass);
> + ResettableClass *rc = RESETTABLE_CLASS(klass);
> +
> + FslImx8mpSrcClass *sc = IMX8MP_SRC_CLASS(klass);
> +
> + resettable_class_set_parent_phases(rc,
> + imx8mp_src_reset_enter,
> + NULL,
> + NULL,
> + &sc->parent_phases);
> + dc->realize = imx8mp_src_realize;
> + dc->vmsd = &imx8mp_src_vmstate;
> + device_class_set_props(dc, imx8mp_src_properties);
> + dc->desc = "i.MX 8M Plus System Reset Controller"; }
> +
> +static const TypeInfo imx8mp_src_types[] = {
> + {
> + .name = TYPE_IMX8MP_SRC,
> + .parent = TYPE_SYS_BUS_DEVICE,
> + .instance_size = sizeof(FslImx8mpSrcState),
> + .class_size = sizeof(FslImx8mpSrcClass),
> + .class_init = imx8mp_src_class_init,
> + },
> +};
> +
> +DEFINE_TYPES(imx8mp_src_types)
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build index
> 5eda094d34..79954586e8 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -64,6 +64,7 @@ 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_FSL_IMX8MP_SRC', if_true:
> +files('imx8mp_src.c'))
> system_ss.add(when: 'CONFIG_IMX', if_true: files(
> 'imx25_ccm.c',
> 'imx31_ccm.c',
> diff --git a/hw/misc/trace-events b/hw/misc/trace-events index
> 6db4934dc5..6239200f37 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -131,6 +131,11 @@ imx7_gpr_write(uint64_t offset, uint64_t value)
> "addr 0x%08" PRIx64 "value 0x%08 imx7_snvs_read(uint64_t offset, uint64_t
> value, unsigned size) "i.MX SNVS read: offset 0x%08" PRIx64 " value 0x%08"
> PRIx64 " size %u"
> imx7_snvs_write(uint64_t offset, uint64_t value, unsigned size) "i.MX SNVS
> write: offset 0x%08" PRIx64 " value 0x%08" PRIx64 " size %u"
>
> +# imx8mp_src.c
> +fsl_imx8mp_src_read(uint64_t offset, const char *reg_name, uint32_t
> +value) "[0x%" PRIx64 "%s] <- 0x%" PRIx32 fsl_imx8mp_src_write(uint64_t
> +offset, const char *reg_name, uint64_t value) "[0x%" PRIx64 "%s] <-
> +0x%" PRIx64 fsl_imx8mp_src_clear_reset_bit(const char *reg_name,
> +uint32_t value) "reg[%s] <= 0x%" PRIx32
> +
> # mos6522.c
> mos6522_set_counter(int index, unsigned int val) "T%d.counter=%d"
> mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta)
> "latch=%d counter=0x%"PRIx64 " delta_next=0x%"PRIx64 diff --git
> a/include/hw/arm/fsl-imx8mp.h b/include/hw/arm/fsl-imx8mp.h index
> 8f07b2a270..87c83c3f0e 100644
> --- a/include/hw/arm/fsl-imx8mp.h
> +++ b/include/hw/arm/fsl-imx8mp.h
> @@ -19,6 +19,7 @@
> #include "hw/misc/imx8mp_ccm.h"
> #include "hw/misc/imx8mp_gpc.h"
> #include "hw/misc/imx8mp_gpr.h"
> +#include "hw/misc/imx8mp_src.h"
> #include "hw/net/flexcan.h"
> #include "hw/net/imx_fec.h"
> #include "hw/core/or-irq.h"
> @@ -66,6 +67,7 @@ struct FslImx8mpState {
> IMX8MPAnalogState analog;
> IMX7SNVSState snvs;
> IMXSPIState spi[FSL_IMX8MP_NUM_ECSPIS];
> + FslImx8mpSrcState src;
> IMXI2CState i2c[FSL_IMX8MP_NUM_I2CS];
> IMXSerialState uart[FSL_IMX8MP_NUM_UARTS];
> IMXFECState enet;
> diff --git a/include/hw/misc/imx8mp_src.h b/include/hw/misc/imx8mp_src.h
> new file mode 100644 index 0000000000..d888410539
> --- /dev/null
> +++ b/include/hw/misc/imx8mp_src.h
> @@ -0,0 +1,52 @@
> +/*
> + * i.MX 8M Plus System Reset Controller
> + *
> + * Copyright (c) 2025 Bernhard Beschow <[email protected]>
> + *
> + * SPDX-License-Identifier: GPL-2.0-or-later */
> +
> +#ifndef FSL_IMX8MP_SRC_H
> +#define FSL_IMX8MP_SRC_H
> +
> +#include "hw/core/resettable.h"
> +#include "hw/core/sysbus.h"
> +#include "system/memory.h"
> +#include "qom/object.h"
> +#include "target/arm/cpu-qom.h"
> +
> +typedef struct IMX8MPGPRState IMX8MPGPRState;
> +
> +#define TYPE_IMX8MP_SRC "fsl-imx8mp-src"
> +OBJECT_DECLARE_TYPE(FslImx8mpSrcState, FslImx8mpSrcClass,
> IMX8MP_SRC)
> +
> +struct FslImx8mpSrcClass {
> + SysBusDeviceClass parent_class;
> + ResettablePhases parent_phases;
> +};
> +
> +/*
> + * ITCM base in the A53/system-bus view.
> + * Used by SRC to probe for a valid M7 reset vector when GPR6 == 0.
> + */
> +#define IMX8MP_ITCM_BASE 0x007E0000U
This was added to support ITCM probe, but we don't need it now, as per
my explanation earlier for imx8mp_src_cm7_vbase. Will be removed for
v4.
> +
> +#define FSL_IMX8MP_SRC_NUM_REGS (0x100 / 4)
> +
> +#define SRC_M7RCR_SW_M7C_NON_SCLR_RST (1u << 0) /* latching core
> reset */
> +#define SRC_M7RCR_SW_M7C_RST (1u << 1) /* self-clearing pulse */
> +
> +struct FslImx8mpSrcState {
> + SysBusDevice parent_obj;
> +
> + MemoryRegion iomem;
> +
> + uint32_t regs[FSL_IMX8MP_SRC_NUM_REGS];
> +
> + ARMCPU *cm7_cpu;
> + IMX8MPGPRState *gpr;
> + bool cm7_cpuwait;
> + uint32_t cm7_vector_base;
> +};
> +
> +#endif /* FSL_IMX8MP_SRC_H */
> --
> 2.34.1