summaryrefslogtreecommitdiff
path: root/arch/arm/mach-imx/imx9/soc.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx/imx9/soc.c')
-rw-r--r--arch/arm/mach-imx/imx9/soc.c584
1 files changed, 578 insertions, 6 deletions
diff --git a/arch/arm/mach-imx/imx9/soc.c b/arch/arm/mach-imx/imx9/soc.c
index 66200dbe3c..02edced9aa 100644
--- a/arch/arm/mach-imx/imx9/soc.c
+++ b/arch/arm/mach-imx/imx9/soc.c
@@ -32,6 +32,10 @@
#include <asm/mach-imx/optee.h>
#include <linux/delay.h>
#include <fuse.h>
+#include <imx_thermal.h>
+#include <thermal.h>
+#include <imx_sip.h>
+#include <linux/arm-smccc.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -153,6 +157,56 @@ int board_usb_gadget_port_auto(void)
}
#endif
+u32 get_cpu_speed_grade_hz(void)
+{
+ u32 speed, max_speed;
+ u32 grade;
+ u32 val = readl((ulong)FSB_BASE_ADDR + 0x8000 + (19 << 2));
+ val >>= 6;
+ val &= 0xf;
+
+ speed = 2300000000 - val * 100000000;
+
+ if (is_imx93()) {
+ grade = get_cpu_temp_grade(NULL, NULL);
+ if (grade == TEMP_INDUSTRIAL)
+ max_speed = 1500000000;
+ else
+ max_speed = 1700000000;
+
+ /* 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)
+{
+ u32 val = readl((ulong)FSB_BASE_ADDR + 0x8000 + (19 << 2));
+
+ val >>= 4;
+ 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 sentinel_get_info_data *info)
{
gd->arch.soc_rev = info->soc;
@@ -160,10 +214,33 @@ static void set_cpu_info(struct sentinel_get_info_data *info)
memcpy((void *)&gd->arch.uid, &info->uid, 4 * sizeof(u32));
}
+static u32 get_cpu_variant_type(u32 type)
+{
+ /* word 19 */
+ u32 val = readl((ulong)FSB_BASE_ADDR + 0x8000 + (19 << 2));
+ u32 val2 = readl((ulong)FSB_BASE_ADDR + 0x8000 + (20 << 2));
+ bool npu_disable = !!(val & BIT(13));
+ bool core1_disable = !!(val & BIT(15));
+ u32 pack_9x9_fused = BIT(4) | BIT(17) | BIT(19) | BIT(24);
+
+ if ((val2 & pack_9x9_fused) == pack_9x9_fused)
+ type = MXC_CPU_IMX9322;
+
+ if (npu_disable && core1_disable)
+ return type + 3;
+ else if (npu_disable)
+ return type + 2;
+ else if (core1_disable)
+ return type + 1;
+
+ return type;
+}
+
u32 get_cpu_rev(void)
{
u32 rev = (gd->arch.soc_rev >> 24) - 0xa0;
- return (MXC_CPU_IMX93 << 12) | (CHIP_REV_1_0 + rev);
+ return (get_cpu_variant_type(MXC_CPU_IMX93) << 12) |
+ (CHIP_REV_1_0 + rev);
}
#define UNLOCK_WORD 0xD928C520 /* unlock word */
@@ -262,13 +339,161 @@ static struct mm_region imx93_mem_map[] = {
struct mm_region *mem_map = imx93_mem_map;
+static unsigned int imx9_find_dram_entry_in_mem_map(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(imx93_mem_map); i++)
+ if (imx93_mem_map[i].phys == CONFIG_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
+ * imx93_mem_map for DRAM1
+ */
+ int entry = imx9_find_dram_entry_in_mem_map();
+ u64 attrs = imx93_mem_map[entry].attrs;
+
+ while (i < CONFIG_NR_DRAM_BANKS &&
+ entry < ARRAY_SIZE(imx93_mem_map)) {
+ if (gd->bd->bi_dram[i].start == 0)
+ break;
+ imx93_mem_map[entry].phys = gd->bd->bi_dram[i].start;
+ imx93_mem_map[entry].virt = gd->bd->bi_dram[i].start;
+ imx93_mem_map[entry].size = gd->bd->bi_dram[i].size;
+ imx93_mem_map[entry].attrs = attrs;
+ debug("Added memory mapping (%d): %llx %llx\n", entry,
+ imx93_mem_map[entry].phys, imx93_mem_map[entry].size);
+ i++; entry++;
+ }
+
+ icache_enable();
+ dcache_enable();
+}
+
+__weak int board_phys_sdram_size(phys_size_t *size)
+{
+ if (!size)
+ return -EINVAL;
+
+ *size = PHYS_SDRAM_SIZE;
+
+#ifdef PHYS_SDRAM_2_SIZE
+ *size += PHYS_SDRAM_2_SIZE;
+#endif
+ return 0;
+}
+
int dram_init(void)
{
- gd->ram_size = PHYS_SDRAM_SIZE;
+ 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 = 0x80000000;
+ sdram_b2_size = sdram_size - 0x80000000;
+ } 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 = 0x80000000;
+ } 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] = {};
@@ -315,26 +540,370 @@ err:
printf("%s: fuse read err: %d\n", __func__, ret);
}
+const char *get_imx_type(u32 imxtype)
+{
+ switch (imxtype) {
+ case MXC_CPU_IMX93:
+ return "93(52)";/* iMX93 Dual core with NPU */
+ case MXC_CPU_IMX9351:
+ return "93(51)";/* iMX93 Single core with NPU */
+ case MXC_CPU_IMX9332:
+ return "93(32)";/* iMX93 Dual core without NPU */
+ case MXC_CPU_IMX9331:
+ return "93(31)";/* iMX93 Single core without NPU */
+ case MXC_CPU_IMX9322:
+ return "93(22)";/* iMX93 9x9 Dual core */
+ case MXC_CPU_IMX9321:
+ return "93(21)";/* iMX93 9x9 Single core */
+ case MXC_CPU_IMX9312:
+ return "93(12)";/* iMX93 9x9 Dual core without NPU */
+ case MXC_CPU_IMX9311:
+ return "93(11)";/* iMX93 9x9 Single core without NPU */
+ default:
+ return "??";
+ }
+}
+
+#define SRC_SRSR_RESET_CAUSE_NUM 16
+const char *reset_cause[SRC_SRSR_RESET_CAUSE_NUM] = {
+ "POR ",
+ "JTAG ",
+ "IPP USER ",
+ "WDOG1 ",
+ "WDOG2 ",
+ "WDOG3 ",
+ "WDOG4 ",
+ "WDOG5 ",
+ "TEMPSENSE ",
+ "CSU ",
+ "JTAG_SW ",
+ "M33_REQ ",
+ "M33_LOCKUP "
+ "UNK ",
+ "UNK ",
+ "UNK ",
+};
+
+static void save_reset_cause(void)
+{
+ struct src_general_regs *src = (struct src_general_regs *)SRC_GLOBAL_RBASE;
+ u32 srsr = readl(&src->srsr);
+ writel(srsr, &src->srsr); /* clear srsr in sec mode */
+
+ /* Save value to GPR1 to pass to nonsecure */
+ writel(srsr, &src->gpr[0]);
+}
+
+static const char *get_reset_cause(u32 *srsr_ret)
+{
+ struct src_general_regs *src = (struct src_general_regs *)SRC_GLOBAL_RBASE;
+ u32 srsr;
+ u32 i;
+
+ srsr = readl(&src->gpr[0]);
+ if (srsr_ret)
+ *srsr_ret = srsr;
+
+ for (i = SRC_SRSR_RESET_CAUSE_NUM; i > 0; i--) {
+ if (srsr & (1 << (i - 1)))
+ return reset_cause[i - 1];
+ }
+
+ return "unknown reset";
+}
+
int print_cpuinfo(void)
{
- u32 cpurev;
+ u32 cpurev, max_freq;
+ int minc, maxc;
+ u32 ssrs_ret;
cpurev = get_cpu_rev();
- printf("CPU: i.MX93 rev%d.%d at %d MHz\n",
- (cpurev & 0x000F0) >> 4, (cpurev & 0x0000F) >> 0,
- mxc_get_clock(MXC_ARM_CLK) / 1000000);
+ 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:
+ puts("Extended Consumer temperature grade ");
+ break;
+ default:
+ puts("Consumer temperature grade ");
+ break;
+ }
+ printf("(%dC to %dC)", minc, maxc);
+
+#if defined(CONFIG_IMX_TMU)
+ struct udevice *udev;
+ int ret, temp;
+
+ ret = uclass_get_device_by_name(UCLASS_THERMAL, "cpu-thermal", &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");
+
+ printf("Reset cause: %s", get_reset_cause(&ssrs_ret));
+ printf("(0x%x)\n", ssrs_ret);
return 0;
}
+void build_info(void)
+{
+ u32 fw_version, sha1, res, status;
+ int ret;
+
+ printf("\nBuildInfo:\n");
+
+ ret = ahab_get_fw_status(&status, &res);
+ if (ret) {
+ printf(" - ELE firmware status failed %d, 0x%x\n", ret, res);
+ } else if ((status & 0xff) == 1) {
+ ret = ahab_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;
+}
+
+static int delete_fdt_nodes(void *blob, const char *const nodes_path[], int size_array)
+{
+ int i = 0;
+ int rc;
+ int nodeoff;
+
+ for (i = 0; i < size_array; i++) {
+ nodeoff = fdt_path_offset(blob, nodes_path[i]);
+ if (nodeoff < 0)
+ continue; /* Not found, skip it */
+
+ debug("Found %s node\n", nodes_path[i]);
+
+ rc = fdt_del_node(blob, nodeoff);
+ if (rc < 0) {
+ printf("Unable to delete node %s, err=%s\n",
+ nodes_path[i], fdt_strerror(rc));
+ } else {
+ printf("Delete node %s\n", nodes_path[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int disable_npu_nodes(void *blob)
+{
+ static const char * const nodes_path_npu[] = {
+ "/ethosu",
+ "/reserved-memory/ethosu_region@C0000000"
+ };
+
+ return delete_fdt_nodes(blob, nodes_path_npu, ARRAY_SIZE(nodes_path_npu));
+}
+
+static void disable_thermal_cpu_nodes(void *blob, u32 disabled_cores)
+{
+ static const char * const thermal_path[] = {
+ "/thermal-zones/cpu-thermal/cooling-maps/map0"
+ };
+
+ int nodeoff, cnt, i, ret, j;
+ u32 cooling_dev[6];
+
+ for (i = 0; i < ARRAY_SIZE(thermal_path); i++) {
+ nodeoff = fdt_path_offset(blob, thermal_path[i]);
+ if (nodeoff < 0)
+ continue; /* Not found, skip it */
+
+ cnt = fdtdec_get_int_array_count(blob, nodeoff, "cooling-device", cooling_dev, 6);
+ if (cnt < 0)
+ continue;
+
+ if (cnt != 6)
+ printf("Warning: %s, cooling-device count %d\n", thermal_path[i], cnt);
+
+ for (j = 0; j < cnt; j++)
+ cooling_dev[j] = cpu_to_fdt32(cooling_dev[j]);
+
+ ret = fdt_setprop(blob, nodeoff, "cooling-device", &cooling_dev,
+ sizeof(u32) * (6 - disabled_cores * 3));
+ if (ret < 0) {
+ printf("Warning: %s, cooling-device setprop failed %d\n",
+ thermal_path[i], ret);
+ continue;
+ }
+
+ printf("Update node %s, cooling-device prop\n", thermal_path[i]);
+ }
+}
+
+static int disable_cpu_nodes(void *blob, u32 disabled_cores)
+{
+ u32 i = 0;
+ int rc;
+ int nodeoff;
+ char nodes_path[32];
+
+ for (i = 1; i <= disabled_cores; i++) {
+
+ sprintf(nodes_path, "/cpus/cpu@%u00", i);
+
+ nodeoff = fdt_path_offset(blob, nodes_path);
+ if (nodeoff < 0)
+ continue; /* Not found, skip it */
+
+ debug("Found %s node\n", nodes_path);
+
+ rc = fdt_del_node(blob, nodeoff);
+ if (rc < 0) {
+ printf("Unable to delete node %s, err=%s\n",
+ nodes_path, fdt_strerror(rc));
+ } else {
+ printf("Delete node %s\n", nodes_path);
+ }
+ }
+
+ disable_thermal_cpu_nodes(blob, disabled_cores);
+
return 0;
}
+struct low_drive_freq_entry {
+ const char *node_path;
+ u32 clk;
+ u32 new_rate;
+};
+
+static int low_drive_fdt_fix_clock(void *fdt, int node_off, u32 clk_index, u32 new_rate)
+{
+#define MAX_ASSIGNED_CLKS 8
+ int cnt, j;
+ u32 assignedclks[MAX_ASSIGNED_CLKS]; /* max 8 clocks*/
+
+ cnt = fdtdec_get_int_array_count(fdt, node_off, "assigned-clock-rates",
+ assignedclks, MAX_ASSIGNED_CLKS);
+ if (cnt > 0) {
+ if (cnt <= clk_index)
+ return -ENOENT;
+
+ if (assignedclks[clk_index] <= new_rate)
+ return 0;
+
+ assignedclks[clk_index] = new_rate;
+ for (j = 0; j < cnt; j++)
+ assignedclks[j] = cpu_to_fdt32(assignedclks[j]);
+
+ return fdt_setprop(fdt, node_off, "assigned-clock-rates", &assignedclks, cnt * sizeof(u32));
+ }
+
+ return -ENOENT;
+}
+
+static int low_drive_freq_update(void *blob)
+{
+ int nodeoff, ret;
+ int i;
+
+ /* Update kernel dtb clocks for low drive mode */
+ struct low_drive_freq_entry table[] = {
+ {"/soc@0/lcd-controller@4ae30000", 2, 200000000},
+ {"/soc@0/bus@42800000/camera/isi@4ae40000", 0, 200000000},
+ {"/soc@0/bus@42800000/mmc@42850000", 0, 266666667},
+ {"/soc@0/bus@42800000/mmc@42860000", 0, 266666667},
+ {"/soc@0/bus@42800000/mmc@428b0000", 0, 266666667},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(table); i++) {
+ nodeoff = fdt_path_offset(blob, table[i].node_path);
+ if (nodeoff >= 0) {
+ ret = low_drive_fdt_fix_clock(blob, nodeoff, table[i].clk, table[i].new_rate);
+ if (!ret)
+ printf("%s freq updated\n", table[i].node_path);
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_OF_BOARD_FIXUP
+#ifndef CONFIG_SPL_BUILD
+int board_fix_fdt(void *fdt)
+{
+ /* Update u-boot dtb clocks for low drive mode */
+ if (IS_ENABLED(CONFIG_IMX9_LOW_DRIVE_MODE)){
+ int nodeoff;
+ int i;
+
+ struct low_drive_freq_entry table[] = {
+ {"/soc@0/lcd-controller@4ae30000", 0, 200000000},
+ {"/soc@0/bus@42800000/mmc@42850000", 0, 266666667},
+ {"/soc@0/bus@42800000/mmc@42860000", 0, 266666667},
+ {"/soc@0/bus@42800000/mmc@428b0000", 0, 266666667},
+ };
+
+ for (i = 0; i < ARRAY_SIZE(table); i++) {
+ nodeoff = fdt_path_offset(fdt, table[i].node_path);
+ if (nodeoff >= 0)
+ low_drive_fdt_fix_clock(fdt, nodeoff, table[i].clk, table[i].new_rate);
+ }
+ }
+
+ return 0;
+}
+#endif
+#endif
+
int ft_system_setup(void *blob, struct bd_info *bd)
{
+ if (is_imx9351() || is_imx9331() || is_imx9321() || is_imx9311())
+ disable_cpu_nodes(blob, 1);
+
+ if (is_imx9332() || is_imx9331() || is_imx9312() || is_imx9311())
+ disable_npu_nodes(blob);
+
+ if (IS_ENABLED(CONFIG_IMX9_LOW_DRIVE_MODE))
+ low_drive_freq_update(blob);
+
return ft_add_optee_node(blob, bd);
}
@@ -358,6 +927,9 @@ int arch_cpu_init(void)
clock_init();
trdc_early_init();
+
+ /* Save SRC SRSR to GPR1 and clear it */
+ save_reset_cause();
}
return 0;