xref: /openbmc/u-boot/arch/arm/mach-zynqmp/mp.c (revision d01806a8)
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 
is_core_valid(unsigned int core)40 int is_core_valid(unsigned int core)
41 {
42 	if (core < ZYNQMP_MAX_CORES)
43 		return 1;
44 
45 	return 0;
46 }
47 
cpu_reset(u32 nr)48 int cpu_reset(u32 nr)
49 {
50 	puts("Feature is not implemented.\n");
51 	return 0;
52 }
53 
set_r5_halt_mode(u8 halt,u8 mode)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 
set_r5_tcm_mode(u8 mode)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 
set_r5_reset(u8 mode)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 
release_r5_reset(u8 mode)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 
enable_clock_r5(void)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 
cpu_disable(u32 nr)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 
cpu_status(u32 nr)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 
set_r5_start(u8 high)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 
write_tcm_boot_trampoline(u32 boot_addr)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 
initialize_tcm(bool mode)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 
cpu_release(u32 nr,int argc,char * const argv[])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