diff options
55 files changed, 2950 insertions, 100 deletions
@@ -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; +}; + +®_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 |