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