]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/powerpc/powermac/platform_powermac.c
As <machine/pmap.h> is included from <vm/pmap.h>, there is no need to
[FreeBSD/FreeBSD.git] / sys / powerpc / powermac / platform_powermac.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/altivec.h>    /* For save_vec() */
42 #include <machine/bus.h>
43 #include <machine/cpu.h>
44 #include <machine/fpu.h>        /* For save_fpu() */
45 #include <machine/hid.h>
46 #include <machine/platformvar.h>
47 #include <machine/setjmp.h>
48 #include <machine/smp.h>
49 #include <machine/spr.h>
50
51 #include <dev/ofw/openfirm.h>
52 #include <machine/ofw_machdep.h>
53
54 #include "platform_if.h"
55
56 extern void *ap_pcpu;
57
58 static int powermac_probe(platform_t);
59 static int powermac_attach(platform_t);
60 void powermac_mem_regions(platform_t, struct mem_region *phys, int *physsz,
61     struct mem_region *avail, int *availsz);
62 static u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref);
63 static int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref);
64 static int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref);
65 static int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref);
66 static int powermac_smp_start_cpu(platform_t, struct pcpu *cpu);
67 static void powermac_reset(platform_t);
68 static void powermac_sleep(platform_t);
69
70 static platform_method_t powermac_methods[] = {
71         PLATFORMMETHOD(platform_probe,          powermac_probe),
72         PLATFORMMETHOD(platform_attach,         powermac_attach),
73         PLATFORMMETHOD(platform_mem_regions,    powermac_mem_regions),
74         PLATFORMMETHOD(platform_timebase_freq,  powermac_timebase_freq),
75         
76         PLATFORMMETHOD(platform_smp_first_cpu,  powermac_smp_first_cpu),
77         PLATFORMMETHOD(platform_smp_next_cpu,   powermac_smp_next_cpu),
78         PLATFORMMETHOD(platform_smp_get_bsp,    powermac_smp_get_bsp),
79         PLATFORMMETHOD(platform_smp_start_cpu,  powermac_smp_start_cpu),
80
81         PLATFORMMETHOD(platform_reset,          powermac_reset),
82         PLATFORMMETHOD(platform_sleep,          powermac_sleep),
83
84         PLATFORMMETHOD_END
85 };
86
87 static platform_def_t powermac_platform = {
88         "powermac",
89         powermac_methods,
90         0
91 };
92
93 PLATFORM_DEF(powermac_platform);
94
95 static int
96 powermac_probe(platform_t plat)
97 {
98         char compat[255];
99         ssize_t compatlen;
100         char *curstr;
101         phandle_t root;
102
103         root = OF_peer(0);
104         if (root == 0)
105                 return (ENXIO);
106
107         compatlen = OF_getprop(root, "compatible", compat, sizeof(compat));
108         
109         for (curstr = compat; curstr < compat + compatlen;
110             curstr += strlen(curstr) + 1) {
111                 if (strncmp(curstr, "MacRISC", 7) == 0)
112                         return (BUS_PROBE_SPECIFIC);
113         }
114
115         return (ENXIO);
116 }
117
118 void
119 powermac_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
120     struct mem_region *avail, int *availsz)
121 {
122         phandle_t memory;
123         cell_t memoryprop[PHYS_AVAIL_SZ * 2];
124         ssize_t propsize, i, j;
125         int physacells = 1;
126
127         memory = OF_finddevice("/memory");
128         if (memory == -1)
129                 memory = OF_finddevice("/memory@0");
130
131         /* "reg" has variable #address-cells, but #size-cells is always 1 */
132         OF_getprop(OF_parent(memory), "#address-cells", &physacells,
133             sizeof(physacells));
134
135         propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop));
136         propsize /= sizeof(cell_t);
137         for (i = 0, j = 0; i < propsize; i += physacells+1, j++) {
138                 phys[j].mr_start = memoryprop[i];
139                 if (physacells == 2) {
140 #ifndef __powerpc64__
141                         /* On 32-bit PPC, ignore regions starting above 4 GB */
142                         if (memoryprop[i] != 0) {
143                                 j--;
144                                 continue;
145                         }
146 #else
147                         phys[j].mr_start <<= 32;
148 #endif
149                         phys[j].mr_start |= memoryprop[i+1];
150                 }
151                 phys[j].mr_size = memoryprop[i + physacells];
152         }
153         *physsz = j;
154
155         /* "available" always has #address-cells = 1 */
156         propsize = OF_getprop(memory, "available", memoryprop,
157             sizeof(memoryprop));
158         if (propsize <= 0) {
159                 for (i = 0; i < *physsz; i++) {
160                         avail[i].mr_start = phys[i].mr_start;
161                         avail[i].mr_size = phys[i].mr_size;
162                 }
163
164                 *availsz = *physsz;
165         } else {
166                 propsize /= sizeof(cell_t);
167                 for (i = 0, j = 0; i < propsize; i += 2, j++) {
168                         avail[j].mr_start = memoryprop[i];
169                         avail[j].mr_size = memoryprop[i + 1];
170                 }
171
172 #ifdef __powerpc64__
173                 /* Add in regions above 4 GB to the available list */
174                 for (i = 0; i < *physsz; i++) {
175                         if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
176                                 avail[j].mr_start = phys[i].mr_start;
177                                 avail[j].mr_size = phys[i].mr_size;
178                                 j++;
179                         }
180                 }
181 #endif
182                 *availsz = j;
183         }
184 }
185
186 static int
187 powermac_attach(platform_t plat)
188 {
189         phandle_t rootnode;
190         char model[32];
191
192
193         /*
194          * Quiesce Open Firmware on PowerMac11,2 and 12,1. It is
195          * necessary there to shut down a background thread doing fan
196          * management, and is harmful on other machines (it will make OF
197          * shut off power to various system components it had turned on).
198          *
199          * Note: we don't need to worry about which OF module we are
200          * using since this is called only from very early boot, within
201          * OF's boot context.
202          */
203
204         rootnode = OF_finddevice("/");
205         if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
206                 if (strcmp(model, "PowerMac11,2") == 0 ||
207                     strcmp(model, "PowerMac12,1") == 0) {
208                         ofw_quiesce();
209                 }
210         }
211
212         return (0);
213 }
214
215 static u_long
216 powermac_timebase_freq(platform_t plat, struct cpuref *cpuref)
217 {
218         phandle_t phandle;
219         int32_t ticks = -1;
220
221         phandle = cpuref->cr_hwref;
222
223         OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
224
225         if (ticks <= 0)
226                 panic("Unable to determine timebase frequency!");
227
228         return (ticks);
229 }
230
231
232 static int
233 powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu)
234 {
235         cell_t cpuid;
236         int res;
237
238         cpuref->cr_hwref = cpu;
239         res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
240
241         /*
242          * psim doesn't have a reg property, so assume 0 as for the
243          * uniprocessor case in the CHRP spec. 
244          */
245         if (res < 0) {
246                 cpuid = 0;
247         }
248
249         cpuref->cr_cpuid = cpuid & 0xff;
250         return (0);
251 }
252
253 static int
254 powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
255 {
256         char buf[8];
257         phandle_t cpu, dev, root;
258         int res;
259
260         root = OF_peer(0);
261
262         dev = OF_child(root);
263         while (dev != 0) {
264                 res = OF_getprop(dev, "name", buf, sizeof(buf));
265                 if (res > 0 && strcmp(buf, "cpus") == 0)
266                         break;
267                 dev = OF_peer(dev);
268         }
269         if (dev == 0) {
270                 /*
271                  * psim doesn't have a name property on the /cpus node,
272                  * but it can be found directly
273                  */
274                 dev = OF_finddevice("/cpus");
275                 if (dev == -1)
276                         return (ENOENT);
277         }
278
279         cpu = OF_child(dev);
280
281         while (cpu != 0) {
282                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
283                 if (res > 0 && strcmp(buf, "cpu") == 0)
284                         break;
285                 cpu = OF_peer(cpu);
286         }
287         if (cpu == 0)
288                 return (ENOENT);
289
290         return (powermac_smp_fill_cpuref(cpuref, cpu));
291 }
292
293 static int
294 powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
295 {
296         char buf[8];
297         phandle_t cpu;
298         int res;
299
300         cpu = OF_peer(cpuref->cr_hwref);
301         while (cpu != 0) {
302                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
303                 if (res > 0 && strcmp(buf, "cpu") == 0)
304                         break;
305                 cpu = OF_peer(cpu);
306         }
307         if (cpu == 0)
308                 return (ENOENT);
309
310         return (powermac_smp_fill_cpuref(cpuref, cpu));
311 }
312
313 static int
314 powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
315 {
316         ihandle_t inst;
317         phandle_t bsp, chosen;
318         int res;
319
320         chosen = OF_finddevice("/chosen");
321         if (chosen == -1)
322                 return (ENXIO);
323
324         res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
325         if (res < 0)
326                 return (ENXIO);
327
328         bsp = OF_instance_to_package(inst);
329         return (powermac_smp_fill_cpuref(cpuref, bsp));
330 }
331
332 static int
333 powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
334 {
335 #ifdef SMP
336         phandle_t cpu;
337         volatile uint8_t *rstvec;
338         static volatile uint8_t *rstvec_virtbase = NULL;
339         int res, reset, timeout;
340
341         cpu = pc->pc_hwref;
342         res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset));
343         if (res < 0) {
344                 reset = 0x58;
345
346                 switch (pc->pc_cpuid) {
347                 case 0:
348                         reset += 0x03;
349                         break;
350                 case 1:
351                         reset += 0x04;
352                         break;
353                 case 2:
354                         reset += 0x0f;
355                         break;
356                 case 3:
357                         reset += 0x10;
358                         break;
359                 default:
360                         return (ENXIO);
361                 }
362         }
363
364         ap_pcpu = pc;
365
366         if (rstvec_virtbase == NULL)
367                 rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE);
368
369         rstvec = rstvec_virtbase + reset;
370
371         *rstvec = 4;
372         powerpc_sync();
373         (void)(*rstvec);
374         powerpc_sync();
375         DELAY(1);
376         *rstvec = 0;
377         powerpc_sync();
378         (void)(*rstvec);
379         powerpc_sync();
380
381         timeout = 10000;
382         while (!pc->pc_awake && timeout--)
383                 DELAY(100);
384
385         return ((pc->pc_awake) ? 0 : EBUSY);
386 #else
387         /* No SMP support */
388         return (ENXIO);
389 #endif
390 }
391
392 static void
393 powermac_reset(platform_t platform)
394 {
395         OF_reboot();
396 }
397
398 void
399 powermac_sleep(platform_t platform)
400 {
401
402         *(unsigned long *)0x80 = 0x100;
403         cpu_sleep();
404 }
405