1 // SPDX-License-Identifier: GPL-2.0+
3 * (C) Copyright 2014 - 2015 Xilinx, Inc.
4 * Michal Simek <michal.simek@xilinx.com>
10 #include <asm/arch/hardware.h>
11 #include <asm/arch/sys_proto.h>
13 #include <linux/delay.h>
21 #define ZYNQMP_BOOTADDR_HIGH_MASK 0xFFFFFFFF
22 #define ZYNQMP_R5_HIVEC_ADDR 0xFFFF0000
23 #define ZYNQMP_R5_LOVEC_ADDR 0x0
24 #define ZYNQMP_RPU_CFG_CPU_HALT_MASK 0x01
25 #define ZYNQMP_RPU_CFG_HIVEC_MASK 0x04
26 #define ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK 0x08
27 #define ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK 0x40
28 #define ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK 0x10
30 #define ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK 0x04
31 #define ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK 0x01
32 #define ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK 0x02
33 #define ZYNQMP_CRLAPB_CPU_R5_CTRL_CLKACT_MASK 0x1000000
35 #define ZYNQMP_TCM_START_ADDRESS 0xFFE00000
36 #define ZYNQMP_TCM_BOTH_SIZE 0x40000
38 #define ZYNQMP_CORE_APU0 0
39 #define ZYNQMP_CORE_APU3 3
40 #define ZYNQMP_CORE_RPU0 4
41 #define ZYNQMP_CORE_RPU1 5
43 #define ZYNQMP_MAX_CORES 6
45 #define ZYNQMP_RPU0_USE_MASK BIT(1)
46 #define ZYNQMP_RPU1_USE_MASK BIT(2)
48 int is_core_valid(unsigned int core)
50 if (core < ZYNQMP_MAX_CORES)
58 puts("Feature is not implemented.\n");
62 static void set_r5_halt_mode(u32 nr, u8 halt, u8 mode)
66 if (mode == LOCK || nr == ZYNQMP_CORE_RPU0) {
67 tmp = readl(&rpu_base->rpu0_cfg);
69 tmp &= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK;
71 tmp |= ZYNQMP_RPU_CFG_CPU_HALT_MASK;
72 writel(tmp, &rpu_base->rpu0_cfg);
75 if (mode == LOCK || nr == ZYNQMP_CORE_RPU1) {
76 tmp = readl(&rpu_base->rpu1_cfg);
78 tmp &= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK;
80 tmp |= ZYNQMP_RPU_CFG_CPU_HALT_MASK;
81 writel(tmp, &rpu_base->rpu1_cfg);
85 static void set_r5_tcm_mode(u8 mode)
89 tmp = readl(&rpu_base->rpu_glbl_ctrl);
91 tmp &= ~ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK;
92 tmp |= ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK |
93 ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK;
95 tmp |= ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK;
96 tmp &= ~(ZYNQMP_RPU_GLBL_CTRL_TCM_COMB_MASK |
97 ZYNQMP_RPU_GLBL_CTRL_SLCLAMP_MASK);
100 writel(tmp, &rpu_base->rpu_glbl_ctrl);
103 static void set_r5_reset(u32 nr, u8 mode)
107 tmp = readl(&crlapb_base->rst_lpd_top);
109 tmp |= (ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK |
110 ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK |
111 ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK);
113 if (nr == ZYNQMP_CORE_RPU0) {
114 tmp |= ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK;
115 if (tmp & ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK)
116 tmp |= ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK;
118 tmp |= ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK;
119 if (tmp & ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK)
120 tmp |= ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK;
124 writel(tmp, &crlapb_base->rst_lpd_top);
127 static void release_r5_reset(u32 nr, u8 mode)
131 tmp = readl(&crlapb_base->rst_lpd_top);
132 if (mode == LOCK || nr == ZYNQMP_CORE_RPU0)
133 tmp &= ~(ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK |
134 ZYNQMP_CRLAPB_RST_LPD_R50_RST_MASK);
136 if (mode == LOCK || nr == ZYNQMP_CORE_RPU1)
137 tmp &= ~(ZYNQMP_CRLAPB_RST_LPD_AMBA_RST_MASK |
138 ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK);
140 writel(tmp, &crlapb_base->rst_lpd_top);
143 static void enable_clock_r5(void)
147 tmp = readl(&crlapb_base->cpu_r5_ctrl);
148 tmp |= ZYNQMP_CRLAPB_CPU_R5_CTRL_CLKACT_MASK;
149 writel(tmp, &crlapb_base->cpu_r5_ctrl);
151 /* Give some delay for clock
156 static int check_r5_mode(void)
160 tmp = readl(&rpu_base->rpu_glbl_ctrl);
161 if (tmp & ZYNQMP_RPU_GLBL_CTRL_SPLIT_LOCK_MASK)
167 int cpu_disable(u32 nr)
169 if (nr <= ZYNQMP_CORE_APU3) {
170 u32 val = readl(&crfapb_base->rst_fpd_apu);
172 writel(val, &crfapb_base->rst_fpd_apu);
174 set_r5_reset(nr, check_r5_mode());
180 int cpu_status(u32 nr)
182 if (nr <= ZYNQMP_CORE_APU3) {
183 u32 addr_low = readl(((u8 *)&apu_base->rvbar_addr0_l) + nr * 8);
184 u32 addr_high = readl(((u8 *)&apu_base->rvbar_addr0_h) +
186 u32 val = readl(&crfapb_base->rst_fpd_apu);
188 printf("APU CPU%d %s - starting address HI: %x, LOW: %x\n",
189 nr, val ? "OFF" : "ON" , addr_high, addr_low);
191 u32 val = readl(&crlapb_base->rst_lpd_top);
192 val &= 1 << (nr - 4);
193 printf("RPU CPU%d %s\n", nr - 4, val ? "OFF" : "ON");
199 static void set_r5_start(u8 high)
203 tmp = readl(&rpu_base->rpu0_cfg);
205 tmp |= ZYNQMP_RPU_CFG_HIVEC_MASK;
207 tmp &= ~ZYNQMP_RPU_CFG_HIVEC_MASK;
208 writel(tmp, &rpu_base->rpu0_cfg);
210 tmp = readl(&rpu_base->rpu1_cfg);
212 tmp |= ZYNQMP_RPU_CFG_HIVEC_MASK;
214 tmp &= ~ZYNQMP_RPU_CFG_HIVEC_MASK;
215 writel(tmp, &rpu_base->rpu1_cfg);
218 static void write_tcm_boot_trampoline(u32 boot_addr)
222 * Boot trampoline is simple ASM code below.
227 * over: ldr r0, =label
231 debug("Write boot trampoline for %x\n", boot_addr);
232 writel(0xea000000, ZYNQMP_TCM_START_ADDRESS);
233 writel(boot_addr, ZYNQMP_TCM_START_ADDRESS + 0x4);
234 writel(0xe59f0004, ZYNQMP_TCM_START_ADDRESS + 0x8);
235 writel(0xe5901000, ZYNQMP_TCM_START_ADDRESS + 0xc);
236 writel(0xe12fff11, ZYNQMP_TCM_START_ADDRESS + 0x10);
237 writel(0x00000004, ZYNQMP_TCM_START_ADDRESS + 0x14); // address for
241 void initialize_tcm(bool mode)
244 set_r5_tcm_mode(LOCK);
245 set_r5_halt_mode(ZYNQMP_CORE_RPU0, HALT, LOCK);
247 release_r5_reset(ZYNQMP_CORE_RPU0, LOCK);
249 set_r5_tcm_mode(SPLIT);
250 set_r5_halt_mode(ZYNQMP_CORE_RPU1, HALT, SPLIT);
252 release_r5_reset(ZYNQMP_CORE_RPU1, SPLIT);
256 static void mark_r5_used(u32 nr, u8 mode)
261 mask = ZYNQMP_RPU0_USE_MASK | ZYNQMP_RPU1_USE_MASK;
264 case ZYNQMP_CORE_RPU0:
265 mask = ZYNQMP_RPU0_USE_MASK;
267 case ZYNQMP_CORE_RPU1:
268 mask = ZYNQMP_RPU1_USE_MASK;
274 zynqmp_mmio_write((ulong)&pmu_base->gen_storage4, mask, mask);
277 int cpu_release(u32 nr, int argc, char *const argv[])
279 if (nr <= ZYNQMP_CORE_APU3) {
280 u64 boot_addr = simple_strtoull(argv[0], NULL, 16);
282 writel((u32)(boot_addr >> 32),
283 ((u8 *)&apu_base->rvbar_addr0_h) + nr * 8);
285 writel((u32)(boot_addr & ZYNQMP_BOOTADDR_HIGH_MASK),
286 ((u8 *)&apu_base->rvbar_addr0_l) + nr * 8);
288 u32 val = readl(&crfapb_base->rst_fpd_apu);
290 writel(val, &crfapb_base->rst_fpd_apu);
293 printf("Invalid number of arguments to release.\n");
294 printf("<addr> <mode>-Start addr lockstep or split\n");
298 u32 boot_addr = hextoul(argv[0], NULL);
299 u32 boot_addr_uniq = 0;
300 if (!(boot_addr == ZYNQMP_R5_LOVEC_ADDR ||
301 boot_addr == ZYNQMP_R5_HIVEC_ADDR)) {
302 printf("Using TCM jump trampoline for address 0x%x\n",
304 /* Save boot address for later usage */
305 boot_addr_uniq = boot_addr;
307 * R5 needs to start from LOVEC at TCM
308 * OCM will be probably occupied by ATF
310 boot_addr = ZYNQMP_R5_LOVEC_ADDR;
314 * Since we don't know where the user may have loaded the image
315 * for an R5 we have to flush all the data cache to ensure
320 if (!strncmp(argv[1], "lockstep", 8)) {
321 printf("R5 lockstep mode\n");
322 set_r5_reset(nr, LOCK);
323 set_r5_tcm_mode(LOCK);
324 set_r5_halt_mode(nr, HALT, LOCK);
325 set_r5_start(boot_addr);
327 release_r5_reset(nr, LOCK);
329 write_tcm_boot_trampoline(boot_addr_uniq);
331 set_r5_halt_mode(nr, RELEASE, LOCK);
332 mark_r5_used(nr, LOCK);
333 } else if (!strncmp(argv[1], "split", 5)) {
334 printf("R5 split mode\n");
335 set_r5_reset(nr, SPLIT);
336 set_r5_tcm_mode(SPLIT);
337 set_r5_halt_mode(nr, HALT, SPLIT);
338 set_r5_start(boot_addr);
340 release_r5_reset(nr, SPLIT);
342 write_tcm_boot_trampoline(boot_addr_uniq);
344 set_r5_halt_mode(nr, RELEASE, SPLIT);
345 mark_r5_used(nr, SPLIT);
347 printf("Unsupported mode\n");