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