1192067Snwhitehorn/*-
2192067Snwhitehorn * Copyright (c) 2008 Marcel Moolenaar
3192067Snwhitehorn * Copyright (c) 2009 Nathan Whitehorn
4192067Snwhitehorn * All rights reserved.
5192067Snwhitehorn *
6192067Snwhitehorn * Redistribution and use in source and binary forms, with or without
7192067Snwhitehorn * modification, are permitted provided that the following conditions
8192067Snwhitehorn * are met:
9192067Snwhitehorn *
10192067Snwhitehorn * 1. Redistributions of source code must retain the above copyright
11192067Snwhitehorn *    notice, this list of conditions and the following disclaimer.
12192067Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright
13192067Snwhitehorn *    notice, this list of conditions and the following disclaimer in the
14192067Snwhitehorn *    documentation and/or other materials provided with the distribution.
15192067Snwhitehorn *
16192067Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17192067Snwhitehorn * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18192067Snwhitehorn * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19192067Snwhitehorn * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20192067Snwhitehorn * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21192067Snwhitehorn * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22192067Snwhitehorn * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23192067Snwhitehorn * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24192067Snwhitehorn * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25192067Snwhitehorn * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26192067Snwhitehorn */
27192067Snwhitehorn
28192067Snwhitehorn#include <sys/cdefs.h>
29192067Snwhitehorn__FBSDID("$FreeBSD$");
30192067Snwhitehorn
31192067Snwhitehorn#include <sys/param.h>
32192067Snwhitehorn#include <sys/systm.h>
33192067Snwhitehorn#include <sys/kernel.h>
34192067Snwhitehorn#include <sys/bus.h>
35192067Snwhitehorn#include <sys/pcpu.h>
36192067Snwhitehorn#include <sys/proc.h>
37192067Snwhitehorn#include <sys/smp.h>
38198212Snwhitehorn#include <vm/vm.h>
39198212Snwhitehorn#include <vm/pmap.h>
40192067Snwhitehorn
41262675Sjhibbits#include <machine/altivec.h>	/* For save_vec() */
42192067Snwhitehorn#include <machine/bus.h>
43192067Snwhitehorn#include <machine/cpu.h>
44262675Sjhibbits#include <machine/fpu.h>	/* For save_fpu() */
45192067Snwhitehorn#include <machine/hid.h>
46192067Snwhitehorn#include <machine/platformvar.h>
47198212Snwhitehorn#include <machine/pmap.h>
48262675Sjhibbits#include <machine/setjmp.h>
49192067Snwhitehorn#include <machine/smp.h>
50192067Snwhitehorn#include <machine/spr.h>
51192067Snwhitehorn
52192067Snwhitehorn#include <dev/ofw/openfirm.h>
53192067Snwhitehorn#include <machine/ofw_machdep.h>
54192067Snwhitehorn
55192067Snwhitehorn#include "platform_if.h"
56192067Snwhitehorn
57192067Snwhitehornextern void *ap_pcpu;
58192067Snwhitehorn
59212054Snwhitehornstatic int powermac_probe(platform_t);
60255910Snwhitehornstatic int powermac_attach(platform_t);
61266020Sianvoid powermac_mem_regions(platform_t, struct mem_region *phys, int *physsz,
62266020Sian    struct mem_region *avail, int *availsz);
63212054Snwhitehornstatic u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref);
64212054Snwhitehornstatic int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref);
65212054Snwhitehornstatic int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref);
66212054Snwhitehornstatic int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref);
67212054Snwhitehornstatic int powermac_smp_start_cpu(platform_t, struct pcpu *cpu);
68212054Snwhitehornstatic void powermac_reset(platform_t);
69262675Sjhibbitsstatic void powermac_sleep(platform_t);
70192067Snwhitehorn
71212054Snwhitehornstatic platform_method_t powermac_methods[] = {
72212054Snwhitehorn	PLATFORMMETHOD(platform_probe, 		powermac_probe),
73255910Snwhitehorn	PLATFORMMETHOD(platform_attach,		powermac_attach),
74212054Snwhitehorn	PLATFORMMETHOD(platform_mem_regions,	powermac_mem_regions),
75212054Snwhitehorn	PLATFORMMETHOD(platform_timebase_freq,	powermac_timebase_freq),
76192067Snwhitehorn
77212054Snwhitehorn	PLATFORMMETHOD(platform_smp_first_cpu,	powermac_smp_first_cpu),
78212054Snwhitehorn	PLATFORMMETHOD(platform_smp_next_cpu,	powermac_smp_next_cpu),
79212054Snwhitehorn	PLATFORMMETHOD(platform_smp_get_bsp,	powermac_smp_get_bsp),
80212054Snwhitehorn	PLATFORMMETHOD(platform_smp_start_cpu,	powermac_smp_start_cpu),
81192067Snwhitehorn
82212054Snwhitehorn	PLATFORMMETHOD(platform_reset,		powermac_reset),
83262675Sjhibbits	PLATFORMMETHOD(platform_sleep,		powermac_sleep),
84212054Snwhitehorn
85246732Srpaulo	PLATFORMMETHOD_END
86192067Snwhitehorn};
87192067Snwhitehorn
88212054Snwhitehornstatic platform_def_t powermac_platform = {
89212054Snwhitehorn	"powermac",
90212054Snwhitehorn	powermac_methods,
91192067Snwhitehorn	0
92192067Snwhitehorn};
93192067Snwhitehorn
94212054SnwhitehornPLATFORM_DEF(powermac_platform);
95192067Snwhitehorn
96192067Snwhitehornstatic int
97212054Snwhitehornpowermac_probe(platform_t plat)
98192067Snwhitehorn{
99255420Snwhitehorn	char compat[255];
100255420Snwhitehorn	ssize_t compatlen;
101255420Snwhitehorn	char *curstr;
102255420Snwhitehorn	phandle_t root;
103192067Snwhitehorn
104255420Snwhitehorn	root = OF_peer(0);
105255420Snwhitehorn	if (root == 0)
106255420Snwhitehorn		return (ENXIO);
107255420Snwhitehorn
108255420Snwhitehorn	compatlen = OF_getprop(root, "compatible", compat, sizeof(compat));
109255420Snwhitehorn
110255420Snwhitehorn	for (curstr = compat; curstr < compat + compatlen;
111255420Snwhitehorn	    curstr += strlen(curstr) + 1) {
112255420Snwhitehorn		if (strncmp(curstr, "MacRISC", 7) == 0)
113255420Snwhitehorn			return (BUS_PROBE_SPECIFIC);
114255420Snwhitehorn	}
115255420Snwhitehorn
116192067Snwhitehorn	return (ENXIO);
117192067Snwhitehorn}
118192067Snwhitehorn
119192067Snwhitehornvoid
120266020Sianpowermac_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
121266020Sian    struct mem_region *avail, int *availsz)
122192067Snwhitehorn{
123266020Sian	phandle_t memory;
124266020Sian	cell_t memoryprop[PHYS_AVAIL_SZ * 2];
125266020Sian	ssize_t propsize, i, j;
126266020Sian	int physacells = 1;
127266020Sian
128266020Sian	memory = OF_finddevice("/memory");
129266020Sian
130266020Sian	/* "reg" has variable #address-cells, but #size-cells is always 1 */
131266020Sian	OF_getprop(OF_parent(memory), "#address-cells", &physacells,
132266020Sian	    sizeof(physacells));
133266020Sian
134266020Sian	propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop));
135266020Sian	propsize /= sizeof(cell_t);
136266020Sian	for (i = 0, j = 0; i < propsize; i += physacells+1, j++) {
137266020Sian		phys[j].mr_start = memoryprop[i];
138266020Sian		if (physacells == 2) {
139266020Sian#ifndef __powerpc64__
140266020Sian			/* On 32-bit PPC, ignore regions starting above 4 GB */
141266020Sian			if (memoryprop[i] != 0) {
142266020Sian				j--;
143266020Sian				continue;
144266020Sian			}
145266020Sian#else
146266020Sian			phys[j].mr_start <<= 32;
147266020Sian#endif
148266020Sian			phys[j].mr_start |= memoryprop[i+1];
149266020Sian		}
150266020Sian		phys[j].mr_size = memoryprop[i + physacells];
151266020Sian	}
152266020Sian	*physsz = j;
153266020Sian
154266020Sian	/* "available" always has #address-cells = 1 */
155266020Sian	propsize = OF_getprop(memory, "available", memoryprop,
156266020Sian	    sizeof(memoryprop));
157266020Sian	propsize /= sizeof(cell_t);
158266020Sian	for (i = 0, j = 0; i < propsize; i += 2, j++) {
159266020Sian		avail[j].mr_start = memoryprop[i];
160266020Sian		avail[j].mr_size = memoryprop[i + 1];
161266020Sian	}
162266020Sian
163266020Sian#ifdef __powerpc64__
164266020Sian	/* Add in regions above 4 GB to the available list */
165266020Sian	for (i = 0; i < *physsz; i++) {
166266020Sian		if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
167266020Sian			avail[j].mr_start = phys[i].mr_start;
168266020Sian			avail[j].mr_size = phys[i].mr_size;
169266020Sian			j++;
170266020Sian		}
171266020Sian	}
172266020Sian#endif
173266020Sian	*availsz = j;
174192067Snwhitehorn}
175192067Snwhitehorn
176255910Snwhitehornstatic int
177255910Snwhitehornpowermac_attach(platform_t plat)
178255910Snwhitehorn{
179255910Snwhitehorn	phandle_t rootnode;
180255910Snwhitehorn	char model[32];
181255910Snwhitehorn
182255910Snwhitehorn
183255910Snwhitehorn	/*
184255910Snwhitehorn	 * Quiesce Open Firmware on PowerMac11,2 and 12,1. It is
185255910Snwhitehorn	 * necessary there to shut down a background thread doing fan
186255910Snwhitehorn	 * management, and is harmful on other machines (it will make OF
187255910Snwhitehorn	 * shut off power to various system components it had turned on).
188255910Snwhitehorn	 *
189255910Snwhitehorn	 * Note: we don't need to worry about which OF module we are
190255910Snwhitehorn	 * using since this is called only from very early boot, within
191255910Snwhitehorn	 * OF's boot context.
192255910Snwhitehorn	 */
193255910Snwhitehorn
194255910Snwhitehorn	rootnode = OF_finddevice("/");
195255910Snwhitehorn	if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
196255910Snwhitehorn		if (strcmp(model, "PowerMac11,2") == 0 ||
197255910Snwhitehorn		    strcmp(model, "PowerMac12,1") == 0) {
198255910Snwhitehorn			ofw_quiesce();
199255910Snwhitehorn		}
200255910Snwhitehorn	}
201255910Snwhitehorn
202255910Snwhitehorn	return (0);
203255910Snwhitehorn}
204255910Snwhitehorn
205192067Snwhitehornstatic u_long
206212054Snwhitehornpowermac_timebase_freq(platform_t plat, struct cpuref *cpuref)
207192067Snwhitehorn{
208192067Snwhitehorn	phandle_t phandle;
209209851Snwhitehorn	int32_t ticks = -1;
210192067Snwhitehorn
211192067Snwhitehorn	phandle = cpuref->cr_hwref;
212192067Snwhitehorn
213192067Snwhitehorn	OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
214192067Snwhitehorn
215192067Snwhitehorn	if (ticks <= 0)
216192067Snwhitehorn		panic("Unable to determine timebase frequency!");
217192067Snwhitehorn
218192067Snwhitehorn	return (ticks);
219192067Snwhitehorn}
220192067Snwhitehorn
221192067Snwhitehorn
222192067Snwhitehornstatic int
223212054Snwhitehornpowermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu)
224192067Snwhitehorn{
225260673Sjhibbits	cell_t cpuid;
226260673Sjhibbits	int res;
227192067Snwhitehorn
228192067Snwhitehorn	cpuref->cr_hwref = cpu;
229192067Snwhitehorn	res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
230192067Snwhitehorn
231193909Sgrehan	/*
232193909Sgrehan	 * psim doesn't have a reg property, so assume 0 as for the
233193909Sgrehan	 * uniprocessor case in the CHRP spec.
234193909Sgrehan	 */
235193909Sgrehan	if (res < 0) {
236193909Sgrehan		cpuid = 0;
237193909Sgrehan	}
238193909Sgrehan
239192067Snwhitehorn	cpuref->cr_cpuid = cpuid & 0xff;
240192067Snwhitehorn	return (0);
241192067Snwhitehorn}
242192067Snwhitehorn
243192067Snwhitehornstatic int
244212054Snwhitehornpowermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
245192067Snwhitehorn{
246192067Snwhitehorn	char buf[8];
247192067Snwhitehorn	phandle_t cpu, dev, root;
248192067Snwhitehorn	int res;
249192067Snwhitehorn
250192067Snwhitehorn	root = OF_peer(0);
251192067Snwhitehorn
252192067Snwhitehorn	dev = OF_child(root);
253192067Snwhitehorn	while (dev != 0) {
254192067Snwhitehorn		res = OF_getprop(dev, "name", buf, sizeof(buf));
255192067Snwhitehorn		if (res > 0 && strcmp(buf, "cpus") == 0)
256192067Snwhitehorn			break;
257192067Snwhitehorn		dev = OF_peer(dev);
258192067Snwhitehorn	}
259193909Sgrehan	if (dev == 0) {
260193909Sgrehan		/*
261193909Sgrehan		 * psim doesn't have a name property on the /cpus node,
262193909Sgrehan		 * but it can be found directly
263193909Sgrehan		 */
264193909Sgrehan		dev = OF_finddevice("/cpus");
265228201Sjchandra		if (dev == -1)
266193909Sgrehan			return (ENOENT);
267193909Sgrehan	}
268192067Snwhitehorn
269192067Snwhitehorn	cpu = OF_child(dev);
270193909Sgrehan
271192067Snwhitehorn	while (cpu != 0) {
272192067Snwhitehorn		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
273192067Snwhitehorn		if (res > 0 && strcmp(buf, "cpu") == 0)
274192067Snwhitehorn			break;
275192067Snwhitehorn		cpu = OF_peer(cpu);
276192067Snwhitehorn	}
277192067Snwhitehorn	if (cpu == 0)
278192067Snwhitehorn		return (ENOENT);
279192067Snwhitehorn
280212054Snwhitehorn	return (powermac_smp_fill_cpuref(cpuref, cpu));
281192067Snwhitehorn}
282192067Snwhitehorn
283192067Snwhitehornstatic int
284212054Snwhitehornpowermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
285192067Snwhitehorn{
286192067Snwhitehorn	char buf[8];
287192067Snwhitehorn	phandle_t cpu;
288192067Snwhitehorn	int res;
289192067Snwhitehorn
290192067Snwhitehorn	cpu = OF_peer(cpuref->cr_hwref);
291192067Snwhitehorn	while (cpu != 0) {
292192067Snwhitehorn		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
293192067Snwhitehorn		if (res > 0 && strcmp(buf, "cpu") == 0)
294192067Snwhitehorn			break;
295192067Snwhitehorn		cpu = OF_peer(cpu);
296192067Snwhitehorn	}
297192067Snwhitehorn	if (cpu == 0)
298192067Snwhitehorn		return (ENOENT);
299192067Snwhitehorn
300212054Snwhitehorn	return (powermac_smp_fill_cpuref(cpuref, cpu));
301192067Snwhitehorn}
302192067Snwhitehorn
303192067Snwhitehornstatic int
304212054Snwhitehornpowermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
305192067Snwhitehorn{
306192067Snwhitehorn	ihandle_t inst;
307192067Snwhitehorn	phandle_t bsp, chosen;
308192067Snwhitehorn	int res;
309192067Snwhitehorn
310192067Snwhitehorn	chosen = OF_finddevice("/chosen");
311228201Sjchandra	if (chosen == -1)
312192067Snwhitehorn		return (ENXIO);
313192067Snwhitehorn
314192067Snwhitehorn	res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
315192067Snwhitehorn	if (res < 0)
316192067Snwhitehorn		return (ENXIO);
317192067Snwhitehorn
318192067Snwhitehorn	bsp = OF_instance_to_package(inst);
319212054Snwhitehorn	return (powermac_smp_fill_cpuref(cpuref, bsp));
320192067Snwhitehorn}
321192067Snwhitehorn
322192067Snwhitehornstatic int
323212054Snwhitehornpowermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
324192067Snwhitehorn{
325192067Snwhitehorn#ifdef SMP
326192067Snwhitehorn	phandle_t cpu;
327192067Snwhitehorn	volatile uint8_t *rstvec;
328198212Snwhitehorn	static volatile uint8_t *rstvec_virtbase = NULL;
329192067Snwhitehorn	int res, reset, timeout;
330192067Snwhitehorn
331192067Snwhitehorn	cpu = pc->pc_hwref;
332192067Snwhitehorn	res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset));
333209114Snwhitehorn	if (res < 0) {
334209114Snwhitehorn		reset = 0x58;
335192067Snwhitehorn
336209114Snwhitehorn		switch (pc->pc_cpuid) {
337209114Snwhitehorn		case 0:
338209114Snwhitehorn			reset += 0x03;
339209114Snwhitehorn			break;
340209114Snwhitehorn		case 1:
341209114Snwhitehorn			reset += 0x04;
342209114Snwhitehorn			break;
343209114Snwhitehorn		case 2:
344209114Snwhitehorn			reset += 0x0f;
345209114Snwhitehorn			break;
346209853Snwhitehorn		case 3:
347209114Snwhitehorn			reset += 0x10;
348209114Snwhitehorn			break;
349209114Snwhitehorn		default:
350209114Snwhitehorn			return (ENXIO);
351209114Snwhitehorn		}
352209114Snwhitehorn	}
353209114Snwhitehorn
354192067Snwhitehorn	ap_pcpu = pc;
355192067Snwhitehorn
356198212Snwhitehorn	if (rstvec_virtbase == NULL)
357198212Snwhitehorn		rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE);
358192067Snwhitehorn
359198212Snwhitehorn	rstvec = rstvec_virtbase + reset;
360198212Snwhitehorn
361192067Snwhitehorn	*rstvec = 4;
362209114Snwhitehorn	powerpc_sync();
363198378Snwhitehorn	(void)(*rstvec);
364192067Snwhitehorn	powerpc_sync();
365192067Snwhitehorn	DELAY(1);
366192067Snwhitehorn	*rstvec = 0;
367209114Snwhitehorn	powerpc_sync();
368198378Snwhitehorn	(void)(*rstvec);
369192067Snwhitehorn	powerpc_sync();
370192067Snwhitehorn
371198378Snwhitehorn	timeout = 10000;
372192067Snwhitehorn	while (!pc->pc_awake && timeout--)
373192067Snwhitehorn		DELAY(100);
374192067Snwhitehorn
375192067Snwhitehorn	return ((pc->pc_awake) ? 0 : EBUSY);
376192067Snwhitehorn#else
377192067Snwhitehorn	/* No SMP support */
378192067Snwhitehorn	return (ENXIO);
379192067Snwhitehorn#endif
380192067Snwhitehorn}
381192067Snwhitehorn
382212054Snwhitehornstatic void
383212054Snwhitehornpowermac_reset(platform_t platform)
384212054Snwhitehorn{
385212054Snwhitehorn	OF_reboot();
386212054Snwhitehorn}
387212054Snwhitehorn
388262675Sjhibbitsvoid
389262675Sjhibbitspowermac_sleep(platform_t platform)
390262675Sjhibbits{
391262675Sjhibbits
392262675Sjhibbits	*(unsigned long *)0x80 = 0x100;
393262675Sjhibbits	cpu_sleep();
394262675Sjhibbits}
395262675Sjhibbits
396