summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--arch/arm/dts/imx95-19x19-evk-u-boot.dtsi62
-rw-r--r--arch/arm/dts/imx95-u-boot.dtsi188
-rw-r--r--arch/arm/include/asm/arch-imx/cpu.h2
-rw-r--r--arch/arm/include/asm/arch-imx9/clock.h10
-rw-r--r--arch/arm/include/asm/arch-imx9/imx-regs.h6
-rw-r--r--arch/arm/include/asm/arch-imx9/sys_proto.h1
-rw-r--r--arch/arm/include/asm/mach-imx/sys_proto.h39
-rw-r--r--arch/arm/mach-imx/Kconfig2
-rw-r--r--arch/arm/mach-imx/image-container.c119
-rw-r--r--arch/arm/mach-imx/imx9/Kconfig15
-rw-r--r--arch/arm/mach-imx/imx9/Makefile9
-rw-r--r--arch/arm/mach-imx/imx9/scmi/Makefile6
-rw-r--r--arch/arm/mach-imx/imx9/scmi/clock.c70
-rw-r--r--arch/arm/mach-imx/imx9/scmi/clock_scmi.c133
-rw-r--r--arch/arm/mach-imx/imx9/scmi/container.cfg10
-rw-r--r--arch/arm/mach-imx/imx9/scmi/imximage.cfg15
-rw-r--r--arch/arm/mach-imx/imx9/scmi/soc.c749
-rw-r--r--arch/sandbox/include/asm/scmi_test.h4
-rw-r--r--board/freescale/imx95_evk/Kconfig12
-rw-r--r--board/freescale/imx95_evk/MAINTAINERS6
-rw-r--r--board/freescale/imx95_evk/Makefile11
-rw-r--r--board/freescale/imx95_evk/imx95_19x19_evk.env91
-rw-r--r--board/freescale/imx95_evk/imx95_evk.c36
-rw-r--r--board/freescale/imx95_evk/spl.c69
-rw-r--r--common/spl/Kconfig6
-rw-r--r--common/spl/spl_imx_container.c38
-rw-r--r--configs/imx8mm_beacon_defconfig1
-rw-r--r--configs/imx8mn_beacon_defconfig1
-rw-r--r--configs/imx8mp_beacon_defconfig1
-rw-r--r--configs/imx95_19x19_evk_defconfig152
-rw-r--r--doc/board/nxp/imx95_evk.rst114
-rw-r--r--doc/board/nxp/index.rst1
-rw-r--r--doc/imx/imx95_container.txt136
-rw-r--r--drivers/clk/clk_scmi.c204
-rw-r--r--drivers/cpu/imx8_cpu.c2
-rw-r--r--drivers/firmware/scmi/base.c24
-rw-r--r--drivers/firmware/scmi/sandbox-scmi_agent.c56
-rw-r--r--drivers/firmware/scmi/scmi_agent-uclass.c50
-rw-r--r--drivers/pinctrl/nxp/Kconfig13
-rw-r--r--drivers/pinctrl/nxp/Makefile1
-rw-r--r--drivers/pinctrl/nxp/pinctrl-imx-scmi.c165
-rw-r--r--drivers/power/domain/scmi-power-domain.c8
-rw-r--r--drivers/power/regulator/scmi_regulator.c8
-rw-r--r--drivers/reset/reset-scmi.c8
-rw-r--r--include/configs/imx95_evk.h24
-rw-r--r--include/imx8image.h19
-rw-r--r--include/scmi_agent-uclass.h17
-rw-r--r--include/scmi_protocols.h104
-rw-r--r--tools/binman/entries.rst10
-rw-r--r--tools/binman/etype/nxp_header_ddrfw.py29
-rw-r--r--tools/binman/ftest.py11
-rw-r--r--tools/binman/test/346_nxp_ddrfw_imx95.dts24
-rw-r--r--tools/imx8image.c147
-rwxr-xr-xtools/imx9_image.sh8
55 files changed, 2950 insertions, 100 deletions
diff --git a/Makefile b/Makefile
index 7451d7a0059..8f60b27b710 100644
--- a/Makefile
+++ b/Makefile
@@ -2231,7 +2231,8 @@ CLEAN_FILES += include/autoconf.mk* include/bmp_logo.h include/bmp_logo_data.h \
itb.fit.fit itb.fit.itb itb.map spl.map mkimage-out.rom.mkimage \
mkimage.rom.mkimage mkimage-in-simple-bin* rom.map simple-bin* \
idbloader-spi.img lib/efi_loader/helloworld_efi.S *.itb \
- Test* capsule*.*.efi-capsule capsule*.map
+ Test* capsule*.*.efi-capsule capsule*.map mkimage.imx-boot.spl \
+ mkimage.imx-boot.u-boot mkimage-out.imx-boot.spl mkimage-out.imx-boot.u-boot
# Directories & files removed with 'make mrproper'
MRPROPER_DIRS += include/config include/generated spl tpl vpl \
diff --git a/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi b/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi
new file mode 100644
index 00000000000..2d1f02baa5f
--- /dev/null
+++ b/arch/arm/dts/imx95-19x19-evk-u-boot.dtsi
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include "imx95-u-boot.dtsi"
+
+&lpuart1 {
+ bootph-pre-ram;
+};
+
+&reg_usdhc2_vmmc {
+ bootph-pre-ram;
+};
+
+&usdhc1 {
+ bootph-pre-ram;
+};
+
+&usdhc2 {
+ bootph-pre-ram;
+};
+
+&wdog3 {
+ status = "disabled";
+};
+
+&pinctrl_uart1 {
+ bootph-pre-ram;
+};
+
+&pinctrl_usdhc1 {
+ bootph-pre-ram;
+};
+
+&pinctrl_usdhc1_100mhz {
+ bootph-pre-ram;
+};
+
+&pinctrl_usdhc1_200mhz {
+ bootph-pre-ram;
+};
+
+&pinctrl_usdhc2 {
+ bootph-pre-ram;
+};
+
+&pinctrl_usdhc2_100mhz {
+ bootph-pre-ram;
+};
+
+&pinctrl_usdhc2_200mhz {
+ bootph-pre-ram;
+};
+
+&pinctrl_usdhc2_gpio {
+ bootph-pre-ram;
+};
+
+&pinctrl_reg_usdhc2_vmmc {
+ bootph-pre-ram;
+};
diff --git a/arch/arm/dts/imx95-u-boot.dtsi b/arch/arm/dts/imx95-u-boot.dtsi
new file mode 100644
index 00000000000..5ec3b1c51d6
--- /dev/null
+++ b/arch/arm/dts/imx95-u-boot.dtsi
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+/ {
+ binman {
+ multiple-images;
+
+ m33-oei-ddrfw {
+ pad-byte = <0x00>;
+ align-size = <0x8>;
+ filename = "m33-oei-ddrfw.bin";
+
+ oei-m33-ddr {
+ align-size = <0x4>;
+ filename = "oei-m33-ddr.bin";
+ type = "blob-ext";
+ };
+
+ imx-lpddr {
+ type = "nxp-header-ddrfw";
+
+ imx-lpddr-imem {
+ filename = "lpddr5_imem_v202311.bin";
+ type = "blob-ext";
+ };
+
+ imx-lpddr-dmem {
+ filename = "lpddr5_dmem_v202311.bin";
+ type = "blob-ext";
+ };
+ };
+
+ imx-lpddr-qb {
+ type = "nxp-header-ddrfw";
+
+ imx-lpddr-imem-qb {
+ filename = "lpddr5_imem_qb_v202311.bin";
+ type = "blob-ext";
+ };
+
+ imx-lpddr-dmem-qb {
+ filename = "lpddr5_dmem_qb_v202311.bin";
+ type = "blob-ext";
+ };
+ };
+ };
+
+ imx-boot {
+ filename = "flash.bin";
+ pad-byte = <0x00>;
+
+ spl {
+ align = <0x400>;
+ align-size = <0x400>;
+ type = "mkimage";
+ args = "-n spl/u-boot-spl.cfgout -T imx8image";
+ };
+
+ u-boot {
+ type = "mkimage";
+ args = "-n u-boot-container.cfgout -T imx8image";
+ };
+ };
+ };
+};
+
+&A55_0 {
+ clocks = <&scmi_clk IMX95_CLK_ARMPLL_PFD0>;
+ /delete-property/ power-domains;
+};
+
+&A55_1 {
+ clocks = <&scmi_clk IMX95_CLK_ARMPLL_PFD0>;
+ /delete-property/ power-domains;
+};
+
+&A55_2 {
+ clocks = <&scmi_clk IMX95_CLK_ARMPLL_PFD0>;
+ /delete-property/ power-domains;
+};
+
+&A55_3 {
+ clocks = <&scmi_clk IMX95_CLK_ARMPLL_PFD0>;
+ /delete-property/ power-domains;
+};
+
+&A55_4 {
+ clocks = <&scmi_clk IMX95_CLK_ARMPLL_PFD0>;
+ /delete-property/ power-domains;
+};
+
+&A55_5 {
+ clocks = <&scmi_clk IMX95_CLK_ARMPLL_PFD0>;
+ /delete-property/ power-domains;
+};
+
+&aips1 {
+ bootph-all;
+};
+
+&aips2 {
+ bootph-all;
+};
+
+&aips3 {
+ bootph-pre-ram;
+};
+
+&clk_ext1 {
+ bootph-all;
+};
+
+&elemu1 {
+ bootph-all;
+ status = "okay";
+};
+
+&elemu3 {
+ bootph-all;
+ status = "okay";
+};
+
+&{/firmware} {
+ bootph-all;
+};
+
+&{/firmware/scmi} {
+ bootph-all;
+};
+
+&{/firmware/scmi/protocol@11} {
+ bootph-all;
+};
+
+&{/firmware/scmi/protocol@13} {
+ bootph-all;
+};
+
+&{/firmware/scmi/protocol@14} {
+ bootph-all;
+};
+
+&{/firmware/scmi/protocol@19} {
+ bootph-all;
+};
+
+&gpio2 {
+ bootph-pre-ram;
+};
+
+&gpio3 {
+ bootph-pre-ram;
+};
+
+&gpio4 {
+ bootph-pre-ram;
+};
+
+&gpio5 {
+ bootph-pre-ram;
+};
+
+&mu2 {
+ bootph-all;
+};
+
+&osc_24m {
+ bootph-all;
+};
+
+&{/soc} {
+ bootph-all;
+};
+
+&sram0 {
+ bootph-all;
+};
+
+&scmi_buf0 {
+ reg = <0x0 0x400>;
+ bootph-all;
+};
+
+&scmi_buf1 {
+ bootph-all;
+};
diff --git a/arch/arm/include/asm/arch-imx/cpu.h b/arch/arm/include/asm/arch-imx/cpu.h
index 0d7a5734616..1f669c72d00 100644
--- a/arch/arm/include/asm/arch-imx/cpu.h
+++ b/arch/arm/include/asm/arch-imx/cpu.h
@@ -76,6 +76,8 @@
#define MXC_CPU_IMX9111 0xCD /* dummy ID */
#define MXC_CPU_IMX9101 0xCE /* dummy ID */
+#define MXC_CPU_IMX95 0x1C1 /* dummy ID */
+
#define MXC_SOC_MX6 0x60
#define MXC_SOC_MX7 0x70
#define MXC_SOC_IMX8M 0x80
diff --git a/arch/arm/include/asm/arch-imx9/clock.h b/arch/arm/include/asm/arch-imx9/clock.h
index 60d48b13b11..ffaf6b5f7d8 100644
--- a/arch/arm/include/asm/arch-imx9/clock.h
+++ b/arch/arm/include/asm/arch-imx9/clock.h
@@ -255,5 +255,15 @@ int ccm_shared_gpr_tz_access(u32 gpr, bool non_secure, bool user_mode, bool lock
void enable_usboh3_clk(unsigned char enable);
int set_clk_enet(enum enet_freq type);
int set_clk_eqos(enum enet_freq type);
+
+int imx_clk_scmi_enable(u32 clock_id, bool enable);
+ulong imx_clk_scmi_set_rate(u32 clock_id, ulong rate);
+ulong imx_clk_scmi_get_rate(u32 clock_id);
+int imx_clk_scmi_set_parent(u32 clock_id, u32 parent_id);
void set_arm_clk(ulong freq);
+
+int imx_clk_scmi_enable(u32 clock_id, bool enable);
+ulong imx_clk_scmi_set_rate(u32 clock_id, ulong rate);
+ulong imx_clk_scmi_get_rate(u32 clock_id);
+int imx_clk_scmi_set_parent(u32 clock_id, u32 parent_id);
#endif
diff --git a/arch/arm/include/asm/arch-imx9/imx-regs.h b/arch/arm/include/asm/arch-imx9/imx-regs.h
index ef9538bd42e..5127fe8f286 100644
--- a/arch/arm/include/asm/arch-imx9/imx-regs.h
+++ b/arch/arm/include/asm/arch-imx9/imx-regs.h
@@ -13,6 +13,7 @@
#define CCM_BASE_ADDR 0x44450000UL
#define CCM_CCGR_BASE_ADDR 0x44458000UL
#define SYSCNT_CTRL_BASE_ADDR 0x44290000
+#define SYSCNT_CMP_BASE_ADDR (SYSCNT_CTRL_BASE_ADDR + 0x10000)
#define ANATOP_BASE_ADDR 0x44480000UL
@@ -20,6 +21,11 @@
#define WDG4_BASE_ADDR 0x424a0000UL
#define WDG5_BASE_ADDR 0x424b0000UL
+#define GPIO2_BASE_ADDR 0x43810000UL
+#define GPIO3_BASE_ADDR 0x43820000UL
+#define GPIO4_BASE_ADDR 0x43840000UL
+#define GPIO5_BASE_ADDR 0x43850000UL
+
#define FSB_BASE_ADDR 0x47510000UL
#define ANATOP_BASE_ADDR 0x44480000UL
diff --git a/arch/arm/include/asm/arch-imx9/sys_proto.h b/arch/arm/include/asm/arch-imx9/sys_proto.h
index e4bf6a63424..df2148a53c7 100644
--- a/arch/arm/include/asm/arch-imx9/sys_proto.h
+++ b/arch/arm/include/asm/arch-imx9/sys_proto.h
@@ -12,6 +12,7 @@ enum imx9_soc_voltage_mode {
VOLT_LOW_DRIVE = 0,
VOLT_NOMINAL_DRIVE,
VOLT_OVER_DRIVE,
+ VOLT_SUPER_OVER_DRIVE,
};
void soc_power_init(void);
diff --git a/arch/arm/include/asm/mach-imx/sys_proto.h b/arch/arm/include/asm/mach-imx/sys_proto.h
index 109a806852a..0780f99b49a 100644
--- a/arch/arm/include/asm/mach-imx/sys_proto.h
+++ b/arch/arm/include/asm/mach-imx/sys_proto.h
@@ -97,6 +97,8 @@ struct bd_info;
#define is_imx9302() (is_cpu_type(MXC_CPU_IMX9302))
#define is_imx9301() (is_cpu_type(MXC_CPU_IMX9301))
+#define is_imx95() (is_cpu_type(MXC_CPU_IMX95))
+
#define is_imx9121() (is_cpu_type(MXC_CPU_IMX9121))
#define is_imx9111() (is_cpu_type(MXC_CPU_IMX9111))
#define is_imx9101() (is_cpu_type(MXC_CPU_IMX9101))
@@ -216,6 +218,43 @@ ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev);
u32 rom_api_download_image(u8 *dest, u32 offset, u32 size);
u32 rom_api_query_boot_infor(u32 info_type, u32 *info);
+#if IS_ENABLED(CONFIG_SCMI_FIRMWARE)
+typedef struct rom_passover {
+ u16 tag; // Tag
+ u8 len; // Fixed value of 0x80
+ u8 ver; // Version
+ u32 boot_mode; // Boot mode
+ u32 card_addr_mode; // SD card address mode
+ u32 bad_blks_of_img_set0; // NAND bad block count skipped 1
+ u32 ap_mu_id; // AP MU ID
+ u32 bad_blks_of_img_set1; // NAND bad block count skipped 1
+ u8 boot_stage; // Boot stage
+ u8 img_set_sel; // Image set booted from
+ u8 rsv0[2]; // Reserved
+ u32 img_set_end; // Offset of Image End
+ u32 rom_version; // ROM version
+ u8 boot_dev_state; // Boot device state
+ u8 boot_dev_inst; // Boot device type
+ u8 boot_dev_type; // Boot device instance
+ u8 rsv1; // Reserved
+ u32 dev_page_size; // Boot device page size
+ u32 cnt_header_ofs; // Container header offset
+ u32 img_ofs; // Image offset
+} __packed rom_passover_t;
+
+/**
+ * struct scmi_rom_passover_out - Response payload for ROM_PASSOVER_GET command
+ * @status: SCMI clock ID
+ * @attributes: Attributes of the targets clock state
+ */
+struct scmi_rom_passover_get_out {
+ u32 status;
+ u32 numPassover;
+ u32 passover[(sizeof(rom_passover_t) + 8) / 4];
+};
+
+#endif
+
/* For i.MX ULP */
#define BT0CFG_LPBOOT_MASK 0x1
#define BT0CFG_DUALBOOT_MASK 0x2
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 134e42028c3..e4014226582 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -180,7 +180,7 @@ config DDRMC_VF610_CALIBRATION
config IMX8_ROMAPI
def_bool y
- depends on IMX8MN || IMX8MP || IMX8ULP || IMX9
+ depends on IMX8MN || IMX8MP || IMX8ULP || IMX91 || IMX93
config SPL_IMX_ROMAPI_LOADADDR
hex "Default load address to load image through ROM API"
diff --git a/arch/arm/mach-imx/image-container.c b/arch/arm/mach-imx/image-container.c
index 2afe9d38a06..f84e23f4b2a 100644
--- a/arch/arm/mach-imx/image-container.c
+++ b/arch/arm/mach-imx/image-container.c
@@ -41,6 +41,52 @@
#define FUSE_IMG_SET_OFF_WORD 720
#endif
+#define MAX_V2X_CTNR_IMG_NUM (4)
+#define MIN_V2X_CTNR_IMG_NUM (2)
+
+#define IMG_FLAGS_IMG_TYPE_SHIFT (0u)
+#define IMG_FLAGS_IMG_TYPE_MASK (0xfU)
+#define IMG_FLAGS_IMG_TYPE(x) (((x) & IMG_FLAGS_IMG_TYPE_MASK) >> \
+ IMG_FLAGS_IMG_TYPE_SHIFT)
+
+#define IMG_FLAGS_CORE_ID_SHIFT (4u)
+#define IMG_FLAGS_CORE_ID_MASK (0xf0U)
+#define IMG_FLAGS_CORE_ID(x) (((x) & IMG_FLAGS_CORE_ID_MASK) >> \
+ IMG_FLAGS_CORE_ID_SHIFT)
+
+#define IMG_TYPE_V2X_PRI_FW (0x0Bu) /* Primary V2X FW */
+#define IMG_TYPE_V2X_SND_FW (0x0Cu) /* Secondary V2X FW */
+
+#define CORE_V2X_PRI 9
+#define CORE_V2X_SND 10
+
+static bool is_v2x_fw_container(ulong addr)
+{
+ struct container_hdr *phdr;
+ struct boot_img_t *img_entry;
+
+ phdr = (struct container_hdr *)addr;
+ if (phdr->tag != 0x87 || phdr->version != 0x0) {
+ debug("Wrong container header\n");
+ return false;
+ }
+
+ if (phdr->num_images >= MIN_V2X_CTNR_IMG_NUM && phdr->num_images <= MAX_V2X_CTNR_IMG_NUM) {
+ img_entry = (struct boot_img_t *)(addr + sizeof(struct container_hdr));
+
+ if (IMG_FLAGS_IMG_TYPE(img_entry->hab_flags) == IMG_TYPE_V2X_PRI_FW &&
+ IMG_FLAGS_CORE_ID(img_entry->hab_flags) == CORE_V2X_PRI) {
+ img_entry++;
+
+ if (IMG_FLAGS_IMG_TYPE(img_entry->hab_flags) == IMG_TYPE_V2X_SND_FW &&
+ IMG_FLAGS_CORE_ID(img_entry->hab_flags) == CORE_V2X_SND)
+ return true;
+ }
+ }
+
+ return false;
+}
+
int get_container_size(ulong addr, u16 *header_length)
{
struct container_hdr *phdr;
@@ -83,7 +129,7 @@ int get_container_size(ulong addr, u16 *header_length)
return max_offset;
}
-static int get_dev_container_size(void *dev, int dev_type, unsigned long offset, u16 *header_length)
+static int get_dev_container_size(void *dev, int dev_type, unsigned long offset, u16 *header_length, bool *v2x_cntr)
{
u8 *buf = malloc(CONTAINER_HDR_ALIGNMENT);
int ret = 0;
@@ -150,6 +196,9 @@ static int get_dev_container_size(void *dev, int dev_type, unsigned long offset,
ret = get_container_size((ulong)buf, header_length);
+ if (v2x_cntr)
+ *v2x_cntr = is_v2x_fw_container((ulong)buf);
+
free(buf);
return ret;
@@ -231,45 +280,67 @@ static unsigned long get_boot_device_offset(void *dev, int dev_type)
return offset;
}
-static int get_imageset_end(void *dev, int dev_type)
+static ulong get_imageset_end(void *dev, int dev_type)
{
- unsigned long offset1 = 0, offset2 = 0;
- int value_container[2];
+ unsigned long offset[3] = {};
+ int value_container[3] = {};
u16 hdr_length;
+ bool v2x_fw = false;
- offset1 = get_boot_device_offset(dev, dev_type);
- offset2 = CONTAINER_HDR_ALIGNMENT + offset1;
+ offset[0] = get_boot_device_offset(dev, dev_type);
- value_container[0] = get_dev_container_size(dev, dev_type, offset1, &hdr_length);
+ value_container[0] = get_dev_container_size(dev, dev_type, offset[0], &hdr_length, NULL);
if (value_container[0] < 0) {
printf("Parse seco container failed %d\n", value_container[0]);
- return value_container[0];
+ return 0;
}
debug("seco container size 0x%x\n", value_container[0]);
- value_container[1] = get_dev_container_size(dev, dev_type, offset2, &hdr_length);
- if (value_container[1] < 0) {
- debug("Parse scu container failed %d, only seco container\n",
- value_container[1]);
- /* return seco container total size */
- return value_container[0] + offset1;
+ if (is_imx95()) {
+ offset[1] = ALIGN(hdr_length, CONTAINER_HDR_ALIGNMENT) + offset[0];
+
+ value_container[1] = get_dev_container_size(dev, dev_type, offset[1], &hdr_length, &v2x_fw);
+ if (value_container[1] < 0) {
+ printf("Parse v2x container failed %d\n", value_container[1]);
+ return value_container[0] + offset[0]; /* return seco container total size */
+ }
+
+ if (v2x_fw) {
+ debug("v2x container size 0x%x\n", value_container[1]);
+ offset[2] = ALIGN(hdr_length, CONTAINER_HDR_ALIGNMENT) + offset[1];
+ } else {
+ printf("no v2x container included\n");
+ offset[2] = offset[1];
+ }
+ } else {
+ /* Skip offset[1] */
+ offset[2] = ALIGN(hdr_length, CONTAINER_HDR_ALIGNMENT) + offset[0];
+ }
+
+ value_container[2] = get_dev_container_size(dev, dev_type, offset[2], &hdr_length, NULL);
+ if (value_container[2] < 0) {
+ debug("Parse scu container image failed %d, only seco container\n", value_container[2]);
+ if (is_imx95())
+ return value_container[1] + offset[1]; /* return seco + v2x container total size */
+ else
+ return value_container[0] + offset[0]; /* return seco container total size */
}
- debug("scu container size 0x%x\n", value_container[1]);
+ debug("scu container size 0x%x\n", value_container[2]);
- return value_container[1] + offset2;
+ return value_container[2] + offset[2];
}
#ifdef CONFIG_SPL_SPI_LOAD
unsigned int spl_spi_get_uboot_offs(struct spi_flash *flash)
{
- int end;
+ ulong end;
end = get_imageset_end(flash, QSPI_DEV);
end = ROUND(end, SZ_1K);
- printf("Load image from QSPI 0x%x\n", end);
+ printf("Load image from QSPI 0x%lx\n", end);
return end;
}
@@ -279,12 +350,12 @@ unsigned int spl_spi_get_uboot_offs(struct spi_flash *flash)
unsigned long arch_spl_mmc_get_uboot_raw_sector(struct mmc *mmc,
unsigned long raw_sect)
{
- int end;
+ ulong end;
end = get_imageset_end(mmc, MMC_DEV);
end = ROUND(end, SZ_1K);
- printf("Load image from MMC/SD 0x%x\n", end);
+ printf("Load image from MMC/SD 0x%lx\n", end);
return end / mmc->read_bl_len;
}
@@ -312,12 +383,12 @@ int spl_mmc_emmc_boot_partition(struct mmc *mmc)
#ifdef CONFIG_SPL_NAND_SUPPORT
uint32_t spl_nand_get_uboot_raw_page(void)
{
- int end;
+ ulong end;
end = get_imageset_end((void *)NULL, NAND_DEV);
end = ROUND(end, SZ_16K);
- printf("Load image from NAND 0x%x\n", end);
+ printf("Load image from NAND 0x%lx\n", end);
return end;
}
@@ -326,7 +397,7 @@ uint32_t spl_nand_get_uboot_raw_page(void)
#ifdef CONFIG_SPL_NOR_SUPPORT
unsigned long spl_nor_get_uboot_base(void)
{
- int end;
+ ulong end;
/* Calculate the image set end,
* if it is less than CFG_SYS_UBOOT_BASE(0x8281000),
@@ -339,7 +410,7 @@ unsigned long spl_nor_get_uboot_base(void)
else
end = ROUND(end, SZ_1K);
- printf("Load image from NOR 0x%x\n", end);
+ printf("Load image from NOR 0x%lx\n", end);
return end;
}
diff --git a/arch/arm/mach-imx/imx9/Kconfig b/arch/arm/mach-imx/imx9/Kconfig
index 1ccdb1cf64f..0fd82dc0811 100644
--- a/arch/arm/mach-imx/imx9/Kconfig
+++ b/arch/arm/mach-imx/imx9/Kconfig
@@ -24,6 +24,13 @@ config IMX91
select IMX9
select ARMV8_SPL_EXCEPTION_VECTORS
+config IMX95
+ bool
+ select ARMV8_SPL_EXCEPTION_VECTORS
+ select IMX9
+ select DM_MAILBOX
+ select SCMI_FIRMWARE
+ select SPL_IMX_CONTAINER_USE_TRAMPOLINE
config SYS_SOC
default "imx9"
@@ -69,6 +76,13 @@ config TARGET_PHYCORE_IMX93
select OF_BOARD_FIXUP
select OF_BOARD_SETUP
+config TARGET_IMX95_19X19_EVK
+ bool "imx95_19x19_evk"
+ select IMX95
+ imply BOOTSTD_BOOTCOMMAND
+ imply BOOTSTD_FULL
+ imply OF_UPSTREAM
+
endchoice
source "board/freescale/imx91_evk/Kconfig"
@@ -76,6 +90,7 @@ source "board/freescale/imx93_evk/Kconfig"
source "board/freescale/imx93_qsb/Kconfig"
source "board/phytec/phycore_imx93/Kconfig"
source "board/variscite/imx93_var_som/Kconfig"
+source "board/freescale/imx95_evk/Kconfig"
endif
diff --git a/arch/arm/mach-imx/imx9/Makefile b/arch/arm/mach-imx/imx9/Makefile
index 45a9105a75a..53cc97c6b47 100644
--- a/arch/arm/mach-imx/imx9/Makefile
+++ b/arch/arm/mach-imx/imx9/Makefile
@@ -3,8 +3,13 @@
# Copyright 2022 NXP
obj-y += lowlevel_init.o
+
+ifeq ($(CONFIG_SCMI_FIRMWARE),y)
+obj-y += scmi/
+else
obj-y += soc.o clock.o clock_root.o trdc.o
+endif
-#ifndef CONFIG_XPL_BUILD
+ifneq ($(CONFIG_SPL_BUILD),y)
obj-y += imx_bootaux.o
-#endif
+endif \ No newline at end of file
diff --git a/arch/arm/mach-imx/imx9/scmi/Makefile b/arch/arm/mach-imx/imx9/scmi/Makefile
new file mode 100644
index 00000000000..4534db08d28
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2025 NXP
+
+obj-y += soc.o
+obj-y += clock_scmi.o clock.o
diff --git a/arch/arm/mach-imx/imx9/scmi/clock.c b/arch/arm/mach-imx/imx9/scmi/clock.c
new file mode 100644
index 00000000000..6e6541eaa31
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/clock.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <asm/arch/clock.h>
+#include <dm/uclass.h>
+#include <scmi_agent.h>
+#include "../../../../../dts/upstream/src/arm64/freescale/imx95-clock.h"
+
+u32 get_arm_core_clk(void)
+{
+ u32 val;
+
+ val = imx_clk_scmi_get_rate(IMX95_CLK_SEL_A55C0);
+ if (val)
+ return val;
+ return imx_clk_scmi_get_rate(IMX95_CLK_A55);
+}
+
+void init_uart_clk(u32 index)
+{
+ u32 clock_id;
+
+ switch (index) {
+ case 0:
+ clock_id = IMX95_CLK_LPUART1;
+ break;
+ case 1:
+ clock_id = IMX95_CLK_LPUART2;
+ break;
+ case 2:
+ clock_id = IMX95_CLK_LPUART3;
+ break;
+ default:
+ return;
+ }
+
+ /* 24MHz */
+ imx_clk_scmi_enable(clock_id, false);
+ imx_clk_scmi_set_parent(clock_id, IMX95_CLK_24M);
+ imx_clk_scmi_set_rate(clock_id, 24000000);
+ imx_clk_scmi_enable(clock_id, true);
+}
+
+unsigned int mxc_get_clock(enum mxc_clock clk)
+{
+ switch (clk) {
+ case MXC_ARM_CLK:
+ return get_arm_core_clk();
+ case MXC_IPG_CLK:
+ return imx_clk_scmi_get_rate(IMX95_CLK_BUSWAKEUP);
+ case MXC_CSPI_CLK:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPSPI1);
+ case MXC_ESDHC_CLK:
+ return imx_clk_scmi_get_rate(IMX95_CLK_USDHC1);
+ case MXC_ESDHC2_CLK:
+ return imx_clk_scmi_get_rate(IMX95_CLK_USDHC2);
+ case MXC_ESDHC3_CLK:
+ return imx_clk_scmi_get_rate(IMX95_CLK_USDHC3);
+ case MXC_UART_CLK:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPUART1);
+ case MXC_FLEXSPI_CLK:
+ return imx_clk_scmi_get_rate(IMX95_CLK_FLEXSPI1);
+ default:
+ return -1;
+ };
+
+ return -1;
+};
diff --git a/arch/arm/mach-imx/imx9/scmi/clock_scmi.c b/arch/arm/mach-imx/imx9/scmi/clock_scmi.c
new file mode 100644
index 00000000000..fa15b5f8df9
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/clock_scmi.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ *
+ * Peng Fan <peng.fan@nxp.com>
+ */
+
+#include <dm/uclass.h>
+#include <scmi_agent.h>
+
+int imx_clk_scmi_enable(u32 clock_id, bool enable)
+{
+ struct scmi_clk_state_in in = {
+ .clock_id = clock_id,
+ .attributes = (enable) ? 1 : 0,
+ };
+ struct scmi_clk_state_out out;
+ struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
+ SCMI_CLOCK_CONFIG_SET,
+ in, out);
+ int ret;
+ struct udevice *dev;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev);
+ if (ret)
+ return ret;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret)
+ return ret;
+
+ return scmi_to_linux_errno(out.status);
+}
+
+ulong imx_clk_scmi_set_rate(u32 clock_id, ulong rate)
+{
+ struct scmi_clk_rate_set_in in = {
+ .clock_id = clock_id,
+ .flags = SCMI_CLK_RATE_ROUND_CLOSEST,
+ .rate_lsb = (u32)rate,
+ .rate_msb = (u32)((u64)rate >> 32),
+ };
+ struct scmi_clk_rate_set_out out;
+ struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
+ SCMI_CLOCK_RATE_SET,
+ in, out);
+ int ret;
+ struct udevice *dev;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev);
+ if (ret)
+ return ret;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret < 0)
+ return ret;
+
+ ret = scmi_to_linux_errno(out.status);
+ if (ret < 0)
+ return ret;
+
+ struct scmi_clk_rate_get_in in_rate = {
+ .clock_id = clock_id,
+ };
+ struct scmi_clk_rate_get_out out_rate;
+
+ msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK, SCMI_CLOCK_RATE_GET, in_rate, out_rate);
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret < 0)
+ return ret;
+
+ ret = scmi_to_linux_errno(out_rate.status);
+ if (ret < 0)
+ return ret;
+
+ return (ulong)(((u64)out_rate.rate_msb << 32) | out_rate.rate_lsb);
+}
+
+ulong imx_clk_scmi_get_rate(u32 clock_id)
+{
+ struct scmi_clk_rate_get_in in = {
+ .clock_id = clock_id,
+ };
+ struct scmi_clk_rate_get_out out;
+ struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
+ SCMI_CLOCK_RATE_GET,
+ in, out);
+ int ret;
+ struct udevice *dev;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev);
+ if (ret)
+ return ret;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret < 0)
+ return ret;
+
+ ret = scmi_to_linux_errno(out.status);
+ if (ret < 0)
+ return ret;
+
+ return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
+}
+
+int imx_clk_scmi_set_parent(u32 clock_id, u32 parent_id)
+{
+ struct scmi_clk_parent_set_in in = {
+ .clock_id = clock_id,
+ .parent_clk = parent_id,
+ };
+ struct scmi_clk_parent_set_out out;
+ struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
+ SCMI_CLOCK_PARENT_SET,
+ in, out);
+ int ret;
+ struct udevice *dev;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev);
+ if (ret)
+ return ret;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret < 0)
+ return ret;
+
+ ret = scmi_to_linux_errno(out.status);
+ if (ret < 0 && ret != -EACCES)
+ printf("%s: %d, clock_id %u\n", __func__, ret, clock_id);
+
+ return ret;
+}
diff --git a/arch/arm/mach-imx/imx9/scmi/container.cfg b/arch/arm/mach-imx/imx9/scmi/container.cfg
new file mode 100644
index 00000000000..441d9beedd1
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/container.cfg
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2025 NXP
+ */
+
+BOOT_FROM SD
+SOC_TYPE IMX9
+CONTAINER
+IMAGE A55 bl31.bin 0x8a200000
+IMAGE A55 u-boot.bin CONFIG_TEXT_BASE
diff --git a/arch/arm/mach-imx/imx9/scmi/imximage.cfg b/arch/arm/mach-imx/imx9/scmi/imximage.cfg
new file mode 100644
index 00000000000..6af1c4ba628
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/imximage.cfg
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2025 NXP
+ */
+
+BOOT_FROM SD
+SOC_TYPE IMX9
+APPEND mx95a0-ahab-container.img
+CONTAINER
+IMAGE OEI m33-oei-ddrfw.bin 0x1ffc0000
+HOLD 0x10000
+IMAGE OEI oei-m33-tcm.bin 0x1ffc0000
+IMAGE M33 m33_image.bin 0x1ffc0000
+IMAGE A55 spl/u-boot-spl.bin 0x20480000
+DUMMY_V2X 0x8b000000
diff --git a/arch/arm/mach-imx/imx9/scmi/soc.c b/arch/arm/mach-imx/imx9/scmi/soc.c
new file mode 100644
index 00000000000..d2b0455bff9
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/soc.c
@@ -0,0 +1,749 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ *
+ * Peng Fan <peng.fan@nxp.com>
+ */
+
+#include <asm/arch/clock.h>
+#include <asm/arch/ddr.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/armv8/mmu.h>
+#include <asm/mach-imx/boot_mode.h>
+#include <asm/mach-imx/ele_api.h>
+#include <asm/setup.h>
+#include <dm/uclass.h>
+#include <dm/device.h>
+#include <env_internal.h>
+#include <fuse.h>
+#include <imx_thermal.h>
+#include <linux/iopoll.h>
+#include <scmi_agent.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static rom_passover_t rom_passover_data = {0};
+
+uint32_t scmi_get_rom_data(rom_passover_t *rom_data)
+{
+ /* Read ROM passover data */
+ struct scmi_rom_passover_get_out out;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_IMX_MISC,
+ .message_id = SCMI_MISC_ROM_PASSOVER_GET,
+ .in_msg = (u8 *)NULL,
+ .in_msg_sz = 0,
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+ int ret;
+ struct udevice *dev;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev);
+ if (ret)
+ return ret;
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret == 0 && out.status == 0) {
+ memcpy(rom_data, (struct rom_passover_t *)out.passover, sizeof(rom_passover_t));
+ } else {
+ printf("Failed to get ROM passover data, scmi_err = %d, size_of(out) = %ld\n",
+ out.status, sizeof(out));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_ENV_IS_IN_MMC)
+__weak int board_mmc_get_env_dev(int devno)
+{
+ return devno;
+}
+
+int mmc_get_env_dev(void)
+{
+ int ret;
+ u16 boot_type;
+ u8 boot_instance;
+
+ volatile gd_t *pgd = gd;
+ rom_passover_t *rdata;
+
+#if IS_ENABLED(CONFIG_XPL_BUILD)
+ rdata = &rom_passover_data;
+#else
+ rom_passover_t rom_data = {0};
+
+ if (!pgd->reloc_off)
+ rdata = &rom_data;
+ else
+ rdata = &rom_passover_data;
+#endif
+ if (rdata->tag == 0) {
+ ret = scmi_get_rom_data(rdata);
+ if (ret != 0) {
+ puts("SCMI: failure at rom_boot_info\n");
+ return CONFIG_SYS_MMC_ENV_DEV;
+ }
+ }
+ boot_type = rdata->boot_dev_type;
+ boot_instance = rdata->boot_dev_inst;
+ set_gd(pgd);
+
+ debug("boot_type %d, instance %d\n", boot_type, boot_instance);
+
+ /* If not boot from sd/mmc, use default value */
+ if (boot_type != BOOT_TYPE_SD && boot_type != BOOT_TYPE_MMC)
+ return env_get_ulong("mmcdev", 10, CONFIG_SYS_MMC_ENV_DEV);
+
+ return board_mmc_get_env_dev(boot_instance);
+}
+#endif
+
+u32 get_cpu_speed_grade_hz(void)
+{
+ u32 speed, max_speed;
+ int ret;
+ u32 val, word, offset;
+
+ word = 17;
+ offset = 14;
+
+ ret = fuse_read(word / 8, word % 8, &val);
+ if (ret)
+ val = 0; /* If read fuse failed, return as blank fuse */
+
+ val >>= offset;
+ val &= 0xf;
+
+ max_speed = 2300000000;
+ speed = max_speed - val * 100000000;
+
+ if (is_imx95())
+ max_speed = 2000000000;
+
+ /* In case the fuse of speed grade not programmed */
+ if (speed > max_speed)
+ speed = max_speed;
+
+ return speed;
+}
+
+u32 get_cpu_temp_grade(int *minc, int *maxc)
+{
+ int ret;
+ u32 val, word, offset;
+
+ word = 17;
+ offset = 12;
+
+ ret = fuse_read(word / 8, word % 8, &val);
+ if (ret)
+ val = 0; /* If read fuse failed, return as blank fuse */
+
+ val >>= offset;
+ val &= 0x3;
+
+ if (minc && maxc) {
+ if (val == TEMP_AUTOMOTIVE) {
+ *minc = -40;
+ *maxc = 125;
+ } else if (val == TEMP_INDUSTRIAL) {
+ *minc = -40;
+ *maxc = 105;
+ } else if (val == TEMP_EXTCOMMERCIAL) {
+ *minc = -20;
+ *maxc = 105;
+ } else {
+ *minc = 0;
+ *maxc = 95;
+ }
+ }
+ return val;
+}
+
+static void set_cpu_info(struct ele_get_info_data *info)
+{
+ gd->arch.soc_rev = info->soc;
+ gd->arch.lifecycle = info->lc;
+ memcpy((void *)&gd->arch.uid, &info->uid, 4 * sizeof(u32));
+}
+
+u32 get_cpu_rev(void)
+{
+ u32 rev = (gd->arch.soc_rev >> 24) - 0xa0;
+
+ return (MXC_CPU_IMX95 << 12) | (CHIP_REV_1_0 + rev);
+}
+
+#define UNLOCK_WORD 0xD928C520
+#define REFRESH_WORD 0xB480A602
+
+static void disable_wdog(void __iomem *wdog_base)
+{
+ u32 val_cs = readl(wdog_base + 0x00);
+ int ret = 0;
+
+ if (!(val_cs & 0x80))
+ return;
+
+ /* default is 32bits cmd */
+ writel(REFRESH_WORD, (wdog_base + 0x04)); /* Refresh the CNT */
+
+ if (!(val_cs & 0x800)) {
+ writel(UNLOCK_WORD, (wdog_base + 0x04));
+ while (!(readl(wdog_base + 0x00) & 0x800))
+ ;
+ }
+ writel(0x0, (wdog_base + 0x0C)); /* Set WIN to 0 */
+ writel(0x400, (wdog_base + 0x08)); /* Set timeout to default 0x400 */
+ writel(0x2120, (wdog_base + 0x00)); /* Disable it and set update */
+
+ ret = readl_poll_timeout(wdog_base, val_cs, val_cs & 0x400, 100000);
+ if (ret < 0)
+ debug("%s timeout\n", __func__);
+}
+
+static struct mm_region imx9_mem_map[] = {
+ {
+ /* M7 TCM */
+ .virt = 0x203c0000UL,
+ .phys = 0x203c0000UL,
+ .size = 0x80000UL,
+ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
+ PTE_BLOCK_NON_SHARE |
+ PTE_BLOCK_PXN | PTE_BLOCK_UXN
+ }, {
+ /* OCRAM */
+ .virt = 0x20480000UL,
+ .phys = 0x20480000UL,
+ .size = 0xA0000UL,
+ .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+ PTE_BLOCK_OUTER_SHARE
+ }, {
+ /* AIPS */
+ .virt = 0x40000000UL,
+ .phys = 0x40000000UL,
+ .size = 0x40000000UL,
+ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
+ PTE_BLOCK_NON_SHARE |
+ PTE_BLOCK_PXN | PTE_BLOCK_UXN
+ }, {
+ /* Flexible Serial Peripheral Interface */
+ .virt = 0x28000000UL,
+ .phys = 0x28000000UL,
+ .size = 0x8000000UL,
+ .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
+ PTE_BLOCK_NON_SHARE |
+ PTE_BLOCK_PXN | PTE_BLOCK_UXN
+ }, {
+ /* DRAM1 */
+ .virt = PHYS_SDRAM,
+ .phys = PHYS_SDRAM,
+ .size = PHYS_SDRAM_SIZE,
+ .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+ PTE_BLOCK_OUTER_SHARE
+ }, {
+#ifdef PHYS_SDRAM_2_SIZE
+ /* DRAM2 */
+ .virt = 0x100000000UL,
+ .phys = 0x100000000UL,
+ .size = PHYS_SDRAM_2_SIZE,
+ .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+ PTE_BLOCK_OUTER_SHARE
+ }, {
+#endif
+ /* empty entry to split table entry 5 if needed when TEEs are used */
+ 0,
+ }, {
+ /* List terminator */
+ 0,
+ }
+};
+
+struct mm_region *mem_map = imx9_mem_map;
+
+static unsigned int imx9_find_dram_entry_in_mem_map(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx9_mem_map); i++)
+ if (imx9_mem_map[i].phys == CFG_SYS_SDRAM_BASE)
+ return i;
+
+ hang(); /* Entry not found, this must never happen. */
+}
+
+void enable_caches(void)
+{
+ /* If OPTEE runs, remove OPTEE memory from MMU table to avoid speculative prefetch
+ * If OPTEE does not run, still update the MMU table according to dram banks structure
+ * to set correct dram size from board_phys_sdram_size
+ */
+ int i = 0;
+ /*
+ * please make sure that entry initial value matches
+ * imx9_mem_map for DRAM1
+ */
+ int entry = imx9_find_dram_entry_in_mem_map();
+ u64 attrs = imx9_mem_map[entry].attrs;
+
+ while (i < CONFIG_NR_DRAM_BANKS &&
+ entry < ARRAY_SIZE(imx9_mem_map)) {
+ if (gd->bd->bi_dram[i].start == 0)
+ break;
+ imx9_mem_map[entry].phys = gd->bd->bi_dram[i].start;
+ imx9_mem_map[entry].virt = gd->bd->bi_dram[i].start;
+ imx9_mem_map[entry].size = gd->bd->bi_dram[i].size;
+ imx9_mem_map[entry].attrs = attrs;
+ debug("Added memory mapping (%d): %llx %llx\n", entry,
+ imx9_mem_map[entry].phys, imx9_mem_map[entry].size);
+ i++; entry++;
+ }
+
+ icache_enable();
+ dcache_enable();
+}
+
+__weak int board_phys_sdram_size(phys_size_t *size)
+{
+ phys_size_t start, end;
+ phys_size_t val;
+
+ if (!size)
+ return -EINVAL;
+
+ val = readl(REG_DDR_CS0_BNDS);
+ start = (val >> 16) << 24;
+ end = (val & 0xFFFF);
+ end = end ? end + 1 : 0;
+ end = end << 24;
+ *size = end - start;
+
+ val = readl(REG_DDR_CS1_BNDS);
+ start = (val >> 16) << 24;
+ end = (val & 0xFFFF);
+ end = end ? end + 1 : 0;
+ end = end << 24;
+ *size += end - start;
+
+ return 0;
+}
+
+int dram_init(void)
+{
+ phys_size_t sdram_size;
+ int ret;
+
+ ret = board_phys_sdram_size(&sdram_size);
+ if (ret)
+ return ret;
+
+ /* rom_pointer[1] contains the size of TEE occupies */
+ if (rom_pointer[1] && PHYS_SDRAM < (phys_addr_t)rom_pointer[0])
+ gd->ram_size = sdram_size - rom_pointer[1];
+ else
+ gd->ram_size = sdram_size;
+
+ return 0;
+}
+
+int dram_init_banksize(void)
+{
+ int bank = 0;
+ int ret;
+ phys_size_t sdram_size;
+ phys_size_t sdram_b1_size, sdram_b2_size;
+
+ ret = board_phys_sdram_size(&sdram_size);
+ if (ret)
+ return ret;
+
+ /* Bank 1 can't cross over 4GB space */
+ if (sdram_size > 0x80000000) {
+ sdram_b1_size = 0x100000000UL - PHYS_SDRAM;
+ sdram_b2_size = sdram_size - sdram_b1_size;
+ } else {
+ sdram_b1_size = sdram_size;
+ sdram_b2_size = 0;
+ }
+
+ gd->bd->bi_dram[bank].start = PHYS_SDRAM;
+ if (rom_pointer[1] && PHYS_SDRAM < (phys_addr_t)rom_pointer[0]) {
+ phys_addr_t optee_start = (phys_addr_t)rom_pointer[0];
+ phys_size_t optee_size = (size_t)rom_pointer[1];
+
+ gd->bd->bi_dram[bank].size = optee_start - gd->bd->bi_dram[bank].start;
+ if ((optee_start + optee_size) < (PHYS_SDRAM + sdram_b1_size)) {
+ if (++bank >= CONFIG_NR_DRAM_BANKS) {
+ puts("CONFIG_NR_DRAM_BANKS is not enough\n");
+ return -1;
+ }
+
+ gd->bd->bi_dram[bank].start = optee_start + optee_size;
+ gd->bd->bi_dram[bank].size = PHYS_SDRAM +
+ sdram_b1_size - gd->bd->bi_dram[bank].start;
+ }
+ } else {
+ gd->bd->bi_dram[bank].size = sdram_b1_size;
+ }
+
+ if (sdram_b2_size) {
+ if (++bank >= CONFIG_NR_DRAM_BANKS) {
+ puts("CONFIG_NR_DRAM_BANKS is not enough for SDRAM_2\n");
+ return -1;
+ }
+ gd->bd->bi_dram[bank].start = 0x100000000UL;
+ gd->bd->bi_dram[bank].size = sdram_b2_size;
+ }
+
+ return 0;
+}
+
+phys_size_t get_effective_memsize(void)
+{
+ int ret;
+ phys_size_t sdram_size;
+ phys_size_t sdram_b1_size;
+
+ ret = board_phys_sdram_size(&sdram_size);
+ if (!ret) {
+ /* Bank 1 can't cross over 4GB space */
+ if (sdram_size > 0x80000000)
+ sdram_b1_size = 0x100000000UL - PHYS_SDRAM;
+ else
+ sdram_b1_size = sdram_size;
+
+ if (rom_pointer[1]) {
+ /* We will relocate u-boot to Top of dram1. Tee position has three cases:
+ * 1. At the top of dram1, Then return the size removed optee size.
+ * 2. In the middle of dram1, return the size of dram1.
+ * 3. Not in the scope of dram1, return the size of dram1.
+ */
+ if ((rom_pointer[0] + rom_pointer[1]) == (PHYS_SDRAM + sdram_b1_size))
+ return ((phys_addr_t)rom_pointer[0] - PHYS_SDRAM);
+ }
+
+ return sdram_b1_size;
+ } else {
+ return PHYS_SDRAM_SIZE;
+ }
+}
+
+void imx_get_mac_from_fuse(int dev_id, unsigned char *mac)
+{
+ u32 val[2] = {};
+ int ret, num_of_macs;
+
+ ret = fuse_read(40, 5, &val[0]);
+ if (ret)
+ goto err;
+
+ ret = fuse_read(40, 6, &val[1]);
+ if (ret)
+ goto err;
+
+ num_of_macs = (val[1] >> 24) & 0xff;
+ if (num_of_macs <= (dev_id * 3)) {
+ printf("WARNING: no MAC address assigned for MAC%d\n", dev_id);
+ goto err;
+ }
+
+ mac[0] = val[0] & 0xff;
+ mac[1] = (val[0] >> 8) & 0xff;
+ mac[2] = (val[0] >> 16) & 0xff;
+ mac[3] = (val[0] >> 24) & 0xff;
+ mac[4] = val[1] & 0xff;
+ mac[5] = (val[1] >> 8) & 0xff;
+ if (dev_id == 1)
+ mac[5] = mac[5] + 3;
+ if (dev_id == 2)
+ mac[5] = mac[5] + 6;
+
+ debug("%s: MAC%d: %pM\n", __func__, dev_id, mac);
+ return;
+err:
+ memset(mac, 0, 6);
+ printf("%s: fuse read err: %d\n", __func__, ret);
+}
+
+const char *get_imx_type(u32 imxtype)
+{
+ switch (imxtype) {
+ case MXC_CPU_IMX95:
+ return "95";/* iMX95 FULL */
+ default:
+ return "??";
+ }
+}
+
+void build_info(void)
+{
+ u32 fw_version, sha1, res = 0, status;
+ int ret;
+
+ printf("\nBuildInfo:\n");
+
+ ret = ele_get_fw_status(&status, &res);
+ if (ret) {
+ printf(" - ELE firmware status failed %d, 0x%x\n", ret, res);
+ } else if ((status & 0xff) == 1) {
+ ret = ele_get_fw_version(&fw_version, &sha1, &res);
+ if (ret) {
+ printf(" - ELE firmware version failed %d, 0x%x\n", ret, res);
+ } else {
+ printf(" - ELE firmware version %u.%u.%u-%x",
+ (fw_version & (0x00ff0000)) >> 16,
+ (fw_version & (0x0000fff0)) >> 4,
+ (fw_version & (0x0000000f)), sha1);
+ ((fw_version & (0x80000000)) >> 31) == 1 ? puts("-dirty\n") : puts("\n");
+ }
+ } else {
+ printf(" - ELE firmware not included\n");
+ }
+ puts("\n");
+}
+
+int arch_misc_init(void)
+{
+ build_info();
+ return 0;
+}
+
+#if defined(CONFIG_OF_BOARD_FIXUP) && !defined(CONFIG_SPL_BUILD)
+int board_fix_fdt(void *fdt)
+{
+ return 0;
+}
+#endif
+
+int ft_system_setup(void *blob, struct bd_info *bd)
+{
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG)
+void get_board_serial(struct tag_serialnr *serialnr)
+{
+ printf("UID: %08x%08x%08x%08x\n", __be32_to_cpu(gd->arch.uid[0]),
+ __be32_to_cpu(gd->arch.uid[1]), __be32_to_cpu(gd->arch.uid[2]),
+ __be32_to_cpu(gd->arch.uid[3]));
+
+ serialnr->low = __be32_to_cpu(gd->arch.uid[1]);
+ serialnr->high = __be32_to_cpu(gd->arch.uid[0]);
+}
+#endif
+
+static void gpio_reset(ulong gpio_base)
+{
+ writel(0, gpio_base + 0x10);
+ writel(0, gpio_base + 0x14);
+ writel(0, gpio_base + 0x18);
+ writel(0, gpio_base + 0x1c);
+}
+
+int arch_cpu_init(void)
+{
+ if (IS_ENABLED(CONFIG_SPL_BUILD)) {
+ disable_wdog((void __iomem *)WDG3_BASE_ADDR);
+ disable_wdog((void __iomem *)WDG4_BASE_ADDR);
+
+ gpio_reset(GPIO2_BASE_ADDR);
+ gpio_reset(GPIO3_BASE_ADDR);
+ gpio_reset(GPIO4_BASE_ADDR);
+ gpio_reset(GPIO5_BASE_ADDR);
+ }
+
+ return 0;
+}
+
+int imx9_probe_mu(void)
+{
+ struct udevice *dev;
+ int ret;
+ u32 res;
+ struct ele_get_info_data info;
+
+ ret = uclass_get_device_by_driver(UCLASS_SCMI_AGENT, DM_DRIVER_GET(scmi_mbox), &dev);
+ if (ret)
+ return ret;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, "protocol@14", &dev);
+ if (ret)
+ return ret;
+
+ ret = devm_scmi_of_get_channel(dev);
+ if (ret)
+ return ret;
+
+ ret = uclass_get_device_by_name(UCLASS_PINCTRL, "protocol@19", &dev);
+ if (ret)
+ return ret;
+
+#if defined(CONFIG_SPL_BUILD)
+ ret = uclass_get_device_by_name(UCLASS_MISC, "mailbox@47530000", &dev);
+#else
+ ret = uclass_get_device_by_name(UCLASS_MISC, "mailbox@47550000", &dev);
+#endif
+ if (ret)
+ return ret;
+
+ if (gd->flags & GD_FLG_RELOC)
+ return 0;
+
+ ret = ele_get_info(&info, &res);
+ if (ret)
+ return ret;
+
+ set_cpu_info(&info);
+
+ return 0;
+}
+
+EVENT_SPY_SIMPLE(EVT_DM_POST_INIT_F, imx9_probe_mu);
+EVENT_SPY_SIMPLE(EVT_DM_POST_INIT_R, imx9_probe_mu);
+
+int timer_init(void)
+{
+ gd->arch.tbl = 0;
+ gd->arch.tbu = 0;
+
+ if (IS_ENABLED(CONFIG_SPL_BUILD)) {
+ unsigned long freq = 24000000;
+
+ asm volatile("msr cntfrq_el0, %0" : : "r" (freq) : "memory");
+
+ /* Clear the compare frame interrupt */
+ unsigned long sctr_cmpcr_addr = SYSCNT_CMP_BASE_ADDR + 0x2c;
+ unsigned long sctr_cmpcr = readl(sctr_cmpcr_addr);
+
+ sctr_cmpcr &= ~0x1;
+ writel(sctr_cmpcr, sctr_cmpcr_addr);
+ }
+
+ return 0;
+}
+
+enum env_location env_get_location(enum env_operation op, int prio)
+{
+ enum boot_device dev = get_boot_device();
+ enum env_location env_loc = ENVL_UNKNOWN;
+
+ if (prio)
+ return env_loc;
+
+ switch (dev) {
+ case QSPI_BOOT:
+ env_loc = ENVL_SPI_FLASH;
+ break;
+ case SD1_BOOT:
+ case SD2_BOOT:
+ case SD3_BOOT:
+ case MMC1_BOOT:
+ case MMC2_BOOT:
+ case MMC3_BOOT:
+ env_loc = ENVL_MMC;
+ break;
+ default:
+ env_loc = ENVL_NOWHERE;
+ break;
+ }
+
+ return env_loc;
+}
+
+enum imx9_soc_voltage_mode soc_target_voltage_mode(void)
+{
+ u32 speed = get_cpu_speed_grade_hz();
+ enum imx9_soc_voltage_mode voltage = VOLT_OVER_DRIVE;
+
+ if (is_imx95()) {
+ if (speed == 2000000000)
+ voltage = VOLT_SUPER_OVER_DRIVE;
+ else if (speed == 1800000000)
+ voltage = VOLT_OVER_DRIVE;
+ else if (speed == 1400000000)
+ voltage = VOLT_NOMINAL_DRIVE;
+ else /* boot not support low drive mode according to AS */
+ printf("Unexpected A55 freq %u, default to OD\n", speed);
+ }
+
+ return voltage;
+}
+
+#if IS_ENABLED(CONFIG_SCMI_FIRMWARE)
+enum boot_device get_boot_device(void)
+{
+ volatile gd_t *pgd = gd;
+ int ret;
+ u16 boot_type;
+ u8 boot_instance;
+ enum boot_device boot_dev = 0;
+ rom_passover_t *rdata;
+
+#if IS_ENABLED(CONFIG_SPL_BUILD)
+ rdata = &rom_passover_data;
+#else
+ rom_passover_t rom_data = {0};
+
+ if (pgd->reloc_off == 0)
+ rdata = &rom_data;
+ else
+ rdata = &rom_passover_data;
+#endif
+ if (rdata->tag == 0) {
+ ret = scmi_get_rom_data(rdata);
+ if (ret != 0) {
+ puts("SCMI: failure at rom_boot_info\n");
+ return -1;
+ }
+ }
+ boot_type = rdata->boot_dev_type;
+ boot_instance = rdata->boot_dev_inst;
+
+ set_gd(pgd);
+
+ switch (boot_type) {
+ case BT_DEV_TYPE_SD:
+ boot_dev = boot_instance + SD1_BOOT;
+ break;
+ case BT_DEV_TYPE_MMC:
+ boot_dev = boot_instance + MMC1_BOOT;
+ break;
+ case BT_DEV_TYPE_NAND:
+ boot_dev = NAND_BOOT;
+ break;
+ case BT_DEV_TYPE_FLEXSPINOR:
+ boot_dev = QSPI_BOOT;
+ break;
+ case BT_DEV_TYPE_USB:
+ boot_dev = boot_instance + USB_BOOT;
+ if (IS_ENABLED(CONFIG_IMX95))
+ boot_dev -= 3; //iMX95 usb instance start at 3
+ break;
+ default:
+ break;
+ }
+
+ return boot_dev;
+}
+#endif
+
+bool arch_check_dst_in_secure(void *start, ulong size)
+{
+ ulong ns_end = CFG_SYS_SDRAM_BASE + PHYS_SDRAM_SIZE;
+#ifdef PHYS_SDRAM_2_SIZE
+ ns_end += PHYS_SDRAM_2_SIZE;
+#endif
+
+ if ((ulong)start < CFG_SYS_SDRAM_BASE || (ulong)start + size > ns_end)
+ return true;
+
+ return false;
+}
+
+void *arch_get_container_trampoline(void)
+{
+ return (void *)((ulong)CFG_SYS_SDRAM_BASE + PHYS_SDRAM_SIZE - SZ_16M);
+}
diff --git a/arch/sandbox/include/asm/scmi_test.h b/arch/sandbox/include/asm/scmi_test.h
index 619f8f5098c..c9717118bcb 100644
--- a/arch/sandbox/include/asm/scmi_test.h
+++ b/arch/sandbox/include/asm/scmi_test.h
@@ -27,10 +27,12 @@ struct sandbox_scmi_pwd {
* @id: Identifier of the clock used in the SCMI protocol
* @enabled: Clock state: true if enabled, false if disabled
* @rate: Clock rate in Hertz
+ * @perm: Indicating state/parent/rate permission
*/
struct sandbox_scmi_clk {
bool enabled;
ulong rate;
+ u32 perm;
};
/**
@@ -108,7 +110,7 @@ struct sandbox_scmi_devices {
size_t regul_count;
};
-#ifdef CONFIG_SCMI_FIRMWARE
+#if IS_ENABLED(CONFIG_SCMI_FIRMWARE)
/**
* sandbox_scmi_channel_id - Get the channel id
* @dev: Reference to the SCMI protocol device
diff --git a/board/freescale/imx95_evk/Kconfig b/board/freescale/imx95_evk/Kconfig
new file mode 100644
index 00000000000..f9a67353e5d
--- /dev/null
+++ b/board/freescale/imx95_evk/Kconfig
@@ -0,0 +1,12 @@
+if TARGET_IMX95_19X19_EVK
+
+config SYS_BOARD
+ default "imx95_evk"
+
+config SYS_VENDOR
+ default "freescale"
+
+config SYS_CONFIG_NAME
+ default "imx95_evk"
+
+endif
diff --git a/board/freescale/imx95_evk/MAINTAINERS b/board/freescale/imx95_evk/MAINTAINERS
new file mode 100644
index 00000000000..5caf763e0cc
--- /dev/null
+++ b/board/freescale/imx95_evk/MAINTAINERS
@@ -0,0 +1,6 @@
+i.MX95 EVK BOARD
+M: Alice Guo <alice.guo@nxp.com>
+S: Maintained
+F: board/freescale/imx95_evk/
+F: include/configs/imx95_evk.h
+F: configs/imx95_19x19_evk_defconfig
diff --git a/board/freescale/imx95_evk/Makefile b/board/freescale/imx95_evk/Makefile
new file mode 100644
index 00000000000..0f5cec385c3
--- /dev/null
+++ b/board/freescale/imx95_evk/Makefile
@@ -0,0 +1,11 @@
+#
+# Copyright 2025 NXP
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-y += imx95_evk.o
+
+ifdef CONFIG_SPL_BUILD
+obj-y += spl.o
+endif
diff --git a/board/freescale/imx95_evk/imx95_19x19_evk.env b/board/freescale/imx95_evk/imx95_19x19_evk.env
new file mode 100644
index 00000000000..a002767e874
--- /dev/null
+++ b/board/freescale/imx95_evk/imx95_19x19_evk.env
@@ -0,0 +1,91 @@
+sec_boot=no
+initrd_addr=0x93800000
+emmc_dev=0
+sd_dev=1
+scriptaddr=0x93500000
+kernel_addr_r=CONFIG_SYS_LOAD_ADDR
+image=Image
+splashimage=0xA0000000
+console=ttyLP0,115200 earlycon
+fdt_addr_r=0x93000000
+fdt_addr=0x93000000
+cntr_addr=0xA8000000
+cntr_file=os_cntr_signed.bin
+boot_fit=no
+fdtfile=CONFIG_DEFAULT_FDT_FILE
+bootm_size=0x10000000
+mmcdev=CONFIG_SYS_MMC_ENV_DEV
+mmcautodetect=yes
+mmcargs=setenv bootargs console=${console} root=${mmcroot}
+loadbootscript=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${script};
+bootscript=echo Running bootscript from mmc ...; source
+loadimage=fatload mmc ${mmcdev}:${mmcpart} ${loadaddr} ${image}
+loadfdt=fatload mmc ${mmcdev}:${mmcpart} ${fdt_addr_r} ${fdtfile}
+loadcntr=fatload mmc ${mmcdev}:${mmcpart} ${cntr_addr} ${cntr_file}
+auth_os=auth_cntr ${cntr_addr}
+boot_os=booti ${loadaddr} - ${fdt_addr_r};
+mmcboot=echo Booting from mmc ...;
+ run mmcargs;
+ if test ${sec_boot} = yes; then
+ if run auth_os; then
+ run boot_os;
+ else
+ echo ERR: failed to authenticate;
+ fi;
+ else
+ if test ${boot_fit} = yes || test ${boot_fit} = try; then
+ bootm ${loadaddr};
+ else
+ if run loadfdt; then
+ run boot_os;
+ else
+ echo WARN: Cannot load the DT;
+ fi;
+ fi;
+ fi;
+netargs=setenv bootargs console=${console} root=/dev/nfs
+ ip=dhcp nfsroot=${serverip}:${nfsroot},v3,tcp
+netboot=echo Booting from net ...;
+ run netargs;
+ if test ${ip_dyn} = yes; then
+ setenv get_cmd dhcp;
+ else
+ setenv get_cmd tftp;
+ fi;
+ if test ${sec_boot} = yes; then
+ ${get_cmd} ${cntr_addr} ${cntr_file};
+ if run auth_os; then
+ run boot_os;
+ else
+ echo ERR: failed to authenticate;
+ fi;
+ else
+ ${get_cmd} ${loadaddr} ${image};
+ if test ${boot_fit} = yes || test ${boot_fit} = try; then
+ bootm ${loadaddr};
+ else
+ if ${get_cmd} ${fdt_addr_r} ${fdtfile}; then
+ run boot_os;
+ else
+ echo WARN: Cannot load the DT;
+ fi;
+ fi;
+ fi;
+bsp_bootcmd=echo Running BSP bootcmd ...;
+ mmc dev ${mmcdev}; if mmc rescan; then
+ if run loadbootscript; then
+ run bootscript;
+ else
+ if test ${sec_boot} = yes; then
+ if run loadcntr; then
+ run mmcboot;
+ else run netboot;
+ fi;
+ else
+ if run loadimage; then
+ run mmcboot;
+ else run netboot;
+ fi;
+ fi;
+ fi;
+ fi;
diff --git a/board/freescale/imx95_evk/imx95_evk.c b/board/freescale/imx95_evk/imx95_evk.c
new file mode 100644
index 00000000000..d5f5e310b6b
--- /dev/null
+++ b/board/freescale/imx95_evk/imx95_evk.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <asm/arch/clock.h>
+#include <asm/gpio.h>
+#include <asm/mach-imx/sys_proto.h>
+
+int board_early_init_f(void)
+{
+ /* UART1: A55, UART2: M33, UART3: M7 */
+ init_uart_clk(0);
+
+ return 0;
+}
+
+int board_init(void)
+{
+ return 0;
+}
+
+int board_late_init(void)
+{
+ if (IS_ENABLED(CONFIG_ENV_IS_IN_MMC))
+ board_late_mmc_env_init();
+
+ return 0;
+}
+
+int board_phys_sdram_size(phys_size_t *size)
+{
+ *size = PHYS_SDRAM_SIZE + PHYS_SDRAM_2_SIZE;
+
+ return 0;
+}
diff --git a/board/freescale/imx95_evk/spl.c b/board/freescale/imx95_evk/spl.c
new file mode 100644
index 00000000000..08f4da0bb73
--- /dev/null
+++ b/board/freescale/imx95_evk/spl.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <asm/arch/clock.h>
+#include <asm/arch/mu.h>
+#include <asm/mach-imx/boot_mode.h>
+#include <asm/sections.h>
+#include <hang.h>
+#include <init.h>
+#include <spl.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int spl_board_boot_device(enum boot_device boot_dev_spl)
+{
+ switch (boot_dev_spl) {
+ case SD1_BOOT:
+ case MMC1_BOOT:
+ return BOOT_DEVICE_MMC1;
+ case SD2_BOOT:
+ case MMC2_BOOT:
+ return BOOT_DEVICE_MMC2;
+ case USB_BOOT:
+ return BOOT_DEVICE_BOARD;
+ default:
+ return BOOT_DEVICE_NONE;
+ }
+}
+
+void spl_board_init(void)
+{
+ puts("Normal Boot\n");
+}
+
+void board_init_f(ulong dummy)
+{
+ int ret;
+
+ /* Clear the BSS. */
+ memset(__bss_start, 0, __bss_end - __bss_start);
+
+#ifdef CONFIG_SPL_RECOVER_DATA_SECTION
+ if (IS_ENABLED(CONFIG_SPL_BUILD))
+ spl_save_restore_data();
+#endif
+
+ timer_init();
+
+ /* Need dm_init() to run before any SCMI calls can be made. */
+ spl_early_init();
+
+ /* Need enable SCMI drivers and ELE driver before enabling console */
+ ret = imx9_probe_mu();
+ if (ret)
+ hang(); /* if MU not probed, nothing can output, just hang here */
+
+ arch_cpu_init();
+
+ board_early_init_f();
+
+ preloader_console_init();
+
+ debug("SOC: 0x%x\n", gd->arch.soc_rev);
+ debug("LC: 0x%x\n", gd->arch.lifecycle);
+
+ board_init_r(NULL, 0);
+}
diff --git a/common/spl/Kconfig b/common/spl/Kconfig
index b076f49ac00..0bc96d0a781 100644
--- a/common/spl/Kconfig
+++ b/common/spl/Kconfig
@@ -363,6 +363,12 @@ config SPL_LOAD_IMX_CONTAINER
Support booting U-Boot from an i.MX8 container image. If you are not
using i.MX8, say 'n'.
+config SPL_IMX_CONTAINER_USE_TRAMPOLINE
+ bool
+ depends on SPL
+ help
+ Enable SPL load reader to load data to a trampoline buffer.
+
config IMX_CONTAINER_CFG
string "i.MX8 Container config file"
depends on SPL && SPL_LOAD_IMX_CONTAINER
diff --git a/common/spl/spl_imx_container.c b/common/spl/spl_imx_container.c
index 2c31777fcd3..b3565efb225 100644
--- a/common/spl/spl_imx_container.c
+++ b/common/spl/spl_imx_container.c
@@ -14,6 +14,16 @@
#include <asm/mach-imx/ahab.h>
#endif
+__weak bool arch_check_dst_in_secure(void *start, ulong size)
+{
+ return false;
+}
+
+__weak void *arch_get_container_trampoline(void)
+{
+ return NULL;
+}
+
static struct boot_img_t *read_auth_image(struct spl_image_info *spl_image,
struct spl_load_info *info,
struct container_hdr *container,
@@ -22,6 +32,7 @@ static struct boot_img_t *read_auth_image(struct spl_image_info *spl_image,
{
struct boot_img_t *images;
ulong offset, overhead, size;
+ void *buf, *trampoline;
if (image_index > container->num_images) {
debug("Invalid image number\n");
@@ -42,12 +53,27 @@ static struct boot_img_t *read_auth_image(struct spl_image_info *spl_image,
debug("%s: container: %p offset: %lu size: %lu\n", __func__,
container, offset, size);
- if (info->read(info, offset, size,
- map_sysmem(images[image_index].dst - overhead,
- images[image_index].size)) <
- images[image_index].size) {
- printf("%s wrong\n", __func__);
- return NULL;
+
+ buf = map_sysmem(images[image_index].dst - overhead, images[image_index].size);
+ if (IS_ENABLED(CONFIG_SPL_IMX_CONTAINER_USE_TRAMPOLINE) &&
+ arch_check_dst_in_secure(buf, size)) {
+ trampoline = arch_get_container_trampoline();
+ if (!trampoline) {
+ printf("%s: trampoline size is zero\n", __func__);
+ return NULL;
+ }
+
+ if (info->read(info, offset, size, trampoline) < images[image_index].size) {
+ printf("%s: failed to load image to a trampoline buffer\n", __func__);
+ return NULL;
+ }
+
+ memcpy(buf, trampoline, images[image_index].size);
+ } else {
+ if (info->read(info, offset, size, buf) < images[image_index].size) {
+ printf("%s: failed to load image to a non-secure region\n", __func__);
+ return NULL;
+ }
}
#ifdef CONFIG_AHAB_BOOT
diff --git a/configs/imx8mm_beacon_defconfig b/configs/imx8mm_beacon_defconfig
index e4eca19a10b..27007bd8687 100644
--- a/configs/imx8mm_beacon_defconfig
+++ b/configs/imx8mm_beacon_defconfig
@@ -24,6 +24,7 @@ CONFIG_SPL_BSS_MAX_SIZE=0x2000
CONFIG_SYS_BOOTM_LEN=0x800000
CONFIG_SYS_LOAD_ADDR=0x40480000
CONFIG_SPL=y
+CONFIG_IMX_BOOTAUX=y
CONFIG_FIT=y
CONFIG_FIT_EXTERNAL_OFFSET=0x3000
CONFIG_SPL_LOAD_FIT=y
diff --git a/configs/imx8mn_beacon_defconfig b/configs/imx8mn_beacon_defconfig
index c605567d9a6..115288aa27f 100644
--- a/configs/imx8mn_beacon_defconfig
+++ b/configs/imx8mn_beacon_defconfig
@@ -29,6 +29,7 @@ CONFIG_SPL=y
CONFIG_SPL_IMX_ROMAPI_LOADADDR=0x48000000
CONFIG_SYS_MEMTEST_START=0x40000000
CONFIG_SYS_MEMTEST_END=0x44000000
+CONFIG_IMX_BOOTAUX=y
CONFIG_REMAKE_ELF=y
CONFIG_FIT=y
CONFIG_FIT_EXTERNAL_OFFSET=0x3000
diff --git a/configs/imx8mp_beacon_defconfig b/configs/imx8mp_beacon_defconfig
index 288626c1511..9f6fabc894f 100644
--- a/configs/imx8mp_beacon_defconfig
+++ b/configs/imx8mp_beacon_defconfig
@@ -33,6 +33,7 @@ CONFIG_ARMV8_SET_SMPEN=y
CONFIG_ARMV8_EA_EL3_FIRST=y
CONFIG_SPL_IMX_ROMAPI_LOADADDR=0x48000000
CONFIG_PCI=y
+CONFIG_IMX_BOOTAUX=y
# CONFIG_ANDROID_BOOT_IMAGE is not set
CONFIG_FIT=y
CONFIG_FIT_EXTERNAL_OFFSET=0x3000
diff --git a/configs/imx95_19x19_evk_defconfig b/configs/imx95_19x19_evk_defconfig
new file mode 100644
index 00000000000..fe968d5a239
--- /dev/null
+++ b/configs/imx95_19x19_evk_defconfig
@@ -0,0 +1,152 @@
+CONFIG_ARM=y
+CONFIG_ARCH_IMX9=y
+CONFIG_TEXT_BASE=0x90200000
+CONFIG_SYS_MALLOC_LEN=0x2000000
+CONFIG_SYS_MALLOC_F_LEN=0x10000
+CONFIG_SPL_GPIO=y
+CONFIG_SPL_LIBCOMMON_SUPPORT=y
+CONFIG_SPL_LIBGENERIC_SUPPORT=y
+CONFIG_NR_DRAM_BANKS=3
+CONFIG_ENV_SOURCE_FILE="imx95_19x19_evk"
+CONFIG_SF_DEFAULT_SPEED=40000000
+CONFIG_ENV_SIZE=0x4000
+CONFIG_ENV_OFFSET=0x700000
+CONFIG_IMX_CONFIG="arch/arm/mach-imx/imx9/scmi/imximage.cfg"
+CONFIG_DM_GPIO=y
+CONFIG_DEFAULT_DEVICE_TREE="freescale/imx95-19x19-evk"
+CONFIG_TARGET_IMX95_19X19_EVK=y
+CONFIG_OF_LIBFDT_OVERLAY=y
+CONFIG_SYS_MONITOR_LEN=524288
+CONFIG_SPL_MMC=y
+CONFIG_SPL_SERIAL=y
+CONFIG_SPL_DRIVERS_MISC=y
+CONFIG_SPL_STACK=0x204d6000
+CONFIG_SPL_TEXT_BASE=0x20480000
+CONFIG_SPL_HAS_BSS_LINKER_SECTION=y
+CONFIG_SPL_BSS_START_ADDR=0x204d6000
+CONFIG_SPL_BSS_MAX_SIZE=0x2000
+CONFIG_SYS_LOAD_ADDR=0x90400000
+CONFIG_SPL=y
+CONFIG_SPL_RECOVER_DATA_SECTION=y
+CONFIG_PCI=y
+CONFIG_SYS_MEMTEST_START=0x90000000
+CONFIG_SYS_MEMTEST_END=0xA0000000
+CONFIG_REMAKE_ELF=y
+CONFIG_FIT=y
+CONFIG_FIT_VERBOSE=y
+CONFIG_OF_SYSTEM_SETUP=y
+CONFIG_BOOTCOMMAND="bootflow scan -l; run bsp_bootcmd"
+CONFIG_DEFAULT_FDT_FILE="imx95-19x19-evk.dtb"
+CONFIG_SYS_CBSIZE=2048
+CONFIG_SYS_PBSIZE=2074
+CONFIG_ARCH_MISC_INIT=y
+CONFIG_BOARD_EARLY_INIT_F=y
+CONFIG_BOARD_LATE_INIT=y
+CONFIG_SPL_MAX_SIZE=0x20000
+CONFIG_SPL_BOARD_INIT=y
+CONFIG_SPL_LOAD_IMX_CONTAINER=y
+CONFIG_IMX_CONTAINER_CFG="arch/arm/mach-imx/imx9/scmi/container.cfg"
+# CONFIG_SPL_SHARES_INIT_SP_ADDR is not set
+CONFIG_SPL_SYS_MALLOC=y
+CONFIG_SPL_HAS_CUSTOM_MALLOC_START=y
+CONFIG_SPL_CUSTOM_SYS_MALLOC_ADDR=0x93200000
+CONFIG_SPL_SYS_MALLOC_SIZE=0x80000
+CONFIG_SPL_SYS_MMCSD_RAW_MODE=y
+CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR=0x1040
+CONFIG_SPL_I2C=y
+CONFIG_SPL_DM_MAILBOX=y
+CONFIG_SPL_POWER=y
+CONFIG_SPL_WATCHDOG=y
+CONFIG_SYS_PROMPT="u-boot=> "
+CONFIG_CMD_ERASEENV=y
+CONFIG_CMD_NVEDIT_EFI=y
+CONFIG_CRC32_VERIFY=y
+CONFIG_CMD_MEMTEST=y
+CONFIG_CMD_CLK=y
+CONFIG_CMD_DFU=y
+CONFIG_CMD_FUSE=y
+CONFIG_CMD_GPIO=y
+CONFIG_CMD_GPT=y
+CONFIG_CMD_I2C=y
+CONFIG_CMD_MMC=y
+CONFIG_CMD_PCI=y
+CONFIG_CMD_POWEROFF=y
+CONFIG_CMD_SNTP=y
+CONFIG_CMD_CACHE=y
+CONFIG_CMD_EFIDEBUG=y
+CONFIG_CMD_RTC=y
+CONFIG_CMD_TIME=y
+CONFIG_CMD_GETTIME=y
+CONFIG_CMD_TIMER=y
+CONFIG_CMD_REGULATOR=y
+CONFIG_CMD_HASH=y
+CONFIG_CMD_EXT4_WRITE=y
+CONFIG_OF_CONTROL=y
+CONFIG_SPL_OF_CONTROL=y
+CONFIG_ENV_OVERWRITE=y
+CONFIG_ENV_IS_NOWHERE=y
+CONFIG_ENV_IS_IN_MMC=y
+CONFIG_SYS_RELOC_GD_ENV_ADDR=y
+CONFIG_SYS_MMC_ENV_DEV=1
+CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG=y
+CONFIG_USE_ETHPRIME=y
+CONFIG_ETHPRIME="eth0"
+CONFIG_NET_RANDOM_ETHADDR=y
+CONFIG_SYS_RX_ETH_BUFFER=8
+CONFIG_SPL_DM=y
+CONFIG_REGMAP=y
+CONFIG_SYSCON=y
+CONFIG_SPL_OF_TRANSLATE=y
+CONFIG_CLK=y
+CONFIG_SPL_CLK=y
+CONFIG_SPL_CLK_CCF=y
+CONFIG_CLK_CCF=y
+CONFIG_CLK_SCMI=y
+CONFIG_SPL_CLK_SCMI=y
+CONFIG_DFU_MMC=y
+CONFIG_DFU_RAM=y
+CONFIG_SPL_FIRMWARE=y
+# CONFIG_SCMI_AGENT_SMCCC is not set
+CONFIG_IMX_RGPIO2P=y
+CONFIG_DM_PCA953X=y
+CONFIG_ADP5585_GPIO=y
+CONFIG_DM_I2C=y
+CONFIG_SYS_I2C_IMX_LPI2C=y
+CONFIG_IMX_MU_MBOX=y
+CONFIG_SUPPORT_EMMC_BOOT=y
+CONFIG_MMC_IO_VOLTAGE=y
+CONFIG_MMC_UHS_SUPPORT=y
+CONFIG_MMC_HS400_ES_SUPPORT=y
+CONFIG_MMC_HS400_SUPPORT=y
+CONFIG_FSL_USDHC=y
+CONFIG_MTD=y
+CONFIG_DM_SPI_FLASH=y
+CONFIG_SPI_FLASH_STMICRO=y
+CONFIG_SPI_FLASH_MT35XU=y
+CONFIG_PHYLIB=y
+CONFIG_PHY_AQUANTIA=y
+CONFIG_PHY_REALTEK=y
+CONFIG_DM_MDIO=y
+CONFIG_MII=y
+CONFIG_FSL_ENETC=y
+CONFIG_PCIE_ECAM_GENERIC=y
+CONFIG_PHY=y
+CONFIG_PINCTRL=y
+CONFIG_SPL_PINCTRL=y
+CONFIG_PINCTRL_IMX_SCMI=y
+CONFIG_POWER_DOMAIN=y
+CONFIG_SCMI_POWER_DOMAIN=y
+CONFIG_DM_REGULATOR=y
+CONFIG_SPL_DM_REGULATOR=y
+CONFIG_DM_REGULATOR_FIXED=y
+CONFIG_SPL_DM_REGULATOR_FIXED=y
+CONFIG_DM_REGULATOR_GPIO=y
+CONFIG_DM_RTC=y
+CONFIG_DM_SERIAL=y
+CONFIG_FSL_LPUART=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_NXP_FSPI=y
+CONFIG_ULP_WATCHDOG=y
+CONFIG_LZO=y
+CONFIG_BZIP2=y
diff --git a/doc/board/nxp/imx95_evk.rst b/doc/board/nxp/imx95_evk.rst
new file mode 100644
index 00000000000..9121f7561ab
--- /dev/null
+++ b/doc/board/nxp/imx95_evk.rst
@@ -0,0 +1,114 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+imx95_evk
+=======================
+
+U-Boot for the NXP i.MX95 19x19 EVK board
+
+Quick Start
+-----------
+
+- Get ahab-container.img
+- Get DDR PHY Firmware Images
+- Get and Build OEI Images
+- Get and Build System Manager Image
+- Get and Build the ARM Trusted Firmware
+- Build the Bootloader Image
+- Boot
+
+Get ahab-container.img
+--------------------------------------
+
+Note: srctree is U-Boot source directory
+
+.. code-block:: bash
+
+ $ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-ele-imx-1.3.0-17945fc.bin
+ $ sh firmware-ele-imx-1.3.0-17945fc.bin --auto-accept
+ $ cp firmware-ele-imx-1.3.0-17945fc/mx95a0-ahab-container.img $(srctree)
+
+Get DDR PHY Firmware Images
+--------------------------------------
+
+Note: srctree is U-Boot source directory
+
+.. code-block:: bash
+
+ $ wget https://www.nxp.com/lgfiles/NMG/MAD/YOCTO/firmware-imx-8.26-d4c33ab.bin
+ $ sh firmware-imx-8.26-d4c33ab.bin --auto-accept
+ $ cp firmware-imx-8.26-d4c33ab/firmware/ddr/synopsys/lpddr5*v202311.bin $(srctree)
+
+Get and Build OEI Images
+--------------------------------------
+
+Note: srctree is U-Boot source directory
+Get OEI from: https://github.com/nxp-imx/imx-oei
+branch: master
+
+.. code-block:: bash
+
+ $ sudo apt -y install make gcc g++-multilib srecord
+ $ wget https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz
+ $ tar xvf arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz
+ $ export TOOLS=$PWD
+ $ git clone -b master https://github.com/nxp-imx/imx-oei.git
+ $ cd imx-oei
+ $ make board=mx95lp5 oei=ddr DEBUG=1
+ $ cp build/mx95lp5/ddr/oei-m33-ddr.bin $(srctree)
+
+ $ make board=mx95lp5 oei=tcm DEBUG=1
+ $ cp build/mx95lp5/tcm/oei-m33-tcm.bin $(srctree)
+
+Get and Build System Manager Image
+--------------------------------------
+
+Note: srctree is U-Boot source directory
+Get System Manager from: https://github.com/nxp-imx/imx-sm
+branch: master
+
+.. code-block:: bash
+
+ $ sudo apt -y install make gcc g++-multilib srecord
+ $ wget https://developer.arm.com/-/media/Files/downloads/gnu/13.3.rel1/binrel/arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz
+ $ tar xvf arm-gnu-toolchain-13.3.rel1-x86_64-arm-none-eabi.tar.xz
+ $ export TOOLS=$PWD
+ $ git clone -b master https://github.com/nxp-imx/imx-sm.git
+ $ cd imx-sm
+ $ make config=mx95evk all
+ $ cp build/mx95evk/m33_image.bin $(srctree)
+
+Get and Build the ARM Trusted Firmware
+--------------------------------------
+
+Note: srctree is U-Boot source directory
+Get ATF from: https://github.com/nxp-imx/imx-atf/
+branch: lf_v2.10
+
+.. code-block:: bash
+
+ $ export CROSS_COMPILE=aarch64-poky-linux-
+ $ unset LDFLAGS
+ $ git clone -b lf_v2.10 https://github.com/nxp-imx/imx-atf.git
+ $ cd imx-atf
+ $ make PLAT=imx95 bl31
+ $ cp build/imx95/release/bl31.bin $(srctree)
+
+Build the Bootloader Image
+--------------------------
+
+.. code-block:: bash
+
+ $ export CROSS_COMPILE=aarch64-poky-linux-
+ $ make imx95_19x19_evk_defconfig
+ $ make
+
+Copy imx-boot-imx95.bin to the MicroSD card:
+
+.. code-block:: bash
+
+ $ sudo dd if=flash.bin of=/dev/sd[x] bs=1k seek=32 conv=fsync
+
+Boot
+----
+
+Set i.MX95 boot device to MicroSD card
diff --git a/doc/board/nxp/index.rst b/doc/board/nxp/index.rst
index 8ca4b561986..e7ec725cc04 100644
--- a/doc/board/nxp/index.rst
+++ b/doc/board/nxp/index.rst
@@ -15,6 +15,7 @@ NXP Semiconductors
imx91_11x11_evk
imx93_9x9_qsb
imx93_11x11_evk
+ imx95_evk
imxrt1020-evk
imxrt1050-evk
imxrt1170-evk
diff --git a/doc/imx/imx95_container.txt b/doc/imx/imx95_container.txt
new file mode 100644
index 00000000000..05f223d962a
--- /dev/null
+++ b/doc/imx/imx95_container.txt
@@ -0,0 +1,136 @@
+i.MX95 Image Container Format
+-----------------------------
+
+The image container set consists of some image containers, and image container
+contains boot images. Each image container has its own container header which is
+defined in Figure 1. All container headers are placed together in a continuous
+8KB space at the beginning of the image container set - image container set header.
+
+ROM code addresses image containers in image container set one by one based on
+their headers’ order in image container set header.
+
+If ELE container exists, its container header must be the 1st one in the image
+container set header.
+
+If V2X container exists, its container header must be the 2nd one in the image
+container set header. V2X must be combined with ELE container.
+
+The information of boot images are recorded in image container header. System
+ROM code needs to retrieve the information from the image container header, like
+the offset on boot source, the target address in RAM, the length of boot image.
+The order of ROM code handling these boot images is based on the order of each
+boot image information present in image container header.
+
+Figure 1:
+------------------- +--------------+--------------+--------------+--------------+
+ ^ |Tag |Length |Length |Version |
+ | +--------------+--------------+--------------+--------------+
+ | | Flags |
+ | +--------------+--------------+--------------+--------------+
+ | |# of Images |Fuse version |SW version |
+Image | +--------------+--------------+--------------+--------------+
+Container | |Reserved |Signature Block Offset |
+Header | ------- +--------------+--------------+--------------+--------------+
+ | ^ |Image0: Offset, Size, LoadAddr, EntryPoint, Flags, Hash, IV|
+ | | +--------------+--------------+--------------+--------------+
+ | Image | |Image1: Offset, Size, LoadAddr, EntryPoint, Flags, Hash, IV|
+ | Array | +--------------+--------------+--------------+--------------+
+ | | | ... |
+ | | +--------------+--------------+--------------+--------------+
+ v v |ImageN: Offset, Size, LoadAddr, EntryPoint, Flags, Hash, IV|
+------------------- +--------------+--------------+--------------+--------------+
+ | ... |
+ ----------- +--------------+--------------+--------------+--------------+ <-- SignOffset
+ ^ |Tag |Length |Length |Version |
+ | +--------------+--------------+--------------+--------------+
+ | |SRK table offset |Certificate Offset |
+ | +--------------+--------------+--------------+--------------+
+ | |Blob Offset |Signature Offset |
+ | +--------------+--------------+--------------+--------------+
+ Signature | | SRK Table |
+ Block | +--------------+--------------+--------------+--------------+
+ | | Signature |
+ | +--------------+--------------+--------------+--------------+
+ | | Certificate (optional) |
+ | +--------------+--------------+--------------+--------------+
+ v | Blob (optional) |
+ ----------- +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+ <-- Image0Offset
+ | Image0 |
+ +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+ <-- ImageNOffset
+ | ImageN |
+ +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+
+
+i.MX95 Low Power Boot Image Sets Layout
+---------------------------------------
+
+ Image container sets are handled by M33 ROM.
+
+ --------------- +--------------+--------------+--------------+--------------+
+ ^ | 1st Container Header |
+ | +--------------+--------------+--------------+--------------+
+ ELE + | | Padding for 1KB alignment |
+ OEM | +--------------+--------------+--------------+--------------+
+ Container | | 2nd Container Header |
+ Set | +--------------+--------------+--------------+--------------+
+ Header | | Padding for 1KB alignment |
+ | +--------------+--------------+--------------+--------------+
+ | | 3rd Container Header |
+ | +--------------+--------------+--------------+--------------+
+ v | Padding for 1KB alignment |
+ --------------- +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+
+ | mx95a0-ahab-container.img |
+ +--------------+--------------+--------------+--------------+
+ | ... |
+------------------- +--------------+--------------+--------------+--------------+
+ ^ | oei-m33-ddr.bin |
+ | +--------------+--------------+--------------+--------------+
+ | | ddrfw-header.bin |
+ | +--------------+--------------+--------------+--------------+
+ | | lpddr5_imem_v202311.bin |
+ | +--------------+--------------+--------------+--------------+
+m33-oei-ddrfw.bin | | lpddr5_dmem_v202311.bin |
+ | +--------------+--------------+--------------+--------------+
+ | | ddrfw-qb-header.bin |
+ | +--------------+--------------+--------------+--------------+
+ | | lpddr5_imem_qb_v202311.bin |
+ | +--------------+--------------+--------------+--------------+
+ v | lpddr5_dmem_qb_v202311.bin |
+------------------- +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+
+ | oei-m33-tcm.bin |
+ +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+
+ | m33_image.bin |
+ +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+
+ | u-boot-spl.bin |
+ +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+
+
+ --------------- +--------------+--------------+--------------+--------------+
+ u-boot-atf ^ | 1st Container Header |
+ Container | +--------------+--------------+--------------+--------------+
+ Header v | Padding for 1KB alignment |
+ --------------- +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+
+ | bl31.bin |
+ +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+
+ | u-boot.bin |
+ +--------------+--------------+--------------+--------------+
+ | ... |
+ +--------------+--------------+--------------+--------------+
diff --git a/drivers/clk/clk_scmi.c b/drivers/clk/clk_scmi.c
index e42d2032d45..af69850cdd8 100644
--- a/drivers/clk/clk_scmi.c
+++ b/drivers/clk/clk_scmi.c
@@ -8,10 +8,59 @@
#include <clk-uclass.h>
#include <dm.h>
#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <asm/types.h>
#include <linux/clk-provider.h>
+struct clk_scmi {
+ struct clk clk;
+ u32 ctrl_flags;
+};
+
+struct scmi_clock_priv {
+ u32 version;
+};
+
+static int scmi_clk_get_permissions(struct udevice *dev, int clkid, u32 *perm)
+{
+ struct scmi_clock_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ struct scmi_clk_get_permissions_in in = {
+ .clock_id = clkid,
+ };
+ struct scmi_clk_get_permissions_out out;
+ struct scmi_msg msg = {
+ .protocol_id = SCMI_PROTOCOL_ID_CLOCK,
+ .message_id = SCMI_CLOCK_GET_PERMISSIONS,
+ .in_msg = (u8 *)&in,
+ .in_msg_sz = sizeof(in),
+ .out_msg = (u8 *)&out,
+ .out_msg_sz = sizeof(out),
+ };
+
+ if (priv->version < CLOCK_PROTOCOL_VERSION_3_0) {
+ log_debug("%s: SCMI clock management protocol version is less than 3.0.\n", __func__);
+ return -EINVAL;
+ }
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret) {
+ log_debug("%s: get SCMI clock management protocol permissions failed\n", __func__);
+ return ret;
+ }
+
+ ret = scmi_to_linux_errno(out.status);
+ if (ret < 0) {
+ log_debug("%s: the status code of getting permissions: %d\n", __func__, ret);
+ return ret;
+ }
+
+ *perm = out.permissions;
+ return 0;
+}
+
static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
{
struct scmi_clk_protocol_attr_out out;
@@ -32,7 +81,8 @@ static int scmi_clk_get_num_clock(struct udevice *dev, size_t *num_clocks)
return 0;
}
-static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
+static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name,
+ u32 *attr)
{
struct scmi_clk_attribute_in in = {
.clock_id = clkid,
@@ -53,6 +103,7 @@ static int scmi_clk_get_attibute(struct udevice *dev, int clkid, char **name)
return ret;
*name = strdup(out.clock_name);
+ *attr = out.attributes;
return 0;
}
@@ -78,12 +129,48 @@ static int scmi_clk_gate(struct clk *clk, int enable)
static int scmi_clk_enable(struct clk *clk)
{
- return scmi_clk_gate(clk, 1);
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK_CCF))
+ return scmi_clk_gate(clk, 1);
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
+ return scmi_clk_gate(clk, 1);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ log_debug("%s: SCMI CLOCK: the clock cannot be enabled by the agent.\n", __func__);
+ return 0;
}
static int scmi_clk_disable(struct clk *clk)
{
- return scmi_clk_gate(clk, 0);
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK_CCF))
+ return scmi_clk_gate(clk, 0);
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_STAT_CONTROL)
+ return scmi_clk_gate(clk, 0);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ log_debug("%s: SCMI CLOCK: the clock cannot be disabled by the agent.\n", __func__);
+ return 0;
}
static ulong scmi_clk_get_rate(struct clk *clk)
@@ -108,7 +195,7 @@ static ulong scmi_clk_get_rate(struct clk *clk)
return (ulong)(((u64)out.rate_msb << 32) | out.rate_lsb);
}
-static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
+static ulong __scmi_clk_set_rate(struct clk *clk, ulong rate)
{
struct scmi_clk_rate_set_in in = {
.clock_id = clk->id,
@@ -133,9 +220,33 @@ static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
return scmi_clk_get_rate(clk);
}
+static ulong scmi_clk_set_rate(struct clk *clk, ulong rate)
+{
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK_CCF))
+ return __scmi_clk_set_rate(clk, rate);
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_RATE_CONTROL)
+ return __scmi_clk_set_rate(clk, rate);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ log_debug("%s: SCMI CLOCK: the clock rate cannot be changed by the agent.\n", __func__);
+ return 0;
+}
+
static int scmi_clk_probe(struct udevice *dev)
{
- struct clk *clk;
+ struct clk_scmi *clk_scmi;
+ struct scmi_clock_priv *priv = dev_get_priv(dev);
size_t num_clocks, i;
int ret;
@@ -154,35 +265,98 @@ static int scmi_clk_probe(struct udevice *dev)
if (ret)
return ret;
+ ret = scmi_generic_protocol_version(dev, SCMI_PROTOCOL_ID_CLOCK, &priv->version);
+ if (ret) {
+ log_debug("%s: get SCMI clock management protocol version failed\n", __func__);
+ return ret;
+ }
+
for (i = 0; i < num_clocks; i++) {
char *clock_name;
+ u32 attributes;
- if (!scmi_clk_get_attibute(dev, i, &clock_name)) {
- clk = kzalloc(sizeof(*clk), GFP_KERNEL);
- if (!clk || !clock_name)
+ if (!scmi_clk_get_attibute(dev, i, &clock_name, &attributes)) {
+ clk_scmi = kzalloc(sizeof(*clk_scmi), GFP_KERNEL);
+ if (!clk_scmi || !clock_name)
ret = -ENOMEM;
else
- ret = clk_register(clk, dev->driver->name,
+ ret = clk_register(&clk_scmi->clk, dev->driver->name,
clock_name, dev->name);
if (ret) {
- free(clk);
+ free(clk_scmi);
free(clock_name);
return ret;
}
- clk_dm(i, clk);
+ clk_dm(i, &clk_scmi->clk);
+
+ if (CLK_HAS_RESTRICTIONS(attributes)) {
+ u32 perm;
+
+ ret = scmi_clk_get_permissions(dev, i, &perm);
+ if (ret < 0)
+ clk_scmi->ctrl_flags = 0;
+ else
+ clk_scmi->ctrl_flags = perm;
+ } else {
+ clk_scmi->ctrl_flags = SUPPORT_CLK_STAT_CONTROL | SUPPORT_CLK_PARENT_CONTROL |
+ SUPPORT_CLK_RATE_CONTROL;
+ }
}
}
return 0;
}
+static int __scmi_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct scmi_clk_parent_set_in in = {
+ .clock_id = clk->id,
+ .parent_clk = parent->id,
+ };
+ struct scmi_clk_parent_set_out out;
+ struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_CLOCK,
+ SCMI_CLOCK_PARENT_SET,
+ in, out);
+ int ret;
+
+ ret = devm_scmi_process_msg(clk->dev, &msg);
+ if (ret < 0)
+ return ret;
+
+ return scmi_to_linux_errno(out.status);
+}
+
+static int scmi_clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ struct clk_scmi *clkscmi;
+ struct clk *c;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK_CCF))
+ return -ENOTSUPP;
+
+ ret = clk_get_by_id(clk->id, &c);
+ if (ret)
+ return ret;
+
+ clkscmi = container_of(c, struct clk_scmi, clk);
+
+ if (clkscmi->ctrl_flags & SUPPORT_CLK_PARENT_CONTROL)
+ return __scmi_clk_set_parent(clk, parent);
+
+ /* Following Linux drivers/clk/clk-scmi.c, directly return 0 if agent has no permission. */
+ log_debug("%s: SCMI CLOCK: the clock's parent cannot be changed by the agent.\n", __func__);
+ return 0;
+}
+
static const struct clk_ops scmi_clk_ops = {
.enable = scmi_clk_enable,
.disable = scmi_clk_disable,
.get_rate = scmi_clk_get_rate,
.set_rate = scmi_clk_set_rate,
+ .set_parent = scmi_clk_set_parent,
};
U_BOOT_DRIVER(scmi_clock) = {
@@ -190,4 +364,12 @@ U_BOOT_DRIVER(scmi_clock) = {
.id = UCLASS_CLK,
.ops = &scmi_clk_ops,
.probe = scmi_clk_probe,
+ .priv_auto = sizeof(struct scmi_clock_priv),
};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_CLOCK },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_clock, match);
diff --git a/drivers/cpu/imx8_cpu.c b/drivers/cpu/imx8_cpu.c
index 4e1eccaa5b0..4836bddd93b 100644
--- a/drivers/cpu/imx8_cpu.c
+++ b/drivers/cpu/imx8_cpu.c
@@ -111,6 +111,8 @@ static const char *get_imx_type_str(u32 imxtype)
return "91(11)";/* iMX91 9x9 Reduced feature */
case MXC_CPU_IMX9101:
return "91(01)";/* iMX91 9x9 Specific feature */
+ case MXC_CPU_IMX95:
+ return "95";
default:
return "??";
}
diff --git a/drivers/firmware/scmi/base.c b/drivers/firmware/scmi/base.c
index f4e3974ff5b..78ee2ffd2da 100644
--- a/drivers/firmware/scmi/base.c
+++ b/drivers/firmware/scmi/base.c
@@ -258,7 +258,7 @@ static int scmi_base_discover_impl_version_int(struct udevice *dev,
static int scmi_base_discover_list_protocols_int(struct udevice *dev,
u8 **protocols)
{
- struct scmi_base_discover_list_protocols_out out;
+ struct scmi_base_discover_list_protocols_out *out;
int cur;
struct scmi_msg msg = {
.protocol_id = SCMI_PROTOCOL_ID_BASE,
@@ -268,7 +268,7 @@ static int scmi_base_discover_list_protocols_int(struct udevice *dev,
.out_msg = (u8 *)&out,
.out_msg_sz = sizeof(out),
};
- u32 num_agents, num_protocols;
+ u32 num_agents, num_protocols, out_size;
u8 *buf;
int i, ret;
@@ -276,22 +276,31 @@ static int scmi_base_discover_list_protocols_int(struct udevice *dev,
if (ret)
return ret;
+ out_size = sizeof(*out) + sizeof(u32) * (1 + num_protocols / 4);
+ out = calloc(1, out_size);
+ if (!out)
+ return -ENOMEM;
+ msg.out_msg = (u8 *)out;
+ msg.out_msg_sz = out_size;
+
buf = calloc(sizeof(u8), num_protocols);
- if (!buf)
+ if (!buf) {
+ free(out);
return -ENOMEM;
+ }
cur = 0;
do {
ret = devm_scmi_process_msg(dev, &msg);
if (ret)
goto err;
- if (out.status) {
- ret = scmi_to_linux_errno(out.status);
+ if (out->status) {
+ ret = scmi_to_linux_errno(out->status);
goto err;
}
- for (i = 0; i < out.num_protocols; i++, cur++)
- buf[cur] = out.protocols[i / 4] >> ((i % 4) * 8);
+ for (i = 0; i < out->num_protocols; i++, cur++)
+ buf[cur] = out->protocols[i / 4] >> ((i % 4) * 8);
} while (cur < num_protocols);
*protocols = buf;
@@ -299,6 +308,7 @@ static int scmi_base_discover_list_protocols_int(struct udevice *dev,
return num_protocols;
err:
free(buf);
+ free(out);
return ret;
}
diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c
index 19be280ec44..74a87832dcb 100644
--- a/drivers/firmware/scmi/sandbox-scmi_agent.c
+++ b/drivers/firmware/scmi/sandbox-scmi_agent.c
@@ -80,9 +80,9 @@ static struct sandbox_scmi_pwd scmi_pwdom[] = {
};
static struct sandbox_scmi_clk scmi_clk[] = {
- { .rate = 333 },
- { .rate = 200 },
- { .rate = 1000 },
+ { .rate = 333, .perm = 0xE0000000 },
+ { .rate = 200, .perm = 0xE0000000 },
+ { .rate = 1000, .perm = 0xE0000000 },
};
static struct sandbox_scmi_reset scmi_reset[] = {
@@ -700,6 +700,21 @@ static int sandbox_scmi_pwd_name_get(struct udevice *dev, struct scmi_msg *msg)
/* Clock Protocol */
+static int sandbox_scmi_clock_protocol_version(struct udevice *dev,
+ struct scmi_msg *msg)
+{
+ struct scmi_protocol_version_out *out = NULL;
+
+ if (!msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ out = (struct scmi_protocol_version_out *)msg->out_msg;
+ out->version = 0x30000;
+ out->status = SCMI_SUCCESS;
+
+ return 0;
+}
+
static int sandbox_scmi_clock_protocol_attribs(struct udevice *dev,
struct scmi_msg *msg)
{
@@ -740,6 +755,9 @@ static int sandbox_scmi_clock_attribs(struct udevice *dev, struct scmi_msg *msg)
if (clk_state->enabled)
out->attributes = 1;
+ /* Restricted clock */
+ out->attributes |= BIT(1);
+
ret = snprintf(out->clock_name, sizeof(out->clock_name),
"clk%u", in->clock_id);
assert(ret > 0 && ret < sizeof(out->clock_name));
@@ -837,6 +855,34 @@ static int sandbox_scmi_clock_gate(struct udevice *dev, struct scmi_msg *msg)
return 0;
}
+static int sandbox_scmi_clock_permissions_get(struct udevice *dev,
+ struct scmi_msg *msg)
+{
+ struct scmi_clk_get_permissions_in *in = NULL;
+ struct scmi_clk_get_permissions_out *out = NULL;
+ struct sandbox_scmi_clk *clk_state = NULL;
+
+ if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+ !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+ return -EINVAL;
+
+ in = (struct scmi_clk_get_permissions_in *)msg->in_msg;
+ out = (struct scmi_clk_get_permissions_out *)msg->out_msg;
+
+ clk_state = get_scmi_clk_state(in->clock_id);
+ if (!clk_state) {
+ dev_err(dev, "Unexpected clock ID %u\n", in->clock_id);
+
+ out->status = SCMI_NOT_FOUND;
+ } else {
+ out->permissions = clk_state->perm;
+
+ out->status = SCMI_SUCCESS;
+ }
+
+ return 0;
+}
+
static int sandbox_scmi_rd_attribs(struct udevice *dev, struct scmi_msg *msg)
{
struct scmi_rd_attr_in *in = NULL;
@@ -1193,6 +1239,8 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev,
return sandbox_proto_not_supported(msg);
switch (msg->message_id) {
+ case SCMI_PROTOCOL_VERSION:
+ return sandbox_scmi_clock_protocol_version(dev, msg);
case SCMI_PROTOCOL_ATTRIBUTES:
return sandbox_scmi_clock_protocol_attribs(dev, msg);
case SCMI_CLOCK_ATTRIBUTES:
@@ -1203,6 +1251,8 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev,
return sandbox_scmi_clock_rate_get(dev, msg);
case SCMI_CLOCK_CONFIG_SET:
return sandbox_scmi_clock_gate(dev, msg);
+ case SCMI_CLOCK_GET_PERMISSIONS:
+ return sandbox_scmi_clock_permissions_get(dev, msg);
default:
break;
}
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c
index e6e43ae936a..e7ec2c108e6 100644
--- a/drivers/firmware/scmi/scmi_agent-uclass.c
+++ b/drivers/firmware/scmi/scmi_agent-uclass.c
@@ -97,6 +97,9 @@ struct udevice *scmi_get_protocol(struct udevice *dev,
case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
proto = priv->voltagedom_dev;
break;
+ case SCMI_PROTOCOL_ID_PINCTRL:
+ proto = priv->pinctrl_dev;
+ break;
default:
dev_err(dev, "Protocol not supported\n");
proto = NULL;
@@ -147,6 +150,9 @@ static int scmi_add_protocol(struct udevice *dev,
case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
priv->voltagedom_dev = proto;
break;
+ case SCMI_PROTOCOL_ID_PINCTRL:
+ priv->pinctrl_dev = proto;
+ break;
default:
dev_err(dev, "Protocol not supported\n");
return -EPROTO;
@@ -352,6 +358,22 @@ static int scmi_fill_base_info(struct udevice *agent, struct udevice *dev)
return 0;
}
+static struct driver *scmi_proto_driver_get(unsigned int proto_id)
+{
+ struct scmi_proto_driver *start, *entry;
+ int n_ents;
+
+ start = ll_entry_start(struct scmi_proto_driver, scmi_proto_driver);
+ n_ents = ll_entry_count(struct scmi_proto_driver, scmi_proto_driver);
+
+ for (entry = start; entry != start + n_ents; entry++) {
+ if (entry->match->proto_id == proto_id)
+ return entry->driver;
+ }
+
+ return NULL;
+}
+
/*
* SCMI agent devices binds devices of various uclasses depending on
* the FDT description. scmi_bind_protocol() is a generic bind sequence
@@ -409,31 +431,11 @@ static int scmi_bind_protocols(struct udevice *dev)
drv = NULL;
name = ofnode_get_name(node);
- switch (protocol_id) {
- case SCMI_PROTOCOL_ID_POWER_DOMAIN:
- if (CONFIG_IS_ENABLED(SCMI_POWER_DOMAIN) &&
- scmi_protocol_is_supported(dev, protocol_id))
- drv = DM_DRIVER_GET(scmi_power_domain);
- break;
- case SCMI_PROTOCOL_ID_CLOCK:
- if (CONFIG_IS_ENABLED(CLK_SCMI) &&
- scmi_protocol_is_supported(dev, protocol_id))
- drv = DM_DRIVER_GET(scmi_clock);
- break;
- case SCMI_PROTOCOL_ID_RESET_DOMAIN:
- if (IS_ENABLED(CONFIG_RESET_SCMI) &&
- scmi_protocol_is_supported(dev, protocol_id))
- drv = DM_DRIVER_GET(scmi_reset_domain);
- break;
- case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
- if (IS_ENABLED(CONFIG_DM_REGULATOR_SCMI) &&
- scmi_protocol_is_supported(dev, protocol_id))
- drv = DM_DRIVER_GET(scmi_voltage_domain);
- break;
- default:
- break;
- }
+ if (!scmi_protocol_is_supported(dev, protocol_id))
+ continue;
+
+ drv = scmi_proto_driver_get(protocol_id);
if (!drv) {
dev_dbg(dev, "Ignore unsupported SCMI protocol %#x\n",
protocol_id);
diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig
index d13c5f2a6d5..84d9a3641ff 100644
--- a/drivers/pinctrl/nxp/Kconfig
+++ b/drivers/pinctrl/nxp/Kconfig
@@ -139,6 +139,19 @@ config PINCTRL_IMXRT
only parses the 'fsl,pins' property and configure related
registers.
+config PINCTRL_IMX_SCMI
+ bool "IMX pinctrl SCMI driver"
+ depends on ARCH_IMX9 && PINCTRL_FULL
+ select PINCTRL_IMX
+ help
+ This provides a simple pinctrl driver for i.MX SoC which supports
+ SCMI. This feature depends on device tree configuration. This driver
+ is different from the linux one, this is a simple implementation,
+ only parses the 'fsl,pins' property and configure related
+ registers.
+
+ Say Y here to enable the imx pinctrl SCMI driver
+
config PINCTRL_VYBRID
bool "Vybrid (vf610) pinctrl driver"
depends on ARCH_VF610 && PINCTRL_FULL
diff --git a/drivers/pinctrl/nxp/Makefile b/drivers/pinctrl/nxp/Makefile
index 44e37c631e5..7d861ae52c1 100644
--- a/drivers/pinctrl/nxp/Makefile
+++ b/drivers/pinctrl/nxp/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_PINCTRL_IMX93) += pinctrl-imx93.o
obj-$(CONFIG_PINCTRL_MXS) += pinctrl-mxs.o
obj-$(CONFIG_PINCTRL_VYBRID) += pinctrl-vf610.o
obj-$(CONFIG_PINCTRL_IMXRT) += pinctrl-imxrt.o
+obj-$(CONFIG_PINCTRL_IMX_SCMI) += pinctrl-imx-scmi.o
diff --git a/drivers/pinctrl/nxp/pinctrl-imx-scmi.c b/drivers/pinctrl/nxp/pinctrl-imx-scmi.c
new file mode 100644
index 00000000000..aed47be337d
--- /dev/null
+++ b/drivers/pinctrl/nxp/pinctrl-imx-scmi.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <asm/io.h>
+#include <asm/mach-imx/sys_proto.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <dm/pinctrl.h>
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+
+#include "pinctrl-imx.h"
+
+#define DAISY_OFFSET_IMX95 0x408
+
+/* SCMI pin control types */
+#define PINCTRL_TYPE_MUX 192
+#define PINCTRL_TYPE_CONFIG 193
+#define PINCTRL_TYPE_DAISY_ID 194
+#define PINCTRL_TYPE_DAISY_CFG 195
+#define PINCTRL_NUM_CFGS_SHIFT 2
+
+struct imx_scmi_pinctrl_priv {
+ u16 daisy_offset;
+};
+
+static int imx_pinconf_scmi_set(struct udevice *dev, u32 mux_ofs, u32 mux, u32 config_val,
+ u32 input_ofs, u32 input_val)
+{
+ struct imx_scmi_pinctrl_priv *priv = dev_get_priv(dev);
+ int ret, num_cfgs = 0;
+ struct scmi_msg msg;
+
+ /* Call SCMI API to set the pin mux and configuration. */
+ struct scmi_pinctrl_config_set_out out;
+ struct scmi_pinctrl_config_set_in in = {
+ .identifier = mux_ofs / 4,
+ .function_id = 0xFFFFFFFF,
+ .attributes = 0,
+ };
+
+ if (mux_ofs) {
+ in.configs[num_cfgs].type = PINCTRL_TYPE_MUX;
+ in.configs[num_cfgs].val = mux;
+ num_cfgs++;
+ }
+
+ if (config_val) {
+ in.configs[num_cfgs].type = PINCTRL_TYPE_CONFIG;
+ in.configs[num_cfgs].val = config_val;
+ num_cfgs++;
+ }
+
+ if (input_ofs) {
+ in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_ID;
+ in.configs[num_cfgs].val = (input_ofs - priv->daisy_offset) / 4;
+ num_cfgs++;
+ in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_CFG;
+ in.configs[num_cfgs].val = input_val;
+ num_cfgs++;
+ }
+
+ /* Update the number of configs sent in this call. */
+ in.attributes = num_cfgs << PINCTRL_NUM_CFGS_SHIFT;
+
+ msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_PINCTRL,
+ SCMI_MSG_PINCTRL_CONFIG_SET, in, out);
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret || out.status) {
+ dev_err(dev, "Failed to set PAD = %d, daisy = %d, scmi_err = %d, ret = %d\n",
+ mux_ofs / 4, input_ofs / 4, out.status, ret);
+ }
+
+ return ret;
+}
+
+static int imx_pinctrl_set_state_scmi(struct udevice *dev, struct udevice *config)
+{
+ int mux_ofs, mux, config_val, input_reg, input_val;
+ u32 *pin_data;
+ int i, j = 0;
+ int npins;
+ int ret;
+
+ ret = imx_pinctrl_set_state_common(dev, config, FSL_PIN_SIZE,
+ &pin_data, &npins);
+ if (ret)
+ return ret;
+
+ /*
+ * Refer to linux documentation for details:
+ * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt
+ */
+ for (i = 0; i < npins; i++) {
+ mux_ofs = pin_data[j++];
+ /* Skip config_reg */
+ j++;
+ input_reg = pin_data[j++];
+
+ mux = pin_data[j++];
+ input_val = pin_data[j++];
+ config_val = pin_data[j++];
+
+ if (config_val & IMX_PAD_SION)
+ mux |= IOMUXC_CONFIG_SION;
+
+ config_val &= ~IMX_PAD_SION;
+
+ ret = imx_pinconf_scmi_set(dev, mux_ofs, mux, config_val, input_reg, input_val);
+ if (ret && ret != -EPERM) {
+ dev_err(dev, "Set pin %d, mux %d, val %d, error\n",
+ mux_ofs, mux, config_val);
+ }
+ }
+
+ devm_kfree(dev, pin_data);
+
+ return ret;
+}
+
+static const struct pinctrl_ops imx_scmi_pinctrl_ops = {
+ .set_state = imx_pinctrl_set_state_scmi,
+};
+
+static int imx_scmi_pinctrl_probe(struct udevice *dev)
+{
+ struct imx_scmi_pinctrl_priv *priv = dev_get_priv(dev);
+
+ if (IS_ENABLED(CONFIG_IMX95))
+ priv->daisy_offset = DAISY_OFFSET_IMX95;
+ else
+ return -EINVAL;
+
+ return devm_scmi_of_get_channel(dev);
+}
+
+static int imx_scmi_pinctrl_bind(struct udevice *dev)
+{
+ if (IS_ENABLED(CONFIG_IMX95))
+ return 0;
+
+ return -ENODEV;
+}
+
+U_BOOT_DRIVER(scmi_pinctrl_imx) = {
+ .name = "scmi_pinctrl_imx",
+ .id = UCLASS_PINCTRL,
+ .bind = imx_scmi_pinctrl_bind,
+ .probe = imx_scmi_pinctrl_probe,
+ .priv_auto = sizeof(struct imx_scmi_pinctrl_priv),
+ .ops = &imx_scmi_pinctrl_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_PINCTRL },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_pinctrl_imx, match);
diff --git a/drivers/power/domain/scmi-power-domain.c b/drivers/power/domain/scmi-power-domain.c
index 3cd0f075d95..e8c0ba8878e 100644
--- a/drivers/power/domain/scmi-power-domain.c
+++ b/drivers/power/domain/scmi-power-domain.c
@@ -11,6 +11,7 @@
#include <power-domain.h>
#include <power-domain-uclass.h>
#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <dm/device_compat.h>
@@ -190,3 +191,10 @@ U_BOOT_DRIVER(scmi_power_domain) = {
.probe = scmi_power_domain_probe,
.priv_auto = sizeof(struct scmi_power_domain_priv),
};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_POWER_DOMAIN },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_power_domain, match);
diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c
index 79db1a6a8aa..7d2db1e2bee 100644
--- a/drivers/power/regulator/scmi_regulator.c
+++ b/drivers/power/regulator/scmi_regulator.c
@@ -8,6 +8,7 @@
#include <dm.h>
#include <errno.h>
#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <asm/types.h>
#include <dm/device.h>
@@ -202,3 +203,10 @@ U_BOOT_DRIVER(scmi_voltage_domain) = {
.id = UCLASS_NOP,
.bind = scmi_regulator_bind,
};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_voltage_domain, match);
diff --git a/drivers/reset/reset-scmi.c b/drivers/reset/reset-scmi.c
index 6dc1fcb3365..f92a9e35579 100644
--- a/drivers/reset/reset-scmi.c
+++ b/drivers/reset/reset-scmi.c
@@ -9,6 +9,7 @@
#include <errno.h>
#include <reset-uclass.h>
#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
#include <scmi_protocols.h>
#include <asm/types.h>
@@ -81,3 +82,10 @@ U_BOOT_DRIVER(scmi_reset_domain) = {
.ops = &scmi_reset_domain_ops,
.probe = scmi_reset_probe,
};
+
+static struct scmi_proto_match match[] = {
+ { .proto_id = SCMI_PROTOCOL_ID_RESET_DOMAIN },
+ { /* Sentinel */ }
+};
+
+U_BOOT_SCMI_PROTO_DRIVER(scmi_reset_domain, match);
diff --git a/include/configs/imx95_evk.h b/include/configs/imx95_evk.h
new file mode 100644
index 00000000000..2eebdadc51d
--- /dev/null
+++ b/include/configs/imx95_evk.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2025 NXP
+ */
+
+#ifndef __IMX95_EVK_H
+#define __IMX95_EVK_H
+
+#include <linux/sizes.h>
+#include <linux/stringify.h>
+#include <asm/arch/imx-regs.h>
+
+#define CFG_SYS_INIT_RAM_ADDR 0x90000000
+#define CFG_SYS_INIT_RAM_SIZE 0x200000
+
+#define CFG_SYS_SDRAM_BASE 0x90000000
+#define PHYS_SDRAM 0x90000000
+/* Totally 16GB */
+#define PHYS_SDRAM_SIZE 0x70000000 /* 2GB - 256MB DDR */
+#define PHYS_SDRAM_2_SIZE 0x380000000 /* 14GB */
+
+#define WDOG_BASE_ADDR WDG3_BASE_ADDR
+
+#endif
diff --git a/include/imx8image.h b/include/imx8image.h
index 6b95e93fb50..e0d25c5b6c9 100644
--- a/include/imx8image.h
+++ b/include/imx8image.h
@@ -157,7 +157,9 @@ enum imx8image_cmd {
CMD_SOC_TYPE,
CMD_CONTAINER,
CMD_IMAGE,
- CMD_DATA
+ CMD_DATA,
+ CMD_DUMMY_V2X,
+ CMD_HOLD,
};
enum imx8image_core_type {
@@ -169,7 +171,9 @@ enum imx8image_core_type {
CFG_A35,
CFG_A55,
CFG_A53,
- CFG_A72
+ CFG_A72,
+ CFG_M33,
+ CFG_OEI,
};
enum imx8image_fld_types {
@@ -208,7 +212,10 @@ typedef enum option_type {
FILEOFF,
MSG_BLOCK,
SENTINEL,
- UPOWER
+ UPOWER,
+ OEI,
+ DUMMY_V2X,
+ HOLD,
} option_type_t;
typedef struct {
@@ -227,12 +234,16 @@ typedef struct {
#define CORE_CA35 4
#define CORE_CA72 5
#define CORE_SECO 6
+#define CORE_M33 7
#define CORE_ULP_CM33 0x1
#define CORE_ULP_CA35 0x2
#define CORE_ULP_UPOWER 0x4
#define CORE_ULP_SENTINEL 0x6
+#define CORE_IMX95_M33P 0
+#define CORE_IMX95_A55C0 2
+
#define SC_R_OTP 357U
#define SC_R_DEBUG 354U
#define SC_R_ROM_0 236U
@@ -246,10 +257,12 @@ typedef struct {
#define IMG_TYPE_EXEC 0x03 /* Executable image type */
#define IMG_TYPE_DATA 0x04 /* Data image type */
#define IMG_TYPE_DCD_DDR 0x05 /* DCD/DDR image type */
+#define IMG_TYPE_OEI 0x05 /* Optional Executable image type */
#define IMG_TYPE_SECO 0x06 /* SECO image type */
#define IMG_TYPE_SENTINEL 0x06 /* SENTINEL image type */
#define IMG_TYPE_PROV 0x07 /* Provisioning image type */
#define IMG_TYPE_DEK 0x08 /* DEK validation type */
+#define IMG_TYPE_V2X_DUMMY 0x0E /* V2X Dummy image */
#define IMG_TYPE_SHIFT 0
#define IMG_TYPE_MASK 0x1f
diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h
index 33e0e18c30d..d6586eb3ff9 100644
--- a/include/scmi_agent-uclass.h
+++ b/include/scmi_agent-uclass.h
@@ -27,6 +27,7 @@ struct scmi_channel;
* @clock_dev: SCMI clock protocol device
* @resetdom_dev: SCMI reset domain protocol device
* @voltagedom_dev: SCMI voltage domain protocol device
+ * @pinctrl_dev: SCMI pin control protocol device
*/
struct scmi_agent_priv {
u32 version;
@@ -43,6 +44,7 @@ struct scmi_agent_priv {
struct udevice *clock_dev;
struct udevice *resetdom_dev;
struct udevice *voltagedom_dev;
+ struct udevice *pinctrl_dev;
};
static inline u32 scmi_version(struct udevice *dev)
@@ -115,4 +117,19 @@ struct scmi_agent_ops {
struct scmi_msg *msg);
};
+struct scmi_proto_match {
+ unsigned int proto_id;
+};
+
+struct scmi_proto_driver {
+ struct driver *driver;
+ const struct scmi_proto_match *match;
+};
+
+#define U_BOOT_SCMI_PROTO_DRIVER(__name, __match) \
+ ll_entry_declare(struct scmi_proto_driver, __name, scmi_proto_driver) = { \
+ .driver = llsym(struct driver, __name, driver), \
+ .match = __match, \
+ }
+
#endif /* _SCMI_TRANSPORT_UCLASS_H */
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h
index 7abb2a6f36b..9046de7e3e7 100644
--- a/include/scmi_protocols.h
+++ b/include/scmi_protocols.h
@@ -24,6 +24,8 @@ enum scmi_std_protocol {
SCMI_PROTOCOL_ID_SENSOR = 0x15,
SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16,
SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17,
+ SCMI_PROTOCOL_ID_PINCTRL = 0x19,
+ SCMI_PROTOCOL_ID_IMX_MISC = 0x84,
};
enum scmi_status_code {
@@ -49,6 +51,10 @@ enum scmi_discovery_id {
SCMI_PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
};
+enum scmi_imx_misc_message_id {
+ SCMI_MISC_ROM_PASSOVER_GET = 0x7
+};
+
/*
* SCMI Base Protocol
*/
@@ -139,7 +145,7 @@ struct scmi_base_discover_impl_version_out {
struct scmi_base_discover_list_protocols_out {
s32 status;
u32 num_protocols;
- u32 protocols[3];
+ u32 protocols[];
};
/**
@@ -725,12 +731,15 @@ int scmi_pwd_name_get(struct udevice *dev, u32 domain_id, u8 **name);
/*
* SCMI Clock Protocol
*/
+#define CLOCK_PROTOCOL_VERSION_3_0 0x30000
enum scmi_clock_message_id {
SCMI_CLOCK_ATTRIBUTES = 0x3,
SCMI_CLOCK_RATE_SET = 0x5,
SCMI_CLOCK_RATE_GET = 0x6,
SCMI_CLOCK_CONFIG_SET = 0x7,
+ SCMI_CLOCK_PARENT_SET = 0xD,
+ SCMI_CLOCK_GET_PERMISSIONS = 0xF,
};
#define SCMI_CLK_PROTO_ATTR_COUNT_MASK GENMASK(15, 0)
@@ -769,6 +778,7 @@ struct scmi_clk_attribute_in {
struct scmi_clk_attribute_out {
s32 status;
u32 attributes;
+#define CLK_HAS_RESTRICTIONS(x) ((x) & BIT(1))
char clock_name[SCMI_CLOCK_NAME_LENGTH_MAX];
};
@@ -833,6 +843,45 @@ struct scmi_clk_rate_set_out {
s32 status;
};
+/**
+ * struct scmi_clk_parent_state_in - Message payload for CLOCK_PARENT_SET command
+ * @clock_id: SCMI clock ID
+ * @parent_clk: SCMI clock ID
+ */
+struct scmi_clk_parent_set_in {
+ u32 clock_id;
+ u32 parent_clk;
+};
+
+/**
+ * struct scmi_clk_parent_set_out - Response payload for CLOCK_PARENT_SET command
+ * @status: SCMI command status
+ */
+struct scmi_clk_parent_set_out {
+ s32 status;
+};
+
+/**
+ * @clock_id: Identifier for the clock device.
+ */
+struct scmi_clk_get_permissions_in {
+ u32 clock_id;
+};
+
+/**
+ * @status: Negative 32-bit integers are used to return error status codes.
+ * @permissions: Bit[31] Clock state control, Bit[30] Clock parent control,
+ * Bit[29] Clock rate control, Bits[28:0] Reserved, must be zero.
+ */
+struct scmi_clk_get_permissions_out {
+ s32 status;
+ u32 permissions;
+};
+
+#define SUPPORT_CLK_STAT_CONTROL BIT(31)
+#define SUPPORT_CLK_PARENT_CONTROL BIT(30)
+#define SUPPORT_CLK_RATE_CONTROL BIT(29)
+
/*
* SCMI Reset Domain Protocol
*/
@@ -1005,4 +1054,57 @@ struct scmi_voltd_level_get_out {
s32 voltage_level;
};
+/* SCMI Pinctrl Protocol */
+enum scmi_pinctrl_message_id {
+ SCMI_MSG_PINCTRL_CONFIG_SET = 0x6
+};
+
+struct scmi_pin_config {
+ u32 type;
+ u32 val;
+};
+
+/**
+ * struct scmi_pad_config_set_in - Message payload for PAD_CONFIG_SET command
+ * @identifier: Identifier for the pin or group.
+ * @function_id: Identifier for the function selected to be enabled
+ * for the selected pin or group. This field is set to
+ * 0xFFFFFFFF if no function should be enabled by the
+ * pin or group.
+ * @attributes: Bits[31:11] Reserved, must be zero.
+ * Bit[10] Function valid.
+ * Bits[9:2] Number of configurations to set.
+ * Bits[1:0] Selector: Whether the identifier field
+ * refers to a pin or a group.
+ * @configs: Array of configurations.
+ */
+struct scmi_pinctrl_config_set_in {
+ u32 identifier;
+ u32 function_id;
+ u32 attributes;
+ struct scmi_pin_config configs[4];
+};
+
+struct scmi_pinctrl_config_set_out {
+ s32 status;
+};
+
+/* SCMI Perf Protocol */
+enum scmi_perf_message_id {
+ SCMI_PERF_DOMAIN_ATTRIBUTES = 0x3,
+ SCMI_PERF_DESCRIBE_LEVELS = 0x4,
+ SCMI_PERF_LIMITS_SET = 0x5,
+ SCMI_PERF_LIMITS_GET = 0x6,
+ SCMI_PERF_LEVEL_SET = 0x7,
+ SCMI_PERF_LEVEL_GET = 0x8
+};
+
+struct scmi_perf_in {
+ u32 domain_id;
+ u32 perf_level;
+};
+
+struct scmi_perf_out {
+ s32 status;
+};
#endif /* _SCMI_PROTOCOLS_H */
diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst
index 780e9817fb6..4f05aa0a323 100644
--- a/tools/binman/entries.rst
+++ b/tools/binman/entries.rst
@@ -1663,6 +1663,16 @@ Properties / Entry arguments:
+.. _etype_nxp_header_ddrfw:
+
+Entry: nxp-header-ddrfw: add a header to DDR PHY firmware images
+----------------------------------------------------------------
+
+This entry is used to combine DDR PHY firmware images and their byte counts
+together. See imx95_evk.rst for how to get DDR PHY Firmware Images.
+
+
+
.. _etype_opensbi:
Entry: opensbi: RISC-V OpenSBI fw_dynamic blob
diff --git a/tools/binman/etype/nxp_header_ddrfw.py b/tools/binman/etype/nxp_header_ddrfw.py
new file mode 100644
index 00000000000..655699e6ffa
--- /dev/null
+++ b/tools/binman/etype/nxp_header_ddrfw.py
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2025 NXP
+
+from binman.etype.section import Entry_section
+
+class Entry_nxp_header_ddrfw(Entry_section):
+ """Add a header to DDR PHY firmware images
+
+ This entry is used for i.MX95 to combine DDR PHY firmware images and their
+ byte counts together.
+
+ See imx95_evk.rst for how to get DDR PHY Firmware Images.
+ """
+
+ def __init__(self, section, etype, node):
+ super().__init__(section, etype, node)
+
+ def BuildSectionData(self, required):
+ section_data = bytearray()
+ header_data = bytearray()
+
+ for entry in self._entries.values():
+ entry_data = entry.GetData(required)
+
+ section_data += entry_data
+ header_data += entry.contents_size.to_bytes(4, 'little')
+
+ return header_data + section_data
diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py
index 92df2a03650..fa174900014 100644
--- a/tools/binman/ftest.py
+++ b/tools/binman/ftest.py
@@ -104,6 +104,8 @@ PRE_LOAD_VERSION = 0x11223344.to_bytes(4, 'big')
PRE_LOAD_HDR_SIZE = 0x00001000.to_bytes(4, 'big')
TI_BOARD_CONFIG_DATA = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
TI_UNSECURE_DATA = b'unsecuredata'
+IMX_LPDDR_IMEM_DATA = b'qwertyuiop1234567890'
+IMX_LPDDR_DMEM_DATA = b'asdfghjklzxcvbnm'
# Subdirectory of the input dir to use to put test FDTs
TEST_FDT_SUBDIR = 'fdts'
@@ -202,6 +204,8 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
+ TestFunctional._MakeInputFile('lpddr5_imem.bin', IMX_LPDDR_IMEM_DATA)
+ TestFunctional._MakeInputFile('lpddr5_dmem.bin', IMX_LPDDR_DMEM_DATA)
cls._elf_testdir = os.path.join(cls._indir, 'elftest')
elf_test.BuildElfTestFiles(cls._elf_testdir)
@@ -7848,6 +7852,13 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
"""Test that binman can produce an iMX8 image"""
self._DoTestFile('339_nxp_imx8.dts')
+ def testNxpHeaderDdrfw(self):
+ """Test that binman can add a header to DDR PHY firmware images"""
+ data = self._DoReadFile('346_nxp_ddrfw_imx95.dts')
+ self.assertEqual(len(IMX_LPDDR_IMEM_DATA).to_bytes(4, 'little') +
+ len(IMX_LPDDR_DMEM_DATA).to_bytes(4, 'little') +
+ IMX_LPDDR_IMEM_DATA + IMX_LPDDR_DMEM_DATA, data)
+
def testFitSignSimple(self):
"""Test that image with FIT and signature nodes can be signed"""
if not elf.ELF_TOOLS:
diff --git a/tools/binman/test/346_nxp_ddrfw_imx95.dts b/tools/binman/test/346_nxp_ddrfw_imx95.dts
new file mode 100644
index 00000000000..889f6f29860
--- /dev/null
+++ b/tools/binman/test/346_nxp_ddrfw_imx95.dts
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ imx-lpddr {
+ type = "nxp-header-ddrfw";
+
+ imx-lpddr-imem {
+ filename = "lpddr5_imem.bin";
+ type = "blob-ext";
+ };
+
+ imx-lpddr-dmem {
+ filename = "lpddr5_dmem.bin";
+ type = "blob-ext";
+ };
+ };
+ };
+};
diff --git a/tools/imx8image.c b/tools/imx8image.c
index 0135b190951..a333ded46e2 100644
--- a/tools/imx8image.c
+++ b/tools/imx8image.c
@@ -7,6 +7,7 @@
#include "imx8image.h"
#include <image.h>
+#include <linux/sizes.h>
static int p_idx;
static int sector_size;
@@ -54,7 +55,9 @@ static table_entry_t imx8image_cmds[] = {
{CMD_CONTAINER, "CONTAINER", "new container", },
{CMD_IMAGE, "IMAGE", "new image", },
{CMD_DATA, "DATA", "new data", },
- {-1, "", "", },
+ {CMD_DUMMY_V2X, "DUMMY_V2X", "v2x", },
+ {CMD_HOLD, "HOLD", "hold", },
+ {-1, "", "", }
};
static table_entry_t imx8image_core_entries[] = {
@@ -66,7 +69,9 @@ static table_entry_t imx8image_core_entries[] = {
{CFG_A55, "A55", "A55 core", },
{CFG_A53, "A53", "A53 core", },
{CFG_A72, "A72", "A72 core", },
- {-1, "", "", },
+ {CFG_OEI, "OEI", "OEI", },
+ {CFG_M33, "M33", "M33 core", },
+ {-1, "", "", }
};
static table_entry_t imx8image_sector_size[] = {
@@ -144,6 +149,14 @@ static void parse_cfg_cmd(image_t *param_stack, int32_t cmd, char *token,
exit(EXIT_FAILURE);
}
break;
+ case CMD_DUMMY_V2X:
+ param_stack[p_idx].option = DUMMY_V2X;
+ param_stack[p_idx++].entry = (uint32_t)strtoll(token, NULL, 0);
+ break;
+ case CMD_HOLD:
+ param_stack[p_idx].option = HOLD;
+ param_stack[p_idx].entry = (uint32_t)strtoll(token, NULL, 0);
+ param_stack[p_idx++].filename = NULL;
default:
break;
}
@@ -221,6 +234,16 @@ static void parse_cfg_fld(image_t *param_stack, int32_t *cmd, char *token,
(*cmd == CMD_DATA) ? DATA : AP;
param_stack[p_idx].filename = token;
break;
+ case CFG_OEI:
+ param_stack[p_idx].option = OEI;
+ param_stack[p_idx].filename = token;
+ param_stack[p_idx].ext = CORE_CM4_0;
+ break;
+ case CFG_M33:
+ param_stack[p_idx].option = M40;
+ param_stack[p_idx].ext = 0;
+ param_stack[p_idx].filename = token;
+ break;
}
break;
case CFG_LOAD_ADDR:
@@ -238,9 +261,15 @@ static void parse_cfg_fld(image_t *param_stack, int32_t *cmd, char *token,
case CFG_A53:
case CFG_A55:
case CFG_A72:
+ case CFG_M33:
param_stack[p_idx++].entry =
(uint32_t)strtoll(token, NULL, 0);
break;
+ case CFG_OEI:
+ param_stack[p_idx].dst = (uint32_t)strtoll(token, NULL, 0);
+ param_stack[p_idx].entry = param_stack[p_idx].dst + 1;
+ p_idx++;
+ break;
}
default:
break;
@@ -549,6 +578,7 @@ static void set_image_array_entry(flash_header_v3_t *container,
char *tmp_filename, bool dcd_skip)
{
uint64_t entry = image_stack->entry;
+ uint64_t dst = image_stack->dst;
uint64_t core = image_stack->ext;
uint32_t meta;
char *tmp_name = "";
@@ -558,7 +588,9 @@ static void set_image_array_entry(flash_header_v3_t *container,
img->offset = offset; /* Is re-adjusted later */
img->size = size;
- set_image_hash(img, tmp_filename, IMAGE_HASH_ALGO_DEFAULT);
+ if (type != DUMMY_V2X) {
+ set_image_hash(img, tmp_filename, IMAGE_HASH_ALGO_DEFAULT);
+ }
switch (type) {
case SECO:
@@ -580,6 +612,27 @@ static void set_image_array_entry(flash_header_v3_t *container,
img->dst = 0xe4000000; /* S400 IRAM base */
img->entry = 0xe4000000;
break;
+ case OEI:
+ if (soc != IMX9) {
+ fprintf(stderr, "Error: invalid core id: %" PRIi64 "\n", core);
+ exit(EXIT_FAILURE);
+ }
+
+ img->hab_flags |= IMG_TYPE_OEI;
+ if (core == CORE_CM4_0) {
+ img->hab_flags |= CORE_ULP_CM33 << BOOT_IMG_FLAGS_CORE_SHIFT;
+ meta = CORE_IMX95_M33P;
+
+ } else {
+ img->hab_flags |= CORE_ULP_CA35 << BOOT_IMG_FLAGS_CORE_SHIFT;
+ meta = CORE_IMX95_A55C0;
+ }
+ tmp_name = "OEI";
+ img->dst = (dst ? dst : entry);
+ img->entry = entry;
+ img->meta = meta;
+ custom_partition = 0;
+ break;
case AP:
if (soc == QX && core == CORE_CA35) {
meta = IMAGE_A35_DEFAULT_META(custom_partition);
@@ -587,8 +640,10 @@ static void set_image_array_entry(flash_header_v3_t *container,
meta = IMAGE_A53_DEFAULT_META(custom_partition);
} else if (soc == QM && core == CORE_CA72) {
meta = IMAGE_A72_DEFAULT_META(custom_partition);
- } else if (((soc == ULP) || (soc == IMX9)) && core == CORE_CA35) {
+ } else if ((soc == ULP) && core == CORE_CA35) {
meta = 0;
+ } else if ((soc == IMX9) && core == CORE_CA35) {
+ meta = CORE_IMX95_A55C0;
} else {
fprintf(stderr,
"Error: invalid AP core id: %" PRIu64 "\n",
@@ -687,6 +742,15 @@ static void set_image_array_entry(flash_header_v3_t *container,
img->entry = 0x28300200;
}
break;
+ case DUMMY_V2X:
+ img->hab_flags |= IMG_TYPE_V2X_DUMMY;
+ img->hab_flags |= CORE_SC << BOOT_IMG_FLAGS_CORE_SHIFT;
+ tmp_name = "V2X Dummy";
+ set_image_hash(img, "/dev/null", IMAGE_HASH_ALGO_DEFAULT);
+ img->dst = entry;
+ img->entry = entry;
+ img->size = 0; /* dummy image has no size */
+ break;
default:
fprintf(stderr, "unrecognized image type (%d)\n", type);
exit(EXIT_FAILURE);
@@ -709,15 +773,26 @@ void set_container(flash_header_v3_t *container, uint16_t sw_version,
fprintf(stdout, "container flags: 0x%x\n", container->flags);
}
-static int get_container_image_start_pos(image_t *image_stack, uint32_t align)
+static int get_container_image_start_pos(image_t *image_stack, uint32_t align, uint32_t *v2x)
{
image_t *img_sp = image_stack;
/*8K total container header*/
int file_off = CONTAINER_IMAGE_ARRAY_START_OFFSET;
FILE *fd = NULL;
- flash_header_v3_t header;
+ flash_header_v3_t *header;
+ flash_header_v3_t *header2;
+ void *p;
int ret;
+ p = calloc(1, SZ_4K);
+ if (!p) {
+ fprintf(stderr, "Fail to alloc 4K memory\n");
+ exit(EXIT_FAILURE);
+ }
+
+ header = p;
+ header2 = p + FIRST_CONTAINER_HEADER_LENGTH;
+
while (img_sp->option != NO_IMG) {
if (img_sp->option == APPEND) {
fd = fopen(img_sp->filename, "r");
@@ -726,7 +801,7 @@ static int get_container_image_start_pos(image_t *image_stack, uint32_t align)
exit(EXIT_FAILURE);
}
- ret = fread(&header, sizeof(header), 1, fd);
+ ret = fread(header, SZ_4K, 1, fd);
if (ret != 1) {
printf("Failure Read header %d\n", ret);
exit(EXIT_FAILURE);
@@ -734,19 +809,27 @@ static int get_container_image_start_pos(image_t *image_stack, uint32_t align)
fclose(fd);
- if (header.tag != IVT_HEADER_TAG_B0) {
- fprintf(stderr, "header tag mismatched file %s\n", img_sp->filename);
+ if (header->tag != IVT_HEADER_TAG_B0) {
+ fprintf(stderr, "header tag mismatched \n");
exit(EXIT_FAILURE);
} else {
- file_off +=
- header.img[header.num_images - 1].size;
- file_off = ALIGN(file_off, align);
+ if (header2->tag != IVT_HEADER_TAG_B0) {
+ file_off += header->img[header->num_images - 1].size;
+ file_off = ALIGN(file_off, align);
+ } else {
+ file_off = header2->img[header2->num_images - 1].offset + FIRST_CONTAINER_HEADER_LENGTH;
+ file_off += header2->img[header2->num_images - 1].size;
+ file_off = ALIGN(file_off, align);
+ fprintf(stderr, "Has 2nd container %x\n", file_off);
+ *v2x = true;
+ }
}
}
img_sp++;
}
+ free(p);
return file_off;
}
@@ -838,6 +921,7 @@ static int build_container(soc_type_t soc, uint32_t sector_size,
char *tmp_filename = NULL;
uint32_t size = 0;
uint32_t file_padding = 0;
+ uint32_t v2x = false;
int ret;
int container = -1;
@@ -861,7 +945,7 @@ static int build_container(soc_type_t soc, uint32_t sector_size,
set_imx_hdr_v3(&imx_header, 0);
set_imx_hdr_v3(&imx_header, 1);
- file_off = get_container_image_start_pos(image_stack, sector_size);
+ file_off = get_container_image_start_pos(image_stack, sector_size, &v2x);
fprintf(stdout, "container image offset (aligned):%x\n", file_off);
/* step through image stack and generate the header */
@@ -870,6 +954,7 @@ static int build_container(soc_type_t soc, uint32_t sector_size,
/* stop once we reach null terminator */
while (img_sp->option != NO_IMG) {
switch (img_sp->option) {
+ case OEI:
case AP:
case M40:
case M41:
@@ -892,6 +977,30 @@ static int build_container(soc_type_t soc, uint32_t sector_size,
file_off += ALIGN(sbuf.st_size, sector_size);
break;
+ case DUMMY_V2X:
+ if (container < 0) {
+ fprintf(stderr, "No container found\n");
+ exit(EXIT_FAILURE);
+ }
+ tmp_filename = "dummy";
+ set_image_array_entry(&imx_header.fhdr[container],
+ soc,
+ img_sp,
+ file_off,
+ 0,
+ tmp_filename,
+ dcd_skip);
+ img_sp->src = file_off;
+ break;
+
+ case HOLD:
+ if (container < 0) {
+ fprintf(stderr, "No container found\n");
+ exit(EXIT_FAILURE);
+ }
+ file_off += ALIGN(img_sp->entry, sector_size);
+ break;
+
case SECO:
case SENTINEL:
if (container < 0) {
@@ -963,11 +1072,15 @@ static int build_container(soc_type_t soc, uint32_t sector_size,
do {
if (img_sp->option == APPEND) {
copy_file(ofd, img_sp->filename, 0, 0);
- file_padding += FIRST_CONTAINER_HEADER_LENGTH;
+ if (v2x)
+ file_padding += FIRST_CONTAINER_HEADER_LENGTH * 2;
+ else
+ file_padding += FIRST_CONTAINER_HEADER_LENGTH;
}
img_sp++;
} while (img_sp->option != NO_IMG);
+ fprintf(stderr, "%s: %x %d\n", __func__, file_padding, v2x);
/* Add padding or skip appended container */
ret = lseek(ofd, file_padding, SEEK_SET);
if (ret < 0) {
@@ -980,6 +1093,7 @@ static int build_container(soc_type_t soc, uint32_t sector_size,
/* Note: Image offset are not contained in the image */
tmp = flatten_container_header(&imx_header, container + 1,
&size, file_padding);
+ fprintf(stderr, "error writing image hdr %x\n", size);
/* Write image header */
if (write(ofd, tmp, size) != size) {
fprintf(stderr, "error writing image hdr\n");
@@ -1000,7 +1114,8 @@ static int build_container(soc_type_t soc, uint32_t sector_size,
img_sp->option == AP || img_sp->option == DATA ||
img_sp->option == SCD || img_sp->option == SCFW ||
img_sp->option == SECO || img_sp->option == MSG_BLOCK ||
- img_sp->option == UPOWER || img_sp->option == SENTINEL) {
+ img_sp->option == UPOWER || img_sp->option == SENTINEL ||
+ img_sp->option == OEI) {
copy_file_aligned(ofd, img_sp->filename, img_sp->src,
sector_size);
}
@@ -1031,7 +1146,7 @@ int imx8image_copy_image(int outfd, struct image_tool_params *mparams)
fprintf(stdout, "CONTAINER SW VERSION:\t0x%04x\n", sw_version);
build_container(soc, sector_size, emmc_fastboot,
- img_sp, dcd_skip, fuse_version, sw_version, outfd);
+ img_sp, false, fuse_version, sw_version, outfd);
return 0;
}
diff --git a/tools/imx9_image.sh b/tools/imx9_image.sh
index ca78a57a19a..6523d1a0ad1 100755
--- a/tools/imx9_image.sh
+++ b/tools/imx9_image.sh
@@ -18,6 +18,14 @@ for f in $blobs; do
continue
fi
+ if [ $f = "m33-oei-ddrfw.bin" ]; then
+ continue
+ fi
+
+ if [ $f = "u-boot.bin" ]; then
+ continue
+ fi
+
if [ ! -f $tmp ]; then
echo "WARNING '$tmp' not found, resulting binary may be not-functional" >&2