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