| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * (C) Copyright 2021 Mark Kettenis <kettenis@openbsd.org> |
| */ |
| |
| #include <common.h> |
| #include <dm.h> |
| #include <dm/uclass-internal.h> |
| #include <efi_loader.h> |
| #include <lmb.h> |
| |
| #include <asm/armv8/mmu.h> |
| #include <asm/global_data.h> |
| #include <asm/io.h> |
| #include <asm/system.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| /* Apple M1/M2 */ |
| |
| static struct mm_region t8103_mem_map[] = { |
| { |
| /* I/O */ |
| .virt = 0x200000000, |
| .phys = 0x200000000, |
| .size = 2UL * SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x380000000, |
| .phys = 0x380000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x500000000, |
| .phys = 0x500000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x680000000, |
| .phys = 0x680000000, |
| .size = SZ_512M, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* PCIE */ |
| .virt = 0x6a0000000, |
| .phys = 0x6a0000000, |
| .size = SZ_512M, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRE) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* PCIE */ |
| .virt = 0x6c0000000, |
| .phys = 0x6c0000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRE) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* RAM */ |
| .virt = 0x800000000, |
| .phys = 0x800000000, |
| .size = 8UL * SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | |
| PTE_BLOCK_INNER_SHARE |
| }, { |
| /* Framebuffer */ |
| .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL_NC) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* List terminator */ |
| 0, |
| } |
| }; |
| |
| /* Apple M1 Pro/Max */ |
| |
| static struct mm_region t6000_mem_map[] = { |
| { |
| /* I/O */ |
| .virt = 0x280000000, |
| .phys = 0x280000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x380000000, |
| .phys = 0x380000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x580000000, |
| .phys = 0x580000000, |
| .size = SZ_512M, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* PCIE */ |
| .virt = 0x5a0000000, |
| .phys = 0x5a0000000, |
| .size = SZ_512M, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRE) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* PCIE */ |
| .virt = 0x5c0000000, |
| .phys = 0x5c0000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRE) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x700000000, |
| .phys = 0x700000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0xb00000000, |
| .phys = 0xb00000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0xf00000000, |
| .phys = 0xf00000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x1300000000, |
| .phys = 0x1300000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* RAM */ |
| .virt = 0x10000000000, |
| .phys = 0x10000000000, |
| .size = 16UL * SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | |
| PTE_BLOCK_INNER_SHARE |
| }, { |
| /* Framebuffer */ |
| .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL_NC) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* List terminator */ |
| 0, |
| } |
| }; |
| |
| /* Apple M1 Ultra */ |
| |
| static struct mm_region t6002_mem_map[] = { |
| { |
| /* I/O */ |
| .virt = 0x280000000, |
| .phys = 0x280000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x380000000, |
| .phys = 0x380000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x580000000, |
| .phys = 0x580000000, |
| .size = SZ_512M, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* PCIE */ |
| .virt = 0x5a0000000, |
| .phys = 0x5a0000000, |
| .size = SZ_512M, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRE) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* PCIE */ |
| .virt = 0x5c0000000, |
| .phys = 0x5c0000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRE) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x700000000, |
| .phys = 0x700000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0xb00000000, |
| .phys = 0xb00000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0xf00000000, |
| .phys = 0xf00000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x1300000000, |
| .phys = 0x1300000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x2280000000, |
| .phys = 0x2280000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x2380000000, |
| .phys = 0x2380000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x2580000000, |
| .phys = 0x2580000000, |
| .size = SZ_512M, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* PCIE */ |
| .virt = 0x25a0000000, |
| .phys = 0x25a0000000, |
| .size = SZ_512M, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRE) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* PCIE */ |
| .virt = 0x25c0000000, |
| .phys = 0x25c0000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRE) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x2700000000, |
| .phys = 0x2700000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x2b00000000, |
| .phys = 0x2b00000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x2f00000000, |
| .phys = 0x2f00000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* I/O */ |
| .virt = 0x3300000000, |
| .phys = 0x3300000000, |
| .size = SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | |
| PTE_BLOCK_NON_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* RAM */ |
| .virt = 0x10000000000, |
| .phys = 0x10000000000, |
| .size = 16UL * SZ_1G, |
| .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) | |
| PTE_BLOCK_INNER_SHARE |
| }, { |
| /* Framebuffer */ |
| .attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL_NC) | |
| PTE_BLOCK_INNER_SHARE | |
| PTE_BLOCK_PXN | PTE_BLOCK_UXN |
| }, { |
| /* List terminator */ |
| 0, |
| } |
| }; |
| |
| struct mm_region *mem_map; |
| |
| int board_init(void) |
| { |
| return 0; |
| } |
| |
| int dram_init(void) |
| { |
| return fdtdec_setup_mem_size_base(); |
| } |
| |
| int dram_init_banksize(void) |
| { |
| return fdtdec_setup_memory_banksize(); |
| } |
| |
| extern long fw_dtb_pointer; |
| |
| void *board_fdt_blob_setup(int *err) |
| { |
| /* Return DTB pointer passed by m1n1 */ |
| *err = 0; |
| return (void *)fw_dtb_pointer; |
| } |
| |
| void build_mem_map(void) |
| { |
| ofnode node; |
| fdt_addr_t base; |
| fdt_size_t size; |
| int i; |
| |
| if (of_machine_is_compatible("apple,t8103") || |
| of_machine_is_compatible("apple,t8112")) |
| mem_map = t8103_mem_map; |
| else if (of_machine_is_compatible("apple,t6000")) |
| mem_map = t6000_mem_map; |
| else if (of_machine_is_compatible("apple,t6001")) |
| mem_map = t6000_mem_map; |
| else if (of_machine_is_compatible("apple,t6002")) |
| mem_map = t6002_mem_map; |
| else |
| panic("Unsupported SoC\n"); |
| |
| /* Find list terminator. */ |
| for (i = 0; mem_map[i].size || mem_map[i].attrs; i++) |
| ; |
| |
| /* Align RAM mapping to page boundaries */ |
| base = gd->bd->bi_dram[0].start; |
| size = gd->bd->bi_dram[0].size; |
| size += (base - ALIGN_DOWN(base, SZ_4K)); |
| base = ALIGN_DOWN(base, SZ_4K); |
| size = ALIGN(size, SZ_4K); |
| |
| /* Update RAM mapping */ |
| mem_map[i - 2].virt = base; |
| mem_map[i - 2].phys = base; |
| mem_map[i - 2].size = size; |
| |
| node = ofnode_path("/chosen/framebuffer"); |
| if (!ofnode_valid(node)) |
| return; |
| |
| base = ofnode_get_addr_size(node, "reg", &size); |
| if (base == FDT_ADDR_T_NONE) |
| return; |
| |
| /* Align framebuffer mapping to page boundaries */ |
| size += (base - ALIGN_DOWN(base, SZ_4K)); |
| base = ALIGN_DOWN(base, SZ_4K); |
| size = ALIGN(size, SZ_4K); |
| |
| /* Add framebuffer mapping */ |
| mem_map[i - 1].virt = base; |
| mem_map[i - 1].phys = base; |
| mem_map[i - 1].size = size; |
| } |
| |
| void enable_caches(void) |
| { |
| build_mem_map(); |
| |
| icache_enable(); |
| dcache_enable(); |
| } |
| |
| u64 get_page_table_size(void) |
| { |
| return SZ_256K; |
| } |
| |
| #define KERNEL_COMP_SIZE SZ_128M |
| |
| int board_late_init(void) |
| { |
| struct lmb lmb; |
| u32 status = 0; |
| |
| lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob); |
| |
| /* somewhat based on the Linux Kernel boot requirements: |
| * align by 2M and maximal FDT size 2M |
| */ |
| status |= env_set_hex("loadaddr", lmb_alloc(&lmb, SZ_1G, SZ_2M)); |
| status |= env_set_hex("fdt_addr_r", lmb_alloc(&lmb, SZ_2M, SZ_2M)); |
| status |= env_set_hex("kernel_addr_r", lmb_alloc(&lmb, SZ_128M, SZ_2M)); |
| status |= env_set_hex("ramdisk_addr_r", lmb_alloc(&lmb, SZ_1G, SZ_2M)); |
| status |= env_set_hex("kernel_comp_addr_r", |
| lmb_alloc(&lmb, KERNEL_COMP_SIZE, SZ_2M)); |
| status |= env_set_hex("kernel_comp_size", KERNEL_COMP_SIZE); |
| status |= env_set_hex("scriptaddr", lmb_alloc(&lmb, SZ_4M, SZ_2M)); |
| status |= env_set_hex("pxefile_addr_r", lmb_alloc(&lmb, SZ_4M, SZ_2M)); |
| |
| if (status) |
| log_warning("late_init: Failed to set run time variables\n"); |
| |
| return 0; |
| } |
| |
| int ft_board_setup(void *blob, struct bd_info *bd) |
| { |
| struct udevice *dev; |
| const char *stdoutname; |
| int node, ret; |
| |
| /* |
| * Modify the "stdout-path" property under "/chosen" to point |
| * at "/chosen/framebuffer if a keyboard is available and |
| * we're not running under the m1n1 hypervisor. |
| * Developers can override this behaviour by dropping |
| * "vidconsole" from the "stdout" environment variable. |
| */ |
| |
| /* EL1 means we're running under the m1n1 hypervisor. */ |
| if (current_el() == 1) |
| return 0; |
| |
| ret = uclass_find_device(UCLASS_KEYBOARD, 0, &dev); |
| if (ret < 0) |
| return 0; |
| |
| stdoutname = env_get("stdout"); |
| if (!stdoutname || !strstr(stdoutname, "vidconsole")) |
| return 0; |
| |
| /* Make sure we actually have a framebuffer. */ |
| node = fdt_path_offset(blob, "/chosen/framebuffer"); |
| if (node < 0 || !fdtdec_get_is_enabled(blob, node)) |
| return 0; |
| |
| node = fdt_path_offset(blob, "/chosen"); |
| if (node < 0) |
| return 0; |
| fdt_setprop_string(blob, node, "stdout-path", "/chosen/framebuffer"); |
| |
| return 0; |
| } |