xref: /freebsd/sys/powerpc/powermac/platform_powermac.c (revision f4b37ed0f8b307b1f3f0f630ca725d68f1dff30d)
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 	if (memory == -1)
130 		memory = OF_finddevice("/memory@0");
131 
132 	/* "reg" has variable #address-cells, but #size-cells is always 1 */
133 	OF_getprop(OF_parent(memory), "#address-cells", &physacells,
134 	    sizeof(physacells));
135 
136 	propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop));
137 	propsize /= sizeof(cell_t);
138 	for (i = 0, j = 0; i < propsize; i += physacells+1, j++) {
139 		phys[j].mr_start = memoryprop[i];
140 		if (physacells == 2) {
141 #ifndef __powerpc64__
142 			/* On 32-bit PPC, ignore regions starting above 4 GB */
143 			if (memoryprop[i] != 0) {
144 				j--;
145 				continue;
146 			}
147 #else
148 			phys[j].mr_start <<= 32;
149 #endif
150 			phys[j].mr_start |= memoryprop[i+1];
151 		}
152 		phys[j].mr_size = memoryprop[i + physacells];
153 	}
154 	*physsz = j;
155 
156 	/* "available" always has #address-cells = 1 */
157 	propsize = OF_getprop(memory, "available", memoryprop,
158 	    sizeof(memoryprop));
159 	if (propsize <= 0) {
160 		for (i = 0; i < *physsz; i++) {
161 			avail[i].mr_start = phys[i].mr_start;
162 			avail[i].mr_size = phys[i].mr_size;
163 		}
164 
165 		*availsz = *physsz;
166 	} else {
167 		propsize /= sizeof(cell_t);
168 		for (i = 0, j = 0; i < propsize; i += 2, j++) {
169 			avail[j].mr_start = memoryprop[i];
170 			avail[j].mr_size = memoryprop[i + 1];
171 		}
172 
173 #ifdef __powerpc64__
174 		/* Add in regions above 4 GB to the available list */
175 		for (i = 0; i < *physsz; i++) {
176 			if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
177 				avail[j].mr_start = phys[i].mr_start;
178 				avail[j].mr_size = phys[i].mr_size;
179 				j++;
180 			}
181 		}
182 #endif
183 		*availsz = j;
184 	}
185 }
186 
187 static int
188 powermac_attach(platform_t plat)
189 {
190 	phandle_t rootnode;
191 	char model[32];
192 
193 
194 	/*
195 	 * Quiesce Open Firmware on PowerMac11,2 and 12,1. It is
196 	 * necessary there to shut down a background thread doing fan
197 	 * management, and is harmful on other machines (it will make OF
198 	 * shut off power to various system components it had turned on).
199 	 *
200 	 * Note: we don't need to worry about which OF module we are
201 	 * using since this is called only from very early boot, within
202 	 * OF's boot context.
203 	 */
204 
205 	rootnode = OF_finddevice("/");
206 	if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
207 		if (strcmp(model, "PowerMac11,2") == 0 ||
208 		    strcmp(model, "PowerMac12,1") == 0) {
209 			ofw_quiesce();
210 		}
211 	}
212 
213 	return (0);
214 }
215 
216 static u_long
217 powermac_timebase_freq(platform_t plat, struct cpuref *cpuref)
218 {
219 	phandle_t phandle;
220 	int32_t ticks = -1;
221 
222 	phandle = cpuref->cr_hwref;
223 
224 	OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
225 
226 	if (ticks <= 0)
227 		panic("Unable to determine timebase frequency!");
228 
229 	return (ticks);
230 }
231 
232 
233 static int
234 powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu)
235 {
236 	cell_t cpuid;
237 	int res;
238 
239 	cpuref->cr_hwref = cpu;
240 	res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
241 
242 	/*
243 	 * psim doesn't have a reg property, so assume 0 as for the
244 	 * uniprocessor case in the CHRP spec.
245 	 */
246 	if (res < 0) {
247 		cpuid = 0;
248 	}
249 
250 	cpuref->cr_cpuid = cpuid & 0xff;
251 	return (0);
252 }
253 
254 static int
255 powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
256 {
257 	char buf[8];
258 	phandle_t cpu, dev, root;
259 	int res;
260 
261 	root = OF_peer(0);
262 
263 	dev = OF_child(root);
264 	while (dev != 0) {
265 		res = OF_getprop(dev, "name", buf, sizeof(buf));
266 		if (res > 0 && strcmp(buf, "cpus") == 0)
267 			break;
268 		dev = OF_peer(dev);
269 	}
270 	if (dev == 0) {
271 		/*
272 		 * psim doesn't have a name property on the /cpus node,
273 		 * but it can be found directly
274 		 */
275 		dev = OF_finddevice("/cpus");
276 		if (dev == -1)
277 			return (ENOENT);
278 	}
279 
280 	cpu = OF_child(dev);
281 
282 	while (cpu != 0) {
283 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
284 		if (res > 0 && strcmp(buf, "cpu") == 0)
285 			break;
286 		cpu = OF_peer(cpu);
287 	}
288 	if (cpu == 0)
289 		return (ENOENT);
290 
291 	return (powermac_smp_fill_cpuref(cpuref, cpu));
292 }
293 
294 static int
295 powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
296 {
297 	char buf[8];
298 	phandle_t cpu;
299 	int res;
300 
301 	cpu = OF_peer(cpuref->cr_hwref);
302 	while (cpu != 0) {
303 		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
304 		if (res > 0 && strcmp(buf, "cpu") == 0)
305 			break;
306 		cpu = OF_peer(cpu);
307 	}
308 	if (cpu == 0)
309 		return (ENOENT);
310 
311 	return (powermac_smp_fill_cpuref(cpuref, cpu));
312 }
313 
314 static int
315 powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
316 {
317 	ihandle_t inst;
318 	phandle_t bsp, chosen;
319 	int res;
320 
321 	chosen = OF_finddevice("/chosen");
322 	if (chosen == -1)
323 		return (ENXIO);
324 
325 	res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
326 	if (res < 0)
327 		return (ENXIO);
328 
329 	bsp = OF_instance_to_package(inst);
330 	return (powermac_smp_fill_cpuref(cpuref, bsp));
331 }
332 
333 static int
334 powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
335 {
336 #ifdef SMP
337 	phandle_t cpu;
338 	volatile uint8_t *rstvec;
339 	static volatile uint8_t *rstvec_virtbase = NULL;
340 	int res, reset, timeout;
341 
342 	cpu = pc->pc_hwref;
343 	res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset));
344 	if (res < 0) {
345 		reset = 0x58;
346 
347 		switch (pc->pc_cpuid) {
348 		case 0:
349 			reset += 0x03;
350 			break;
351 		case 1:
352 			reset += 0x04;
353 			break;
354 		case 2:
355 			reset += 0x0f;
356 			break;
357 		case 3:
358 			reset += 0x10;
359 			break;
360 		default:
361 			return (ENXIO);
362 		}
363 	}
364 
365 	ap_pcpu = pc;
366 
367 	if (rstvec_virtbase == NULL)
368 		rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE);
369 
370 	rstvec = rstvec_virtbase + reset;
371 
372 	*rstvec = 4;
373 	powerpc_sync();
374 	(void)(*rstvec);
375 	powerpc_sync();
376 	DELAY(1);
377 	*rstvec = 0;
378 	powerpc_sync();
379 	(void)(*rstvec);
380 	powerpc_sync();
381 
382 	timeout = 10000;
383 	while (!pc->pc_awake && timeout--)
384 		DELAY(100);
385 
386 	return ((pc->pc_awake) ? 0 : EBUSY);
387 #else
388 	/* No SMP support */
389 	return (ENXIO);
390 #endif
391 }
392 
393 static void
394 powermac_reset(platform_t platform)
395 {
396 	OF_reboot();
397 }
398 
399 void
400 powermac_sleep(platform_t platform)
401 {
402 
403 	*(unsigned long *)0x80 = 0x100;
404 	cpu_sleep();
405 }
406 
407