]>
Commit | Line | Data |
---|---|---|
ec2b74ff | 1 | /* |
e81241af | 2 | * Copyright 2008-2011 Freescale Semiconductor, Inc. |
ec2b74ff | 3 | * |
1a459660 | 4 | * SPDX-License-Identifier: GPL-2.0+ |
ec2b74ff KG |
5 | */ |
6 | ||
7 | #include <common.h> | |
8 | #include <asm/processor.h> | |
9 | #include <ioports.h> | |
dd6c910a | 10 | #include <lmb.h> |
ec2b74ff | 11 | #include <asm/io.h> |
c7259086 | 12 | #include <asm/mmu.h> |
39a7e7fd | 13 | #include <asm/fsl_law.h> |
5614e71b | 14 | #include <fsl_ddr_sdram.h> |
ec2b74ff KG |
15 | #include "mp.h" |
16 | ||
17 | DECLARE_GLOBAL_DATA_PTR; | |
eb539412 | 18 | u32 fsl_ddr_get_intl3r(void); |
ec2b74ff | 19 | |
ffd06e02 YS |
20 | extern u32 __spin_table[]; |
21 | ||
ec2b74ff KG |
22 | u32 get_my_id() |
23 | { | |
24 | return mfspr(SPRN_PIR); | |
25 | } | |
26 | ||
9d64c6bb AS |
27 | /* |
28 | * Determine if U-Boot should keep secondary cores in reset, or let them out | |
29 | * of reset and hold them in a spinloop | |
30 | */ | |
31 | int hold_cores_in_reset(int verbose) | |
32 | { | |
62a3b7dd | 33 | /* Default to no, overridden by 'y', 'yes', 'Y', 'Yes', or '1' */ |
bfebc8c9 | 34 | if (env_get_yesno("mp_holdoff") == 1) { |
9d64c6bb AS |
35 | if (verbose) { |
36 | puts("Secondary cores are being held in reset.\n"); | |
37 | puts("See 'mp_holdoff' environment variable\n"); | |
38 | } | |
39 | ||
40 | return 1; | |
41 | } | |
42 | ||
43 | return 0; | |
44 | } | |
45 | ||
ec2b74ff KG |
46 | int cpu_reset(int nr) |
47 | { | |
680c613a | 48 | volatile ccsr_pic_t *pic = (void *)(CONFIG_SYS_MPC8xxx_PIC_ADDR); |
ec2b74ff | 49 | out_be32(&pic->pir, 1 << nr); |
c840d26c | 50 | /* the dummy read works around an errata on early 85xx MP PICs */ |
ec2b74ff KG |
51 | (void)in_be32(&pic->pir); |
52 | out_be32(&pic->pir, 0x0); | |
53 | ||
54 | return 0; | |
55 | } | |
56 | ||
57 | int cpu_status(int nr) | |
58 | { | |
59 | u32 *table, id = get_my_id(); | |
60 | ||
9d64c6bb AS |
61 | if (hold_cores_in_reset(1)) |
62 | return 0; | |
63 | ||
ec2b74ff | 64 | if (nr == id) { |
ffd06e02 | 65 | table = (u32 *)&__spin_table; |
348753d4 | 66 | printf("table base @ 0x%p\n", table); |
0c9ab437 YS |
67 | } else if (is_core_disabled(nr)) { |
68 | puts("Disabled\n"); | |
ec2b74ff | 69 | } else { |
ffd06e02 | 70 | table = (u32 *)&__spin_table + nr * NUM_BOOT_ENTRY; |
ec2b74ff KG |
71 | printf("Running on cpu %d\n", id); |
72 | printf("\n"); | |
348753d4 | 73 | printf("table @ 0x%p\n", table); |
79679d80 | 74 | printf(" addr - 0x%08x\n", table[BOOT_ENTRY_ADDR_LOWER]); |
79679d80 | 75 | printf(" r3 - 0x%08x\n", table[BOOT_ENTRY_R3_LOWER]); |
3f0997b3 | 76 | printf(" pir - 0x%08x\n", table[BOOT_ENTRY_PIR]); |
ec2b74ff KG |
77 | } |
78 | ||
79 | return 0; | |
80 | } | |
81 | ||
a9c3ac78 | 82 | #ifdef CONFIG_FSL_CORENET |
4194b366 KG |
83 | int cpu_disable(int nr) |
84 | { | |
a9c3ac78 KG |
85 | volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); |
86 | ||
87 | setbits_be32(&gur->coredisrl, 1 << nr); | |
88 | ||
89 | return 0; | |
90 | } | |
8f3a7fa4 KG |
91 | |
92 | int is_core_disabled(int nr) { | |
93 | ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); | |
94 | u32 coredisrl = in_be32(&gur->coredisrl); | |
95 | ||
96 | return (coredisrl & (1 << nr)); | |
97 | } | |
a9c3ac78 KG |
98 | #else |
99 | int cpu_disable(int nr) | |
100 | { | |
101 | volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); | |
102 | ||
103 | switch (nr) { | |
104 | case 0: | |
105 | setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_CPU0); | |
106 | break; | |
107 | case 1: | |
108 | setbits_be32(&gur->devdisr, MPC85xx_DEVDISR_CPU1); | |
109 | break; | |
110 | default: | |
111 | printf("Invalid cpu number for disable %d\n", nr); | |
112 | return 1; | |
113 | } | |
114 | ||
115 | return 0; | |
4194b366 | 116 | } |
8f3a7fa4 KG |
117 | |
118 | int is_core_disabled(int nr) { | |
119 | ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); | |
120 | u32 devdisr = in_be32(&gur->devdisr); | |
121 | ||
122 | switch (nr) { | |
123 | case 0: | |
124 | return (devdisr & MPC85xx_DEVDISR_CPU0); | |
125 | case 1: | |
126 | return (devdisr & MPC85xx_DEVDISR_CPU1); | |
127 | default: | |
128 | printf("Invalid cpu number for disable %d\n", nr); | |
129 | } | |
130 | ||
131 | return 0; | |
132 | } | |
a9c3ac78 | 133 | #endif |
4194b366 | 134 | |
79679d80 KG |
135 | static u8 boot_entry_map[4] = { |
136 | 0, | |
137 | BOOT_ENTRY_PIR, | |
138 | BOOT_ENTRY_R3_LOWER, | |
79679d80 KG |
139 | }; |
140 | ||
54841ab5 | 141 | int cpu_release(int nr, int argc, char * const argv[]) |
ec2b74ff | 142 | { |
ffd06e02 | 143 | u32 i, val, *table = (u32 *)&__spin_table + nr * NUM_BOOT_ENTRY; |
79679d80 | 144 | u64 boot_addr; |
ec2b74ff | 145 | |
9d64c6bb AS |
146 | if (hold_cores_in_reset(1)) |
147 | return 0; | |
148 | ||
ec2b74ff KG |
149 | if (nr == get_my_id()) { |
150 | printf("Invalid to release the boot core.\n\n"); | |
151 | return 1; | |
152 | } | |
153 | ||
79679d80 | 154 | if (argc != 4) { |
ec2b74ff KG |
155 | printf("Invalid number of arguments to release.\n\n"); |
156 | return 1; | |
157 | } | |
158 | ||
79679d80 | 159 | boot_addr = simple_strtoull(argv[0], NULL, 16); |
79679d80 | 160 | |
3f0997b3 YS |
161 | /* handle pir, r3 */ |
162 | for (i = 1; i < 3; i++) { | |
ec2b74ff | 163 | if (argv[i][0] != '-') { |
79679d80 | 164 | u8 entry = boot_entry_map[i]; |
ec2b74ff | 165 | val = simple_strtoul(argv[i], NULL, 16); |
79679d80 | 166 | table[entry] = val; |
ec2b74ff KG |
167 | } |
168 | } | |
169 | ||
79679d80 | 170 | table[BOOT_ENTRY_ADDR_UPPER] = (u32)(boot_addr >> 32); |
cf6cc014 KG |
171 | |
172 | /* ensure all table updates complete before final address write */ | |
173 | eieio(); | |
174 | ||
79679d80 | 175 | table[BOOT_ENTRY_ADDR_LOWER] = (u32)(boot_addr & 0xffffffff); |
ec2b74ff KG |
176 | |
177 | return 0; | |
178 | } | |
179 | ||
eb539412 | 180 | u32 determine_mp_bootpg(unsigned int *pagesize) |
c840d26c | 181 | { |
eb539412 YS |
182 | u32 bootpg; |
183 | #ifdef CONFIG_SYS_FSL_ERRATUM_A004468 | |
184 | u32 svr = get_svr(); | |
185 | u32 granule_size, check; | |
186 | struct law_entry e; | |
187 | #endif | |
188 | ||
ffd06e02 YS |
189 | |
190 | /* use last 4K of mapped memory */ | |
191 | bootpg = ((gd->ram_size > CONFIG_MAX_MEM_MAPPED) ? | |
192 | CONFIG_MAX_MEM_MAPPED : gd->ram_size) + | |
193 | CONFIG_SYS_SDRAM_BASE - 4096; | |
eb539412 YS |
194 | if (pagesize) |
195 | *pagesize = 4096; | |
196 | ||
197 | #ifdef CONFIG_SYS_FSL_ERRATUM_A004468 | |
198 | /* | |
199 | * Erratum A004468 has two parts. The 3-way interleaving applies to T4240, | |
200 | * to be fixed in rev 2.0. The 2-way interleaving applies to many SoCs. But | |
201 | * the way boot page chosen in u-boot avoids hitting this erratum. So only | |
202 | * thw workaround for 3-way interleaving is needed. | |
203 | * | |
204 | * To make sure boot page translation works with 3-Way DDR interleaving | |
205 | * enforce a check for the following constrains | |
206 | * 8K granule size requires BRSIZE=8K and | |
207 | * bootpg >> log2(BRSIZE) %3 == 1 | |
208 | * 4K and 1K granule size requires BRSIZE=4K and | |
209 | * bootpg >> log2(BRSIZE) %3 == 0 | |
210 | */ | |
211 | if (SVR_SOC_VER(svr) == SVR_T4240 && SVR_MAJ(svr) < 2) { | |
212 | e = find_law(bootpg); | |
213 | switch (e.trgt_id) { | |
214 | case LAW_TRGT_IF_DDR_INTLV_123: | |
215 | granule_size = fsl_ddr_get_intl3r() & 0x1f; | |
216 | if (granule_size == FSL_DDR_3WAY_8KB_INTERLEAVING) { | |
217 | if (pagesize) | |
218 | *pagesize = 8192; | |
219 | bootpg &= 0xffffe000; /* align to 8KB */ | |
220 | check = bootpg >> 13; | |
221 | while ((check % 3) != 1) | |
222 | check--; | |
223 | bootpg = check << 13; | |
224 | debug("Boot page (8K) at 0x%08x\n", bootpg); | |
225 | break; | |
226 | } else { | |
227 | bootpg &= 0xfffff000; /* align to 4KB */ | |
228 | check = bootpg >> 12; | |
229 | while ((check % 3) != 0) | |
230 | check--; | |
231 | bootpg = check << 12; | |
232 | debug("Boot page (4K) at 0x%08x\n", bootpg); | |
233 | } | |
234 | break; | |
235 | default: | |
236 | break; | |
237 | } | |
238 | } | |
239 | #endif /* CONFIG_SYS_FSL_ERRATUM_A004468 */ | |
c840d26c | 240 | |
eb539412 | 241 | return bootpg; |
c840d26c KG |
242 | } |
243 | ||
ffd06e02 | 244 | phys_addr_t get_spin_phys_addr(void) |
5ccd29c3 | 245 | { |
ffd06e02 | 246 | return virt_to_phys(&__spin_table); |
ec2b74ff KG |
247 | } |
248 | ||
39a7e7fd | 249 | #ifdef CONFIG_FSL_CORENET |
eb539412 | 250 | static void plat_mp_up(unsigned long bootpg, unsigned int pagesize) |
39a7e7fd | 251 | { |
eb539412 | 252 | u32 cpu_up_mask, whoami, brsize = LAW_SIZE_4K; |
ffd06e02 | 253 | u32 *table = (u32 *)&__spin_table; |
39a7e7fd KG |
254 | volatile ccsr_gur_t *gur; |
255 | volatile ccsr_local_t *ccm; | |
256 | volatile ccsr_rcpm_t *rcpm; | |
257 | volatile ccsr_pic_t *pic; | |
258 | int timeout = 10; | |
fbb9ecf7 | 259 | u32 mask = cpu_mask(); |
39a7e7fd KG |
260 | struct law_entry e; |
261 | ||
262 | gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); | |
263 | ccm = (void *)(CONFIG_SYS_FSL_CORENET_CCM_ADDR); | |
264 | rcpm = (void *)(CONFIG_SYS_FSL_CORENET_RCPM_ADDR); | |
680c613a | 265 | pic = (void *)(CONFIG_SYS_MPC8xxx_PIC_ADDR); |
39a7e7fd | 266 | |
39a7e7fd KG |
267 | whoami = in_be32(&pic->whoami); |
268 | cpu_up_mask = 1 << whoami; | |
269 | out_be32(&ccm->bstrl, bootpg); | |
270 | ||
271 | e = find_law(bootpg); | |
eb539412 YS |
272 | /* pagesize is only 4K or 8K */ |
273 | if (pagesize == 8192) | |
274 | brsize = LAW_SIZE_8K; | |
275 | out_be32(&ccm->bstrar, LAW_EN | e.trgt_id << 20 | brsize); | |
276 | debug("BRSIZE is 0x%x\n", brsize); | |
f5ecc6e0 DL |
277 | |
278 | /* readback to sync write */ | |
279 | in_be32(&ccm->bstrar); | |
39a7e7fd KG |
280 | |
281 | /* disable time base at the platform */ | |
282 | out_be32(&rcpm->ctbenrl, cpu_up_mask); | |
283 | ||
fbb9ecf7 | 284 | out_be32(&gur->brrl, mask); |
39a7e7fd KG |
285 | |
286 | /* wait for everyone */ | |
287 | while (timeout) { | |
fbb9ecf7 TT |
288 | unsigned int i, cpu, nr_cpus = cpu_numcores(); |
289 | ||
290 | for_each_cpu(i, cpu, nr_cpus, mask) { | |
291 | if (table[cpu * NUM_BOOT_ENTRY + BOOT_ENTRY_ADDR_LOWER]) | |
292 | cpu_up_mask |= (1 << cpu); | |
293 | } | |
39a7e7fd | 294 | |
fbb9ecf7 | 295 | if ((cpu_up_mask & mask) == mask) |
39a7e7fd KG |
296 | break; |
297 | ||
298 | udelay(100); | |
299 | timeout--; | |
300 | } | |
301 | ||
302 | if (timeout == 0) | |
303 | printf("CPU up timeout. CPU up mask is %x should be %x\n", | |
fbb9ecf7 | 304 | cpu_up_mask, mask); |
39a7e7fd KG |
305 | |
306 | /* enable time base at the platform */ | |
307 | out_be32(&rcpm->ctbenrl, 0); | |
7afc45ad KG |
308 | |
309 | /* readback to sync write */ | |
310 | in_be32(&rcpm->ctbenrl); | |
311 | ||
39a7e7fd KG |
312 | mtspr(SPRN_TBWU, 0); |
313 | mtspr(SPRN_TBWL, 0); | |
7afc45ad | 314 | |
fbb9ecf7 | 315 | out_be32(&rcpm->ctbenrl, mask); |
5ccd29c3 PT |
316 | |
317 | #ifdef CONFIG_MPC8xxx_DISABLE_BPTR | |
318 | /* | |
319 | * Disabling Boot Page Translation allows the memory region 0xfffff000 | |
320 | * to 0xffffffff to be used normally. Leaving Boot Page Translation | |
321 | * enabled remaps 0xfffff000 to SDRAM which makes that memory region | |
322 | * unusable for normal operation but it does allow OSes to easily | |
323 | * reset a processor core to put it back into U-Boot's spinloop. | |
324 | */ | |
e81241af | 325 | clrbits_be32(&ccm->bstrar, LAW_EN); |
5ccd29c3 | 326 | #endif |
39a7e7fd KG |
327 | } |
328 | #else | |
eb539412 | 329 | static void plat_mp_up(unsigned long bootpg, unsigned int pagesize) |
ec2b74ff KG |
330 | { |
331 | u32 up, cpu_up_mask, whoami; | |
ffd06e02 | 332 | u32 *table = (u32 *)&__spin_table; |
ec2b74ff | 333 | volatile u32 bpcr; |
6d0f6bcf JCPV |
334 | volatile ccsr_local_ecm_t *ecm = (void *)(CONFIG_SYS_MPC85xx_ECM_ADDR); |
335 | volatile ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); | |
680c613a | 336 | volatile ccsr_pic_t *pic = (void *)(CONFIG_SYS_MPC8xxx_PIC_ADDR); |
ec2b74ff KG |
337 | u32 devdisr; |
338 | int timeout = 10; | |
339 | ||
340 | whoami = in_be32(&pic->whoami); | |
341 | out_be32(&ecm->bptr, 0x80000000 | (bootpg >> 12)); | |
342 | ||
343 | /* disable time base at the platform */ | |
344 | devdisr = in_be32(&gur->devdisr); | |
345 | if (whoami) | |
346 | devdisr |= MPC85xx_DEVDISR_TB0; | |
347 | else | |
348 | devdisr |= MPC85xx_DEVDISR_TB1; | |
349 | out_be32(&gur->devdisr, devdisr); | |
350 | ||
351 | /* release the hounds */ | |
0e870980 | 352 | up = ((1 << cpu_numcores()) - 1); |
ec2b74ff KG |
353 | bpcr = in_be32(&ecm->eebpcr); |
354 | bpcr |= (up << 24); | |
355 | out_be32(&ecm->eebpcr, bpcr); | |
356 | asm("sync; isync; msync"); | |
357 | ||
358 | cpu_up_mask = 1 << whoami; | |
359 | /* wait for everyone */ | |
360 | while (timeout) { | |
361 | int i; | |
0e870980 | 362 | for (i = 0; i < cpu_numcores(); i++) { |
97b3ecb5 | 363 | if (table[i * NUM_BOOT_ENTRY + BOOT_ENTRY_ADDR_LOWER]) |
ec2b74ff KG |
364 | cpu_up_mask |= (1 << i); |
365 | }; | |
366 | ||
367 | if ((cpu_up_mask & up) == up) | |
368 | break; | |
369 | ||
370 | udelay(100); | |
371 | timeout--; | |
372 | } | |
373 | ||
97b3ecb5 KG |
374 | if (timeout == 0) |
375 | printf("CPU up timeout. CPU up mask is %x should be %x\n", | |
376 | cpu_up_mask, up); | |
377 | ||
ec2b74ff KG |
378 | /* enable time base at the platform */ |
379 | if (whoami) | |
380 | devdisr |= MPC85xx_DEVDISR_TB1; | |
381 | else | |
382 | devdisr |= MPC85xx_DEVDISR_TB0; | |
383 | out_be32(&gur->devdisr, devdisr); | |
7afc45ad KG |
384 | |
385 | /* readback to sync write */ | |
386 | in_be32(&gur->devdisr); | |
387 | ||
ec2b74ff KG |
388 | mtspr(SPRN_TBWU, 0); |
389 | mtspr(SPRN_TBWL, 0); | |
390 | ||
391 | devdisr &= ~(MPC85xx_DEVDISR_TB0 | MPC85xx_DEVDISR_TB1); | |
392 | out_be32(&gur->devdisr, devdisr); | |
5ccd29c3 PT |
393 | |
394 | #ifdef CONFIG_MPC8xxx_DISABLE_BPTR | |
395 | /* | |
396 | * Disabling Boot Page Translation allows the memory region 0xfffff000 | |
397 | * to 0xffffffff to be used normally. Leaving Boot Page Translation | |
398 | * enabled remaps 0xfffff000 to SDRAM which makes that memory region | |
399 | * unusable for normal operation but it does allow OSes to easily | |
400 | * reset a processor core to put it back into U-Boot's spinloop. | |
401 | */ | |
402 | clrbits_be32(&ecm->bptr, 0x80000000); | |
403 | #endif | |
ec2b74ff | 404 | } |
39a7e7fd | 405 | #endif |
ec2b74ff | 406 | |
dd6c910a KG |
407 | void cpu_mp_lmb_reserve(struct lmb *lmb) |
408 | { | |
eb539412 | 409 | u32 bootpg = determine_mp_bootpg(NULL); |
dd6c910a KG |
410 | |
411 | lmb_reserve(lmb, bootpg, 4096); | |
412 | } | |
413 | ||
ec2b74ff KG |
414 | void setup_mp(void) |
415 | { | |
ffd06e02 YS |
416 | extern u32 __secondary_start_page; |
417 | extern u32 __bootpg_addr, __spin_table_addr, __second_half_boot_page; | |
eb539412 | 418 | |
ffd06e02 YS |
419 | int i; |
420 | ulong fixup = (u32)&__secondary_start_page; | |
eb539412 YS |
421 | u32 bootpg, bootpg_map, pagesize; |
422 | ||
423 | bootpg = determine_mp_bootpg(&pagesize); | |
424 | ||
425 | /* | |
426 | * pagesize is only 4K or 8K | |
427 | * we only use the last 4K of boot page | |
428 | * bootpg_map saves the address for the boot page | |
429 | * 8K is used for the workaround of 3-way DDR interleaving | |
430 | */ | |
431 | ||
432 | bootpg_map = bootpg; | |
433 | ||
434 | if (pagesize == 8192) | |
435 | bootpg += 4096; /* use 2nd half */ | |
ec2b74ff | 436 | |
9d64c6bb AS |
437 | /* Some OSes expect secondary cores to be held in reset */ |
438 | if (hold_cores_in_reset(0)) | |
439 | return; | |
440 | ||
ffd06e02 YS |
441 | /* |
442 | * Store the bootpg's cache-able half address for use by secondary | |
443 | * CPU cores to continue to boot | |
444 | */ | |
445 | __bootpg_addr = (u32)virt_to_phys(&__second_half_boot_page); | |
446 | ||
447 | /* Store spin table's physical address for use by secondary cores */ | |
448 | __spin_table_addr = (u32)get_spin_phys_addr(); | |
449 | ||
450 | /* flush bootpg it before copying invalidate any staled cacheline */ | |
451 | flush_cache(bootpg, 4096); | |
5ccd29c3 | 452 | |
c7259086 | 453 | /* look for the tlb covering the reset page, there better be one */ |
ffd06e02 | 454 | i = find_tlb_idx((void *)CONFIG_BPTR_VIRT_ADDR, 1); |
ec2b74ff | 455 | |
c7259086 KG |
456 | /* we found a match */ |
457 | if (i != -1) { | |
458 | /* map reset page to bootpg so we can copy code there */ | |
459 | disable_tlb(i); | |
39a7e7fd | 460 | |
5ccd29c3 | 461 | set_tlb(1, CONFIG_BPTR_VIRT_ADDR, bootpg, /* tlb, epn, rpn */ |
abc76eb6 | 462 | MAS3_SX|MAS3_SW|MAS3_SR, MAS2_I|MAS2_G, /* perms, wimge */ |
c7259086 KG |
463 | 0, i, BOOKE_PAGESZ_4K, 1); /* ts, esel, tsize, iprot */ |
464 | ||
5ccd29c3 PT |
465 | memcpy((void *)CONFIG_BPTR_VIRT_ADDR, (void *)fixup, 4096); |
466 | ||
eb539412 | 467 | plat_mp_up(bootpg_map, pagesize); |
c7259086 KG |
468 | } else { |
469 | puts("WARNING: No reset page TLB. " | |
470 | "Skipping secondary core setup\n"); | |
471 | } | |
ec2b74ff | 472 | } |