]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powermac/platform_powermac.c
[POWERPC64LE] add mrsas to GENERIC64LE
[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          * Quiesce Open Firmware on PowerMac11,2 and 12,1. It is
198          * necessary there to shut down a background thread doing fan
199          * management, and is harmful on other machines (it will make OF
200          * shut off power to various system components it had turned on).
201          *
202          * Note: we don't need to worry about which OF module we are
203          * using since this is called only from very early boot, within
204          * OF's boot context.
205          */
206
207         rootnode = OF_finddevice("/");
208         if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
209                 if (strcmp(model, "PowerMac11,2") == 0 ||
210                     strcmp(model, "PowerMac12,1") == 0) {
211                         ofw_quiesce();
212                 }
213         }
214
215         return (0);
216 }
217
218 static u_long
219 powermac_timebase_freq(platform_t plat, struct cpuref *cpuref)
220 {
221         phandle_t phandle;
222         int32_t ticks = -1;
223
224         phandle = cpuref->cr_hwref;
225
226         OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
227
228         if (ticks <= 0)
229                 panic("Unable to determine timebase frequency!");
230
231         return (ticks);
232 }
233
234 static int
235 powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu)
236 {
237         cell_t cpuid;
238         int res;
239
240         cpuref->cr_hwref = cpu;
241         res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
242
243         /*
244          * psim doesn't have a reg property, so assume 0 as for the
245          * uniprocessor case in the CHRP spec. 
246          */
247         if (res < 0) {
248                 cpuid = 0;
249         }
250
251         cpuref->cr_cpuid = cpuid & 0xff;
252         return (0);
253 }
254
255 static int
256 powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
257 {
258         char buf[8];
259         phandle_t cpu, dev, root;
260         int res;
261
262         root = OF_peer(0);
263
264         dev = OF_child(root);
265         while (dev != 0) {
266                 res = OF_getprop(dev, "name", buf, sizeof(buf));
267                 if (res > 0 && strcmp(buf, "cpus") == 0)
268                         break;
269                 dev = OF_peer(dev);
270         }
271         if (dev == 0) {
272                 /*
273                  * psim doesn't have a name property on the /cpus node,
274                  * but it can be found directly
275                  */
276                 dev = OF_finddevice("/cpus");
277                 if (dev == -1)
278                         return (ENOENT);
279         }
280
281         cpu = OF_child(dev);
282
283         while (cpu != 0) {
284                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
285                 if (res > 0 && strcmp(buf, "cpu") == 0)
286                         break;
287                 cpu = OF_peer(cpu);
288         }
289         if (cpu == 0)
290                 return (ENOENT);
291
292         return (powermac_smp_fill_cpuref(cpuref, cpu));
293 }
294
295 static int
296 powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
297 {
298         char buf[8];
299         phandle_t cpu;
300         int res;
301
302         cpu = OF_peer(cpuref->cr_hwref);
303         while (cpu != 0) {
304                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
305                 if (res > 0 && strcmp(buf, "cpu") == 0)
306                         break;
307                 cpu = OF_peer(cpu);
308         }
309         if (cpu == 0)
310                 return (ENOENT);
311
312         return (powermac_smp_fill_cpuref(cpuref, cpu));
313 }
314
315 static int
316 powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
317 {
318         ihandle_t inst;
319         phandle_t bsp, chosen;
320         int res;
321
322         chosen = OF_finddevice("/chosen");
323         if (chosen == -1)
324                 return (ENXIO);
325
326         res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
327         if (res < 0)
328                 return (ENXIO);
329
330         bsp = OF_instance_to_package(inst);
331         return (powermac_smp_fill_cpuref(cpuref, bsp));
332 }
333
334 static int
335 powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
336 {
337 #ifdef SMP
338         phandle_t cpu;
339         volatile uint8_t *rstvec;
340         static volatile uint8_t *rstvec_virtbase = NULL;
341         int res, reset, timeout;
342
343         cpu = pc->pc_hwref;
344         res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset));
345         if (res < 0) {
346                 reset = 0x58;
347
348                 switch (pc->pc_cpuid) {
349                 case 0:
350                         reset += 0x03;
351                         break;
352                 case 1:
353                         reset += 0x04;
354                         break;
355                 case 2:
356                         reset += 0x0f;
357                         break;
358                 case 3:
359                         reset += 0x10;
360                         break;
361                 default:
362                         return (ENXIO);
363                 }
364         }
365
366         ap_pcpu = pc;
367
368         if (rstvec_virtbase == NULL)
369                 rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE);
370
371         rstvec = rstvec_virtbase + reset;
372
373         *rstvec = 4;
374         powerpc_sync();
375         (void)(*rstvec);
376         powerpc_sync();
377         DELAY(1);
378         *rstvec = 0;
379         powerpc_sync();
380         (void)(*rstvec);
381         powerpc_sync();
382
383         timeout = 10000;
384         while (!pc->pc_awake && timeout--)
385                 DELAY(100);
386
387         return ((pc->pc_awake) ? 0 : EBUSY);
388 #else
389         /* No SMP support */
390         return (ENXIO);
391 #endif
392 }
393
394 static void
395 powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap)
396 {
397
398         mttb(tb);
399 }
400
401 static void
402 powermac_reset(platform_t platform)
403 {
404         OF_reboot();
405 }
406
407 void
408 powermac_sleep(platform_t platform)
409 {
410
411         *(unsigned long *)0x80 = 0x100;
412         cpu_sleep();
413 }