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