]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/pseries/platform_chrp.c
[PowerPC64LE] LE bringup work: locore / machdep / platform
[FreeBSD/FreeBSD.git] / sys / powerpc / pseries / platform_chrp.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2008 Marcel Moolenaar
5  * Copyright (c) 2009 Nathan Whitehorn
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/endian.h>
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/bus.h>
38 #include <sys/pcpu.h>
39 #include <sys/proc.h>
40 #include <sys/sched.h>
41 #include <sys/smp.h>
42 #include <vm/vm.h>
43 #include <vm/pmap.h>
44
45 #include <machine/bus.h>
46 #include <machine/cpu.h>
47 #include <machine/hid.h>
48 #include <machine/platformvar.h>
49 #include <machine/rtas.h>
50 #include <machine/smp.h>
51 #include <machine/spr.h>
52 #include <machine/trap.h>
53
54 #include <dev/ofw/openfirm.h>
55 #include <machine/ofw_machdep.h>
56
57 #include "platform_if.h"
58
59 #ifdef SMP
60 extern void *ap_pcpu;
61 #endif
62
63 #ifdef __powerpc64__
64 static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
65 #endif
66
67 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
68
69 static int chrp_probe(platform_t);
70 static int chrp_attach(platform_t);
71 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
72     struct mem_region *avail, int *availsz);
73 static vm_offset_t chrp_real_maxaddr(platform_t);
74 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
75 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
76 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
77 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
78 static void chrp_smp_ap_init(platform_t);
79 static int chrp_cpuref_init(void);
80 #ifdef SMP
81 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
82 static void chrp_smp_probe_threads(platform_t plat);
83 static struct cpu_group *chrp_smp_topo(platform_t plat);
84 #endif
85 static void chrp_reset(platform_t);
86 #ifdef __powerpc64__
87 #include "phyp-hvcall.h"
88 static void phyp_cpu_idle(sbintime_t sbt);
89 #endif
90
91 static struct cpuref platform_cpuref[MAXCPU];
92 static int platform_cpuref_cnt;
93 static int platform_cpuref_valid;
94
95 static platform_method_t chrp_methods[] = {
96         PLATFORMMETHOD(platform_probe,          chrp_probe),
97         PLATFORMMETHOD(platform_attach,         chrp_attach),
98         PLATFORMMETHOD(platform_mem_regions,    chrp_mem_regions),
99         PLATFORMMETHOD(platform_real_maxaddr,   chrp_real_maxaddr),
100         PLATFORMMETHOD(platform_timebase_freq,  chrp_timebase_freq),
101
102         PLATFORMMETHOD(platform_smp_ap_init,    chrp_smp_ap_init),
103         PLATFORMMETHOD(platform_smp_first_cpu,  chrp_smp_first_cpu),
104         PLATFORMMETHOD(platform_smp_next_cpu,   chrp_smp_next_cpu),
105         PLATFORMMETHOD(platform_smp_get_bsp,    chrp_smp_get_bsp),
106 #ifdef SMP
107         PLATFORMMETHOD(platform_smp_start_cpu,  chrp_smp_start_cpu),
108         PLATFORMMETHOD(platform_smp_probe_threads,      chrp_smp_probe_threads),
109         PLATFORMMETHOD(platform_smp_topo,       chrp_smp_topo),
110 #endif
111
112         PLATFORMMETHOD(platform_reset,          chrp_reset),
113         { 0, 0 }
114 };
115
116 static platform_def_t chrp_platform = {
117         "chrp",
118         chrp_methods,
119         0
120 };
121
122 PLATFORM_DEF(chrp_platform);
123
124 static int
125 chrp_probe(platform_t plat)
126 {
127         if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
128                 return (BUS_PROBE_GENERIC);
129
130         return (ENXIO);
131 }
132
133 static int
134 chrp_attach(platform_t plat)
135 {
136         int quiesce;
137 #ifdef __powerpc64__
138         int i;
139
140         /* XXX: check for /rtas/ibm,hypertas-functions? */
141         if (!(mfmsr() & PSL_HV)) {
142                 struct mem_region *phys, *avail;
143                 int nphys, navail;
144                 vm_offset_t off;
145
146                 mem_regions(&phys, &nphys, &avail, &navail);
147
148                 realmaxaddr = 0;
149                 for (i = 0; i < nphys; i++) {
150                         off = phys[i].mr_start + phys[i].mr_size;
151                         realmaxaddr = MAX(off, realmaxaddr);
152                 }
153
154                 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
155                 cpu_idle_hook = phyp_cpu_idle;
156
157                 /* Set up important VPA fields */
158                 for (i = 0; i < MAXCPU; i++) {
159                         /* First two: VPA size */
160                         splpar_vpa[i][4] =
161                             (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
162                         splpar_vpa[i][5] =
163                             (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
164                         splpar_vpa[i][0xba] = 1;        /* Maintain FPRs */
165                         splpar_vpa[i][0xbb] = 1;        /* Maintain PMCs */
166                         splpar_vpa[i][0xfc] = 0xff;     /* Maintain full SLB */
167                         splpar_vpa[i][0xfd] = 0xff;
168                         splpar_vpa[i][0xff] = 1;        /* Maintain Altivec */
169                 }
170                 mb();
171
172                 /* Set up hypervisor CPU stuff */
173                 chrp_smp_ap_init(plat);
174         }
175 #endif
176         chrp_cpuref_init();
177
178         /* Some systems (e.g. QEMU) need Open Firmware to stand down */
179         quiesce = 1;
180         TUNABLE_INT_FETCH("debug.quiesce_ofw", &quiesce);
181         if (quiesce)
182                 ofw_quiesce();
183
184         return (0);
185 }
186
187 static int
188 parse_drconf_memory(struct mem_region *ofmem, int *msz,
189                     struct mem_region *ofavail, int *asz)
190 {
191         phandle_t phandle;
192         vm_offset_t base;
193         int i, idx, len, lasz, lmsz, res;
194         uint32_t flags, lmb_size[2];
195         uint32_t *dmem;
196
197         lmsz = *msz;
198         lasz = *asz;
199
200         phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
201         if (phandle == -1)
202                 /* No drconf node, return. */
203                 return (0);
204
205         res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
206             sizeof(lmb_size));
207         if (res == -1)
208                 return (0);
209         printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
210
211         /* Parse the /ibm,dynamic-memory.
212            The first position gives the # of entries. The next two words
213            reflect the address of the memory block. The next four words are
214            the DRC index, reserved, list index and flags.
215            (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
216            
217             #el  Addr   DRC-idx  res   list-idx  flags
218            -------------------------------------------------
219            | 4 |   8   |   4   |   4   |   4   |   4   |....
220            -------------------------------------------------
221         */
222
223         len = OF_getproplen(phandle, "ibm,dynamic-memory");
224         if (len > 0) {
225                 /* We have to use a variable length array on the stack
226                    since we have very limited stack space.
227                 */
228                 cell_t arr[len/sizeof(cell_t)];
229
230                 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
231                     sizeof(arr));
232                 if (res == -1)
233                         return (0);
234
235                 /* Number of elements */
236                 idx = arr[0];
237
238                 /* First address, in arr[1], arr[2]*/
239                 dmem = &arr[1];
240
241                 for (i = 0; i < idx; i++) {
242                         base = ((uint64_t)dmem[0] << 32) + dmem[1];
243                         dmem += 4;
244                         flags = dmem[1];
245                         /* Use region only if available and not reserved. */
246                         if ((flags & 0x8) && !(flags & 0x80)) {
247                                 ofmem[lmsz].mr_start = base;
248                                 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
249                                 ofavail[lasz].mr_start = base;
250                                 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
251                                 lmsz++;
252                                 lasz++;
253                         }
254                         dmem += 2;
255                 }
256         }
257
258         *msz = lmsz;
259         *asz = lasz;
260
261         return (1);
262 }
263
264 void
265 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
266     struct mem_region *avail, int *availsz)
267 {
268         vm_offset_t maxphysaddr;
269         int i;
270
271         ofw_mem_regions(phys, physsz, avail, availsz);
272         parse_drconf_memory(phys, physsz, avail, availsz);
273
274         /*
275          * On some firmwares (SLOF), some memory may be marked available that
276          * doesn't actually exist. This manifests as an extension of the last
277          * available segment past the end of physical memory, so truncate that
278          * one.
279          */
280         maxphysaddr = 0;
281         for (i = 0; i < *physsz; i++)
282                 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
283                         maxphysaddr = phys[i].mr_start + phys[i].mr_size;
284
285         for (i = 0; i < *availsz; i++)
286                 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
287                         avail[i].mr_size = maxphysaddr - avail[i].mr_start;
288 }
289
290 static vm_offset_t
291 chrp_real_maxaddr(platform_t plat)
292 {
293         return (realmaxaddr);
294 }
295
296 static u_long
297 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
298 {
299         phandle_t cpus, cpunode;
300         int32_t ticks = -1;
301         int res;
302         char buf[8];
303
304         cpus = OF_finddevice("/cpus");
305         if (cpus == -1)
306                 panic("CPU tree not found on Open Firmware\n");
307
308         for (cpunode = OF_child(cpus); cpunode != 0; cpunode = OF_peer(cpunode)) {
309                 res = OF_getprop(cpunode, "device_type", buf, sizeof(buf));
310                 if (res > 0 && strcmp(buf, "cpu") == 0)
311                         break;
312         }
313         if (cpunode <= 0)
314                 panic("CPU node not found on Open Firmware\n");
315
316         OF_getencprop(cpunode, "timebase-frequency", &ticks, sizeof(ticks));
317
318         if (ticks <= 0)
319                 panic("Unable to determine timebase frequency!");
320
321         return (ticks);
322 }
323
324 static int
325 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
326 {
327
328         if (platform_cpuref_valid == 0)
329                 return (EINVAL);
330
331         cpuref->cr_cpuid = 0;
332         cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
333
334         return (0);
335 }
336
337 static int
338 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
339 {
340         int id;
341
342         if (platform_cpuref_valid == 0)
343                 return (EINVAL);
344
345         id = cpuref->cr_cpuid + 1;
346         if (id >= platform_cpuref_cnt)
347                 return (ENOENT);
348
349         cpuref->cr_cpuid = platform_cpuref[id].cr_cpuid;
350         cpuref->cr_hwref = platform_cpuref[id].cr_hwref;
351
352         return (0);
353 }
354
355 static int
356 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
357 {
358
359         cpuref->cr_cpuid = platform_cpuref[0].cr_cpuid;
360         cpuref->cr_hwref = platform_cpuref[0].cr_hwref;
361         return (0);
362 }
363
364 static void
365 get_cpu_reg(phandle_t cpu, cell_t *reg)
366 {
367         int res;
368
369         res = OF_getproplen(cpu, "reg");
370         if (res != sizeof(cell_t))
371                 panic("Unexpected length for CPU property reg on Open Firmware\n");
372         OF_getencprop(cpu, "reg", reg, res);
373 }
374
375 static int
376 chrp_cpuref_init(void)
377 {
378         phandle_t cpu, dev, chosen, pbsp;
379         ihandle_t ibsp;
380         char buf[32];
381         int a, bsp, res, res2, tmp_cpuref_cnt;
382         static struct cpuref tmp_cpuref[MAXCPU];
383         cell_t interrupt_servers[32], addr_cells, size_cells, reg, bsp_reg;
384
385         if (platform_cpuref_valid)
386                 return (0);
387
388         dev = OF_peer(0);
389         dev = OF_child(dev);
390         while (dev != 0) {
391                 res = OF_getprop(dev, "name", buf, sizeof(buf));
392                 if (res > 0 && strcmp(buf, "cpus") == 0)
393                         break;
394                 dev = OF_peer(dev);
395         }
396
397         /* Make sure that cpus reg property have 1 address cell and 0 size cells */
398         res = OF_getproplen(dev, "#address-cells");
399         res2 = OF_getproplen(dev, "#size-cells");
400         if (res != res2 || res != sizeof(cell_t))
401                 panic("CPU properties #address-cells and #size-cells not found on Open Firmware\n");
402         OF_getencprop(dev, "#address-cells", &addr_cells, sizeof(addr_cells));
403         OF_getencprop(dev, "#size-cells", &size_cells, sizeof(size_cells));
404         if (addr_cells != 1 || size_cells != 0)
405                 panic("Unexpected values for CPU properties #address-cells and #size-cells on Open Firmware\n");
406
407         /* Look for boot CPU in /chosen/cpu and /chosen/fdtbootcpu */
408
409         chosen = OF_finddevice("/chosen");
410         if (chosen == -1)
411                 panic("Device /chosen not found on Open Firmware\n");
412
413         bsp_reg = -1;
414
415         /* /chosen/cpu */
416         if (OF_getproplen(chosen, "cpu") == sizeof(ihandle_t)) {
417                 OF_getprop(chosen, "cpu", &ibsp, sizeof(ibsp));
418                 pbsp = OF_instance_to_package(be32toh(ibsp));
419                 if (pbsp != -1)
420                         get_cpu_reg(pbsp, &bsp_reg);
421         }
422
423         /* /chosen/fdtbootcpu */
424         if (bsp_reg == -1) {
425                 if (OF_getproplen(chosen, "fdtbootcpu") == sizeof(cell_t))
426                         OF_getprop(chosen, "fdtbootcpu", &bsp_reg, sizeof(bsp_reg));
427         }
428
429         if (bsp_reg == -1)
430                 panic("Boot CPU not found on Open Firmware\n");
431
432         bsp = -1;
433         tmp_cpuref_cnt = 0;
434         for (cpu = OF_child(dev); cpu != 0; cpu = OF_peer(cpu)) {
435                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
436                 if (res > 0 && strcmp(buf, "cpu") == 0) {
437                         res = OF_getproplen(cpu, "ibm,ppc-interrupt-server#s");
438                         if (res > 0) {
439                                 OF_getencprop(cpu, "ibm,ppc-interrupt-server#s",
440                                     interrupt_servers, res);
441
442                                 get_cpu_reg(cpu, &reg);
443                                 if (reg == bsp_reg)
444                                         bsp = tmp_cpuref_cnt;
445
446                                 for (a = 0; a < res/sizeof(cell_t); a++) {
447                                         tmp_cpuref[tmp_cpuref_cnt].cr_hwref = interrupt_servers[a];
448                                         tmp_cpuref[tmp_cpuref_cnt].cr_cpuid = tmp_cpuref_cnt;
449                                         tmp_cpuref_cnt++;
450                                 }
451                         }
452                 }
453         }
454
455         if (bsp == -1)
456                 panic("Boot CPU not found\n");
457
458         /* Map IDs, so BSP has CPUID 0 regardless of hwref */
459         for (a = bsp; a < tmp_cpuref_cnt; a++) {
460                 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
461                 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
462                 platform_cpuref_cnt++;
463         }
464         for (a = 0; a < bsp; a++) {
465                 platform_cpuref[platform_cpuref_cnt].cr_hwref = tmp_cpuref[a].cr_hwref;
466                 platform_cpuref[platform_cpuref_cnt].cr_cpuid = platform_cpuref_cnt;
467                 platform_cpuref_cnt++;
468         }
469
470         platform_cpuref_valid = 1;
471
472         return (0);
473 }
474
475 #ifdef SMP
476 static int
477 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
478 {
479         cell_t start_cpu;
480         int result, err, timeout;
481
482         if (!rtas_exists()) {
483                 printf("RTAS uninitialized: unable to start AP %d\n",
484                     pc->pc_cpuid);
485                 return (ENXIO);
486         }
487
488         start_cpu = rtas_token_lookup("start-cpu");
489         if (start_cpu == -1) {
490                 printf("RTAS unknown method: unable to start AP %d\n",
491                     pc->pc_cpuid);
492                 return (ENXIO);
493         }
494
495         ap_pcpu = pc;
496         powerpc_sync();
497
498         result = rtas_call_method(start_cpu, 3, 1, pc->pc_hwref, EXC_RST, pc,
499             &err);
500         if (result < 0 || err != 0) {
501                 printf("RTAS error (%d/%d): unable to start AP %d\n",
502                     result, err, pc->pc_cpuid);
503                 return (ENXIO);
504         }
505
506         timeout = 10000;
507         while (!pc->pc_awake && timeout--)
508                 DELAY(100);
509
510         return ((pc->pc_awake) ? 0 : EBUSY);
511 }
512
513 static void
514 chrp_smp_probe_threads(platform_t plat)
515 {
516         struct pcpu *pc, *last_pc;
517         int i, ncores;
518
519         ncores = 0;
520         last_pc = NULL;
521         for (i = 0; i <= mp_maxid; i++) {
522                 pc = pcpu_find(i);
523                 if (pc == NULL)
524                         continue;
525                 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
526                         ncores++;
527                 last_pc = pc;
528         }
529
530         mp_ncores = ncores;
531         if (mp_ncpus % ncores == 0)
532                 smp_threads_per_core = mp_ncpus / ncores;
533 }
534
535 static struct cpu_group *
536 chrp_smp_topo(platform_t plat)
537 {
538
539         if (mp_ncpus % mp_ncores != 0) {
540                 printf("WARNING: Irregular SMP topology. Performance may be "
541                      "suboptimal (%d CPUS, %d cores)\n", mp_ncpus, mp_ncores);
542                 return (smp_topo_none());
543         }
544
545         /* Don't do anything fancier for non-threaded SMP */
546         if (mp_ncpus == mp_ncores)
547                 return (smp_topo_none());
548
549         return (smp_topo_1level(CG_SHARE_L1, smp_threads_per_core,
550             CG_FLAG_SMT));
551 }
552 #endif
553
554 static void
555 chrp_reset(platform_t platform)
556 {
557         OF_reboot();
558 }
559
560 #ifdef __powerpc64__
561 static void
562 phyp_cpu_idle(sbintime_t sbt)
563 {
564         register_t msr;
565
566         msr = mfmsr();
567
568         mtmsr(msr & ~PSL_EE);
569         if (sched_runnable()) {
570                 mtmsr(msr);
571                 return;
572         }
573
574         phyp_hcall(H_CEDE); /* Re-enables interrupts internally */
575         mtmsr(msr);
576 }
577
578 static void
579 chrp_smp_ap_init(platform_t platform)
580 {
581         if (!(mfmsr() & PSL_HV)) {
582                 /* Register VPA */
583                 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(hwref),
584                     splpar_vpa[PCPU_GET(hwref)]);
585
586                 /* Set interrupt priority */
587                 phyp_hcall(H_CPPR, 0xff);
588         }
589 }
590 #else
591 static void
592 chrp_smp_ap_init(platform_t platform)
593 {
594 }
595 #endif