]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powermac/platform_powermac.c
stand/powerpc: Only build loader.kboot for powerpc64
[FreeBSD/FreeBSD.git] / sys / powerpc / powermac / platform_powermac.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2008 Marcel Moolenaar
5  * Copyright (c) 2009 Nathan Whitehorn
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/bus.h>
37 #include <sys/pcpu.h>
38 #include <sys/proc.h>
39 #include <sys/smp.h>
40 #include <vm/vm.h>
41 #include <vm/pmap.h>
42
43 #include <machine/altivec.h>    /* For save_vec() */
44 #include <machine/bus.h>
45 #include <machine/cpu.h>
46 #include <machine/fpu.h>        /* For save_fpu() */
47 #include <machine/hid.h>
48 #include <machine/platformvar.h>
49 #include <machine/setjmp.h>
50 #include <machine/smp.h>
51 #include <machine/spr.h>
52
53 #include <dev/ofw/openfirm.h>
54 #include <machine/ofw_machdep.h>
55
56 #include "platform_if.h"
57
58 extern void *ap_pcpu;
59
60 static int powermac_probe(platform_t);
61 static int powermac_attach(platform_t);
62 void powermac_mem_regions(platform_t, struct mem_region *phys, int *physsz,
63     struct mem_region *avail, int *availsz);
64 static u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref);
65 static int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref);
66 static int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref);
67 static int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref);
68 static int powermac_smp_start_cpu(platform_t, struct pcpu *cpu);
69 static void powermac_smp_timebase_sync(platform_t, u_long tb, int ap);
70 static void powermac_reset(platform_t);
71 static void powermac_sleep(platform_t);
72
73 static platform_method_t powermac_methods[] = {
74         PLATFORMMETHOD(platform_probe,          powermac_probe),
75         PLATFORMMETHOD(platform_attach,         powermac_attach),
76         PLATFORMMETHOD(platform_mem_regions,    powermac_mem_regions),
77         PLATFORMMETHOD(platform_timebase_freq,  powermac_timebase_freq),
78         
79         PLATFORMMETHOD(platform_smp_first_cpu,  powermac_smp_first_cpu),
80         PLATFORMMETHOD(platform_smp_next_cpu,   powermac_smp_next_cpu),
81         PLATFORMMETHOD(platform_smp_get_bsp,    powermac_smp_get_bsp),
82         PLATFORMMETHOD(platform_smp_start_cpu,  powermac_smp_start_cpu),
83         PLATFORMMETHOD(platform_smp_timebase_sync, powermac_smp_timebase_sync),
84
85         PLATFORMMETHOD(platform_reset,          powermac_reset),
86         PLATFORMMETHOD(platform_sleep,          powermac_sleep),
87
88         PLATFORMMETHOD_END
89 };
90
91 static platform_def_t powermac_platform = {
92         "powermac",
93         powermac_methods,
94         0
95 };
96
97 PLATFORM_DEF(powermac_platform);
98
99 static int
100 powermac_probe(platform_t plat)
101 {
102         char compat[255];
103         ssize_t compatlen;
104         char *curstr;
105         phandle_t root;
106
107         root = OF_peer(0);
108         if (root == 0)
109                 return (ENXIO);
110
111         compatlen = OF_getprop(root, "compatible", compat, sizeof(compat));
112         
113         for (curstr = compat; curstr < compat + compatlen;
114             curstr += strlen(curstr) + 1) {
115                 if (strncmp(curstr, "MacRISC", 7) == 0)
116                         return (BUS_PROBE_SPECIFIC);
117         }
118
119         return (ENXIO);
120 }
121
122 void
123 powermac_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
124     struct mem_region *avail, int *availsz)
125 {
126         phandle_t memory;
127         cell_t memoryprop[PHYS_AVAIL_SZ * 2];
128         ssize_t propsize, i, j;
129         int physacells = 1;
130
131         memory = OF_finddevice("/memory");
132         if (memory == -1)
133                 memory = OF_finddevice("/memory@0");
134
135         /* "reg" has variable #address-cells, but #size-cells is always 1 */
136         OF_getprop(OF_parent(memory), "#address-cells", &physacells,
137             sizeof(physacells));
138
139         propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop));
140         propsize /= sizeof(cell_t);
141         for (i = 0, j = 0; i < propsize; i += physacells+1, j++) {
142                 phys[j].mr_start = memoryprop[i];
143                 if (physacells == 2) {
144 #ifndef __powerpc64__
145                         /* On 32-bit PPC, ignore regions starting above 4 GB */
146                         if (memoryprop[i] != 0) {
147                                 j--;
148                                 continue;
149                         }
150 #else
151                         phys[j].mr_start <<= 32;
152 #endif
153                         phys[j].mr_start |= memoryprop[i+1];
154                 }
155                 phys[j].mr_size = memoryprop[i + physacells];
156         }
157         *physsz = j;
158
159         /* "available" always has #address-cells = 1 */
160         propsize = OF_getprop(memory, "available", memoryprop,
161             sizeof(memoryprop));
162         if (propsize <= 0) {
163                 for (i = 0; i < *physsz; i++) {
164                         avail[i].mr_start = phys[i].mr_start;
165                         avail[i].mr_size = phys[i].mr_size;
166                 }
167
168                 *availsz = *physsz;
169         } else {
170                 propsize /= sizeof(cell_t);
171                 for (i = 0, j = 0; i < propsize; i += 2, j++) {
172                         avail[j].mr_start = memoryprop[i];
173                         avail[j].mr_size = memoryprop[i + 1];
174                 }
175
176 #ifdef __powerpc64__
177                 /* Add in regions above 4 GB to the available list */
178                 for (i = 0; i < *physsz; i++) {
179                         if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
180                                 avail[j].mr_start = phys[i].mr_start;
181                                 avail[j].mr_size = phys[i].mr_size;
182                                 j++;
183                         }
184                 }
185 #endif
186                 *availsz = j;
187         }
188 }
189
190 static int
191 powermac_attach(platform_t plat)
192 {
193         phandle_t rootnode;
194         char model[32];
195
196
197         /*
198          * Quiesce Open Firmware on PowerMac11,2 and 12,1. It is
199          * necessary there to shut down a background thread doing fan
200          * management, and is harmful on other machines (it will make OF
201          * shut off power to various system components it had turned on).
202          *
203          * Note: we don't need to worry about which OF module we are
204          * using since this is called only from very early boot, within
205          * OF's boot context.
206          */
207
208         rootnode = OF_finddevice("/");
209         if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
210                 if (strcmp(model, "PowerMac11,2") == 0 ||
211                     strcmp(model, "PowerMac12,1") == 0) {
212                         ofw_quiesce();
213                 }
214         }
215
216         return (0);
217 }
218
219 static u_long
220 powermac_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_getprop(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
236 static int
237 powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu)
238 {
239         cell_t cpuid;
240         int res;
241
242         cpuref->cr_hwref = cpu;
243         res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
244
245         /*
246          * psim doesn't have a reg property, so assume 0 as for the
247          * uniprocessor case in the CHRP spec. 
248          */
249         if (res < 0) {
250                 cpuid = 0;
251         }
252
253         cpuref->cr_cpuid = cpuid & 0xff;
254         return (0);
255 }
256
257 static int
258 powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
259 {
260         char buf[8];
261         phandle_t cpu, dev, root;
262         int res;
263
264         root = OF_peer(0);
265
266         dev = OF_child(root);
267         while (dev != 0) {
268                 res = OF_getprop(dev, "name", buf, sizeof(buf));
269                 if (res > 0 && strcmp(buf, "cpus") == 0)
270                         break;
271                 dev = OF_peer(dev);
272         }
273         if (dev == 0) {
274                 /*
275                  * psim doesn't have a name property on the /cpus node,
276                  * but it can be found directly
277                  */
278                 dev = OF_finddevice("/cpus");
279                 if (dev == -1)
280                         return (ENOENT);
281         }
282
283         cpu = OF_child(dev);
284
285         while (cpu != 0) {
286                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
287                 if (res > 0 && strcmp(buf, "cpu") == 0)
288                         break;
289                 cpu = OF_peer(cpu);
290         }
291         if (cpu == 0)
292                 return (ENOENT);
293
294         return (powermac_smp_fill_cpuref(cpuref, cpu));
295 }
296
297 static int
298 powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
299 {
300         char buf[8];
301         phandle_t cpu;
302         int res;
303
304         cpu = OF_peer(cpuref->cr_hwref);
305         while (cpu != 0) {
306                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
307                 if (res > 0 && strcmp(buf, "cpu") == 0)
308                         break;
309                 cpu = OF_peer(cpu);
310         }
311         if (cpu == 0)
312                 return (ENOENT);
313
314         return (powermac_smp_fill_cpuref(cpuref, cpu));
315 }
316
317 static int
318 powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
319 {
320         ihandle_t inst;
321         phandle_t bsp, chosen;
322         int res;
323
324         chosen = OF_finddevice("/chosen");
325         if (chosen == -1)
326                 return (ENXIO);
327
328         res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
329         if (res < 0)
330                 return (ENXIO);
331
332         bsp = OF_instance_to_package(inst);
333         return (powermac_smp_fill_cpuref(cpuref, bsp));
334 }
335
336 static int
337 powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
338 {
339 #ifdef SMP
340         phandle_t cpu;
341         volatile uint8_t *rstvec;
342         static volatile uint8_t *rstvec_virtbase = NULL;
343         int res, reset, timeout;
344
345         cpu = pc->pc_hwref;
346         res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset));
347         if (res < 0) {
348                 reset = 0x58;
349
350                 switch (pc->pc_cpuid) {
351                 case 0:
352                         reset += 0x03;
353                         break;
354                 case 1:
355                         reset += 0x04;
356                         break;
357                 case 2:
358                         reset += 0x0f;
359                         break;
360                 case 3:
361                         reset += 0x10;
362                         break;
363                 default:
364                         return (ENXIO);
365                 }
366         }
367
368         ap_pcpu = pc;
369
370         if (rstvec_virtbase == NULL)
371                 rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE);
372
373         rstvec = rstvec_virtbase + reset;
374
375         *rstvec = 4;
376         powerpc_sync();
377         (void)(*rstvec);
378         powerpc_sync();
379         DELAY(1);
380         *rstvec = 0;
381         powerpc_sync();
382         (void)(*rstvec);
383         powerpc_sync();
384
385         timeout = 10000;
386         while (!pc->pc_awake && timeout--)
387                 DELAY(100);
388
389         return ((pc->pc_awake) ? 0 : EBUSY);
390 #else
391         /* No SMP support */
392         return (ENXIO);
393 #endif
394 }
395
396 static void
397 powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap)
398 {
399
400         mttb(tb);
401 }
402
403 static void
404 powermac_reset(platform_t platform)
405 {
406         OF_reboot();
407 }
408
409 void
410 powermac_sleep(platform_t platform)
411 {
412
413         *(unsigned long *)0x80 = 0x100;
414         cpu_sleep();
415 }
416