qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Add support for the NXP i.MX8MM EVK (Evaluation Kit) board
@ 2025-11-08 12:27 Gaurav Sharma
  2025-11-08 19:04 ` Peter Maydell
  0 siblings, 1 reply; 4+ messages in thread
From: Gaurav Sharma @ 2025-11-08 12:27 UTC (permalink / raw)
  To: qemu-devel@nongnu.org; +Cc: pbonzini@redhat.com, peter.maydell@linaro.org

[-- Attachment #1: Type: text/plain, Size: 66081 bytes --]

From: Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
Date: Sat, 8 Nov 2025 17:12:56 +0530
Subject: [PATCH] Add support for the NXP i.MX8MM EVK (Evaluation Kit) board
including: - i.MX8MM SoC implementation with CPU, memory, and peripherals -
Board-specific configuration and device tree - Clock Control Module (CCM) for
i.MX8MM - Analog mixed-signal controller - Updated GPT timer support for
i.MX8MM - Documentation for the new board

Signed-off-by: Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>"
---
MAINTAINERS                     |  12 +
docs/system/arm/imx8mm-evk.rst  |  69 ++++
hw/arm/Kconfig                  |  24 ++
hw/arm/fsl-imx8mm.c             | 675 ++++++++++++++++++++++++++++++++
hw/arm/imx8mm-evk.c             | 103 +++++
hw/arm/meson.build              |   2 +
hw/misc/Kconfig                 |   6 +
hw/misc/imx8mm_analog.c         | 160 ++++++++
hw/misc/imx8mm_ccm.c            | 175 +++++++++
hw/misc/meson.build             |   3 +
hw/timer/imx_gpt.c              |  28 ++
include/hw/arm/fsl-imx8mm.h     | 242 ++++++++++++
include/hw/misc/imx8mm_analog.h |  81 ++++
include/hw/misc/imx8mm_ccm.h    |  30 ++
include/hw/timer/imx_gpt.h      |   3 +
15 files changed, 1613 insertions(+)
create mode 100644 docs/system/arm/imx8mm-evk.rst
create mode 100644 hw/arm/fsl-imx8mm.c
create mode 100644 hw/arm/imx8mm-evk.c
create mode 100644 hw/misc/imx8mm_analog.c
create mode 100644 hw/misc/imx8mm_ccm.c
create mode 100644 include/hw/arm/fsl-imx8mm.h
create mode 100644 include/hw/misc/imx8mm_analog.h
create mode 100644 include/hw/misc/imx8mm_ccm.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 667acd933c..c743cd4fe1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -897,6 +897,18 @@ F: docs/system/arm/imx8mp-evk.rst
F: tests/functional/aarch64/test_imx8mp_evk.py
F: tests/qtest/rs5c372-test.c
+8MMINILPD4-EVK / i.MX8MM
+M: Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
+S: Maintained
+F: hw/arm/fsl-imx8mm.c
+F: hw/arm/imx8mm-evk.c
+F: hw/misc/imx8mm_analog.c
+F: hw/misc/imx8mm_ccm.c
+F: include/hw/arm/fsl-imx8mm.h
+F: include/hw/misc/imx8mm_analog.h
+F: include/hw/misc/imx8mm_ccm.h
+F: docs/system/arm/imx8mm-evk.rst
+
MPS2 / MPS3
M: Peter Maydell peter.maydell@linaro.org<mailto:peter.maydell@linaro.org>
L: qemu-arm@nongnu.org<mailto:qemu-arm@nongnu.org>
diff --git a/docs/system/arm/imx8mm-evk.rst b/docs/system/arm/imx8mm-evk.rst
new file mode 100644
index 0000000000..53105b8f3b
--- /dev/null
+++ b/docs/system/arm/imx8mm-evk.rst
@@ -0,0 +1,69 @@
+SPDX-License-Identifier: GPL-2.0-or-later
+
+NXP i.MX 8MM Evaluation Kit (``imx8mm-evk``)
+================================================
+
+The ``imx8mm-evk`` machine models the i.MX 8MM Evaluation Kit, based on an
+i.MX 8MM SoC.
+
+Supported devices
+-----------------
+
+The ``imx8mm-evk`` machine implements the following devices:
+
+ * Up to 4 Cortex-A53 cores
+ * Generic Interrupt Controller (GICv3)
+ * 4 UARTs
+ * 3 USDHC Storage Controllers
+ * 1 Designware PCI Express Controller
+ * 1 Ethernet Controller
+ * 2 Designware USB 3 Controllers
+ * 5 GPIO Controllers
+ * 4 I2C Controllers
+ * 3 SPI Controllers
+ * 3 Watchdogs
+ * 6 General Purpose Timers
+ * Secure Non-Volatile Storage (SNVS) including an RTC
+ * Clock Tree
+
+Boot options
+------------
+
+The ``imx8mm-evk`` machine can start a Linux kernel directly using the standard
+``-kernel`` functionality.
+
+Direct Linux Kernel Boot
+''''''''''''''''''''''''
+
+Probably the easiest way to get started with a whole Linux system on the machine
+is to generate an image with Buildroot. Version 2024.11.1 is tested at the time
+of writing and involves two steps. First run the following commands in the
+toplevel directory of the Buildroot source tree:
+
+.. code-block:: bash
+
+  $ make freescale_imx8mmevk_defconfig
+  $ make
+
+Once finished successfully there is an ``output/image`` subfolder. Navigate into
+it and resize the SD card image to a power of two:
+
+.. code-block:: bash
+
+  $ qemu-img resize sdcard.img 256M
+
+Now that everything is prepared the machine can be started as follows:
+
+.. code-block:: bash
+
+  $ qemu-system-aarch64 -M imx8mm-evk -smp 4 -m 3G \
+      -display none -serial null -serial stdio \
+      -kernel Image \
+      -dtb imx8mm-evk.dtb \
+      -append "root=/dev/mmcblk2p2" \
+      -drive file=sdcard.img,if=sd,bus=2,format=raw,id=mmcblk2
+
+Known Bugs
+----------
+
+Currently CSI and DSI is not supported as part of this emulation so it is advised to disable csi and dsi in the device-tree before booting the kernel.
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index b44b85f436..b5e37724fa 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -625,6 +625,30 @@ config FSL_IMX8MP_EVK
     depends on TCG && AARCH64
     select FSL_IMX8MP
+config FSL_IMX8MM
+    bool
+    imply I2C_DEVICES
+    imply PCI_DEVICES
+    select ARM_GIC
+    select FSL_IMX8MM_ANALOG
+    select FSL_IMX8MM_CCM
+    select IMX
+    select IMX_FEC
+    select IMX_I2C
+    select OR_IRQ
+    select PCI_EXPRESS_DESIGNWARE
+    select PCI_EXPRESS_FSL_IMX8M_PHY
+    select SDHCI
+    select UNIMP
+    select USB_DWC3
+    select WDT_IMX2
+
+config FSL_IMX8MM_EVK
+    bool
+    default y
+    depends on TCG && AARCH64
+    select FSL_IMX8MM
+
config ARM_SMMUV3
     bool
diff --git a/hw/arm/fsl-imx8mm.c b/hw/arm/fsl-imx8mm.c
new file mode 100644
index 0000000000..cb8bfbd351
--- /dev/null
+++ b/hw/arm/fsl-imx8mm.c
@@ -0,0 +1,675 @@
+/*
+ * i.MX 8MM SoC Implementation
+ *
+ * Based on hw/arm/fsl-imx6.c
+ *
+ * Copyright (c) 2025, Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "system/address-spaces.h"
+#include "hw/arm/bsa.h"
+#include "hw/arm/fsl-imx8mm.h"
+#include "hw/intc/arm_gicv3.h"
+#include "hw/misc/unimp.h"
+#include "hw/boards.h"
+#include "system/system.h"
+#include "target/arm/cpu-qom.h"
+#include "qapi/error.h"
+#include "qobject/qlist.h"
+
+static const struct {
+    hwaddr addr;
+    size_t size;
+    const char *name;
+} fsl_imx8mm_memmap[] = {
+    [FSL_IMX8MM_RAM] = { FSL_IMX8MM_RAM_START, FSL_IMX8MM_RAM_SIZE_MAX, "ram" },
+    [FSL_IMX8MM_DDR_PHY_BROADCAST] = { 0x3dc00000, 4 * MiB, "ddr_phy_broadcast" },
+    [FSL_IMX8MM_DDR_PERF_MON] = { 0x3d800000, 4 * MiB, "ddr_perf_mon" },
+    [FSL_IMX8MM_DDR_CTL] = { 0x3d400000, 4 * MiB, "ddr_ctl" },
+    [FSL_IMX8MM_DDR_PHY] = { 0x3c000000, 16 * MiB, "ddr_phy" },
+    [FSL_IMX8MM_GIC_DIST] = { 0x38800000, 512 * KiB, "gic_dist" },
+    [FSL_IMX8MM_GIC_REDIST] = { 0x38880000, 512 * KiB, "gic_redist" },
+    [FSL_IMX8MM_VPU] = { 0x38340000, 2 * MiB, "vpu" },
+    [FSL_IMX8MM_VPU_BLK_CTRL] = { 0x38330000, 2 * MiB, "vpu_blk_ctrl" },
+    [FSL_IMX8MM_VPU_G2_DECODER] = { 0x38310000, 1 * MiB, "vpu_g2_decoder" },
+    [FSL_IMX8MM_VPU_G1_DECODER] = { 0x38300000, 1 * MiB, "vpu_g1_decoder" },
+    [FSL_IMX8MM_USB2_OTG] = { 0x32e50200, 0x200, "usb2_otg" },
+    [FSL_IMX8MM_USB2] = { 0x32e50000, 0x200, "usb2" },
+    [FSL_IMX8MM_USB1_OTG] = { 0x32e40200, 0x200, "usb1_otg" },
+    [FSL_IMX8MM_USB1] = { 0x32e40000, 0x200, "usb1" },
+    [FSL_IMX8MM_GPU2D] = { 0x38000000, 64 * KiB, "gpu2d" },
+    [FSL_IMX8MM_QSPI1_RX_BUFFER] = { 0x34000000, 32 * MiB, "qspi1_rx_buffer" },
+    [FSL_IMX8MM_PCIE1] = { 0x33800000, 4 * MiB, "pcie1" },
+    [FSL_IMX8MM_QSPI1_TX_BUFFER] = { 0x33008000, 32 * KiB, "qspi1_tx_buffer" },
+    [FSL_IMX8MM_APBH_DMA] = { 0x33000000, 32 * KiB, "apbh_dma" },
+
+    /* AIPS-4 Begin */
+    [FSL_IMX8MM_TZASC] = { 0x32f80000, 64 * KiB, "tzasc" },
+    [FSL_IMX8MM_PCIE_PHY1] = { 0x32f00000, 64 * KiB, "pcie_phy1" },
+    [FSL_IMX8MM_MEDIA_BLK_CTL] = { 0x32e28000, 256, "media_blk_ctl" },
+    [FSL_IMX8MM_LCDIF] = { 0x32e00000, 64 * KiB, "lcdif" },
+    [FSL_IMX8MM_MIPI_DSI] = { 0x32e10000, 64 * KiB, "mipi_dsi" },
+    [FSL_IMX8MM_MIPI_CSI] = { 0x32e30000, 64 * KiB, "mipi_csi" },
+    [FSL_IMX8MM_AIPS4_CONFIGURATION] = { 0x32df0000, 64 * KiB, "aips4_configuration" },
+    /* AIPS-4 End */
+
+    [FSL_IMX8MM_INTERCONNECT] = { 0x32700000, 1 * MiB, "interconnect" },
+
+    /* AIPS-3 Begin */
+    [FSL_IMX8MM_ENET1] = { 0x30be0000, 64 * KiB, "enet1" },
+    [FSL_IMX8MM_SDMA1] = { 0x30bd0000, 64 * KiB, "sdma1" },
+    [FSL_IMX8MM_QSPI] = { 0x30bb0000, 64 * KiB, "qspi" },
+    [FSL_IMX8MM_USDHC3] = { 0x30b60000, 64 * KiB, "usdhc3" },
+    [FSL_IMX8MM_USDHC2] = { 0x30b50000, 64 * KiB, "usdhc2" },
+    [FSL_IMX8MM_USDHC1] = { 0x30b40000, 64 * KiB, "usdhc1" },
+    [FSL_IMX8MM_SEMAPHORE_HS] = { 0x30ac0000, 64 * KiB, "semaphore_hs" },
+    [FSL_IMX8MM_MU_B] = { 0x30ab0000, 64 * KiB, "mu_b" },
+    [FSL_IMX8MM_MU_A] = { 0x30aa0000, 64 * KiB, "mu_a" },
+    [FSL_IMX8MM_UART4] = { 0x30a60000, 64 * KiB, "uart4" },
+    [FSL_IMX8MM_I2C4] = { 0x30a50000, 64 * KiB, "i2c4" },
+    [FSL_IMX8MM_I2C3] = { 0x30a40000, 64 * KiB, "i2c3" },
+    [FSL_IMX8MM_I2C2] = { 0x30a30000, 64 * KiB, "i2c2" },
+    [FSL_IMX8MM_I2C1] = { 0x30a20000, 64 * KiB, "i2c1" },
+    [FSL_IMX8MM_AIPS3_CONFIGURATION] = { 0x309f0000, 64 * KiB, "aips3_configuration" },
+    [FSL_IMX8MM_CAAM] = { 0x30900000, 256 * KiB, "caam" },
+    [FSL_IMX8MM_SPBA1] = { 0x308f0000, 64 * KiB, "spba1" },
+    [FSL_IMX8MM_UART2] = { 0x30890000, 64 * KiB, "uart2" },
+    [FSL_IMX8MM_UART3] = { 0x30880000, 64 * KiB, "uart3" },
+    [FSL_IMX8MM_UART1] = { 0x30860000, 64 * KiB, "uart1" },
+    [FSL_IMX8MM_ECSPI3] = { 0x30840000, 64 * KiB, "ecspi3" },
+    [FSL_IMX8MM_ECSPI2] = { 0x30830000, 64 * KiB, "ecspi2" },
+    [FSL_IMX8MM_ECSPI1] = { 0x30820000, 64 * KiB, "ecspi1" },
+    /* AIPS-3 End */
+
+    /* AIPS-2 Begin */
+    [FSL_IMX8MM_QOSC] = { 0x307f0000, 64 * KiB, "qosc" },
+    [FSL_IMX8MM_PERFMON2] = { 0x307d0000, 64 * KiB, "perfmon2" },
+    [FSL_IMX8MM_PERFMON1] = { 0x307c0000, 64 * KiB, "perfmon1" },
+    [FSL_IMX8MM_GPT4] = { 0x30700000, 64 * KiB, "gpt4" },
+    [FSL_IMX8MM_GPT5] = { 0x306f0000, 64 * KiB, "gpt5" },
+    [FSL_IMX8MM_GPT6] = { 0x306e0000, 64 * KiB, "gpt6" },
+    [FSL_IMX8MM_SYSCNT_CTRL] = { 0x306c0000, 64 * KiB, "syscnt_ctrl" },
+    [FSL_IMX8MM_SYSCNT_CMP] = { 0x306b0000, 64 * KiB, "syscnt_cmp" },
+    [FSL_IMX8MM_SYSCNT_RD] = { 0x306a0000, 64 * KiB, "syscnt_rd" },
+    [FSL_IMX8MM_PWM4] = { 0x30690000, 64 * KiB, "pwm4" },
+    [FSL_IMX8MM_PWM3] = { 0x30680000, 64 * KiB, "pwm3" },
+    [FSL_IMX8MM_PWM2] = { 0x30670000, 64 * KiB, "pwm2" },
+    [FSL_IMX8MM_PWM1] = { 0x30660000, 64 * KiB, "pwm1" },
+    [FSL_IMX8MM_AIPS2_CONFIGURATION] = { 0x305f0000, 64 * KiB, "aips2_configuration" },
+    /* AIPS-2 End */
+
+    /* AIPS-1 Begin */
+    [FSL_IMX8MM_CSU] = { 0x303e0000, 64 * KiB, "csu" },
+    [FSL_IMX8MM_RDC] = { 0x303d0000, 64 * KiB, "rdc" },
+    [FSL_IMX8MM_SEMAPHORE2] = { 0x303c0000, 64 * KiB, "semaphore2" },
+    [FSL_IMX8MM_SEMAPHORE1] = { 0x303b0000, 64 * KiB, "semaphore1" },
+    [FSL_IMX8MM_GPC] = { 0x303a0000, 64 * KiB, "gpc" },
+    [FSL_IMX8MM_SRC] = { 0x30390000, 64 * KiB, "src" },
+    [FSL_IMX8MM_CCM] = { 0x30380000, 64 * KiB, "ccm" },
+    [FSL_IMX8MM_SNVS_HP] = { 0x30370000, 64 * KiB, "snvs_hp" },
+    [FSL_IMX8MM_ANA_PLL] = { 0x30360000, 64 * KiB, "ana_pll" },
+    [FSL_IMX8MM_OCOTP_CTRL] = { 0x30350000, 64 * KiB, "ocotp_ctrl" },
+    [FSL_IMX8MM_IOMUXC_GPR] = { 0x30340000, 64 * KiB, "iomuxc_gpr" },
+    [FSL_IMX8MM_IOMUXC] = { 0x30330000, 64 * KiB, "iomuxc" },
+    [FSL_IMX8MM_GPT3] = { 0x302f0000, 64 * KiB, "gpt3" },
+    [FSL_IMX8MM_GPT2] = { 0x302e0000, 64 * KiB, "gpt2" },
+    [FSL_IMX8MM_GPT1] = { 0x302d0000, 64 * KiB, "gpt1" },
+    [FSL_IMX8MM_SDMA2] = { 0x302c0000, 64 * KiB, "sdma2" },
+    [FSL_IMX8MM_SDMA3] = { 0x302b0000, 64 * KiB, "sdma3" },
+    [FSL_IMX8MM_WDOG3] = { 0x302a0000, 64 * KiB, "wdog3" },
+    [FSL_IMX8MM_WDOG2] = { 0x30290000, 64 * KiB, "wdog2" },
+    [FSL_IMX8MM_WDOG1] = { 0x30280000, 64 * KiB, "wdog1" },
+    [FSL_IMX8MM_ANA_OSC] = { 0x30270000, 64 * KiB, "ana_osc" },
+    [FSL_IMX8MM_ANA_TSENSOR] = { 0x30260000, 64 * KiB, "ana_tsensor" },
+    [FSL_IMX8MM_GPIO5] = { 0x30240000, 64 * KiB, "gpio5" },
+    [FSL_IMX8MM_GPIO4] = { 0x30230000, 64 * KiB, "gpio4" },
+    [FSL_IMX8MM_GPIO3] = { 0x30220000, 64 * KiB, "gpio3" },
+    [FSL_IMX8MM_GPIO2] = { 0x30210000, 64 * KiB, "gpio2" },
+    [FSL_IMX8MM_GPIO1] = { 0x30200000, 64 * KiB, "gpio1" },
+    [FSL_IMX8MM_AIPS1_CONFIGURATION] = { 0x301f0000, 64 * KiB, "aips1_configuration" },
+    [FSL_IMX8MM_SAI6] = { 0x30060000, 64 * KiB, "sai6" },
+    [FSL_IMX8MM_SAI5] = { 0x30050000, 64 * KiB, "sai5" },
+    [FSL_IMX8MM_SAI3] = { 0x30030000, 64 * KiB, "sai3" },
+    [FSL_IMX8MM_SAI2] = { 0x30020000, 64 * KiB, "sai2" },
+    [FSL_IMX8MM_SAI1] = { 0x30010000, 64 * KiB, "sai1" },
+
+    /* AIPS-1 End */
+
+    [FSL_IMX8MM_A53_DAP] = { 0x28000000, 16 * MiB, "a53_dap" },
+    [FSL_IMX8MM_PCIE1_MEM] = { 0x18000000, 128 * MiB, "pcie1_mem" },
+    [FSL_IMX8MM_QSPI_MEM] = { 0x08000000, 256 * MiB, "qspi_mem" },
+    [FSL_IMX8MM_OCRAM] = { 0x00900000, 256 * KiB, "ocram" },
+    [FSL_IMX8MM_TCM_DTCM] = { 0x00800000, 128 * KiB, "tcm_dtcm" },
+    [FSL_IMX8MM_TCM_ITCM] = { 0x007e0000, 128 * KiB, "tcm_itcm" },
+    [FSL_IMX8MM_OCRAM_S] = { 0x00180000, 32 * KiB, "ocram_s" },
+    [FSL_IMX8MM_CAAM_MEM] = { 0x00100000, 32 * KiB, "caam_mem" },
+    [FSL_IMX8MM_BOOT_ROM_PROTECTED] = { 0x0003f000, 4 * KiB, "boot_rom_protected" },
+    [FSL_IMX8MM_BOOT_ROM] = { 0x00000000, 252 * KiB, "boot_rom" },
+};
+
+static void fsl_imx8mm_init(Object *obj)
+{
+    MachineState *ms = MACHINE(qdev_get_machine());
+    FslImx8mmState *s = FSL_IMX8MM(obj);
+    int i;
+
+    for (i = 0; i < MIN(ms->smp.cpus, FSL_IMX8MM_NUM_CPUS); i++) {
+        g_autofree char *name = g_strdup_printf("cpu%d", i);
+        object_initialize_child(obj, name, &s->cpu[i],
+                                ARM_CPU_TYPE_NAME("cortex-a53"));
+    }
+
+    object_initialize_child(obj, "gic", &s->gic, TYPE_ARM_GICV3);
+
+    object_initialize_child(obj, "ccm", &s->ccm, TYPE_IMX8MM_CCM);
+
+    object_initialize_child(obj, "analog", &s->analog, TYPE_IMX8MM_ANALOG);
+
+    object_initialize_child(obj, "snvs", &s->snvs, TYPE_IMX7_SNVS);
+
+    for (i = 0; i < FSL_IMX8MM_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);
+    }
+
+    for (i = 0; i < FSL_IMX8MM_NUM_GPTS; i++) {
+        g_autofree char *name = g_strdup_printf("gpt%d", i + 1);
+        object_initialize_child(obj, name, &s->gpt[i], TYPE_IMX8MM_GPT);
+    }
+    object_initialize_child(obj, "gpt5-gpt6-irq", &s->gpt5_gpt6_irq,
+                            TYPE_OR_IRQ);
+
+    for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) {
+        g_autofree char *name = g_strdup_printf("i2c%d", i + 1);
+        object_initialize_child(obj, name, &s->i2c[i], TYPE_IMX_I2C);
+    }
+
+    for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) {
+        g_autofree char *name = g_strdup_printf("gpio%d", i + 1);
+        object_initialize_child(obj, name, &s->gpio[i], TYPE_IMX_GPIO);
+    }
+
+    for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) {
+        g_autofree char *name = g_strdup_printf("usdhc%d", i + 1);
+        object_initialize_child(obj, name, &s->usdhc[i], TYPE_IMX_USDHC);
+    }
+
+    for (i = 0; i < FSL_IMX8MM_NUM_USBS; i++) {
+        g_autofree char *name = g_strdup_printf("usb%d", i);
+        object_initialize_child(obj, name, &s->usb[i], TYPE_USB_DWC3);
+    }
+
+    for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) {
+        g_autofree char *name = g_strdup_printf("spi%d", i + 1);
+        object_initialize_child(obj, name, &s->spi[i], TYPE_IMX_SPI);
+    }
+
+    for (i = 0; i < FSL_IMX8MM_NUM_WDTS; i++) {
+        g_autofree char *name = g_strdup_printf("wdt%d", i);
+        object_initialize_child(obj, name, &s->wdt[i], TYPE_IMX2_WDT);
+    }
+
+    object_initialize_child(obj, "eth0", &s->enet, TYPE_IMX_ENET);
+
+    object_initialize_child(obj, "pcie", &s->pcie, TYPE_DESIGNWARE_PCIE_HOST);
+    object_initialize_child(obj, "pcie_phy", &s->pcie_phy,
+                            TYPE_FSL_IMX8M_PCIE_PHY);
+}
+
+static void fsl_imx8mm_realize(DeviceState *dev, Error **errp)
+{
+    MachineState *ms = MACHINE(qdev_get_machine());
+    FslImx8mmState *s = FSL_IMX8MM(dev);
+    DeviceState *gicdev = DEVICE(&s->gic);
+    int i;
+
+    if (ms->smp.cpus > FSL_IMX8MM_NUM_CPUS) {
+        error_setg(errp, "%s: Only %d CPUs are supported (%d requested)",
+                   TYPE_FSL_IMX8MM, FSL_IMX8MM_NUM_CPUS, ms->smp.cpus);
+        return;
+    }
+
+    /* CPUs */
+    for (i = 0; i < ms->smp.cpus; i++) {
+        /* On uniprocessor, the CBAR is set to 0 */
+        if (ms->smp.cpus > 1) {
+            object_property_set_int(OBJECT(&s->cpu[i]), "reset-cbar",
+                                    fsl_imx8mm_memmap[FSL_IMX8MM_GIC_DIST].addr,
+                                    &error_abort);
+        }
+
+        /*
+         * CNTFID0 base frequency in Hz of system counter
+         */
+        object_property_set_int(OBJECT(&s->cpu[i]), "cntfrq", 8000000,
+                                &error_abort);
+
+        if (i) {
+            /*
+             * Secondary CPUs start in powered-down state (and can be
+             * powered up via the SRC system reset controller)
+             */
+            object_property_set_bool(OBJECT(&s->cpu[i]), "start-powered-off",
+                                     true, &error_abort);
+        }
+
+        if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
+            return;
+        }
+    }
+
+    /* GIC */
+    {
+        SysBusDevice *gicsbd = SYS_BUS_DEVICE(&s->gic);
+        QList *redist_region_count;
+
+        qdev_prop_set_uint32(gicdev, "num-cpu", ms->smp.cpus);
+        qdev_prop_set_uint32(gicdev, "num-irq",
+                             FSL_IMX8MM_NUM_IRQS + GIC_INTERNAL);
+        redist_region_count = qlist_new();
+        qlist_append_int(redist_region_count, ms->smp.cpus);
+        qdev_prop_set_array(gicdev, "redist-region-count", redist_region_count);
+        object_property_set_link(OBJECT(&s->gic), "sysmem",
+                                 OBJECT(get_system_memory()), &error_fatal);
+        if (!sysbus_realize(gicsbd, errp)) {
+            return;
+        }
+        sysbus_mmio_map(gicsbd, 0, fsl_imx8mm_memmap[FSL_IMX8MM_GIC_DIST].addr);
+        sysbus_mmio_map(gicsbd, 1, fsl_imx8mm_memmap[FSL_IMX8MM_GIC_REDIST].addr);
+
+        /*
+         * Wire the outputs from each CPU's generic timer and the GICv3
+         * maintenance interrupt signal to the appropriate GIC PPI inputs, and
+         * the GIC's IRQ/FIQ interrupt outputs to the CPU's inputs.
+         */
+        for (i = 0; i < ms->smp.cpus; i++) {
+            DeviceState *cpudev = DEVICE(&s->cpu[i]);
+            int intidbase = FSL_IMX8MM_NUM_IRQS + i * GIC_INTERNAL;
+            qemu_irq irq;
+
+            /*
+             * Mapping from the output timer irq lines from the CPU to the
+             * GIC PPI inputs.
+             */
+            static const int timer_irqs[] = {
+                [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ,
+                [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ,
+                [GTIMER_HYP]  = ARCH_TIMER_NS_EL2_IRQ,
+                [GTIMER_SEC]  = ARCH_TIMER_S_EL1_IRQ,
+            };
+
+            for (int j = 0; j < ARRAY_SIZE(timer_irqs); j++) {
+                irq = qdev_get_gpio_in(gicdev, intidbase + timer_irqs[j]);
+                qdev_connect_gpio_out(cpudev, j, irq);
+            }
+
+            irq = qdev_get_gpio_in(gicdev, intidbase + ARCH_GIC_MAINT_IRQ);
+            qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
+                                        0, irq);
+
+            irq = qdev_get_gpio_in(gicdev, intidbase + VIRTUAL_PMU_IRQ);
+            qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, irq);
+
+            sysbus_connect_irq(gicsbd, i,
+                               qdev_get_gpio_in(cpudev, ARM_CPU_IRQ));
+            sysbus_connect_irq(gicsbd, i + ms->smp.cpus,
+                               qdev_get_gpio_in(cpudev, ARM_CPU_FIQ));
+            sysbus_connect_irq(gicsbd, i + 2 * ms->smp.cpus,
+                               qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ));
+            sysbus_connect_irq(gicsbd, i + 3 * ms->smp.cpus,
+                               qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ));
+        }
+    }
+
+    /* CCM */
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->ccm), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0,
+                    fsl_imx8mm_memmap[FSL_IMX8MM_CCM].addr);
+
+    /* Analog */
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->analog), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->analog), 0,
+                    fsl_imx8mm_memmap[FSL_IMX8MM_ANA_PLL].addr);
+
+    /* UARTs */
+    for (i = 0; i < FSL_IMX8MM_NUM_UARTS; i++) {
+        struct {
+            hwaddr addr;
+            unsigned int irq;
+        } serial_table[FSL_IMX8MM_NUM_UARTS] = {
+            { fsl_imx8mm_memmap[FSL_IMX8MM_UART1].addr, FSL_IMX8MM_UART1_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_UART2].addr, FSL_IMX8MM_UART2_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_UART3].addr, FSL_IMX8MM_UART3_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_UART4].addr, FSL_IMX8MM_UART4_IRQ },
+        };
+
+        qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i));
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) {
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, serial_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0,
+                           qdev_get_gpio_in(gicdev, serial_table[i].irq));
+    }
+
+    /* GPTs */
+    object_property_set_int(OBJECT(&s->gpt5_gpt6_irq), "num-lines", 2,
+                            &error_abort);
+    if (!qdev_realize(DEVICE(&s->gpt5_gpt6_irq), NULL, errp)) {
+        return;
+    }
+
+    qdev_connect_gpio_out(DEVICE(&s->gpt5_gpt6_irq), 0,
+                          qdev_get_gpio_in(gicdev, FSL_IMX8MM_GPT5_GPT6_IRQ));
+
+    for (i = 0; i < FSL_IMX8MM_NUM_GPTS; i++) {
+        hwaddr gpt_addrs[FSL_IMX8MM_NUM_GPTS] = {
+            fsl_imx8mm_memmap[FSL_IMX8MM_GPT1].addr,
+            fsl_imx8mm_memmap[FSL_IMX8MM_GPT2].addr,
+            fsl_imx8mm_memmap[FSL_IMX8MM_GPT3].addr,
+            fsl_imx8mm_memmap[FSL_IMX8MM_GPT4].addr,
+            fsl_imx8mm_memmap[FSL_IMX8MM_GPT5].addr,
+            fsl_imx8mm_memmap[FSL_IMX8MM_GPT6].addr,
+        };
+
+        s->gpt[i].ccm = IMX_CCM(&s->ccm);
+
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpt[i]), errp)) {
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, gpt_addrs[i]);
+
+        if (i < FSL_IMX8MM_NUM_GPTS - 2) {
+            static const unsigned int gpt_irqs[FSL_IMX8MM_NUM_GPTS - 2] = {
+                FSL_IMX8MM_GPT1_IRQ,
+                FSL_IMX8MM_GPT2_IRQ,
+                FSL_IMX8MM_GPT3_IRQ,
+                FSL_IMX8MM_GPT4_IRQ,
+            };
+
+            sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0,
+                               qdev_get_gpio_in(gicdev, gpt_irqs[i]));
+        } else {
+            int irq = i - FSL_IMX8MM_NUM_GPTS + 2;
+
+            sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0,
+                               qdev_get_gpio_in(DEVICE(&s->gpt5_gpt6_irq), irq));
+        }
+    }
+
+    /* I2Cs */
+    for (i = 0; i < FSL_IMX8MM_NUM_I2CS; i++) {
+        struct {
+            hwaddr addr;
+            unsigned int irq;
+        } i2c_table[FSL_IMX8MM_NUM_I2CS] = {
+            { fsl_imx8mm_memmap[FSL_IMX8MM_I2C1].addr, FSL_IMX8MM_I2C1_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_I2C2].addr, FSL_IMX8MM_I2C2_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_I2C3].addr, FSL_IMX8MM_I2C3_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_I2C4].addr, FSL_IMX8MM_I2C4_IRQ },
+        };
+
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->i2c[i]), errp)) {
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, i2c_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0,
+                           qdev_get_gpio_in(gicdev, i2c_table[i].irq));
+    }
+
+    /* GPIOs */
+    for (i = 0; i < FSL_IMX8MM_NUM_GPIOS; i++) {
+        struct {
+            hwaddr addr;
+            unsigned int irq_low;
+            unsigned int irq_high;
+        } gpio_table[FSL_IMX8MM_NUM_GPIOS] = {
+            {
+                fsl_imx8mm_memmap[FSL_IMX8MM_GPIO1].addr,
+                FSL_IMX8MM_GPIO1_LOW_IRQ,
+                FSL_IMX8MM_GPIO1_HIGH_IRQ
+            },
+            {
+                fsl_imx8mm_memmap[FSL_IMX8MM_GPIO2].addr,
+                FSL_IMX8MM_GPIO2_LOW_IRQ,
+                FSL_IMX8MM_GPIO2_HIGH_IRQ
+            },
+            {
+                fsl_imx8mm_memmap[FSL_IMX8MM_GPIO3].addr,
+                FSL_IMX8MM_GPIO3_LOW_IRQ,
+                FSL_IMX8MM_GPIO3_HIGH_IRQ
+            },
+            {
+                fsl_imx8mm_memmap[FSL_IMX8MM_GPIO4].addr,
+                FSL_IMX8MM_GPIO4_LOW_IRQ,
+                FSL_IMX8MM_GPIO4_HIGH_IRQ
+            },
+            {
+                fsl_imx8mm_memmap[FSL_IMX8MM_GPIO5].addr,
+                FSL_IMX8MM_GPIO5_LOW_IRQ,
+                FSL_IMX8MM_GPIO5_HIGH_IRQ
+            },
+        };
+
+        object_property_set_bool(OBJECT(&s->gpio[i]), "has-edge-sel", true,
+                                 &error_abort);
+        object_property_set_bool(OBJECT(&s->gpio[i]), "has-upper-pin-irq",
+                                 true, &error_abort);
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio[i]), errp)) {
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, gpio_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0,
+                           qdev_get_gpio_in(gicdev, gpio_table[i].irq_low));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1,
+                           qdev_get_gpio_in(gicdev, gpio_table[i].irq_high));
+    }
+
+    /* USDHCs */
+    for (i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) {
+        struct {
+            hwaddr addr;
+            unsigned int irq;
+        } usdhc_table[FSL_IMX8MM_NUM_USDHCS] = {
+            { fsl_imx8mm_memmap[FSL_IMX8MM_USDHC1].addr, FSL_IMX8MM_USDHC1_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_USDHC2].addr, FSL_IMX8MM_USDHC2_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_USDHC3].addr, FSL_IMX8MM_USDHC3_IRQ },
+        };
+
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), errp)) {
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, usdhc_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0,
+                           qdev_get_gpio_in(gicdev, usdhc_table[i].irq));
+    }
+
+    /* USBs */
+    for (i = 0; i < FSL_IMX8MM_NUM_USBS; i++) {
+        struct {
+            hwaddr addr;
+            unsigned int irq;
+        } usb_table[FSL_IMX8MM_NUM_USBS] = {
+            { fsl_imx8mm_memmap[FSL_IMX8MM_USB1].addr, FSL_IMX8MM_USB1_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_USB2].addr, FSL_IMX8MM_USB2_IRQ },
+        };
+
+        qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p2", 1);
+        qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "p3", 1);
+        qdev_prop_set_uint32(DEVICE(&s->usb[i].sysbus_xhci), "slots", 2);
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->usb[i]), errp)) {
+            return;
+        }
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->usb[i]), 0, usb_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->usb[i].sysbus_xhci), 0,
+                           qdev_get_gpio_in(gicdev, usb_table[i].irq));
+    }
+
+    /* ECSPIs */
+    for (i = 0; i < FSL_IMX8MM_NUM_ECSPIS; i++) {
+        struct {
+            hwaddr addr;
+            unsigned int irq;
+        } spi_table[FSL_IMX8MM_NUM_ECSPIS] = {
+            { fsl_imx8mm_memmap[FSL_IMX8MM_ECSPI1].addr, FSL_IMX8MM_ECSPI1_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_ECSPI2].addr, FSL_IMX8MM_ECSPI2_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_ECSPI3].addr, FSL_IMX8MM_ECSPI3_IRQ },
+        };
+
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->spi[i]), errp)) {
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, spi_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0,
+                           qdev_get_gpio_in(gicdev, spi_table[i].irq));
+    }
+
+    /* ENET1 */
+    object_property_set_uint(OBJECT(&s->enet), "phy-num", s->phy_num,
+                             &error_abort);
+    object_property_set_uint(OBJECT(&s->enet), "tx-ring-num", 3, &error_abort);
+    qemu_configure_nic_device(DEVICE(&s->enet), true, NULL);
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->enet), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->enet), 0,
+                    fsl_imx8mm_memmap[FSL_IMX8MM_ENET1].addr);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 0,
+                       qdev_get_gpio_in(gicdev, FSL_IMX8MM_ENET1_MAC_IRQ));
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->enet), 1,
+                       qdev_get_gpio_in(gicdev, FSL_IMX6_ENET1_MAC_1588_IRQ));
+
+    /* SNVS */
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->snvs), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0,
+                    fsl_imx8mm_memmap[FSL_IMX8MM_SNVS_HP].addr);
+
+    /* Watchdogs */
+    for (i = 0; i < FSL_IMX8MM_NUM_WDTS; i++) {
+        struct {
+            hwaddr addr;
+            unsigned int irq;
+        } wdog_table[FSL_IMX8MM_NUM_WDTS] = {
+            { fsl_imx8mm_memmap[FSL_IMX8MM_WDOG1].addr, FSL_IMX8MM_WDOG1_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_WDOG2].addr, FSL_IMX8MM_WDOG2_IRQ },
+            { fsl_imx8mm_memmap[FSL_IMX8MM_WDOG3].addr, FSL_IMX8MM_WDOG3_IRQ },
+        };
+
+        object_property_set_bool(OBJECT(&s->wdt[i]), "pretimeout-support",
+                                 true, &error_abort);
+        if (!sysbus_realize(SYS_BUS_DEVICE(&s->wdt[i]), errp)) {
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, wdog_table[i].addr);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->wdt[i]), 0,
+                           qdev_get_gpio_in(gicdev, wdog_table[i].irq));
+    }
+
+    /* PCIe */
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie), 0,
+                    fsl_imx8mm_memmap[FSL_IMX8MM_PCIE1].addr);
+
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 0,
+                       qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTA_IRQ));
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 1,
+                       qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTB_IRQ));
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 2,
+                       qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTC_IRQ));
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 3,
+                       qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_INTD_IRQ));
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->pcie), 4,
+                       qdev_get_gpio_in(gicdev, FSL_IMX8MM_PCI_MSI_IRQ));
+
+    if (!sysbus_realize(SYS_BUS_DEVICE(&s->pcie_phy), errp)) {
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcie_phy), 0,
+                    fsl_imx8mm_memmap[FSL_IMX8MM_PCIE_PHY1].addr);
+
+    /* On-Chip RAM */
+    if (!memory_region_init_ram(&s->ocram, NULL, "imx8mm.ocram",
+                                fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].size,
+                                errp)) {
+        return;
+    }
+    memory_region_add_subregion(get_system_memory(),
+                                fsl_imx8mm_memmap[FSL_IMX8MM_OCRAM].addr,
+                                &s->ocram);
+
+    /* Unimplemented devices */
+    for (i = 0; i < ARRAY_SIZE(fsl_imx8mm_memmap); i++) {
+        switch (i) {
+        case FSL_IMX8MM_ANA_PLL:
+        case FSL_IMX8MM_CCM:
+        case FSL_IMX8MM_GIC_DIST:
+        case FSL_IMX8MM_GIC_REDIST:
+        case FSL_IMX8MM_GPIO1 ... FSL_IMX8MM_GPIO5:
+        case FSL_IMX8MM_ECSPI1 ... FSL_IMX8MM_ECSPI3:
+        case FSL_IMX8MM_ENET1:
+        case FSL_IMX8MM_I2C1 ... FSL_IMX8MM_I2C4:
+        case FSL_IMX8MM_OCRAM:
+        case FSL_IMX8MM_PCIE1:
+        case FSL_IMX8MM_PCIE_PHY1:
+        case FSL_IMX8MM_RAM:
+        case FSL_IMX8MM_SNVS_HP:
+        case FSL_IMX8MM_UART1 ... FSL_IMX8MM_UART4:
+        case FSL_IMX8MM_USB1 ... FSL_IMX8MM_USB2:
+        case FSL_IMX8MM_USDHC1 ... FSL_IMX8MM_USDHC3:
+        case FSL_IMX8MM_WDOG1 ... FSL_IMX8MM_WDOG3:
+            /* device implemented and treated above */
+            break;
+
+        default:
+            create_unimplemented_device(fsl_imx8mm_memmap[i].name,
+                                        fsl_imx8mm_memmap[i].addr,
+                                        fsl_imx8mm_memmap[i].size);
+            break;
+        }
+    }
+}
+
+static const Property fsl_imx8mm_properties[] = {
+    DEFINE_PROP_UINT32("fec1-phy-num", FslImx8mmState, phy_num, 0),
+    DEFINE_PROP_BOOL("fec1-phy-connected", FslImx8mmState, phy_connected, true),
+};
+
+static void fsl_imx8mm_class_init(ObjectClass *oc, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    device_class_set_props(dc, fsl_imx8mm_properties);
+    dc->realize = fsl_imx8mm_realize;
+
+    dc->desc = "i.MX 8MM SoC";
+}
+
+static const TypeInfo fsl_imx8mm_types[] = {
+    {
+        .name = TYPE_FSL_IMX8MM,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(FslImx8mmState),
+        .instance_init = fsl_imx8mm_init,
+        .class_init = fsl_imx8mm_class_init,
+    },
+};
+
+DEFINE_TYPES(fsl_imx8mm_types)
diff --git a/hw/arm/imx8mm-evk.c b/hw/arm/imx8mm-evk.c
new file mode 100644
index 0000000000..5cde073323
--- /dev/null
+++ b/hw/arm/imx8mm-evk.c
@@ -0,0 +1,103 @@
+/*
+ * NXP i.MX 8MM Evaluation Kit System Emulation
+ *
+ * Copyright (c) 2025, Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "system/address-spaces.h"
+#include "hw/arm/boot.h"
+#include "hw/arm/fsl-imx8mm.h"
+#include "hw/boards.h"
+#include "hw/qdev-properties.h"
+#include "system/qtest.h"
+#include "qemu/error-report.h"
+#include "qapi/error.h"
+#include <libfdt.h>
+
+static void imx8mm_evk_modify_dtb(const struct arm_boot_info *info, void *fdt)
+{
+    int i, offset;
+
+    /* Temporarily disable following nodes until they are implemented */
+    const char *nodes_to_remove[] = {
+        "nxp,imx8mm-fspi",
+    };
+
+    for (i = 0; i < ARRAY_SIZE(nodes_to_remove); i++) {
+        const char *dev_str = nodes_to_remove[i];
+
+        offset = fdt_node_offset_by_compatible(fdt, -1, dev_str);
+        while (offset >= 0) {
+            fdt_nop_node(fdt, offset);
+            offset = fdt_node_offset_by_compatible(fdt, offset, dev_str);
+        }
+    }
+
+    /* Remove cpu-idle-states property from CPU nodes */
+    offset = fdt_node_offset_by_compatible(fdt, -1, "arm,cortex-a53");
+    while (offset >= 0) {
+        fdt_nop_property(fdt, offset, "cpu-idle-states");
+        offset = fdt_node_offset_by_compatible(fdt, offset, "arm,cortex-a53");
+    }
+}
+
+static void imx8mm_evk_init(MachineState *machine)
+{
+    static struct arm_boot_info boot_info;
+    FslImx8mmState *s;
+
+    if (machine->ram_size > FSL_IMX8MM_RAM_SIZE_MAX) {
+        error_report("RAM size " RAM_ADDR_FMT " above max supported (%08" PRIx64 ")",
+                     machine->ram_size, FSL_IMX8MM_RAM_SIZE_MAX);
+        exit(1);
+    }
+
+    boot_info = (struct arm_boot_info) {
+        .loader_start = FSL_IMX8MM_RAM_START,
+        .board_id = -1,
+        .ram_size = machine->ram_size,
+        .psci_conduit = QEMU_PSCI_CONDUIT_SMC,
+        .modify_dtb = imx8mm_evk_modify_dtb,
+    };
+
+    s = FSL_IMX8MM(object_new(TYPE_FSL_IMX8MM));
+    object_property_add_child(OBJECT(machine), "soc", OBJECT(s));
+    object_property_set_uint(OBJECT(s), "fec1-phy-num", 1, &error_fatal);
+    sysbus_realize_and_unref(SYS_BUS_DEVICE(s), &error_fatal);
+
+    memory_region_add_subregion(get_system_memory(), FSL_IMX8MM_RAM_START,
+                                machine->ram);
+
+    for (int i = 0; i < FSL_IMX8MM_NUM_USDHCS; i++) {
+        BusState *bus;
+        DeviceState *carddev;
+        BlockBackend *blk;
+        DriveInfo *di = drive_get(IF_SD, i, 0);
+
+        if (!di) {
+            continue;
+        }
+
+        blk = blk_by_legacy_dinfo(di);
+        bus = qdev_get_child_bus(DEVICE(&s->usdhc[i]), "sd-bus");
+        carddev = qdev_new(TYPE_SD_CARD);
+        qdev_prop_set_drive_err(carddev, "drive", blk, &error_fatal);
+        qdev_realize_and_unref(carddev, bus, &error_fatal);
+    }
+
+    if (!qtest_enabled()) {
+        arm_load_kernel(&s->cpu[0], machine, &boot_info);
+    }
+}
+
+static void imx8mm_evk_machine_init(MachineClass *mc)
+{
+    mc->desc = "NXP i.MX 8MM EVK Board";
+    mc->init = imx8mm_evk_init;
+    mc->max_cpus = FSL_IMX8MM_NUM_CPUS;
+    mc->default_ram_id = "imx8mm-evk.ram";
+}
+DEFINE_MACHINE("imx8mm-evk", imx8mm_evk_machine_init)
diff --git a/hw/arm/meson.build b/hw/arm/meson.build
index b88b5b06d7..ead706ef73 100644
--- a/hw/arm/meson.build
+++ b/hw/arm/meson.build
@@ -62,6 +62,8 @@ arm_common_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c'))
arm_common_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c'))
arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP', if_true: files('fsl-imx8mp.c'))
arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP_EVK', if_true: files('imx8mp-evk.c'))
+arm_common_ss.add(when: 'CONFIG_FSL_IMX8MM', if_true: files('fsl-imx8mm.c'))
+arm_common_ss.add(when: 'CONFIG_FSL_IMX8MM_EVK', if_true: files('imx8mm-evk.c'))
arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c'))
arm_common_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c'))
arm_common_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c'))
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index 4e35657468..cc8dc658da 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -96,6 +96,12 @@ config FSL_IMX8MP_ANALOG
config FSL_IMX8MP_CCM
     bool
+config FSL_IMX8MM_ANALOG
+    bool
+
+config FSL_IMX8MM_CCM
+    bool
+
config STM32_RCC
     bool
diff --git a/hw/misc/imx8mm_analog.c b/hw/misc/imx8mm_analog.c
new file mode 100644
index 0000000000..d6ba1344d3
--- /dev/null
+++ b/hw/misc/imx8mm_analog.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2025 Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
+ *
+ * i.MX 8MM ANALOG IP block emulation code
+ *
+ * Based on hw/misc/imx7_ccm.c
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+
+#include "hw/misc/imx8mm_analog.h"
+#include "migration/vmstate.h"
+
+#define ANALOG_PLL_LOCK BIT(31)
+
+static void imx8mm_analog_reset(DeviceState *dev)
+{
+    IMX8MMAnalogState *s = IMX8MM_ANALOG(dev);
+
+    memset(s->analog, 0, sizeof(s->analog));
+
+    s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] = 0x00002010;
+    s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL0] = 0x00145032;
+    s->analog[ANALOG_AUDIO_PLL1_FDIV_CTL1] = 0x00000000;
+    s->analog[ANALOG_AUDIO_PLL1_SSCG_CTRL] = 0x00000000;
+    s->analog[ANALOG_AUDIO_PLL1_MNIT_CTRL] = 0x00100103;
+    s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] = 0x00002010;
+    s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL0] = 0x00145032;
+    s->analog[ANALOG_AUDIO_PLL2_FDIV_CTL1] = 0x00000000;
+    s->analog[ANALOG_AUDIO_PLL2_SSCG_CTRL] = 0x00000000;
+    s->analog[ANALOG_AUDIO_PLL2_MNIT_CTRL] = 0x00100103;
+    s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] = 0x00002010;
+    s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL0] = 0x00145032;
+    s->analog[ANALOG_VIDEO_PLL1_FDIV_CTL1] = 0x00000000;
+    s->analog[ANALOG_VIDEO_PLL1_SSCG_CTRL] = 0x00000000;
+    s->analog[ANALOG_VIDEO_PLL1_MNIT_CTRL] = 0x00100103;
+    s->analog[ANALOG_DRAM_PLL_GEN_CTRL] = 0x00002010;
+    s->analog[ANALOG_DRAM_PLL_FDIV_CTL0] = 0x0012c032;
+    s->analog[ANALOG_DRAM_PLL_FDIV_CTL1] = 0x00000000;
+    s->analog[ANALOG_DRAM_PLL_SSCG_CTRL] = 0x00000000;
+    s->analog[ANALOG_DRAM_PLL_MNIT_CTRL] = 0x00100103;
+    s->analog[ANALOG_GPU_PLL_GEN_CTRL] = 0x00000810;
+    s->analog[ANALOG_GPU_PLL_FDIV_CTL0] = 0x000c8031;
+    s->analog[ANALOG_GPU_PLL_LOCKD_CTRL] = 0x0010003f;
+    s->analog[ANALOG_GPU_PLL_MNIT_CTRL] = 0x00280081;
+    s->analog[ANALOG_VPU_PLL_GEN_CTRL] = 0x00000810;
+    s->analog[ANALOG_VPU_PLL_FDIV_CTL0] = 0x0012c032;
+    s->analog[ANALOG_VPU_PLL_LOCKD_CTRL] = 0x0010003f;
+    s->analog[ANALOG_VPU_PLL_MNIT_CTRL] = 0x00280081;
+    s->analog[ANALOG_ARM_PLL_GEN_CTRL] = 0x00000810;
+    s->analog[ANALOG_ARM_PLL_FDIV_CTL0] = 0x000fa030;
+    s->analog[ANALOG_ARM_PLL_LOCKD_CTRL] = 0x0010003f;
+    s->analog[ANALOG_ARM_PLL_MNIT_CTRL] = 0x00280081;
+    s->analog[ANALOG_SYS_PLL1_GEN_CTRL] = 0x0aaaa810;
+    s->analog[ANALOG_SYS_PLL1_FDIV_CTL0] = 0x00190032;
+    s->analog[ANALOG_SYS_PLL1_LOCKD_CTRL] = 0x0010003f;
+    s->analog[ANALOG_SYS_PLL1_MNIT_CTRL] = 0x00280081;
+    s->analog[ANALOG_SYS_PLL2_GEN_CTRL] = 0x0aaaa810;
+    s->analog[ANALOG_SYS_PLL2_FDIV_CTL0] = 0x000fa031;
+    s->analog[ANALOG_SYS_PLL2_LOCKD_CTRL] = 0x0010003f;
+    s->analog[ANALOG_SYS_PLL2_MNIT_CTRL] = 0x00280081;
+    s->analog[ANALOG_SYS_PLL3_GEN_CTRL] = 0x00000810;
+    s->analog[ANALOG_SYS_PLL3_FDIV_CTL0] = 0x000fa031;
+    s->analog[ANALOG_SYS_PLL3_LOCKD_CTRL] = 0x0010003f;
+    s->analog[ANALOG_SYS_PLL3_MNIT_CTRL] = 0x00280081;
+    s->analog[ANALOG_OSC_MISC_CFG] = 0x00000000;
+    s->analog[ANALOG_ANAMIX_PLL_MNIT_CTL] = 0x00000000;
+    s->analog[ANALOG_DIGPROG] = 0x00824010;
+
+    /* all PLLs need to be locked */
+    s->analog[ANALOG_AUDIO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK;
+    s->analog[ANALOG_AUDIO_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK;
+    s->analog[ANALOG_VIDEO_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK;
+    s->analog[ANALOG_DRAM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK;
+    s->analog[ANALOG_GPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK;
+    s->analog[ANALOG_VPU_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK;
+    s->analog[ANALOG_ARM_PLL_GEN_CTRL] |= ANALOG_PLL_LOCK;
+    s->analog[ANALOG_SYS_PLL1_GEN_CTRL] |= ANALOG_PLL_LOCK;
+    s->analog[ANALOG_SYS_PLL2_GEN_CTRL] |= ANALOG_PLL_LOCK;
+    s->analog[ANALOG_SYS_PLL3_GEN_CTRL] |= ANALOG_PLL_LOCK;
+}
+
+static uint64_t imx8mm_analog_read(void *opaque, hwaddr offset, unsigned size)
+{
+    IMX8MMAnalogState *s = opaque;
+
+    return s->analog[offset >> 2];
+}
+
+static void imx8mm_analog_write(void *opaque, hwaddr offset,
+                                uint64_t value, unsigned size)
+{
+    IMX8MMAnalogState *s = opaque;
+
+    if (offset >> 2 == ANALOG_DIGPROG) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "Guest write to read-only ANALOG_DIGPROG register\n");
+    } else {
+        s->analog[offset >> 2] = value;
+    }
+}
+
+static const struct MemoryRegionOps imx8mm_analog_ops = {
+    .read = imx8mm_analog_read,
+    .write = imx8mm_analog_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+};
+
+static void imx8mm_analog_init(Object *obj)
+{
+    IMX8MMAnalogState *s = IMX8MM_ANALOG(obj);
+    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+
+    memory_region_init(&s->mmio.container, obj, TYPE_IMX8MM_ANALOG, 0x10000);
+
+    memory_region_init_io(&s->mmio.analog, obj, &imx8mm_analog_ops, s,
+                          TYPE_IMX8MM_ANALOG, sizeof(s->analog));
+    memory_region_add_subregion(&s->mmio.container, 0, &s->mmio.analog);
+
+    sysbus_init_mmio(sd, &s->mmio.container);
+}
+
+static const VMStateDescription imx8mm_analog_vmstate = {
+    .name = TYPE_IMX8MM_ANALOG,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(analog, IMX8MMAnalogState, ANALOG_MAX),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static void imx8mm_analog_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    device_class_set_legacy_reset(dc, imx8mm_analog_reset);
+    dc->vmsd  = &imx8mm_analog_vmstate;
+    dc->desc  = "i.MX 8MM Analog Module";
+}
+
+static const TypeInfo imx8mm_analog_types[] = {
+    {
+        .name          = TYPE_IMX8MM_ANALOG,
+        .parent        = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(IMX8MMAnalogState),
+        .instance_init = imx8mm_analog_init,
+        .class_init    = imx8mm_analog_class_init,
+    }
+};
+
+DEFINE_TYPES(imx8mm_analog_types);
diff --git a/hw/misc/imx8mm_ccm.c b/hw/misc/imx8mm_ccm.c
new file mode 100644
index 0000000000..352a6ee188
--- /dev/null
+++ b/hw/misc/imx8mm_ccm.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2025 Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
+ *
+ * i.MX 8MM CCM IP block emulation code
+ *
+ * Based on hw/misc/imx7_ccm.c
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+
+#include "hw/misc/imx8mm_ccm.h"
+#include "migration/vmstate.h"
+
+#include "trace.h"
+
+#define CKIH_FREQ 16000000 /* 16MHz crystal input */
+
+static void imx8mm_ccm_reset(DeviceState *dev)
+{
+    IMX8MMCCMState *s = IMX8MM_CCM(dev);
+
+    memset(s->ccm, 0, sizeof(s->ccm));
+}
+
+#define CCM_INDEX(offset)   (((offset) & ~(hwaddr)0xF) / sizeof(uint32_t))
+#define CCM_BITOP(offset)   ((offset) & (hwaddr)0xF)
+
+enum {
+    CCM_BITOP_NONE = 0x00,
+    CCM_BITOP_SET  = 0x04,
+    CCM_BITOP_CLR  = 0x08,
+    CCM_BITOP_TOG  = 0x0C,
+};
+
+static uint64_t imx8mm_set_clr_tog_read(void *opaque, hwaddr offset,
+                                        unsigned size)
+{
+    const uint32_t *mmio = opaque;
+
+    return mmio[CCM_INDEX(offset)];
+}
+
+static void imx8mm_set_clr_tog_write(void *opaque, hwaddr offset,
+                                     uint64_t value, unsigned size)
+{
+    const uint8_t  bitop = CCM_BITOP(offset);
+    const uint32_t index = CCM_INDEX(offset);
+    uint32_t *mmio = opaque;
+
+    switch (bitop) {
+    case CCM_BITOP_NONE:
+        mmio[index]  = value;
+        break;
+    case CCM_BITOP_SET:
+        mmio[index] |= value;
+        break;
+    case CCM_BITOP_CLR:
+        mmio[index] &= ~value;
+        break;
+    case CCM_BITOP_TOG:
+        mmio[index] ^= value;
+        break;
+    };
+}
+
+static const struct MemoryRegionOps imx8mm_set_clr_tog_ops = {
+    .read = imx8mm_set_clr_tog_read,
+    .write = imx8mm_set_clr_tog_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .impl = {
+        /*
+         * 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 imx8mm_ccm_init(Object *obj)
+{
+    SysBusDevice *sd = SYS_BUS_DEVICE(obj);
+    IMX8MMCCMState *s = IMX8MM_CCM(obj);
+
+    memory_region_init_io(&s->iomem,
+                          obj,
+                          &imx8mm_set_clr_tog_ops,
+                          s->ccm,
+                          TYPE_IMX8MM_CCM ".ccm",
+                          sizeof(s->ccm));
+
+    sysbus_init_mmio(sd, &s->iomem);
+}
+
+static const VMStateDescription imx8mm_ccm_vmstate = {
+    .name = TYPE_IMX8MM_CCM,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (const VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(ccm, IMX8MMCCMState, CCM_MAX),
+        VMSTATE_END_OF_LIST()
+    },
+};
+
+static uint32_t imx8mm_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock)
+{
+    /*
+     * This function is "consumed" by GPT emulation code. Some clocks
+     * have fixed frequencies and we can provide requested frequency
+     * easily. However for CCM provided clocks (like IPG) each GPT
+     * timer can have its own clock root.
+     * This means we need additional information when calling this
+     * function to know the requester's identity.
+     */
+    uint32_t freq = 0;
+
+    switch (clock) {
+    case CLK_NONE:
+        break;
+    case CLK_32k:
+        freq = CKIL_FREQ;
+        break;
+    case CLK_HIGH:
+        freq = CKIH_FREQ;
+        break;
+    case CLK_IPG:
+    case CLK_IPG_HIGH:
+        /*
+         * For now we don't have a way to figure out the device this
+         * function is called for. Until then the IPG derived clocks
+         * are left unimplemented.
+         */
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Clock %d Not implemented\n",
+                      TYPE_IMX8MM_CCM, __func__, clock);
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n",
+                      TYPE_IMX8MM_CCM, __func__, clock);
+        break;
+    }
+
+    trace_ccm_clock_freq(clock, freq);
+
+    return freq;
+}
+
+static void imx8mm_ccm_class_init(ObjectClass *klass, const void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    IMXCCMClass *ccm = IMX_CCM_CLASS(klass);
+
+    device_class_set_legacy_reset(dc, imx8mm_ccm_reset);
+    dc->vmsd  = &imx8mm_ccm_vmstate;
+    dc->desc  = "i.MX 8MM Clock Control Module";
+
+    ccm->get_clock_frequency = imx8mm_ccm_get_clock_frequency;
+}
+
+static const TypeInfo imx8mm_ccm_types[] = {
+    {
+        .name          = TYPE_IMX8MM_CCM,
+        .parent        = TYPE_IMX_CCM,
+        .instance_size = sizeof(IMX8MMCCMState),
+        .instance_init = imx8mm_ccm_init,
+        .class_init    = imx8mm_ccm_class_init,
+    },
+};
+
+DEFINE_TYPES(imx8mm_ccm_types);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index b1d8d8e5d2..ae285091e7 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -57,6 +57,9 @@ system_ss.add(when: 'CONFIG_ECCMEMCTL', if_true: files('eccmemctl.c'))
system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_pmu.c', 'exynos4210_clk.c', 'exynos4210_rng.c'))
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_IMX8MM_ANALOG', if_true: files('imx8mm_analog.c'))
+system_ss.add(when: 'CONFIG_FSL_IMX8MM_CCM', if_true: files('imx8mm_ccm.c'))
+
system_ss.add(when: 'CONFIG_IMX', if_true: files(
   'imx25_ccm.c',
   'imx31_ccm.c',
diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c
index 8c7cbfdeac..9bbf26b1f3 100644
--- a/hw/timer/imx_gpt.c
+++ b/hw/timer/imx_gpt.c
@@ -3,9 +3,11 @@
  *
  * Copyright (c) 2008 OK Labs
  * Copyright (c) 2011 NICTA Pty Ltd
+ * Copyright (c) 2025 NXP
  * Originally written by Hans Jiang
  * Updated by Peter Chubb
  * Updated by Jean-Christophe Dubois jcd@tribudubois.net<mailto:jcd@tribudubois.net>
+ * Updated by Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
  *
  * This code is licensed under GPL version 2 or later.  See
  * the COPYING file in the top-level directory.
@@ -137,6 +139,17 @@ static const IMXClk imx8mp_gpt_clocks[] = {
     CLK_NONE,      /* 111 not defined */
};
+static const IMXClk imx8mm_gpt_clocks[] = {
+    CLK_NONE,      /* 000 No clock source */
+    CLK_IPG,       /* 001 ipg_clk, 532MHz */
+    CLK_IPG_HIGH,  /* 010 ipg_clk_highfreq */
+    CLK_EXT,       /* 011 External clock */
+    CLK_32k,       /* 100 ipg_clk_32k */
+    CLK_HIGH,      /* 101 ipg_clk_16M */
+    CLK_NONE,      /* 110 not defined */
+    CLK_NONE,      /* 111 not defined */
+};
+
/* Must be called from within ptimer_transaction_begin/commit block */
static void imx_gpt_set_freq(IMXGPTState *s)
{
@@ -570,6 +583,14 @@ static void imx8mp_gpt_init(Object *obj)
     s->clocks = imx8mp_gpt_clocks;
}
+static void imx8mm_gpt_init(Object *obj)
+{
+    IMXGPTState *s = IMX_GPT(obj);
+
+    s->clocks = imx8mm_gpt_clocks;
+}
+
+
static const TypeInfo imx25_gpt_info = {
     .name = TYPE_IMX25_GPT,
     .parent = TYPE_SYS_BUS_DEVICE,
@@ -608,6 +629,12 @@ static const TypeInfo imx8mp_gpt_info = {
     .instance_init = imx8mp_gpt_init,
};
+static const TypeInfo imx8mm_gpt_info = {
+    .name = TYPE_IMX8MM_GPT,
+    .parent = TYPE_IMX25_GPT,
+    .instance_init = imx8mm_gpt_init,
+};
+
static void imx_gpt_register_types(void)
{
     type_register_static(&imx25_gpt_info);
@@ -616,6 +643,7 @@ static void imx_gpt_register_types(void)
     type_register_static(&imx6ul_gpt_info);
     type_register_static(&imx7_gpt_info);
     type_register_static(&imx8mp_gpt_info);
+    type_register_static(&imx8mm_gpt_info);
}
 type_init(imx_gpt_register_types)
diff --git a/include/hw/arm/fsl-imx8mm.h b/include/hw/arm/fsl-imx8mm.h
new file mode 100644
index 0000000000..5e78ba545e
--- /dev/null
+++ b/include/hw/arm/fsl-imx8mm.h
@@ -0,0 +1,242 @@
+/*
+ * i.MX 8MM SoC Definitions
+ *
+ * Copyright (c) 2025, Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef FSL_IMX8MM_H
+#define FSL_IMX8MM_H
+
+#include "cpu.h"
+#include "hw/char/imx_serial.h"
+#include "hw/gpio/imx_gpio.h"
+#include "hw/i2c/imx_i2c.h"
+#include "hw/intc/arm_gicv3_common.h"
+#include "hw/misc/imx7_snvs.h"
+#include "hw/misc/imx8mm_analog.h"
+#include "hw/misc/imx8mm_ccm.h"
+#include "hw/net/imx_fec.h"
+#include "hw/or-irq.h"
+#include "hw/pci-host/designware.h"
+#include "hw/pci-host/fsl_imx8m_phy.h"
+#include "hw/sd/sdhci.h"
+#include "hw/ssi/imx_spi.h"
+#include "hw/timer/imx_gpt.h"
+#include "hw/usb/hcd-dwc3.h"
+#include "hw/watchdog/wdt_imx2.h"
+#include "hw/sysbus.h"
+#include "qom/object.h"
+#include "qemu/units.h"
+
+#define TYPE_FSL_IMX8MM "fsl-imx8mm"
+OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mmState, FSL_IMX8MM)
+
+#define FSL_IMX8MM_RAM_START        0x40000000
+#define FSL_IMX8MM_RAM_SIZE_MAX     (4 * GiB)
+
+enum FslImx8mmConfiguration {
+    FSL_IMX8MM_NUM_CPUS         = 4,
+    FSL_IMX8MM_NUM_ECSPIS       = 3,
+    FSL_IMX8MM_NUM_GPIOS        = 5,
+    FSL_IMX8MM_NUM_GPTS         = 6,
+    FSL_IMX8MM_NUM_I2CS         = 4,
+    FSL_IMX8MM_NUM_IRQS         = 128,
+    FSL_IMX8MM_NUM_UARTS        = 4,
+    FSL_IMX8MM_NUM_USBS         = 2,
+    FSL_IMX8MM_NUM_USDHCS       = 3,
+    FSL_IMX8MM_NUM_WDTS         = 3,
+};
+
+struct FslImx8mmState {
+    SysBusDevice   parent_obj;
+
+    ARMCPU             cpu[FSL_IMX8MM_NUM_CPUS];
+    GICv3State         gic;
+    IMXGPTState        gpt[FSL_IMX8MM_NUM_GPTS];
+    IMXGPIOState       gpio[FSL_IMX8MM_NUM_GPIOS];
+    IMX8MMCCMState     ccm;
+    IMX8MMAnalogState  analog;
+    IMX7SNVSState      snvs;
+    IMXSPIState        spi[FSL_IMX8MM_NUM_ECSPIS];
+    IMXI2CState        i2c[FSL_IMX8MM_NUM_I2CS];
+    IMXSerialState     uart[FSL_IMX8MM_NUM_UARTS];
+    IMXFECState        enet;
+    SDHCIState         usdhc[FSL_IMX8MM_NUM_USDHCS];
+    IMX2WdtState       wdt[FSL_IMX8MM_NUM_WDTS];
+    USBDWC3            usb[FSL_IMX8MM_NUM_USBS];
+    DesignwarePCIEHost pcie;
+    FslImx8mPciePhyState   pcie_phy;
+    OrIRQState         gpt5_gpt6_irq;
+    MemoryRegion       ocram;
+
+    uint32_t           phy_num;
+    bool               phy_connected;
+};
+
+enum FslImx8mmMemoryRegions {
+    FSL_IMX8MM_A53_DAP,
+    FSL_IMX8MM_AIPS1_CONFIGURATION,
+    FSL_IMX8MM_AIPS2_CONFIGURATION,
+    FSL_IMX8MM_AIPS3_CONFIGURATION,
+    FSL_IMX8MM_AIPS4_CONFIGURATION,
+    FSL_IMX8MM_ANA_OSC,
+    FSL_IMX8MM_ANA_PLL,
+    FSL_IMX8MM_ANA_TSENSOR,
+    FSL_IMX8MM_APBH_DMA,
+    FSL_IMX8MM_BOOT_ROM,
+    FSL_IMX8MM_BOOT_ROM_PROTECTED,
+    FSL_IMX8MM_CAAM,
+    FSL_IMX8MM_CAAM_MEM,
+    FSL_IMX8MM_CCM,
+    FSL_IMX8MM_CSU,
+    FSL_IMX8MM_DDR_CTL,
+    FSL_IMX8MM_DDR_PERF_MON,
+    FSL_IMX8MM_DDR_PHY,
+    FSL_IMX8MM_DDR_PHY_BROADCAST,
+    FSL_IMX8MM_ECSPI1,
+    FSL_IMX8MM_ECSPI2,
+    FSL_IMX8MM_ECSPI3,
+    FSL_IMX8MM_ENET1,
+    FSL_IMX8MM_GIC_DIST,
+    FSL_IMX8MM_GIC_REDIST,
+    FSL_IMX8MM_GPC,
+    FSL_IMX8MM_GPIO1,
+    FSL_IMX8MM_GPIO2,
+    FSL_IMX8MM_GPIO3,
+    FSL_IMX8MM_GPIO4,
+    FSL_IMX8MM_GPIO5,
+    FSL_IMX8MM_GPT1,
+    FSL_IMX8MM_GPT2,
+    FSL_IMX8MM_GPT3,
+    FSL_IMX8MM_GPT4,
+    FSL_IMX8MM_GPT5,
+    FSL_IMX8MM_GPT6,
+    FSL_IMX8MM_GPU2D,
+    FSL_IMX8MM_I2C1,
+    FSL_IMX8MM_I2C2,
+    FSL_IMX8MM_I2C3,
+    FSL_IMX8MM_I2C4,
+    FSL_IMX8MM_INTERCONNECT,
+    FSL_IMX8MM_IOMUXC,
+    FSL_IMX8MM_IOMUXC_GPR,
+    FSL_IMX8MM_MEDIA_BLK_CTL,
+    FSL_IMX8MM_LCDIF,
+    FSL_IMX8MM_MIPI_CSI,
+    FSL_IMX8MM_MIPI_DSI,
+    FSL_IMX8MM_MU_A,
+    FSL_IMX8MM_MU_B,
+    FSL_IMX8MM_OCOTP_CTRL,
+    FSL_IMX8MM_OCRAM,
+    FSL_IMX8MM_OCRAM_S,
+    FSL_IMX8MM_PCIE1,
+    FSL_IMX8MM_PCIE1_MEM,
+    FSL_IMX8MM_PCIE_PHY1,
+    FSL_IMX8MM_PERFMON1,
+    FSL_IMX8MM_PERFMON2,
+    FSL_IMX8MM_PWM1,
+    FSL_IMX8MM_PWM2,
+    FSL_IMX8MM_PWM3,
+    FSL_IMX8MM_PWM4,
+    FSL_IMX8MM_QOSC,
+    FSL_IMX8MM_QSPI,
+    FSL_IMX8MM_QSPI1_RX_BUFFER,
+    FSL_IMX8MM_QSPI1_TX_BUFFER,
+    FSL_IMX8MM_QSPI_MEM,
+    FSL_IMX8MM_RAM,
+    FSL_IMX8MM_RDC,
+    FSL_IMX8MM_SAI1,
+    FSL_IMX8MM_SAI2,
+    FSL_IMX8MM_SAI3,
+    FSL_IMX8MM_SAI5,
+    FSL_IMX8MM_SAI6,
+    FSL_IMX8MM_SDMA1,
+    FSL_IMX8MM_SDMA2,
+    FSL_IMX8MM_SDMA3,
+    FSL_IMX8MM_SEMAPHORE1,
+    FSL_IMX8MM_SEMAPHORE2,
+    FSL_IMX8MM_SEMAPHORE_HS,
+    FSL_IMX8MM_SNVS_HP,
+    FSL_IMX8MM_SPBA1,
+    FSL_IMX8MM_SRC,
+    FSL_IMX8MM_SYSCNT_CMP,
+    FSL_IMX8MM_SYSCNT_CTRL,
+    FSL_IMX8MM_SYSCNT_RD,
+    FSL_IMX8MM_TCM_DTCM,
+    FSL_IMX8MM_TCM_ITCM,
+    FSL_IMX8MM_TZASC,
+    FSL_IMX8MM_UART1,
+    FSL_IMX8MM_UART2,
+    FSL_IMX8MM_UART3,
+    FSL_IMX8MM_UART4,
+    FSL_IMX8MM_USB1,
+    FSL_IMX8MM_USB2,
+    FSL_IMX8MM_USB1_OTG,
+    FSL_IMX8MM_USB2_OTG,
+    FSL_IMX8MM_USDHC1,
+    FSL_IMX8MM_USDHC2,
+    FSL_IMX8MM_USDHC3,
+    FSL_IMX8MM_VPU,
+    FSL_IMX8MM_VPU_BLK_CTRL,
+    FSL_IMX8MM_VPU_G1_DECODER,
+    FSL_IMX8MM_VPU_G2_DECODER,
+    FSL_IMX8MM_WDOG1,
+    FSL_IMX8MM_WDOG2,
+    FSL_IMX8MM_WDOG3,
+};
+
+enum FslImx8mmIrqs {
+    FSL_IMX8MM_USDHC1_IRQ   = 22,
+    FSL_IMX8MM_USDHC2_IRQ   = 23,
+    FSL_IMX8MM_USDHC3_IRQ   = 24,
+
+    FSL_IMX8MM_UART1_IRQ    = 26,
+    FSL_IMX8MM_UART2_IRQ    = 27,
+    FSL_IMX8MM_UART3_IRQ    = 28,
+    FSL_IMX8MM_UART4_IRQ    = 29,
+
+    FSL_IMX8MM_ECSPI1_IRQ   = 31,
+    FSL_IMX8MM_ECSPI2_IRQ   = 32,
+    FSL_IMX8MM_ECSPI3_IRQ   = 33,
+
+    FSL_IMX8MM_I2C1_IRQ     = 35,
+    FSL_IMX8MM_I2C2_IRQ     = 36,
+    FSL_IMX8MM_I2C3_IRQ     = 37,
+    FSL_IMX8MM_I2C4_IRQ     = 38,
+
+    FSL_IMX8MM_USB1_IRQ     = 40,
+    FSL_IMX8MM_USB2_IRQ     = 41,
+
+    FSL_IMX8MM_GPT1_IRQ      = 55,
+    FSL_IMX8MM_GPT2_IRQ      = 54,
+    FSL_IMX8MM_GPT3_IRQ      = 53,
+    FSL_IMX8MM_GPT4_IRQ      = 52,
+    FSL_IMX8MM_GPT5_GPT6_IRQ = 51,
+
+    FSL_IMX8MM_GPIO1_LOW_IRQ  = 64,
+    FSL_IMX8MM_GPIO1_HIGH_IRQ = 65,
+    FSL_IMX8MM_GPIO2_LOW_IRQ  = 66,
+    FSL_IMX8MM_GPIO2_HIGH_IRQ = 67,
+    FSL_IMX8MM_GPIO3_LOW_IRQ  = 68,
+    FSL_IMX8MM_GPIO3_HIGH_IRQ = 69,
+    FSL_IMX8MM_GPIO4_LOW_IRQ  = 70,
+    FSL_IMX8MM_GPIO4_HIGH_IRQ = 71,
+    FSL_IMX8MM_GPIO5_LOW_IRQ  = 72,
+    FSL_IMX8MM_GPIO5_HIGH_IRQ = 73,
+
+    FSL_IMX8MM_WDOG1_IRQ    = 78,
+    FSL_IMX8MM_WDOG2_IRQ    = 79,
+    FSL_IMX8MM_WDOG3_IRQ    = 10,
+
+    FSL_IMX8MM_ENET1_MAC_IRQ    = 118,
+    FSL_IMX6_ENET1_MAC_1588_IRQ = 121,
+
+    FSL_IMX8MM_PCI_INTA_IRQ = 122,
+    FSL_IMX8MM_PCI_INTB_IRQ = 123,
+    FSL_IMX8MM_PCI_INTC_IRQ = 124,
+    FSL_IMX8MM_PCI_INTD_IRQ = 125,
+    FSL_IMX8MM_PCI_MSI_IRQ  = 122,
+};
+
+#endif /* FSL_IMX8MM_H */
diff --git a/include/hw/misc/imx8mm_analog.h b/include/hw/misc/imx8mm_analog.h
new file mode 100644
index 0000000000..e16e2a0cf1
--- /dev/null
+++ b/include/hw/misc/imx8mm_analog.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2025 Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
+ *
+ * i.MX8MM ANALOG IP block emulation code
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef IMX8MM_ANALOG_H
+#define IMX8MM_ANALOG_H
+
+#include "qom/object.h"
+#include "hw/sysbus.h"
+
+enum IMX8MMAnalogRegisters {
+    ANALOG_AUDIO_PLL1_GEN_CTRL = 0x000 / 4,
+    ANALOG_AUDIO_PLL1_FDIV_CTL0 = 0x004 / 4,
+    ANALOG_AUDIO_PLL1_FDIV_CTL1 = 0x008 / 4,
+    ANALOG_AUDIO_PLL1_SSCG_CTRL = 0x00c / 4,
+    ANALOG_AUDIO_PLL1_MNIT_CTRL = 0x010 / 4,
+    ANALOG_AUDIO_PLL2_GEN_CTRL = 0x014 / 4,
+    ANALOG_AUDIO_PLL2_FDIV_CTL0 = 0x018 / 4,
+    ANALOG_AUDIO_PLL2_FDIV_CTL1 = 0x01c / 4,
+    ANALOG_AUDIO_PLL2_SSCG_CTRL = 0x020 / 4,
+    ANALOG_AUDIO_PLL2_MNIT_CTRL = 0x024 / 4,
+    ANALOG_VIDEO_PLL1_GEN_CTRL = 0x028 / 4,
+    ANALOG_VIDEO_PLL1_FDIV_CTL0 = 0x02c / 4,
+    ANALOG_VIDEO_PLL1_FDIV_CTL1 = 0x030 / 4,
+    ANALOG_VIDEO_PLL1_SSCG_CTRL = 0x034 / 4,
+    ANALOG_VIDEO_PLL1_MNIT_CTRL = 0x038 / 4,
+    ANALOG_DRAM_PLL_GEN_CTRL = 0x050 / 4,
+    ANALOG_DRAM_PLL_FDIV_CTL0 = 0x054 / 4,
+    ANALOG_DRAM_PLL_FDIV_CTL1 = 0x058 / 4,
+    ANALOG_DRAM_PLL_SSCG_CTRL = 0x05c / 4,
+    ANALOG_DRAM_PLL_MNIT_CTRL = 0x060 / 4,
+    ANALOG_GPU_PLL_GEN_CTRL = 0x064 / 4,
+    ANALOG_GPU_PLL_FDIV_CTL0 = 0x068 / 4,
+    ANALOG_GPU_PLL_LOCKD_CTRL = 0x06c / 4,
+    ANALOG_GPU_PLL_MNIT_CTRL = 0x070 / 4,
+    ANALOG_VPU_PLL_GEN_CTRL = 0x074 / 4,
+    ANALOG_VPU_PLL_FDIV_CTL0 = 0x078 / 4,
+    ANALOG_VPU_PLL_LOCKD_CTRL = 0x07c / 4,
+    ANALOG_VPU_PLL_MNIT_CTRL = 0x080 / 4,
+    ANALOG_ARM_PLL_GEN_CTRL = 0x084 / 4,
+    ANALOG_ARM_PLL_FDIV_CTL0 = 0x088 / 4,
+    ANALOG_ARM_PLL_LOCKD_CTRL = 0x08c / 4,
+    ANALOG_ARM_PLL_MNIT_CTRL = 0x090 / 4,
+    ANALOG_SYS_PLL1_GEN_CTRL = 0x094 / 4,
+    ANALOG_SYS_PLL1_FDIV_CTL0 = 0x098 / 4,
+    ANALOG_SYS_PLL1_LOCKD_CTRL = 0x09c / 4,
+    ANALOG_SYS_PLL1_MNIT_CTRL = 0x100 / 4,
+    ANALOG_SYS_PLL2_GEN_CTRL = 0x104 / 4,
+    ANALOG_SYS_PLL2_FDIV_CTL0 = 0x108 / 4,
+    ANALOG_SYS_PLL2_LOCKD_CTRL = 0x10c / 4,
+    ANALOG_SYS_PLL2_MNIT_CTRL = 0x110 / 4,
+    ANALOG_SYS_PLL3_GEN_CTRL = 0x114 / 4,
+    ANALOG_SYS_PLL3_FDIV_CTL0 = 0x118 / 4,
+    ANALOG_SYS_PLL3_LOCKD_CTRL = 0x11c / 4,
+    ANALOG_SYS_PLL3_MNIT_CTRL = 0x120 / 4,
+    ANALOG_OSC_MISC_CFG = 0x124 / 4,
+    ANALOG_ANAMIX_PLL_MNIT_CTL = 0x128 / 4,
+
+    ANALOG_DIGPROG = 0x800 / 4,
+    ANALOG_MAX,
+};
+
+#define TYPE_IMX8MM_ANALOG "imx8mm.analog"
+OBJECT_DECLARE_SIMPLE_TYPE(IMX8MMAnalogState, IMX8MM_ANALOG)
+
+struct IMX8MMAnalogState {
+    SysBusDevice parent_obj;
+
+    struct {
+        MemoryRegion container;
+        MemoryRegion analog;
+    } mmio;
+
+    uint32_t analog[ANALOG_MAX];
+};
+
+#endif /* IMX8MM_ANALOG_H */
diff --git a/include/hw/misc/imx8mm_ccm.h b/include/hw/misc/imx8mm_ccm.h
new file mode 100644
index 0000000000..e02e25f178
--- /dev/null
+++ b/include/hw/misc/imx8mm_ccm.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2025 Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
+ *
+ * i.MX 8MM CCM IP block emulation code
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef IMX8MM_CCM_H
+#define IMX8MM_CCM_H
+
+#include "hw/misc/imx_ccm.h"
+#include "qom/object.h"
+
+enum IMX8MMCCMRegisters {
+    CCM_MAX = 0xc6fc / sizeof(uint32_t) + 1,
+};
+
+#define TYPE_IMX8MM_CCM "imx8mm.ccm"
+OBJECT_DECLARE_SIMPLE_TYPE(IMX8MMCCMState, IMX8MM_CCM)
+
+struct IMX8MMCCMState {
+    IMXCCMState parent_obj;
+
+    MemoryRegion iomem;
+
+    uint32_t ccm[CCM_MAX];
+};
+
+#endif /* IMX8MM_CCM_H */
diff --git a/include/hw/timer/imx_gpt.h b/include/hw/timer/imx_gpt.h
index 5488f7e4df..a72cd71e01 100644
--- a/include/hw/timer/imx_gpt.h
+++ b/include/hw/timer/imx_gpt.h
@@ -3,9 +3,11 @@
  *
  * Copyright (c) 2008 OK Labs
  * Copyright (c) 2011 NICTA Pty Ltd
+ * Copyright (c) 2025 NXP
  * Originally written by Hans Jiang
  * Updated by Peter Chubb
  * Updated by Jean-Christophe Dubois jcd@tribudubois.net<mailto:jcd@tribudubois.net>
+ * Updated by Gaurav Sharma gaurav.sharma_7@nxp.com<mailto:gaurav.sharma_7@nxp.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -81,6 +83,7 @@
#define TYPE_IMX6UL_GPT "imx6ul.gpt"
#define TYPE_IMX7_GPT "imx7.gpt"
#define TYPE_IMX8MP_GPT "imx8mp.gpt"
+#define TYPE_IMX8MM_GPT "imx8mm.gpt"
 #define TYPE_IMX_GPT TYPE_IMX25_GPT
--
2.34.1



[-- Attachment #2: Type: text/html, Size: 245716 bytes --]

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

end of thread, other threads:[~2025-11-10 10:18 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-08 12:27 [PATCH] Add support for the NXP i.MX8MM EVK (Evaluation Kit) board Gaurav Sharma
2025-11-08 19:04 ` Peter Maydell
2025-11-10  4:43   ` [EXT] " Gaurav Sharma
2025-11-10 10:08     ` Peter Maydell

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).