Angel Pons | 5f249e6 | 2020-04-04 18:51:01 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | /* Early initialization code for aarch64 (a.k.a. armv8) */ |
David Hendricks | 8cbd569 | 2017-12-01 20:49:48 -0800 | [diff] [blame] | 3 | |
| 4 | #include <arch/asm.h> |
| 5 | #include <soc/addressmap.h> |
| 6 | |
| 7 | ENTRY(_start) |
| 8 | .org 0 |
| 9 | /** |
| 10 | * According to the reference manual the first instruction is fetched from |
| 11 | * offset 0x100, but at offset 0 a branch instruction is always placed. |
| 12 | * Support two entry points for now. |
| 13 | * To save memory put the cavium specific init code between those to entry |
| 14 | * points. |
| 15 | */ |
| 16 | ic ialluis |
| 17 | fmov d30, x0 /* Save X0 in FPR for use later */ |
Patrick Rudolph | e15556e | 2018-02-16 09:04:38 +0100 | [diff] [blame] | 18 | /** |
| 19 | * The BDK stores X1 for later use, but it turns out that we don't need |
| 20 | * this "feature". The idea is to hide the devicetree somewhere in |
| 21 | * flash, that only the ROM will find it and point to it using X1. |
| 22 | */ |
David Hendricks | 8cbd569 | 2017-12-01 20:49:48 -0800 | [diff] [blame] | 23 | adr x1, _start /* x1 = _start location based on PC */ |
| 24 | fmov d29, x1 /* Save PC in FPR for use later */ |
| 25 | |
| 26 | #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| 27 | /* Change the core to big endian mode for EL3 */ |
| 28 | mrs x0, SCTLR_EL3 |
| 29 | mov x1, 1<<25 /* Set SCTLR_EL3[ee]=1 */ |
| 30 | orr x0, x0, x1 |
| 31 | msr SCTLR_EL3, x0 |
| 32 | #define ENDIAN_CONVERT64(reg) rev reg, reg |
| 33 | #define ENDIAN_CONVERT32(reg) rev reg, reg |
| 34 | #define ENDIAN_CONVERT16(reg) rev16 reg, reg |
| 35 | #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| 36 | /* Nothing needed, default is little endian */ |
| 37 | #define ENDIAN_CONVERT64(reg) |
| 38 | #define ENDIAN_CONVERT32(reg) |
| 39 | #define ENDIAN_CONVERT16(reg) |
| 40 | #else |
| 41 | #error Unknown endianness |
| 42 | #endif |
| 43 | |
| 44 | mov x0, (LMC0_PF_BAR0 >> 32) |
| 45 | lsl x0, x0, 32 |
| 46 | mov x1, (LMC0_PF_BAR0 & 0xffffffff) |
| 47 | orr x0, x0, x1 |
| 48 | |
| 49 | /* Test if DRAM PLL is running */ |
| 50 | ldr x1, [x0, LMC0_DDR_PLL_CTL0] |
| 51 | |
| 52 | tst x1, 0x80 |
| 53 | |
| 54 | b.ne cache_setup_done |
| 55 | |
| 56 | bl _setup_car |
| 57 | |
| 58 | cache_setup_done: |
| 59 | |
| 60 | /* Check that we're running on the node we're linked for */ |
| 61 | mrs x0, MPIDR_EL1 |
| 62 | ubfx x0, x0, 16, 8 /* Bits 23:16 are the physical node ID */ |
| 63 | mov x1, 0x0 |
| 64 | cmp x0, x1 |
| 65 | |
| 66 | b.ne _wfi |
| 67 | |
| 68 | node_check_done: |
| 69 | /* Get code position */ |
| 70 | mov x1, 0x020000 |
| 71 | mov x0, BOOTROM_OFFSET |
| 72 | add x1, x0, x1 |
| 73 | |
| 74 | adr x0, _start |
| 75 | |
| 76 | /** |
Patrick Rudolph | e15556e | 2018-02-16 09:04:38 +0100 | [diff] [blame] | 77 | * Check if IROM has loaded the code to BOOTROM_OFFSET. |
David Hendricks | 8cbd569 | 2017-12-01 20:49:48 -0800 | [diff] [blame] | 78 | * In case the offset is wrong, try to relocate. |
| 79 | * Ideally the following code is never executed. |
| 80 | * FIXME: Add region overlap check. |
| 81 | */ |
| 82 | cmp x0, x1 |
| 83 | b.eq after_relocate |
| 84 | |
| 85 | relocate: |
| 86 | /* Get bootblock length */ |
| 87 | ldr x2, =_program |
| 88 | ldr x3, =_eprogram |
| 89 | sub x2, x2, x3 |
| 90 | b copy_code |
| 91 | |
| 92 | .align 7 |
| 93 | copy_code: |
| 94 | ldp q0, q1, [x1], 32 /* Load 32 bytes */ |
| 95 | subs w2, w2, 32 /* Subtract 32 from length, setting flags */ |
| 96 | stp q0, q1, [x0], 32 /* Store 32 bytes */ |
| 97 | b.gt copy_code /* Repeat if length is still positive */ |
| 98 | dmb sy |
| 99 | |
| 100 | /* Load the actual location we're suppose to be at */ |
| 101 | adr x0, after_relocate /* Relative address */ |
| 102 | adr x1, _start /* Relative address */ |
| 103 | sub x0, x0, x1 /* This only works if _start is suppose to be zero */ |
| 104 | mov x1, BOOTROM_OFFSET |
| 105 | add x0, x0, x1 |
| 106 | br x0 /* Branch to relocated code */ |
| 107 | |
| 108 | ic ialluis /* Clear the icache now that all code is correct */ |
| 109 | |
| 110 | after_relocate: |
| 111 | /* Allow unaligned memory access as long as MMU is disabled */ |
| 112 | mrs x22, s3_0_c11_c0_4 |
| 113 | orr x22, x22, # (1 << 37) /* Set DCVA47 */ |
| 114 | msr s3_0_c11_c0_4, x22 |
| 115 | |
| 116 | bl start |
| 117 | |
| 118 | /* Real entry point */ |
| 119 | .org 0x100 |
| 120 | b _start |
| 121 | ENDPROC(_start) |
| 122 | |
| 123 | |
| 124 | ENTRY(_setup_car) |
| 125 | mrs x0, MIDR_EL1 |
| 126 | ubfx x0, x0, 4, 12 /* Bits 15:4 are the part number */ |
| 127 | cmp x0, 0xb0 |
| 128 | b.ge _wfi |
| 129 | |
| 130 | thunder1_cache_setup: |
| 131 | /** |
| 132 | * Setup L2 cache to allow secure access to all of the address space |
Martin Roth | 26f97f9 | 2021-10-01 14:53:22 -0600 | [diff] [blame] | 133 | * thunder1 compatibility list: |
David Hendricks | 8cbd569 | 2017-12-01 20:49:48 -0800 | [diff] [blame] | 134 | * - CN81XX |
| 135 | * - CN83XX |
| 136 | * - CN88XX |
| 137 | */ |
| 138 | #define REGIONX_START 0x1000 |
| 139 | #define REGIONX_END 0x1008 |
| 140 | #define REGIONX_ATTR 0x1010 |
| 141 | mov x0, L2C_PF_BAR0 >> 32 |
| 142 | lsl x0, x0, 32 |
| 143 | mov x1, (L2C_PF_BAR0 & 0xffffffff) |
| 144 | orr x0, x0, x1 |
| 145 | str xzr, [x0, REGIONX_START] /* Start of zero */ |
| 146 | mov x1, 0x3fffff00000 /* End of max address */ |
| 147 | ENDIAN_CONVERT64(x1) |
| 148 | str x1, [x0, REGIONX_END] |
| 149 | mov x1, 2 /* Secure only access */ |
| 150 | ENDIAN_CONVERT64(x1) |
| 151 | str x1, [x0, REGIONX_ATTR] |
| 152 | /* Update way partition to allow core 0 to write to L2 */ |
| 153 | #define L2C_WPAR_PP0_OFFSET 0x40000 |
| 154 | mov x1, L2C_WPAR_PP0_OFFSET |
| 155 | str xzr, [x0, x1] |
| 156 | ldr xzr, [x0, x1] /* Read back to make sure done */ |
| 157 | #undef REGIONX_START |
| 158 | #undef REGIONX_END |
| 159 | #undef REGIONX_ATTR |
| 160 | #undef L2C_WPAR_PP0_OFFSET |
| 161 | |
| 162 | /** |
| 163 | * At this point the whole CAR is readable and writeable, but if |
| 164 | * we touch to many cache-lines our code might get flushed out. |
| 165 | * We have to lock all cache-lines that are to be used as RAM, which are |
| 166 | * the ones marked as SRAM in memlayout. |
| 167 | */ |
| 168 | mrs x0, CTR_EL0 /* Get cache-line size */ |
| 169 | /* [19:16] - Indicates (Log2(number of words in cache line) */ |
| 170 | ubfx x0, x0, 16, 4 |
| 171 | mov x1, 4 /* Bytes in a word (32-bit) */ |
| 172 | lsl x0, x1, x0 /* Number of Bytes in x0 */ |
| 173 | |
| 174 | sub x1, x0, 1 |
| 175 | mvn x1, x1 /* Place mask in x1 */ |
| 176 | |
| 177 | ldr x3, =_sram |
| 178 | and x3, x3, x1 /* Align addresses with cache-lines */ |
| 179 | ldr x4, =_esram |
| 180 | add x4, x4, x0 |
| 181 | sub x4, x4, 1 |
| 182 | and x4, x4, x1 /* Align addresses with cache-lines */ |
| 183 | sub x2, x4, x3 /* Store sram length in x2 */ |
| 184 | |
| 185 | lock_cache_lines: |
| 186 | sys #0, c11, c1, #4, x3 |
| 187 | add x3, x3, x0 /* Increment address by cache-line bytes */ |
| 188 | subs w2, w2, w0 /* Subtract cache-line bytes from length */ |
| 189 | b.gt lock_cache_lines /* Repeat if length is still positive */ |
| 190 | |
| 191 | /** |
| 192 | * The locked region isn't considered dirty by L2. Do read/write of |
| 193 | * each cache line to force each to be dirty. This is needed across the |
| 194 | * whole line to make sure the L2 dirty bits are all up to date. |
| 195 | * NOTE: If we'd relocate we could memset the whole memory ! |
| 196 | */ |
| 197 | ldr x3, =_sram |
| 198 | and x3, x3, x1 /* Align addresses with cache-lines */ |
| 199 | ldr x4, =_esram |
| 200 | add x4, x4, x0 |
| 201 | sub x4, x4, 1 |
| 202 | and x4, x4, x1 /* Align addresses with cache-lines */ |
| 203 | sub x2, x4, x3 /* Store sram length in x2 */ |
| 204 | mov x4, x3 |
| 205 | b dirty_cache_line |
| 206 | |
| 207 | .align 7 |
| 208 | dirty_cache_line: |
| 209 | ldp q0, q1, [x3], 32 /* Load 32 bytes */ |
| 210 | subs w2, w2, 32 /* Subtract 32 from length, setting flags */ |
| 211 | stp q0, q1, [x4], 32 /* Store 32 bytes */ |
| 212 | b.gt dirty_cache_line /* Repeat if length is still positive */ |
| 213 | dmb sy |
| 214 | |
| 215 | clear_interrupts: |
| 216 | /** |
| 217 | * As the memory controller isn't running, but we access the DRAM's |
| 218 | * address space, some interrupt flags had been set. |
| 219 | * Tidy up our mess now on (valid for CN81XX only). |
| 220 | */ |
| 221 | mov x0, (L2C_TAD0_INT_W1C >> 32) |
| 222 | lsl x0, x0, 32 |
| 223 | mov x1, (L2C_TAD0_INT_W1C & 0xffffffff) |
| 224 | orr x0, x0, x1 |
| 225 | |
| 226 | ldr x1, [x0] |
| 227 | orr x1, x1, 0x1c00 /* Clear WRDISLMC, RDDISLMC, RDNXM */ |
| 228 | str x1, [x0] |
| 229 | |
| 230 | ret |
| 231 | ENDPROC(_setup_car) |
| 232 | |
| 233 | ENTRY(_wfi) |
| 234 | wfi |
| 235 | ENDPROC(_wfi) |
| 236 | |
| 237 | ENTRY(start) |
| 238 | bl arm64_init_cpu |
| 239 | |
| 240 | fmov x0, d30 /* The original X0, info from previous image */ |
Patrick Rudolph | e15556e | 2018-02-16 09:04:38 +0100 | [diff] [blame] | 241 | fmov x1, d29 /* The original PC we were loaded at */ |
David Hendricks | 8cbd569 | 2017-12-01 20:49:48 -0800 | [diff] [blame] | 242 | |
| 243 | /* Call C entry */ |
| 244 | bl bootblock_main |
| 245 | |
| 246 | ENDPROC(start) |