diff options
Diffstat (limited to 'arch/arm/mach-imx/imx9/soc.c')
-rw-r--r-- | arch/arm/mach-imx/imx9/soc.c | 584 |
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; |