]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/pseries/platform_chrp.c
Merge compiler-rt release_38 branch r258968.
[FreeBSD/FreeBSD.git] / sys / powerpc / pseries / platform_chrp.c
1 /*-
2  * Copyright (c) 2008 Marcel Moolenaar
3  * Copyright (c) 2009 Nathan Whitehorn
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
54 #include "platform_if.h"
55
56 #ifdef SMP
57 extern void *ap_pcpu;
58 #endif
59
60 #ifdef __powerpc64__
61 static uint8_t splpar_vpa[MAXCPU][640] __aligned(128); /* XXX: dpcpu */
62 #endif
63
64 static vm_offset_t realmaxaddr = VM_MAX_ADDRESS;
65
66 static int chrp_probe(platform_t);
67 static int chrp_attach(platform_t);
68 void chrp_mem_regions(platform_t, struct mem_region *phys, int *physsz,
69     struct mem_region *avail, int *availsz);
70 static vm_offset_t chrp_real_maxaddr(platform_t);
71 static u_long chrp_timebase_freq(platform_t, struct cpuref *cpuref);
72 static int chrp_smp_first_cpu(platform_t, struct cpuref *cpuref);
73 static int chrp_smp_next_cpu(platform_t, struct cpuref *cpuref);
74 static int chrp_smp_get_bsp(platform_t, struct cpuref *cpuref);
75 static void chrp_smp_ap_init(platform_t);
76 #ifdef SMP
77 static int chrp_smp_start_cpu(platform_t, struct pcpu *cpu);
78 static struct cpu_group *chrp_smp_topo(platform_t plat);
79 #endif
80 static void chrp_reset(platform_t);
81 #ifdef __powerpc64__
82 #include "phyp-hvcall.h"
83 static void phyp_cpu_idle(sbintime_t sbt);
84 #endif
85
86 static platform_method_t chrp_methods[] = {
87         PLATFORMMETHOD(platform_probe,          chrp_probe),
88         PLATFORMMETHOD(platform_attach,         chrp_attach),
89         PLATFORMMETHOD(platform_mem_regions,    chrp_mem_regions),
90         PLATFORMMETHOD(platform_real_maxaddr,   chrp_real_maxaddr),
91         PLATFORMMETHOD(platform_timebase_freq,  chrp_timebase_freq),
92         
93         PLATFORMMETHOD(platform_smp_ap_init,    chrp_smp_ap_init),
94         PLATFORMMETHOD(platform_smp_first_cpu,  chrp_smp_first_cpu),
95         PLATFORMMETHOD(platform_smp_next_cpu,   chrp_smp_next_cpu),
96         PLATFORMMETHOD(platform_smp_get_bsp,    chrp_smp_get_bsp),
97 #ifdef SMP
98         PLATFORMMETHOD(platform_smp_start_cpu,  chrp_smp_start_cpu),
99         PLATFORMMETHOD(platform_smp_topo,       chrp_smp_topo),
100 #endif
101
102         PLATFORMMETHOD(platform_reset,          chrp_reset),
103
104         { 0, 0 }
105 };
106
107 static platform_def_t chrp_platform = {
108         "chrp",
109         chrp_methods,
110         0
111 };
112
113 PLATFORM_DEF(chrp_platform);
114
115 static int
116 chrp_probe(platform_t plat)
117 {
118         if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1)
119                 return (BUS_PROBE_GENERIC);
120
121         return (ENXIO);
122 }
123
124 static int
125 chrp_attach(platform_t plat)
126 {
127 #ifdef __powerpc64__
128         int i;
129
130         /* XXX: check for /rtas/ibm,hypertas-functions? */
131         if (!(mfmsr() & PSL_HV)) {
132                 struct mem_region *phys, *avail;
133                 int nphys, navail;
134                 mem_regions(&phys, &nphys, &avail, &navail);
135                 realmaxaddr = phys[0].mr_size;
136
137                 pmap_mmu_install("mmu_phyp", BUS_PROBE_SPECIFIC);
138                 cpu_idle_hook = phyp_cpu_idle;
139
140                 /* Set up important VPA fields */
141                 for (i = 0; i < MAXCPU; i++) {
142                         bzero(splpar_vpa[i], sizeof(splpar_vpa));
143                         /* First two: VPA size */
144                         splpar_vpa[i][4] =
145                             (uint8_t)((sizeof(splpar_vpa[i]) >> 8) & 0xff);
146                         splpar_vpa[i][5] =
147                             (uint8_t)(sizeof(splpar_vpa[i]) & 0xff);
148                         splpar_vpa[i][0xba] = 1;        /* Maintain FPRs */
149                         splpar_vpa[i][0xbb] = 1;        /* Maintain PMCs */
150                         splpar_vpa[i][0xfc] = 0xff;     /* Maintain full SLB */
151                         splpar_vpa[i][0xfd] = 0xff;
152                         splpar_vpa[i][0xff] = 1;        /* Maintain Altivec */
153                 }
154                 mb();
155
156                 /* Set up hypervisor CPU stuff */
157                 chrp_smp_ap_init(plat);
158         }
159 #endif
160
161         /* Some systems (e.g. QEMU) need Open Firmware to stand down */
162         ofw_quiesce();
163
164         return (0);
165 }
166
167 static int
168 parse_drconf_memory(struct mem_region *ofmem, int *msz,
169                     struct mem_region *ofavail, int *asz)
170 {
171         phandle_t phandle;
172         vm_offset_t base;
173         int i, idx, len, lasz, lmsz, res;
174         uint32_t flags, lmb_size[2];
175         uint32_t *dmem;
176
177         lmsz = *msz;
178         lasz = *asz;
179
180         phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
181         if (phandle == -1)
182                 /* No drconf node, return. */
183                 return (0);
184
185         res = OF_getencprop(phandle, "ibm,lmb-size", lmb_size,
186             sizeof(lmb_size));
187         if (res == -1)
188                 return (0);
189         printf("Logical Memory Block size: %d MB\n", lmb_size[1] >> 20);
190
191         /* Parse the /ibm,dynamic-memory.
192            The first position gives the # of entries. The next two words
193            reflect the address of the memory block. The next four words are
194            the DRC index, reserved, list index and flags.
195            (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
196            
197             #el  Addr   DRC-idx  res   list-idx  flags
198            -------------------------------------------------
199            | 4 |   8   |   4   |   4   |   4   |   4   |....
200            -------------------------------------------------
201         */
202
203         len = OF_getproplen(phandle, "ibm,dynamic-memory");
204         if (len > 0) {
205
206                 /* We have to use a variable length array on the stack
207                    since we have very limited stack space.
208                 */
209                 cell_t arr[len/sizeof(cell_t)];
210
211                 res = OF_getencprop(phandle, "ibm,dynamic-memory", arr,
212                     sizeof(arr));
213                 if (res == -1)
214                         return (0);
215
216                 /* Number of elements */
217                 idx = arr[0];
218
219                 /* First address, in arr[1], arr[2]*/
220                 dmem = &arr[1];
221         
222                 for (i = 0; i < idx; i++) {
223                         base = ((uint64_t)dmem[0] << 32) + dmem[1];
224                         dmem += 4;
225                         flags = dmem[1];
226                         /* Use region only if available and not reserved. */
227                         if ((flags & 0x8) && !(flags & 0x80)) {
228                                 ofmem[lmsz].mr_start = base;
229                                 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
230                                 ofavail[lasz].mr_start = base;
231                                 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
232                                 lmsz++;
233                                 lasz++;
234                         }
235                         dmem += 2;
236                 }
237         }
238
239         *msz = lmsz;
240         *asz = lasz;
241
242         return (1);
243 }
244
245 void
246 chrp_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
247     struct mem_region *avail, int *availsz)
248 {
249         vm_offset_t maxphysaddr;
250         int i;
251
252         ofw_mem_regions(phys, physsz, avail, availsz);
253         parse_drconf_memory(phys, physsz, avail, availsz);
254
255         /*
256          * On some firmwares (SLOF), some memory may be marked available that
257          * doesn't actually exist. This manifests as an extension of the last
258          * available segment past the end of physical memory, so truncate that
259          * one.
260          */
261         maxphysaddr = 0;
262         for (i = 0; i < *physsz; i++)
263                 if (phys[i].mr_start + phys[i].mr_size > maxphysaddr)
264                         maxphysaddr = phys[i].mr_start + phys[i].mr_size;
265
266         for (i = 0; i < *availsz; i++)
267                 if (avail[i].mr_start + avail[i].mr_size > maxphysaddr)
268                         avail[i].mr_size = maxphysaddr - avail[i].mr_start;
269 }
270
271 static vm_offset_t
272 chrp_real_maxaddr(platform_t plat)
273 {
274         return (realmaxaddr);
275 }
276
277 static u_long
278 chrp_timebase_freq(platform_t plat, struct cpuref *cpuref)
279 {
280         phandle_t phandle;
281         int32_t ticks = -1;
282
283         phandle = cpuref->cr_hwref;
284
285         OF_getencprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
286
287         if (ticks <= 0)
288                 panic("Unable to determine timebase frequency!");
289
290         return (ticks);
291 }
292
293 static int
294 chrp_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
295 {
296         char buf[8];
297         phandle_t cpu, dev, root;
298         int res, cpuid;
299
300         root = OF_peer(0);
301
302         dev = OF_child(root);
303         while (dev != 0) {
304                 res = OF_getprop(dev, "name", buf, sizeof(buf));
305                 if (res > 0 && strcmp(buf, "cpus") == 0)
306                         break;
307                 dev = OF_peer(dev);
308         }
309         if (dev == 0) {
310                 /*
311                  * psim doesn't have a name property on the /cpus node,
312                  * but it can be found directly
313                  */
314                 dev = OF_finddevice("/cpus");
315                 if (dev == 0)
316                         return (ENOENT);
317         }
318
319         cpu = OF_child(dev);
320
321         while (cpu != 0) {
322                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
323                 if (res > 0 && strcmp(buf, "cpu") == 0)
324                         break;
325                 cpu = OF_peer(cpu);
326         }
327         if (cpu == 0)
328                 return (ENOENT);
329
330         cpuref->cr_hwref = cpu;
331         res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
332             sizeof(cpuid));
333         if (res <= 0)
334                 res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
335         if (res <= 0)
336                 cpuid = 0;
337         cpuref->cr_cpuid = cpuid;
338
339         return (0);
340 }
341
342 static int
343 chrp_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
344 {
345         char buf[8];
346         phandle_t cpu;
347         int i, res, cpuid;
348
349         /* Check for whether it should be the next thread */
350         res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s");
351         if (res > 0) {
352                 cell_t interrupt_servers[res/sizeof(cell_t)];
353                 OF_getencprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s",
354                     interrupt_servers, res);
355                 for (i = 0; i < res/sizeof(cell_t) - 1; i++) {
356                         if (interrupt_servers[i] == cpuref->cr_cpuid) {
357                                 cpuref->cr_cpuid = interrupt_servers[i+1];
358                                 return (0);
359                         }
360                 }
361         }
362
363         /* Next CPU core/package */
364         cpu = OF_peer(cpuref->cr_hwref);
365         while (cpu != 0) {
366                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
367                 if (res > 0 && strcmp(buf, "cpu") == 0)
368                         break;
369                 cpu = OF_peer(cpu);
370         }
371         if (cpu == 0)
372                 return (ENOENT);
373
374         cpuref->cr_hwref = cpu;
375         res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid,
376             sizeof(cpuid));
377         if (res <= 0)
378                 res = OF_getencprop(cpu, "reg", &cpuid, sizeof(cpuid));
379         if (res <= 0)
380                 cpuid = 0;
381         cpuref->cr_cpuid = cpuid;
382
383         return (0);
384 }
385
386 static int
387 chrp_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
388 {
389         ihandle_t inst;
390         phandle_t bsp, chosen;
391         int res, cpuid;
392
393         chosen = OF_finddevice("/chosen");
394         if (chosen == 0)
395                 return (ENXIO);
396
397         res = OF_getencprop(chosen, "cpu", &inst, sizeof(inst));
398         if (res < 0)
399                 return (ENXIO);
400
401         bsp = OF_instance_to_package(inst);
402
403         /* Pick the primary thread. Can it be any other? */
404         cpuref->cr_hwref = bsp;
405         res = OF_getencprop(bsp, "ibm,ppc-interrupt-server#s", &cpuid,
406             sizeof(cpuid));
407         if (res <= 0)
408                 res = OF_getencprop(bsp, "reg", &cpuid, sizeof(cpuid));
409         if (res <= 0)
410                 cpuid = 0;
411         cpuref->cr_cpuid = cpuid;
412
413         return (0);
414 }
415
416 #ifdef SMP
417 static int
418 chrp_smp_start_cpu(platform_t plat, struct pcpu *pc)
419 {
420         cell_t start_cpu;
421         int result, err, timeout;
422
423         if (!rtas_exists()) {
424                 printf("RTAS uninitialized: unable to start AP %d\n",
425                     pc->pc_cpuid);
426                 return (ENXIO);
427         }
428
429         start_cpu = rtas_token_lookup("start-cpu");
430         if (start_cpu == -1) {
431                 printf("RTAS unknown method: unable to start AP %d\n",
432                     pc->pc_cpuid);
433                 return (ENXIO);
434         }
435
436         ap_pcpu = pc;
437         powerpc_sync();
438
439         result = rtas_call_method(start_cpu, 3, 1, pc->pc_cpuid, EXC_RST, pc,
440             &err);
441         if (result < 0 || err != 0) {
442                 printf("RTAS error (%d/%d): unable to start AP %d\n",
443                     result, err, pc->pc_cpuid);
444                 return (ENXIO);
445         }
446
447         timeout = 10000;
448         while (!pc->pc_awake && timeout--)
449                 DELAY(100);
450
451         return ((pc->pc_awake) ? 0 : EBUSY);
452 }
453
454 static struct cpu_group *
455 chrp_smp_topo(platform_t plat)
456 {
457         struct pcpu *pc, *last_pc;
458         int i, ncores, ncpus;
459
460         ncores = ncpus = 0;
461         last_pc = NULL;
462         for (i = 0; i <= mp_maxid; i++) {
463                 pc = pcpu_find(i);
464                 if (pc == NULL)
465                         continue;
466                 if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref)
467                         ncores++;
468                 last_pc = pc;
469                 ncpus++;
470         }
471
472         if (ncpus % ncores != 0) {
473                 printf("WARNING: Irregular SMP topology. Performance may be "
474                      "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores);
475                 return (smp_topo_none());
476         }
477
478         /* Don't do anything fancier for non-threaded SMP */
479         if (ncpus == ncores)
480                 return (smp_topo_none());
481
482         return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT));
483 }
484 #endif
485
486 static void
487 chrp_reset(platform_t platform)
488 {
489         OF_reboot();
490 }
491
492 #ifdef __powerpc64__
493 static void
494 phyp_cpu_idle(sbintime_t sbt)
495 {
496         phyp_hcall(H_CEDE);
497 }
498
499 static void
500 chrp_smp_ap_init(platform_t platform)
501 {
502         if (!(mfmsr() & PSL_HV)) {
503                 /* Register VPA */
504                 phyp_hcall(H_REGISTER_VPA, 1UL, PCPU_GET(cpuid),
505                     splpar_vpa[PCPU_GET(cpuid)]);
506
507                 /* Set interrupt priority */
508                 phyp_hcall(H_CPPR, 0xff);
509         }
510 }
511 #else
512 static void
513 chrp_smp_ap_init(platform_t platform)
514 {
515 }
516 #endif
517