xref: /freebsd/sys/powerpc/powermac/platform_powermac.c (revision a5921bc3653e2e286715e6fe8d473ec0d02da38c)
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 
406