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