2 * (C) Copyright 2014 - 2015 Xilinx, Inc.
3 * Michal Simek <michal.simek@xilinx.com>
5 * SPDX-License-Identifier: GPL-2.0+
9 #include <asm/arch/hardware.h>
10 #include <asm/arch/sys_proto.h>
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
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
33 #define ZYNQMP_TCM_START_ADDRESS 0xFFE00000
34 #define ZYNQMP_TCM_BOTH_SIZE 0x40000
36 #define ZYNQMP_CORE_APU0 0
37 #define ZYNQMP_CORE_APU3 3
39 #define ZYNQMP_MAX_CORES 6
41 int is_core_valid(unsigned int core
)
43 if (core
< ZYNQMP_MAX_CORES
)
51 puts("Feature is not implemented.\n");
55 static void set_r5_halt_mode(u8 halt
, u8 mode
)
59 tmp
= readl(&rpu_base
->rpu0_cfg
);
61 tmp
&= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK
;
63 tmp
|= ZYNQMP_RPU_CFG_CPU_HALT_MASK
;
64 writel(tmp
, &rpu_base
->rpu0_cfg
);
67 tmp
= readl(&rpu_base
->rpu1_cfg
);
69 tmp
&= ~ZYNQMP_RPU_CFG_CPU_HALT_MASK
;
71 tmp
|= ZYNQMP_RPU_CFG_CPU_HALT_MASK
;
72 writel(tmp
, &rpu_base
->rpu1_cfg
);
76 static void set_r5_tcm_mode(u8 mode
)
80 tmp
= readl(&rpu_base
->rpu_glbl_ctrl
);
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
;
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
);
91 writel(tmp
, &rpu_base
->rpu_glbl_ctrl
);
94 static void set_r5_reset(u8 mode
)
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
);
103 tmp
|= ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK
;
105 writel(tmp
, &crlapb_base
->rst_lpd_top
);
108 static void release_r5_reset(u8 mode
)
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
);
117 tmp
&= ~ZYNQMP_CRLAPB_RST_LPD_R51_RST_MASK
;
119 writel(tmp
, &crlapb_base
->rst_lpd_top
);
122 static void enable_clock_r5(void)
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
);
130 /* Give some delay for clock
135 int cpu_disable(int nr
)
137 if (nr
>= ZYNQMP_CORE_APU0
&& nr
<= ZYNQMP_CORE_APU3
) {
138 u32 val
= readl(&crfapb_base
->rst_fpd_apu
);
140 writel(val
, &crfapb_base
->rst_fpd_apu
);
148 int cpu_status(int nr
)
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
) +
154 u32 val
= readl(&crfapb_base
->rst_fpd_apu
);
156 printf("APU CPU%d %s - starting address HI: %x, LOW: %x\n",
157 nr
, val
? "OFF" : "ON" , addr_high
, addr_low
);
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");
167 static void set_r5_start(u8 high
)
171 tmp
= readl(&rpu_base
->rpu0_cfg
);
173 tmp
|= ZYNQMP_RPU_CFG_HIVEC_MASK
;
175 tmp
&= ~ZYNQMP_RPU_CFG_HIVEC_MASK
;
176 writel(tmp
, &rpu_base
->rpu0_cfg
);
178 tmp
= readl(&rpu_base
->rpu1_cfg
);
180 tmp
|= ZYNQMP_RPU_CFG_HIVEC_MASK
;
182 tmp
&= ~ZYNQMP_RPU_CFG_HIVEC_MASK
;
183 writel(tmp
, &rpu_base
->rpu1_cfg
);
186 static void write_tcm_boot_trampoline(u32 boot_addr
)
190 * Boot trampoline is simple ASM code below.
195 * over: ldr r0, =label
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
209 void initialize_tcm(bool mode
)
212 set_r5_tcm_mode(LOCK
);
213 set_r5_halt_mode(HALT
, LOCK
);
215 release_r5_reset(LOCK
);
217 set_r5_tcm_mode(SPLIT
);
218 set_r5_halt_mode(HALT
, SPLIT
);
220 release_r5_reset(SPLIT
);
224 int cpu_release(int nr
, int argc
, char * const argv
[])
226 if (nr
>= ZYNQMP_CORE_APU0
&& nr
<= ZYNQMP_CORE_APU3
) {
227 u64 boot_addr
= simple_strtoull(argv
[0], NULL
, 16);
229 writel((u32
)(boot_addr
>> 32),
230 ((u8
*)&apu_base
->rvbar_addr0_h
) + nr
* 8);
232 writel((u32
)(boot_addr
& ZYNQMP_BOOTADDR_HIGH_MASK
),
233 ((u8
*)&apu_base
->rvbar_addr0_l
) + nr
* 8);
235 u32 val
= readl(&crfapb_base
->rst_fpd_apu
);
237 writel(val
, &crfapb_base
->rst_fpd_apu
);
240 printf("Invalid number of arguments to release.\n");
241 printf("<addr> <mode>-Start addr lockstep or split\n");
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",
251 /* Save boot address for later usage */
252 boot_addr_uniq
= boot_addr
;
254 * R5 needs to start from LOVEC at TCM
255 * OCM will be probably occupied by ATF
257 boot_addr
= ZYNQMP_R5_LOVEC_ADDR
;
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
);
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
);
274 release_r5_reset(SPLIT
);
275 write_tcm_boot_trampoline(boot_addr_uniq
);
276 set_r5_halt_mode(RELEASE
, SPLIT
);
278 printf("Unsupported mode\n");