[PATCH v2 09/17] imx9: scmi: add i.MX95 SoC and clock related code
Alice Guo
alice.guo at oss.nxp.com
Fri Dec 20 03:55:07 CET 2024
From: Peng Fan <peng.fan at nxp.com>
This patch adds i.MX95 SoC and clock related code. Because they are
based on SCMI, put them in the scmi subfolder.
Signed-off-by: Ye Li <ye.li at nxp.com>
Signed-off-by: Peng Fan <peng.fan at nxp.com>
Signed-off-by: Alice Guo <alice.guo at nxp.com>
Reviewed-by: Peng Fan <peng.fan at nxp.com>
---
arch/arm/include/asm/arch-imx/cpu.h | 3 +
arch/arm/include/asm/arch-imx9/clock.h | 10 +-
arch/arm/include/asm/arch-imx9/imx-regs.h | 7 +-
arch/arm/include/asm/arch-imx9/sys_proto.h | 3 +-
arch/arm/include/asm/mach-imx/sys_proto.h | 40 ++
arch/arm/mach-imx/imx9/scmi/Makefile | 6 +
arch/arm/mach-imx/imx9/scmi/clock.c | 328 ++++++++++++
arch/arm/mach-imx/imx9/scmi/clock_scmi.c | 148 +++++
arch/arm/mach-imx/imx9/scmi/soc.c | 832 +++++++++++++++++++++++++++++
9 files changed, 1373 insertions(+), 4 deletions(-)
diff --git a/arch/arm/include/asm/arch-imx/cpu.h b/arch/arm/include/asm/arch-imx/cpu.h
index 0d7a573461690046b27498c7174459110a73689c..b283c49afec83cbed670d07ad0909ad6f453853b 100644
--- a/arch/arm/include/asm/arch-imx/cpu.h
+++ b/arch/arm/include/asm/arch-imx/cpu.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* (C) Copyright 2014 Freescale Semiconductor, Inc.
+ * Copyright 2024 NXP
*/
#define MXC_CPU_MX23 0x23
@@ -76,6 +77,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 60d48b13b11f274c9e4c8caf20954de0431d9d6a..a64c18b28291553d47725d83cb748031faf64488 100644
--- a/arch/arm/include/asm/arch-imx9/clock.h
+++ b/arch/arm/include/asm/arch-imx9/clock.h
@@ -1,8 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * Copyright 2022 NXP
+ * Copyright 2024 NXP
*
- * Peng Fan <peng.fan at nxp.com>
+ * Peng Fan <peng.fan at nxp.com>
*/
#ifndef __CLOCK_IMX9__
@@ -255,5 +255,11 @@ 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 set_clk_netc(enum enet_freq type);
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 ef9538bd42e8b2972d7e6829a402bc4f661e3cbb..5512d66ee0a44348845d2a559bd99327115bc1d0 100644
--- a/arch/arm/include/asm/arch-imx9/imx-regs.h
+++ b/arch/arm/include/asm/arch-imx9/imx-regs.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * Copyright 2022 NXP
+ * Copyright 2024 NXP
*/
#ifndef __ASM_ARCH_IMX9_REGS_H__
@@ -20,6 +20,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 e4bf6a63424f7c54c1dcdf6b882a908940e5fdb1..aac43c74a44a127549c67059e70f570aae5f680c 100644
--- a/arch/arm/include/asm/arch-imx9/sys_proto.h
+++ b/arch/arm/include/asm/arch-imx9/sys_proto.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * Copyright (C) 2022 NXP
+ * Copyright (C) 2024 NXP
*/
#ifndef __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 109a806852ab42d018ce45a4e96af5b57adb6a9c..bcf33769ae5f45125bbf9377eb64d96473dc4996 100644
--- a/arch/arm/include/asm/mach-imx/sys_proto.h
+++ b/arch/arm/include/asm/mach-imx/sys_proto.h
@@ -2,6 +2,7 @@
/*
* (C) Copyright 2009
* Stefano Babic, DENX Software Engineering, sbabic at denx.de.
+ * Copyright 2024 NXP
*/
#ifndef _SYS_PROTO_H_
@@ -97,6 +98,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 +219,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);
+#ifdef CONFIG_SCMI_FIRMWARE
+typedef struct rom_passover {
+ uint16_t tag; //!< Tag
+ uint8_t len; //!< Fixed value of 0x80
+ uint8_t ver; //!< Version
+ uint32_t boot_mode; //!< Boot mode
+ uint32_t card_addr_mode; //!< SD card address mode
+ uint32_t bad_blks_of_img_set0; //!< NAND bad block count skipped 1
+ uint32_t ap_mu_id; //!< AP MU ID
+ uint32_t bad_blks_of_img_set1; //!< NAND bad block count skipped 1
+ uint8_t boot_stage; //!< Boot stage
+ uint8_t img_set_sel; //!< Image set booted from
+ uint8_t rsv0[2]; //!< Reserved
+ uint32_t img_set_end; //!< Offset of Image End
+ uint32_t rom_version; //!< ROM version
+ uint8_t boot_dev_state; //!< Boot device state
+ uint8_t boot_dev_inst; //!< Boot device type
+ uint8_t boot_dev_type; //!< Boot device instance
+ uint8_t rsv1; //!< Reserved
+ uint32_t dev_page_size; //!< Boot device page size
+ uint32_t cnt_header_ofs; //!< Container header offset
+ uint32_t img_ofs; //!< Image offset
+} __attribute__ ((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/imx9/scmi/Makefile b/arch/arm/mach-imx/imx9/scmi/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..78f24c6e5151e2d7280b9c456c0477a3b6a958c4
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2024 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 0000000000000000000000000000000000000000..b46ff3f9944d2b08db56f7ef892fba715afd2655
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/clock.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 NXP
+ */
+
+#include <asm/arch/ccm_regs.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <command.h>
+#include <errno.h>
+#ifdef CONFIG_CLK_SCMI
+#include "../../../../../dts/upstream/src/arm64/freescale/imx95-clock.h"
+#include <dm/uclass.h>
+#include <dm/uclass-internal.h>
+#include <linux/clk-provider.h>
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+u32 get_arm_core_clk(void)
+{
+ u32 val;
+
+ /* TODO: */
+ val = imx_clk_scmi_get_rate(IMX95_CLK_SEL_A55C0);
+ if (val)
+ return val;
+ return imx_clk_scmi_get_rate(IMX95_CLK_A55);
+}
+
+void set_arm_core_max_clk(void)
+{
+ int ret;
+ u32 arm_domain_id = 8;
+
+ struct scmi_perf_in in = {
+ .domain_id = arm_domain_id,
+ .perf_level = 3,
+ };
+ struct scmi_perf_out out;
+ struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_PERF, SCMI_PERF_LEVEL_SET, in, out);
+ struct udevice *dev;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, "protocol at 14", &dev);
+ if (ret)
+ printf("%s: %d\n", __func__, ret);
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret)
+ printf("%s: %d\n", __func__, ret);
+}
+
+void enable_usboh3_clk(unsigned char enable)
+{
+}
+
+int clock_init_early(void)
+{
+ return 0;
+}
+
+/* Set bus and A55 core clock per voltage mode */
+int clock_init_late(void)
+{
+ set_arm_core_max_clk();
+
+ return 0;
+}
+
+u32 get_lpuart_clk(void)
+{
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPUART1);
+}
+
+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);
+}
+
+/* I2C check */
+u32 imx_get_i2cclk(u32 i2c_num)
+{
+ if (i2c_num > 7)
+ return -EINVAL;
+ switch (i2c_num) {
+ case 0:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C1);
+ case 1:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C2);
+ case 2:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C3);
+ case 3:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C4);
+ case 4:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C5);
+ case 5:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C6);
+ case 6:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C7);
+ case 7:
+ return imx_clk_scmi_get_rate(IMX95_CLK_LPI2C8);
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+int enable_i2c_clk(unsigned char enable, u32 i2c_num)
+{
+ u32 clock_id;
+
+ if (i2c_num > 7)
+ return -EINVAL;
+
+ switch (i2c_num) {
+ case 0:
+ clock_id = IMX95_CLK_LPI2C1;
+ break;
+ case 1:
+ clock_id = IMX95_CLK_LPI2C2;
+ break;
+ case 2:
+ clock_id = IMX95_CLK_LPI2C3;
+ break;
+ case 3:
+ clock_id = IMX95_CLK_LPI2C4;
+ break;
+ case 4:
+ clock_id = IMX95_CLK_LPI2C5;
+ break;
+ case 5:
+ clock_id = IMX95_CLK_LPI2C6;
+ break;
+ case 6:
+ clock_id = IMX95_CLK_LPI2C7;
+ break;
+ case 7:
+ clock_id = IMX95_CLK_LPI2C8;
+ break;
+ default:
+ return 0;
+ }
+
+ /* 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);
+
+ return 0;
+}
+
+/* dfs_clkout[1]: 800.00MHz */
+void init_clk_usdhc(u32 usdhc_id)
+{
+ u32 clock_id;
+
+ switch (usdhc_id) {
+ case 0:
+ clock_id = IMX95_CLK_USDHC1;
+ break;
+ case 1:
+ clock_id = IMX95_CLK_USDHC2;
+ break;
+ case 2:
+ clock_id = IMX95_CLK_USDHC3;
+ break;
+ default:
+ return;
+ };
+
+ /* 400MHz */
+ imx_clk_scmi_enable(clock_id, false);
+ imx_clk_scmi_set_parent(clock_id, IMX95_CLK_SYSPLL1_PFD1);
+ imx_clk_scmi_set_rate(clock_id, 400000000);
+ imx_clk_scmi_enable(clock_id, true);
+}
+
+int set_clk_netc(enum enet_freq type)
+{
+ ulong rate;
+
+ switch (type) {
+ case ENET_125MHZ:
+ rate = MHZ(250); /* 250Mhz */
+ break;
+ case ENET_50MHZ:
+ rate = MHZ(100); /* 100Mhz */
+ break;
+ case ENET_25MHZ:
+ rate = MHZ(50); /* 50Mhz */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* disable the clock first */
+ imx_clk_scmi_enable(IMX95_CLK_ENETREF, false);
+ imx_clk_scmi_set_parent(IMX95_CLK_ENETREF, IMX95_CLK_SYSPLL1_PFD0);
+ imx_clk_scmi_set_rate(IMX95_CLK_ENETREF, rate);
+ imx_clk_scmi_enable(IMX95_CLK_ENETREF, true);
+
+ return 0;
+}
+
+#ifdef CONFIG_SPL_BUILD
+void dram_pll_init(ulong pll_val)
+{
+ /* Try to configure the DDR PLL. */
+ u64 ddr_rate = pll_val;
+ /*vco_range 2.5G - 5G */
+ u64 vco_rate = ddr_rate * DIV_ROUND_UP(MHZ(3000), ddr_rate);
+ u64 v_rate, rate;
+
+ v_rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMPLL_VCO, vco_rate);
+ rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMPLL, ddr_rate);
+
+ debug("%s vco:%llu rate:%llu\n", __func__, v_rate, rate);
+}
+
+void dram_enable_bypass(ulong clk_val)
+{
+ u64 rate;
+
+ switch (clk_val) {
+ case MHZ(625):
+ imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD2);
+ rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, clk_val);
+ break;
+ case MHZ(400):
+ imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD1);
+ rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, clk_val);
+ break;
+ case MHZ(333):
+ imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD0);
+ rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, 333333333);
+ break;
+ case MHZ(200):
+ imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD1);
+ rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, clk_val);
+ break;
+ case MHZ(100):
+ imx_clk_scmi_set_parent(IMX95_CLK_DRAMALT, IMX95_CLK_SYSPLL1_PFD1);
+ rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMALT, clk_val);
+ break;
+ default:
+ printf("No matched freq table %lu\n", clk_val);
+ return;
+ }
+
+ debug("%s:%llu\n", __func__, rate);
+
+ /* Set DRAM APB to 133Mhz */
+ imx_clk_scmi_set_parent(IMX95_CLK_DRAMAPB, IMX95_CLK_SYSPLL1_PFD1_DIV2);
+ rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMAPB, 133333333);
+
+ /* Switch from DRAM clock root from PLL to CCM */
+ imx_clk_scmi_set_parent(IMX95_CLK_SEL_DRAM, IMX95_CLK_DRAMALT);
+}
+
+void dram_disable_bypass(void)
+{
+ u64 rate;
+ /* Set DRAM APB to 133Mhz */
+ imx_clk_scmi_set_parent(IMX95_CLK_DRAMAPB, IMX95_CLK_SYSPLL1_PFD1_DIV2);
+ rate = imx_clk_scmi_set_rate(IMX95_CLK_DRAMAPB, 133333333);
+
+ /*Set the DRAM_GPR_SEL to be sourced from DRAM_PLL.*/
+ imx_clk_scmi_set_parent(IMX95_CLK_SEL_DRAM, IMX95_CLK_DRAMPLL);
+ rate = imx_clk_scmi_get_rate(IMX95_CLK_SEL_DRAM);
+ printf("%s:SEL_DRAM: %llu\n", __func__, rate);
+}
+
+#endif
+
+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 0000000000000000000000000000000000000000..543abe9fed481c796602b2b2eeeec6c8f9a94862
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/clock_scmi.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 NXP
+ *
+ * Peng Fan <peng.fan at nxp.com>
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/arch/ccm_regs.h>
+#include <asm/mach-imx/sys_proto.h>
+#include <asm/global_data.h>
+#include <dm/uclass.h>
+#include <dm/uclass-internal.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include "../../../../../dts/upstream/src/arm64/freescale/imx95-clock.h"
+#include <linux/clk-provider.h>
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+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 at 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 at 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 at 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 at 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/soc.c b/arch/arm/mach-imx/imx9/scmi/soc.c
new file mode 100644
index 0000000000000000000000000000000000000000..fefb1a6f4ca58722a5d93c39e4f6a7aaf85aa177
--- /dev/null
+++ b/arch/arm/mach-imx/imx9/scmi/soc.c
@@ -0,0 +1,832 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 NXP
+ *
+ * Peng Fan <peng.fan at nxp.com>
+ */
+
+#include <cpu_func.h>
+#include <init.h>
+#include <log.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/ccm_regs.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/trdc.h>
+#include <asm/mach-imx/boot_mode.h>
+#include <asm/mach-imx/syscounter.h>
+#include <asm/armv8/mmu.h>
+#include <dm/uclass.h>
+#include <dm/device.h>
+#include <env.h>
+#include <env_internal.h>
+#include <errno.h>
+#include <fdt_support.h>
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include <asm/setup.h>
+#include <asm/bootm.h>
+#include <asm/arch-imx/cpu.h>
+#include <asm/mach-imx/ele_api.h>
+#include <linux/delay.h>
+#include <fuse.h>
+#include <imx_thermal.h>
+#include <thermal.h>
+#include <imx_sip.h>
+#include <linux/arm-smccc.h>
+#include <asm/arch/ddr.h>
+#ifdef CONFIG_SCMI_FIRMWARE
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+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 = SCMI_MSG(SCMI_PROTOCOL_ID_IMX_MISC, SCMI_MISC_ROM_PASSOVER_GET, out);
+ int ret;
+ struct udevice *dev;
+
+ ret = uclass_get_device_by_name(UCLASS_CLK, "protocol at 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;
+}
+
+#ifdef 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;
+#ifdef CONFIG_SPL_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 /* unlock word */
+#define REFRESH_WORD 0xB480A602 /* refresh word */
+
+static void disable_wdog(void __iomem *wdog_base)
+{
+ u32 val_cs = readl(wdog_base + 0x00);
+
+ 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 */
+
+ while (!(readl(wdog_base + 0x00) & 0x400))
+ ;
+}
+
+static struct mm_region imx9_mem_map[] = {
+ {
+ /* ROM */
+ .virt = 0x0UL,
+ .phys = 0x0UL,
+ .size = 0x100000UL,
+ .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
+ PTE_BLOCK_OUTER_SHARE
+ }, {
+ /* TCM */
+ .virt = 0x201c0000UL,
+ .phys = 0x201c0000UL,
+ .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])
+ 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_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 two 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.
+ */
+ 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;
+
+ if (dev_id == 0) {
+ ret = fuse_read(39, 3, &val[0]);
+ if (ret)
+ goto err;
+
+ ret = fuse_read(39, 4, &val[1]);
+ if (ret)
+ goto err;
+
+ mac[0] = val[1] >> 8;
+ mac[1] = val[1];
+ mac[2] = val[0] >> 24;
+ mac[3] = val[0] >> 16;
+ mac[4] = val[0] >> 8;
+ mac[5] = val[0];
+
+ } else {
+ ret = fuse_read(39, 5, &val[0]);
+ if (ret)
+ goto err;
+
+ ret = fuse_read(39, 4, &val[1]);
+ if (ret)
+ goto err;
+
+ if (is_soc_rev(CHIP_REV_1_0)) {
+ mac[0] = val[1] >> 24;
+ mac[1] = val[1] >> 16;
+ mac[2] = val[0] >> 24;
+ mac[3] = val[0] >> 16;
+ mac[4] = val[0] >> 8;
+ mac[5] = val[0];
+ } else {
+ mac[0] = val[0] >> 24;
+ mac[1] = val[0] >> 16;
+ mac[2] = val[0] >> 8;
+ mac[3] = val[0];
+ mac[4] = val[1] >> 24;
+ mac[5] = val[1] >> 16;
+ }
+ }
+
+ debug("%s: MAC%d: %02x.%02x.%02x.%02x.%02x.%02x\n",
+ __func__, dev_id, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+ 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 "??";
+ }
+}
+
+int print_cpuinfo(void)
+{
+ u32 cpurev, max_freq;
+ int minc, maxc;
+
+ cpurev = get_cpu_rev();
+
+ printf("CPU: i.MX%s rev%d.%d",
+ get_imx_type((cpurev & 0x1FF000) >> 12),
+ (cpurev & 0x000F0) >> 4, (cpurev & 0x0000F) >> 0);
+
+ max_freq = get_cpu_speed_grade_hz();
+ if (!max_freq || max_freq == mxc_get_clock(MXC_ARM_CLK)) {
+ printf(" at %dMHz\n", mxc_get_clock(MXC_ARM_CLK) / 1000000);
+ } else {
+ printf(" %d MHz (running at %d MHz)\n", max_freq / 1000000,
+ mxc_get_clock(MXC_ARM_CLK) / 1000000);
+ }
+
+ puts("CPU: ");
+ switch (get_cpu_temp_grade(&minc, &maxc)) {
+ case TEMP_AUTOMOTIVE:
+ puts("Automotive temperature grade ");
+ break;
+ case TEMP_INDUSTRIAL:
+ puts("Industrial temperature grade ");
+ break;
+ case TEMP_EXTCOMMERCIAL:
+ if (is_imx93())
+ puts("Extended Industrial temperature grade ");
+ else
+ puts("Extended Consumer temperature grade ");
+ break;
+ default:
+ puts("Consumer temperature grade ");
+ break;
+ }
+ printf("(%dC to %dC)", minc, maxc);
+
+#if defined(CONFIG_DM_THERMAL)
+ struct udevice *udev;
+ int ret, temp;
+
+ if (IS_ENABLED(CONFIG_IMX_TMU))
+ ret = uclass_get_device_by_name(UCLASS_THERMAL, "cpu-thermal", &udev);
+ else
+ ret = uclass_get_device(UCLASS_THERMAL, 0, &udev);
+ if (!ret) {
+ ret = thermal_get_temp(udev, &temp);
+
+ if (!ret)
+ printf(" at %dC", temp);
+ else
+ debug(" - invalid sensor data\n");
+ } else {
+ debug(" - invalid sensor device\n");
+ }
+#endif
+ puts("\n");
+
+ return 0;
+}
+
+void build_info(void)
+{
+ u32 fw_version, sha1, res, 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 & (0x0000ff00)) >> 8,
+ (fw_version & (0x000000ff)), 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;
+}
+
+#ifdef CONFIG_OF_BOARD_FIXUP
+#ifndef CONFIG_SPL_BUILD
+int board_fix_fdt(void *fdt)
+{
+ return 0;
+}
+#endif
+#endif
+
+int ft_system_setup(void *blob, struct bd_info *bd)
+{
+ return 0;
+}
+
+#if defined(CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG)
+void get_board_serial(struct tag_serialnr *serialnr)
+{
+ printf("UID: 0x%x 0x%x 0x%x 0x%x\n",
+ gd->arch.uid[0], gd->arch.uid[1], gd->arch.uid[2], gd->arch.uid[3]);
+
+ serialnr->low = gd->arch.uid[0];
+ serialnr->high = gd->arch.uid[3];
+}
+#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);
+
+ clock_init_early();
+
+ 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 node, 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 at 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 at 19", &dev);
+ if (ret)
+ return ret;
+
+ node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "fsl,imx93-mu-s4");
+
+ ret = uclass_get_device_by_of_offset(UCLASS_MISC, node, &dev);
+ 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;
+
+#ifdef CONFIG_SPL_BUILD
+ unsigned long freq = 24000000;
+
+ asm volatile("msr cntfrq_el0, %0" : : "r" (freq) : "memory");
+#endif
+
+ 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) {
+#if defined(CONFIG_ENV_IS_IN_SPI_FLASH)
+ case QSPI_BOOT:
+ env_loc = ENVL_SPI_FLASH;
+ break;
+#endif
+#if defined(CONFIG_ENV_IS_IN_MMC)
+ case SD1_BOOT:
+ case SD2_BOOT:
+ case SD3_BOOT:
+ case MMC1_BOOT:
+ case MMC2_BOOT:
+ case MMC3_BOOT:
+ env_loc = ENVL_MMC;
+ break;
+#endif
+ default:
+#if defined(CONFIG_ENV_IS_NOWHERE)
+ env_loc = ENVL_NOWHERE;
+#endif
+ 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;
+
+#ifdef 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;
+#ifdef CONFIG_IMX95
+ boot_dev -= 3; //iMX95 usb instance start at 3
+#endif
+ break;
+ default:
+ break;
+ }
+
+ return boot_dev;
+}
+#endif
--
2.34.1
More information about the U-Boot
mailing list