]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powernv/platform_powernv.c
Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
[FreeBSD/FreeBSD.git] / sys / powerpc / powernv / platform_powernv.c
1 /*-
2  * Copyright (c) 2015 Nathan Whitehorn
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/bus.h>
34 #include <sys/pcpu.h>
35 #include <sys/proc.h>
36 #include <sys/smp.h>
37 #include <vm/vm.h>
38 #include <vm/pmap.h>
39
40 #include <machine/bus.h>
41 #include <machine/cpu.h>
42 #include <machine/hid.h>
43 #include <machine/platformvar.h>
44 #include <machine/pmap.h>
45 #include <machine/rtas.h>
46 #include <machine/smp.h>
47 #include <machine/spr.h>
48 #include <machine/trap.h>
49
50 #include <dev/ofw/openfirm.h>
51 #include <machine/ofw_machdep.h>
52 #include <powerpc/aim/mmu_oea64.h>
53
54 #include "platform_if.h"
55 #include "opal.h"
56
57 #ifdef SMP
58 extern void *ap_pcpu;
59 #endif
60
61 static int powernv_probe(platform_t);
62 static int powernv_attach(platform_t);
63 void powernv_mem_regions(platform_t, struct mem_region *phys, int *physsz,
64     struct mem_region *avail, int *availsz);
65 static u_long powernv_timebase_freq(platform_t, struct cpuref *cpuref);
66 static int powernv_smp_first_cpu(platform_t, struct cpuref *cpuref);
67 static int powernv_smp_next_cpu(platform_t, struct cpuref *cpuref);
68 static int powernv_smp_get_bsp(platform_t, struct cpuref *cpuref);
69 static void powernv_smp_ap_init(platform_t);
70 #ifdef SMP
71 static int powernv_smp_start_cpu(platform_t, struct pcpu *cpu);
72 static struct cpu_group *powernv_smp_topo(platform_t plat);
73 #endif
74 static void powernv_reset(platform_t);
75 static void powernv_cpu_idle(sbintime_t sbt);
76
77 static platform_method_t powernv_methods[] = {
78         PLATFORMMETHOD(platform_probe,          powernv_probe),
79         PLATFORMMETHOD(platform_attach,         powernv_attach),
80         PLATFORMMETHOD(platform_mem_regions,    powernv_mem_regions),
81         PLATFORMMETHOD(platform_timebase_freq,  powernv_timebase_freq),
82         
83         PLATFORMMETHOD(platform_smp_ap_init,    powernv_smp_ap_init),
84         PLATFORMMETHOD(platform_smp_first_cpu,  powernv_smp_first_cpu),
85         PLATFORMMETHOD(platform_smp_next_cpu,   powernv_smp_next_cpu),
86         PLATFORMMETHOD(platform_smp_get_bsp,    powernv_smp_get_bsp),
87 #ifdef SMP
88         PLATFORMMETHOD(platform_smp_start_cpu,  powernv_smp_start_cpu),
89         PLATFORMMETHOD(platform_smp_topo,       powernv_smp_topo),
90 #endif
91
92         PLATFORMMETHOD(platform_reset,          powernv_reset),
93
94         { 0, 0 }
95 };
96
97 static platform_def_t powernv_platform = {
98         "powernv",
99         powernv_methods,
100         0
101 };
102
103 PLATFORM_DEF(powernv_platform);
104
105 static int powernv_boot_pir;
106
107 static int
108 powernv_probe(platform_t plat)
109 {
110         if (opal_check() == 0)
111                 return (BUS_PROBE_SPECIFIC);
112
113         return (ENXIO);
114 }
115
116 static int
117 powernv_attach(platform_t plat)
118 {
119         uint32_t nptlp, shift = 0, slb_encoding = 0;
120         int32_t lp_size, lp_encoding;
121         char buf[255];
122         pcell_t prop;
123         phandle_t cpu;
124         int res, len, node, idx;
125
126         /* Ping OPAL again just to make sure */
127         opal_check();
128
129         cpu_idle_hook = powernv_cpu_idle;
130         powernv_boot_pir = mfspr(SPR_PIR);
131
132         /* Init CPU bits */
133         powernv_smp_ap_init(plat);
134
135         /* Set SLB count from device tree */
136         cpu = OF_peer(0);
137         cpu = OF_child(cpu);
138         while (cpu != 0) {
139                 res = OF_getprop(cpu, "name", buf, sizeof(buf));
140                 if (res > 0 && strcmp(buf, "cpus") == 0)
141                         break;
142                 cpu = OF_peer(cpu);
143         }
144         if (cpu == 0)
145                 goto out;
146
147         cpu = OF_child(cpu);
148         while (cpu != 0) {
149                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
150                 if (res > 0 && strcmp(buf, "cpu") == 0)
151                         break;
152                 cpu = OF_peer(cpu);
153         }
154         if (cpu == 0)
155                 goto out;
156
157         res = OF_getencprop(cpu, "ibm,slb-size", &prop, sizeof(prop));
158         if (res > 0)
159                 n_slbs = prop;
160
161         /*
162          * Scan the large page size property for PAPR compatible machines.
163          * See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties'
164          * for the encoding of the property.
165          */
166
167         len = OF_getproplen(node, "ibm,segment-page-sizes");
168         if (len > 0) {
169                 /*
170                  * We have to use a variable length array on the stack
171                  * since we have very limited stack space.
172                  */
173                 pcell_t arr[len/sizeof(cell_t)];
174                 res = OF_getencprop(cpu, "ibm,segment-page-sizes", arr,
175                     sizeof(arr));
176                 len /= 4;
177                 idx = 0;
178                 while (len > 0) {
179                         shift = arr[idx];
180                         slb_encoding = arr[idx + 1];
181                         nptlp = arr[idx + 2];
182                         idx += 3;
183                         len -= 3;
184                         while (len > 0 && nptlp) {
185                                 lp_size = arr[idx];
186                                 lp_encoding = arr[idx+1];
187                                 if (slb_encoding == SLBV_L && lp_encoding == 0)
188                                         break;
189
190                                 idx += 2;
191                                 len -= 2;
192                                 nptlp--;
193                         }
194                         if (nptlp && slb_encoding == SLBV_L && lp_encoding == 0)
195                                 break;
196                 }
197
198                 if (len == 0)
199                         panic("Standard large pages (SLB[L] = 1, PTE[LP] = 0) "
200                             "not supported by this system.");
201
202                 moea64_large_page_shift = shift;
203                 moea64_large_page_size = 1ULL << lp_size;
204         }
205
206 out:
207         return (0);
208 }
209
210
211 void
212 powernv_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
213     struct mem_region *avail, int *availsz)
214 {
215
216         ofw_mem_regions(phys, physsz, avail, availsz);
217 }
218
219 static u_long
220 powernv_timebase_freq(platform_t plat, struct cpuref *cpuref)
221 {
222         phandle_t phandle;
223         int32_t ticks = -1;
224
225         phandle = cpuref->cr_hwref;
226
227         OF_getencprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
228
229         if (ticks <= 0)
230                 panic("Unable to determine timebase frequency!");
231
232         return (ticks);
233 }
234
235 static int
236 powernv_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
237 {
238         char buf[8];
239         phandle_t cpu, dev, root;
240         int res, cpuid;
241
242         root = OF_peer(0);
243
244         dev = OF_child(root);
245         while (dev != 0) {
246                 res = OF_getprop(dev, "name", buf, sizeof(buf));
247                 if (res > 0 && strcmp(buf, "cpus") == 0)
248                         break;
249                 dev = OF_peer(dev);
250         }
251         if (dev == 0) {
252                 /*
253                  * psim doesn't have a name property on the /cpus node,
254                  * but it can be found directly
255                  */
256                 dev = OF_finddevice("/cpus");
257                 if (dev == 0)
258                         return (ENOENT);
259         }
260
261         cpu = OF_child(dev);
262
263         while (cpu != 0) {
264                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
265                 if (res > 0 && strcmp(buf, "cpu") == 0)
266                         break;
267                 cpu = OF_peer(cpu);
268         }
269         if (cpu == 0)
270                 return (ENOENT);
271
272         cpuref->cr_hwref = cpu;
273         res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
274             sizeof(cpuid));
275         if (res <= 0)
276                 res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
277         if (res <= 0)
278                 cpuid = 0;
279         cpuref->cr_cpuid = cpuid;
280
281         return (0);
282 }
283
284 static int
285 powernv_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
286 {
287         char buf[8];
288         phandle_t cpu;
289         int i, res, cpuid;
290
291         /* Check for whether it should be the next thread */
292         res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s");
293         if (res > 0) {
294                 cell_t interrupt_servers[res/sizeof(cell_t)];
295                 OF_getencprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s",
296                     interrupt_servers, res);
297                 for (i = 0; i < res/sizeof(cell_t) - 1; i++) {
298                         if (interrupt_servers[i] == cpuref->cr_cpuid) {
299                                 cpuref->cr_cpuid = interrupt_servers[i+1];
300                                 return (0);
301                         }
302                 }
303         }
304
305         /* Next CPU core/package */
306         cpu = OF_peer(cpuref->cr_hwref);
307         while (cpu != 0) {
308                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
309                 if (res > 0 && strcmp(buf, "cpu") == 0)
310                         break;
311                 cpu = OF_peer(cpu);
312         }
313         if (cpu == 0)
314                 return (ENOENT);
315
316         cpuref->cr_hwref = cpu;
317         res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
318             sizeof(cpuid));
319         if (res <= 0)
320                 res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
321         if (res <= 0)
322                 cpuid = 0;
323         cpuref->cr_cpuid = cpuid;
324
325         return (0);
326 }
327
328 static int
329 powernv_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
330 {
331         phandle_t chosen;
332         int cpuid, res;
333         struct cpuref i;
334
335         chosen = OF_finddevice("/chosen");
336         if (chosen == 0)
337                 return (ENOENT);
338
339         res = OF_getencprop(chosen, "fdtbootcpu", &cpuid, sizeof(cpuid));
340         if (res < 0)
341                 return (ENOENT);
342
343         /* XXX: FDT from kexec lies sometimes. PIR seems not to. */
344         if (cpuid == 0)
345                 cpuid = powernv_boot_pir;
346
347         cpuref->cr_cpuid = cpuid;
348
349         if (powernv_smp_first_cpu(plat, &i) != 0)
350                 return (ENOENT);
351         cpuref->cr_hwref = i.cr_hwref;
352
353         do {
354                 if (i.cr_cpuid == cpuid) {
355                         cpuref->cr_hwref = i.cr_hwref;
356                         break;
357                 }
358         } while (powernv_smp_next_cpu(plat, &i) == 0);
359
360         return (0);
361 }
362
363 #ifdef SMP
364 static int
365 powernv_smp_start_cpu(platform_t plat, struct pcpu *pc)
366 {
367         int result;
368
369         ap_pcpu = pc;
370         powerpc_sync();
371
372         result = opal_call(OPAL_START_CPU, pc->pc_cpuid, EXC_RST);
373         if (result != OPAL_SUCCESS) {
374                 printf("OPAL error (%d): unable to start AP %d\n",
375                     result, pc->pc_cpuid);
376                 return (ENXIO);
377         }
378
379         return (0);
380 }
381
382 static struct cpu_group *
383 powernv_smp_topo(platform_t plat)
384 {
385         struct pcpu *pc, *last_pc;
386         int i, ncores, ncpus;
387
388         ncores = ncpus = 0;
389         last_pc = NULL;
390         CPU_FOREACH(i) {
391                 pc = pcpu_find(i);
392                 if (pc == NULL)
393                         continue;
394                 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
395                         ncores++;
396                 last_pc = pc;
397                 ncpus++;
398         }
399
400         if (ncpus % ncores != 0) {
401                 printf("WARNING: Irregular SMP topology. Performance may be "
402                      "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
403                 return (smp_topo_none());
404         }
405
406         /* Don't do anything fancier for non-threaded SMP */
407         if (ncpus == ncores)
408                 return (smp_topo_none());
409
410 #ifdef NOTYET /* smp_topo_1level() fails with non-consecutive CPU IDs */
411         return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
412 #else
413         return (smp_topo_none());
414 #endif
415 }
416 #endif
417
418 static void
419 powernv_reset(platform_t platform)
420 {
421
422         opal_call(OPAL_CEC_REBOOT);
423 }
424
425 static void
426 powernv_smp_ap_init(platform_t platform)
427 {
428
429         /* Direct interrupts to SRR instead of HSRR and reset LPCR otherwise */
430         mtspr(SPR_LPCR, LPCR_LPES);
431 }
432
433 static void
434 powernv_cpu_idle(sbintime_t sbt)
435 {
436 }