1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * (C) Copyright 2014 - 2015 Xilinx, Inc. 4 * Michal Simek <michal.simek@xilinx.com> 5 */ 6 7 #include <common.h> 8 #include <asm/arch/hardware.h> 9 #include <asm/arch/sys_proto.h> 10 #include <asm/io.h> 11 12 #define LOCK 0 13 #define SPLIT 1 14 15 #define HALT 0 16 #define RELEASE 1 17 18 #define ZYNQMP_BOOTADDR_HIGH_MASK 0xFFFFFFFF 19 #define ZYNQMP_R5_HIVEC_ADDR 0xFFFF0000 20 #define ZYNQMP_R5_LOVEC_ADDR 0x0 21 #define ZYNQMP_RPU_CFG_CPU_HALT_MASK 0x01 22 #define ZYNQMP_RPU_CFG_HIVEC_MASK 0x04 23 #define ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK 0x08 24 #define ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK 0x40 25 #define ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK 0x10 26 27 #define ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK 0x04 28 #define ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK 0x01 29 #define ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK 0x02 30 #define ZYNQMP_CRLAPB_CPU_R5_CTRL_CLKACT_MASK 0x1000000 31 32 #define ZYNQMP_TCM_START_ADDRESS 0xFFE00000 33 #define ZYNQMP_TCM_BOTH_SIZE 0x40000 34 35 #define ZYNQMP_CORE_APU0 0 36 #define ZYNQMP_CORE_APU3 3 37 38 #define ZYNQMP_MAX_CORES 6 39 40 int is_core_valid(unsigned int core) 41 { 42 if (core < ZYNQMP_MAX_CORES) 43 return 1; 44 45 return 0; 46 } 47 48 int cpu_reset(u32 nr) 49 { 50 puts("Feature is not implemented.\n"); 51 return 0; 52 } 53 54 static void set_r5_halt_mode(u8 halt, u8 mode) 55 { 56 u32 tmp; 57 58 tmp = readl(&rpu_base->rpu0_cfg); 59 if (halt == HALT) 60 tmp &= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK; 61 else 62 tmp |= ZYNQMP_RPU_CFG_CPU_HALT_MASK; 63 writel(tmp, &rpu_base->rpu0_cfg); 64 65 if (mode == LOCK) { 66 tmp = readl(&rpu_base->rpu1_cfg); 67 if (halt == HALT) 68 tmp &= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK; 69 else 70 tmp |= ZYNQMP_RPU_CFG_CPU_HALT_MASK; 71 writel(tmp, &rpu_base->rpu1_cfg); 72 } 73 } 74 75 static void set_r5_tcm_mode(u8 mode) 76 { 77 u32 tmp; 78 79 tmp = readl(&rpu_base->rpu_glbl_ctrl); 80 if (mode == LOCK) { 81 tmp &= ~ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK; 82 tmp |= ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK | 83 ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK; 84 } else { 85 tmp |= ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK; 86 tmp &= ~(ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK | 87 ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK); 88 } 89 90 writel(tmp, &rpu_base->rpu_glbl_ctrl); 91 } 92 93 static void set_r5_reset(u8 mode) 94 { 95 u32 tmp; 96 97 tmp = readl(&crlapb_base->rst_lpd_top); 98 tmp |= (ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK | 99 ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK); 100 101 if (mode == LOCK) 102 tmp |= ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK; 103 104 writel(tmp, &crlapb_base->rst_lpd_top); 105 } 106 107 static void release_r5_reset(u8 mode) 108 { 109 u32 tmp; 110 111 tmp = readl(&crlapb_base->rst_lpd_top); 112 tmp &= ~(ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK | 113 ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK); 114 115 if (mode == LOCK) 116 tmp &= ~ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK; 117 118 writel(tmp, &crlapb_base->rst_lpd_top); 119 } 120 121 static void enable_clock_r5(void) 122 { 123 u32 tmp; 124 125 tmp = readl(&crlapb_base->cpu_r5_ctrl); 126 tmp |= ZYNQMP_CRLAPB_CPU_R5_CTRL_CLKACT_MASK; 127 writel(tmp, &crlapb_base->cpu_r5_ctrl); 128 129 /* Give some delay for clock 130 * to propagate */ 131 udelay(0x500); 132 } 133 134 int cpu_disable(u32 nr) 135 { 136 if (nr >= ZYNQMP_CORE_APU0 && nr <= ZYNQMP_CORE_APU3) { 137 u32 val = readl(&crfapb_base->rst_fpd_apu); 138 val |= 1 << nr; 139 writel(val, &crfapb_base->rst_fpd_apu); 140 } else { 141 set_r5_reset(LOCK); 142 } 143 144 return 0; 145 } 146 147 int cpu_status(u32 nr) 148 { 149 if (nr >= ZYNQMP_CORE_APU0 && nr <= ZYNQMP_CORE_APU3) { 150 u32 addr_low = readl(((u8 *)&apu_base->rvbar_addr0_l) + nr * 8); 151 u32 addr_high = readl(((u8 *)&apu_base->rvbar_addr0_h) + 152 nr * 8); 153 u32 val = readl(&crfapb_base->rst_fpd_apu); 154 val &= 1 << nr; 155 printf("APU CPU%d %s - starting address HI: %x, LOW: %x\n", 156 nr, val ? "OFF" : "ON" , addr_high, addr_low); 157 } else { 158 u32 val = readl(&crlapb_base->rst_lpd_top); 159 val &= 1 << (nr - 4); 160 printf("RPU CPU%d %s\n", nr - 4, val ? "OFF" : "ON"); 161 } 162 163 return 0; 164 } 165 166 static void set_r5_start(u8 high) 167 { 168 u32 tmp; 169 170 tmp = readl(&rpu_base->rpu0_cfg); 171 if (high) 172 tmp |= ZYNQMP_RPU_CFG_HIVEC_MASK; 173 else 174 tmp &= ~ZYNQMP_RPU_CFG_HIVEC_MASK; 175 writel(tmp, &rpu_base->rpu0_cfg); 176 177 tmp = readl(&rpu_base->rpu1_cfg); 178 if (high) 179 tmp |= ZYNQMP_RPU_CFG_HIVEC_MASK; 180 else 181 tmp &= ~ZYNQMP_RPU_CFG_HIVEC_MASK; 182 writel(tmp, &rpu_base->rpu1_cfg); 183 } 184 185 static void write_tcm_boot_trampoline(u32 boot_addr) 186 { 187 if (boot_addr) { 188 /* 189 * Boot trampoline is simple ASM code below. 190 * 191 * b over; 192 * label: 193 * .word 0 194 * over: ldr r0, =label 195 * ldr r1, [r0] 196 * bx r1 197 */ 198 debug("Write boot trampoline for %x\n", boot_addr); 199 writel(0xea000000, ZYNQMP_TCM_START_ADDRESS); 200 writel(boot_addr, ZYNQMP_TCM_START_ADDRESS + 0x4); 201 writel(0xe59f0004, ZYNQMP_TCM_START_ADDRESS + 0x8); 202 writel(0xe5901000, ZYNQMP_TCM_START_ADDRESS + 0xc); 203 writel(0xe12fff11, ZYNQMP_TCM_START_ADDRESS + 0x10); 204 writel(0x00000004, ZYNQMP_TCM_START_ADDRESS + 0x14); // address for 205 } 206 } 207 208 void initialize_tcm(bool mode) 209 { 210 if (!mode) { 211 set_r5_tcm_mode(LOCK); 212 set_r5_halt_mode(HALT, LOCK); 213 enable_clock_r5(); 214 release_r5_reset(LOCK); 215 } else { 216 set_r5_tcm_mode(SPLIT); 217 set_r5_halt_mode(HALT, SPLIT); 218 enable_clock_r5(); 219 release_r5_reset(SPLIT); 220 } 221 } 222 223 int cpu_release(u32 nr, int argc, char * const argv[]) 224 { 225 if (nr >= ZYNQMP_CORE_APU0 && nr <= ZYNQMP_CORE_APU3) { 226 u64 boot_addr = simple_strtoull(argv[0], NULL, 16); 227 /* HIGH */ 228 writel((u32)(boot_addr >> 32), 229 ((u8 *)&apu_base->rvbar_addr0_h) + nr * 8); 230 /* LOW */ 231 writel((u32)(boot_addr & ZYNQMP_BOOTADDR_HIGH_MASK), 232 ((u8 *)&apu_base->rvbar_addr0_l) + nr * 8); 233 234 u32 val = readl(&crfapb_base->rst_fpd_apu); 235 val &= ~(1 << nr); 236 writel(val, &crfapb_base->rst_fpd_apu); 237 } else { 238 if (argc != 2) { 239 printf("Invalid number of arguments to release.\n"); 240 printf("<addr> <mode>-Start addr lockstep or split\n"); 241 return 1; 242 } 243 244 u32 boot_addr = simple_strtoul(argv[0], NULL, 16); 245 u32 boot_addr_uniq = 0; 246 if (!(boot_addr == ZYNQMP_R5_LOVEC_ADDR || 247 boot_addr == ZYNQMP_R5_HIVEC_ADDR)) { 248 printf("Using TCM jump trampoline for address 0x%x\n", 249 boot_addr); 250 /* Save boot address for later usage */ 251 boot_addr_uniq = boot_addr; 252 /* 253 * R5 needs to start from LOVEC at TCM 254 * OCM will be probably occupied by ATF 255 */ 256 boot_addr = ZYNQMP_R5_LOVEC_ADDR; 257 } 258 259 /* 260 * Since we don't know where the user may have loaded the image 261 * for an R5 we have to flush all the data cache to ensure 262 * the R5 sees it. 263 */ 264 flush_dcache_all(); 265 266 if (!strncmp(argv[1], "lockstep", 8)) { 267 printf("R5 lockstep mode\n"); 268 set_r5_reset(LOCK); 269 set_r5_tcm_mode(LOCK); 270 set_r5_halt_mode(HALT, LOCK); 271 set_r5_start(boot_addr); 272 enable_clock_r5(); 273 release_r5_reset(LOCK); 274 dcache_disable(); 275 write_tcm_boot_trampoline(boot_addr_uniq); 276 dcache_enable(); 277 set_r5_halt_mode(RELEASE, LOCK); 278 } else if (!strncmp(argv[1], "split", 5)) { 279 printf("R5 split mode\n"); 280 set_r5_reset(SPLIT); 281 set_r5_tcm_mode(SPLIT); 282 set_r5_halt_mode(HALT, SPLIT); 283 set_r5_start(boot_addr); 284 enable_clock_r5(); 285 release_r5_reset(SPLIT); 286 dcache_disable(); 287 write_tcm_boot_trampoline(boot_addr_uniq); 288 dcache_enable(); 289 set_r5_halt_mode(RELEASE, SPLIT); 290 } else { 291 printf("Unsupported mode\n"); 292 return 1; 293 } 294 } 295 296 return 0; 297 } 298