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