// SPDX-License-Identifier: GPL-2.0+ // (C) 2022 Pali Rohár #include #include #include #include #include #include #include #include #include #include #include #include #include "../turris_atsha_otp.h" DECLARE_GLOBAL_DATA_PTR; /* * Reset time cycle register provided by Turris CPLD firmware. * Turris CPLD firmware is open source and available at: * https://gitlab.nic.cz/turris/hw/turris_cpld/-/blob/master/CZ_NIC_Router_CPLD.v */ #define TURRIS_CPLD_RESET_TIME_CYCLE_REG ((void *)CFG_SYS_CPLD_BASE + 0x1f) #define TURRIS_CPLD_RESET_TIME_CYCLE_300MS BIT(0) #define TURRIS_CPLD_RESET_TIME_CYCLE_1S BIT(1) #define TURRIS_CPLD_RESET_TIME_CYCLE_2S BIT(2) #define TURRIS_CPLD_RESET_TIME_CYCLE_3S BIT(3) #define TURRIS_CPLD_RESET_TIME_CYCLE_4S BIT(4) #define TURRIS_CPLD_RESET_TIME_CYCLE_5S BIT(5) #define TURRIS_CPLD_RESET_TIME_CYCLE_6S BIT(6) #define TURRIS_CPLD_LED_BRIGHTNESS_REG_FIRST ((void *)CFG_SYS_CPLD_BASE + 0x13) #define TURRIS_CPLD_LED_BRIGHTNESS_REG_LAST ((void *)CFG_SYS_CPLD_BASE + 0x1e) #define TURRIS_CPLD_LED_SW_OVERRIDE_REG ((void *)CFG_SYS_CPLD_BASE + 0x22) int dram_init_banksize(void) { phys_size_t size = gd->ram_size; static_assert(CONFIG_NR_DRAM_BANKS >= 3); gd->bd->bi_dram[0].start = gd->ram_base; gd->bd->bi_dram[0].size = get_effective_memsize(); size -= gd->bd->bi_dram[0].size; /* Note: This address space is not mapped via TLB entries in U-Boot */ #ifndef CONFIG_SDCARD if (size > 0) { /* * Setup additional overlapping 1 GB DDR LAW at the end of * 32-bit physical address space. It overlaps with all other * peripherals on P2020 mapped to physical address space. * But this is not issue because documentation says: * P2020 QorIQ Integrated Processor Reference Manual, * section 2.3.1 Precedence of local access windows: * If two local access windows overlap, the lower * numbered window takes precedence. */ if (set_ddr_laws(0xc0000000, SZ_1G, LAW_TRGT_IF_DDR_1) < 0) { printf("Error: Cannot setup DDR LAW for more than 2 GB\n"); return 0; } } if (size > 0) { /* Free space between PCIe bus 3 MEM and NOR */ gd->bd->bi_dram[1].start = 0xc0200000; gd->bd->bi_dram[1].size = min(size, 0xef000000 - gd->bd->bi_dram[1].start); size -= gd->bd->bi_dram[1].size; } if (size > 0) { /* Free space between NOR and NAND */ gd->bd->bi_dram[2].start = 0xf0000000; gd->bd->bi_dram[2].size = min(size, 0xff800000 - gd->bd->bi_dram[2].start); size -= gd->bd->bi_dram[2].size; } #else puts("\n\n!!! TODO: fix sdcard >2GB RAM\n\n\n"); #endif return 0; } static inline int fdt_setprop_inplace_u32_partial(void *blob, int node, const char *name, u32 idx, u32 val) { val = cpu_to_fdt32(val); return fdt_setprop_inplace_namelen_partial(blob, node, name, strlen(name), idx * sizeof(u32), &val, sizeof(u32)); } /* Setup correct size of PCIe controller MEM in DT "ranges" property recursively */ static void fdt_fixup_pcie_mem_size(void *blob, int node, phys_size_t pcie1_mem, phys_size_t pcie2_mem, phys_size_t pcie3_mem) { int pci_cells, cpu_cells, size_cells; const u32 *ranges; int pnode; int i, len; u32 pci_flags; u64 cpu_addr; u64 size; u64 new_size; int pcie_id; int idx; int subnode; int ret; if (!fdtdec_get_is_enabled(blob, node)) return; ranges = fdt_getprop(blob, node, "ranges", &len); if (!ranges || !len || len % sizeof(u32)) return; /* * The "ranges" property is an array of * { } * where number of PCI address cells and size cells is stored in the * "#address-cells" and "#size-cells" properties of the same node * containing the "ranges" property and number of CPU address cells * is stored in the parent's "#address-cells" property. * * All 3 elements can span a different number of cells. Fetch them. */ pnode = fdt_parent_offset(blob, node); pci_cells = fdt_address_cells(blob, node); cpu_cells = fdt_address_cells(blob, pnode); size_cells = fdt_size_cells(blob, node); /* PCI addresses always use 3 cells */ if (pci_cells != 3) return; /* CPU addresses and sizes on P2020 may be 32-bit (1 cell) or 64-bit (2 cells) */ if (cpu_cells != 1 && cpu_cells != 2) return; if (size_cells != 1 && size_cells != 2) return; for (i = 0; i < len / sizeof(u32); i += pci_cells + cpu_cells + size_cells) { /* PCI address consists of 3 cells: flags, addr.hi, addr.lo */ pci_flags = fdt32_to_cpu(ranges[i]); cpu_addr = fdt32_to_cpu(ranges[i + pci_cells]); if (cpu_cells == 2) { cpu_addr <<= 32; cpu_addr |= fdt32_to_cpu(ranges[i + pci_cells + 1]); } size = fdt32_to_cpu(ranges[i + pci_cells + cpu_cells]); if (size_cells == 2) { size <<= 32; size |= fdt32_to_cpu(ranges[i + pci_cells + cpu_cells + 1]); } /* * Bits [25:24] of PCI flags defines space code * 0b10 is 32-bit MEM and 0b11 is 64-bit MEM. * Check for any type of PCIe MEM mapping. */ if (!(pci_flags & 0x02000000)) continue; if (cpu_addr == CFG_SYS_PCIE1_MEM_PHYS && size > pcie1_mem) { pcie_id = 1; new_size = pcie1_mem; } else if (cpu_addr == CFG_SYS_PCIE2_MEM_PHYS && size > pcie2_mem) { pcie_id = 2; new_size = pcie2_mem; } else if (cpu_addr == CFG_SYS_PCIE3_MEM_PHYS && size > pcie3_mem) { pcie_id = 3; new_size = pcie3_mem; } else { continue; } printf("Decreasing PCIe MEM %d size from ", pcie_id); print_size(size, " to "); print_size(new_size, "\n"); idx = i + pci_cells + cpu_cells; if (size_cells == 2) { ret = fdt_setprop_inplace_u32_partial(blob, node, "ranges", idx, 0); if (ret) goto err; idx++; } ret = fdt_setprop_inplace_u32_partial(blob, node, "ranges", idx, SZ_2M); if (ret) goto err; } /* Recursively fix also all subnodes */ fdt_for_each_subnode(subnode, blob, node) fdt_fixup_pcie_mem_size(blob, subnode, pcie1_mem, pcie2_mem, pcie3_mem); return; err: printf("Error: Cannot update \"ranges\" property\n"); } static inline phys_size_t get_law_size(phys_addr_t addr, enum law_trgt_if id) { struct law_entry e; e = find_law_by_addr_id(addr, id); if (e.index < 0) return 0; return 2ULL << e.size; } void ft_memory_setup(void *blob, struct bd_info *bd) { phys_size_t pcie1_mem, pcie2_mem, pcie3_mem; u64 start[CONFIG_NR_DRAM_BANKS]; u64 size[CONFIG_NR_DRAM_BANKS]; int count; int node; if (!env_get("bootm_low") && !env_get("bootm_size")) { for (count = 0; count < CONFIG_NR_DRAM_BANKS; count++) { start[count] = gd->bd->bi_dram[count].start; size[count] = gd->bd->bi_dram[count].size; if (!size[count]) break; } fdt_fixup_memory_banks(blob, start, size, count); } else { fdt_fixup_memory(blob, env_get_bootm_low(), env_get_bootm_size()); } pcie1_mem = get_law_size(CFG_SYS_PCIE1_MEM_PHYS, LAW_TRGT_IF_PCIE_1); pcie2_mem = get_law_size(CFG_SYS_PCIE2_MEM_PHYS, LAW_TRGT_IF_PCIE_2); pcie3_mem = get_law_size(CFG_SYS_PCIE3_MEM_PHYS, LAW_TRGT_IF_PCIE_3); fdt_for_each_node_by_compatible(node, blob, -1, "fsl,mpc8548-pcie") fdt_fixup_pcie_mem_size(blob, node, pcie1_mem, pcie2_mem, pcie3_mem); } static int detect_model_serial(const char **model, char serial[17]) { u32 version_num; int err; err = turris_atsha_otp_get_serial_number(serial); if (err) { *model = "Turris 1.x"; strcpy(serial, "unknown"); return -1; } version_num = simple_strtoull(serial, NULL, 16) >> 32; /* * Turris 1.0 boards (RTRS01) have version_num 0x5. * Turris 1.1 boards (RTRS02) have version_num 0x6, 0x7, 0x8 and 0x9. */ if (be32_to_cpu(version_num) >= 0x6) { *model = "Turris 1.1 (RTRS02)"; return 1; } *model = "Turris 1.0 (RTRS01)"; return 0; } void p1_p2_rdb_pc_fix_fdt_model(void *blob) { const char *model; char serial[17]; int len; int off; int rev; char c; rev = detect_model_serial(&model, serial); if (rev < 0) return; /* Turris 1.0 boards (RTRS01) do not have third PCIe controller */ if (rev == 0) { off = fdt_path_offset(blob, "pci2"); if (off >= 0) fdt_del_node(blob, off); } /* Fix model string only in case it is generic "Turris 1.x" */ model = fdt_getprop(blob, 0, "model", &len); if (len < sizeof("Turris 1.x") - 1) return; if (memcmp(model, "Turris 1.x", sizeof("Turris 1.x") - 1) != 0) return; c = '0' + rev; fdt_setprop_inplace_namelen_partial(blob, 0, "model", sizeof("model") - 1, sizeof("Turris 1.") - 1, &c, 1); } int misc_init_r(void) { turris_atsha_otp_init_mac_addresses(0); turris_atsha_otp_init_serial_number(); return 0; } /* This comes from ../../freescale/p1_p2_rdb_pc/p1_p2_rdb_pc.c */ extern int checkboard_p1_p2(void); int checkboard(void) { const char *model; char serial[17]; void *reg; /* Disable software control of all Turris LEDs */ out_8(TURRIS_CPLD_LED_SW_OVERRIDE_REG, 0x00); /* Reset colors of all Turris LEDs to their default values */ for (reg = TURRIS_CPLD_LED_BRIGHTNESS_REG_FIRST; reg <= TURRIS_CPLD_LED_BRIGHTNESS_REG_LAST; reg++) out_8(reg, 0xff); detect_model_serial(&model, serial); printf("Revision: %s\n", model); printf("Serial Number: %s\n", serial); return checkboard_p1_p2(); } static void handle_reset_button(void) { const char * const vars[1] = { "bootcmd_rescue", }; u8 reset_time_raw, reset_time; /* * Ensure that bootcmd_rescue has always stock value, so that running * run bootcmd_rescue * always works correctly. */ env_set_default_vars(1, (char * const *)vars, 0); reset_time_raw = in_8(TURRIS_CPLD_RESET_TIME_CYCLE_REG); if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_6S) reset_time = 6; else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_5S) reset_time = 5; else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_4S) reset_time = 4; else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_3S) reset_time = 3; else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_2S) reset_time = 2; else if (reset_time_raw & TURRIS_CPLD_RESET_TIME_CYCLE_1S) reset_time = 1; else reset_time = 0; env_set_ulong("turris_reset", reset_time); /* Check if red reset button was hold for at least six seconds. */ if (reset_time >= 6) { const char * const vars[3] = { "bootcmd", "bootdelay", "distro_bootcmd", }; /* * Set the above envs to their default values, in case the user * managed to break them. */ env_set_default_vars(3, (char * const *)vars, 0); /* Ensure bootcmd_rescue is used by distroboot */ env_set("boot_targets", "rescue"); printf("RESET button was hold for >= 6s, overwriting boot_targets for system rescue!\n"); } else { /* * In case the user somehow managed to save environment with * boot_targets=rescue, reset boot_targets to default value. * This could happen in subsequent commands if bootcmd_rescue * failed. */ if (!strcmp(env_get("boot_targets"), "rescue")) { const char * const vars[1] = { "boot_targets", }; env_set_default_vars(1, (char * const *)vars, 0); } if (reset_time > 0) printf("RESET button was hold for %us.\n", reset_time); } } static int recalculate_pcie_mem_law(phys_addr_t addr, pci_size_t pcie_size, enum law_trgt_if id, phys_addr_t *free_start, phys_size_t *free_size) { phys_size_t cur_size, new_size; struct law_entry e; e = find_law_by_addr_id(addr, id); if (e.index < 0) { *free_start = *free_size = 0; return 0; } cur_size = 2ULL << e.size; new_size = roundup_pow_of_two(pcie_size); if (new_size >= cur_size) { *free_start = *free_size = 0; return 0; } set_law(e.index, addr, law_size_bits(new_size), id); *free_start = addr + new_size; *free_size = cur_size - new_size; return 1; } static void recalculate_used_pcie_mem(void) { phys_addr_t free_start1, free_start2; phys_size_t free_size1, free_size2; pci_size_t pcie1_used_mem_size; pci_size_t pcie2_used_mem_size; struct law_entry e; phys_size_t size; ofnode node; int i; size = gd->ram_size; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) size -= gd->bd->bi_dram[i].size; if (size == 0) return; e = find_law_by_addr_id(CFG_SYS_PCIE3_MEM_PHYS, LAW_TRGT_IF_PCIE_3); if (e.index < 0 && gd->bd->bi_dram[1].size > 0) { /* * If there is no LAW for PCIe 3 MEM then 3rd PCIe controller * is inactive, which is the case for Turris 1.0 boards. So * use its reserved 2 MB physical space for DDR RAM. */ unsigned int bank_size = SZ_2M; if (bank_size > size) bank_size = size; printf("Reserving unused "); print_size(bank_size, ""); printf(" of PCIe 3 MEM for DDR RAM\n"); gd->bd->bi_dram[1].start -= bank_size; gd->bd->bi_dram[1].size += bank_size; size -= bank_size; if (size == 0) return; } #ifdef CONFIG_PCI_PNP /* * Detect how much space of PCIe MEM is needed for both PCIe 1 and * PCIe 2 controllers with all connected cards on whole hierarchy. * This works only when U-Boot has enabled PCI PNP code which scans * all PCI devices and calculate required memory for every PCI BAR of * every PCI device. */ ofnode_for_each_compatible_node(node, "fsl,mpc8548-pcie") { struct udevice *dev; if (device_find_global_by_ofnode(node, &dev)) continue; struct pci_controller *hose = dev_get_uclass_priv(pci_get_controller(dev)); if (!hose) continue; if (!hose->pci_mem) continue; if (!hose->pci_mem->size) continue; pci_size_t used_mem_size = hose->pci_mem->bus_lower - hose->pci_mem->bus_start; if (hose->pci_mem->phys_start == CFG_SYS_PCIE1_MEM_PHYS) pcie1_used_mem_size = used_mem_size; else if (hose->pci_mem->phys_start == CFG_SYS_PCIE2_MEM_PHYS) pcie2_used_mem_size = used_mem_size; } if (pcie1_used_mem_size == 0 && pcie2_used_mem_size == 0) return; e = find_law_by_addr_id(0xc0000000, LAW_TRGT_IF_DDR_1); if (e.index < 0) { printf("Error: Cannot setup DDR LAW for more than 3 GB of RAM\n"); return; } /* * Increase additional overlapping 1 GB DDR LAW from 1GB to 2GB by * moving its left side from 0xc0000000 to 0x80000000. After this * change it would overlap with PCIe MEM 1 and 2 LAWs. */ set_law(e.index, 0x80000000, LAW_SIZE_2G, LAW_TRGT_IF_DDR_1); i = 3; static_assert(CONFIG_NR_DRAM_BANKS >= 3 + 2); if (recalculate_pcie_mem_law(CFG_SYS_PCIE2_MEM_PHYS, pcie2_used_mem_size, LAW_TRGT_IF_PCIE_2, &free_start2, &free_size2)) { printf("Reserving unused "); print_size(free_size2, ""); printf(" of PCIe 2 MEM for DDR RAM\n"); gd->bd->bi_dram[i].start = free_start2; gd->bd->bi_dram[i].size = min(size, free_size2); size -= gd->bd->bi_dram[i].start; i++; if (size == 0) return; } if (recalculate_pcie_mem_law(CFG_SYS_PCIE1_MEM_PHYS, pcie1_used_mem_size, LAW_TRGT_IF_PCIE_1, &free_start1, &free_size1)) { printf("Reserving unused "); print_size(free_size1, ""); printf(" of PCIe 1 MEM for DDR RAM\n"); gd->bd->bi_dram[i].start = free_start1; gd->bd->bi_dram[i].size = min(size, free_size1); size -= gd->bd->bi_dram[i].size; i++; if (size == 0) return; } #endif } int last_stage_init(void) { handle_reset_button(); recalculate_used_pcie_mem(); return 0; } int get_serial_clock(void) { return get_bus_freq(0); }