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