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