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