1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008 Marcel Moolenaar
5 * Copyright (c) 2009 Nathan Whitehorn
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/kernel.h>
36#include <sys/bus.h>
37#include <sys/pcpu.h>
38#include <sys/proc.h>
39#include <sys/smp.h>
40#include <vm/vm.h>
41#include <vm/pmap.h>
42
43#include <machine/altivec.h>	/* For save_vec() */
44#include <machine/bus.h>
45#include <machine/cpu.h>
46#include <machine/fpu.h>	/* For save_fpu() */
47#include <machine/hid.h>
48#include <machine/platformvar.h>
49#include <machine/setjmp.h>
50#include <machine/smp.h>
51#include <machine/spr.h>
52
53#include <dev/ofw/openfirm.h>
54#include <machine/ofw_machdep.h>
55
56#include "platform_if.h"
57
58extern void *ap_pcpu;
59
60static int powermac_probe(platform_t);
61static int powermac_attach(platform_t);
62void powermac_mem_regions(platform_t, struct mem_region *phys, int *physsz,
63    struct mem_region *avail, int *availsz);
64static u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref);
65static int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref);
66static int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref);
67static int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref);
68static int powermac_smp_start_cpu(platform_t, struct pcpu *cpu);
69static void powermac_smp_timebase_sync(platform_t, u_long tb, int ap);
70static void powermac_reset(platform_t);
71static void powermac_sleep(platform_t);
72
73static platform_method_t powermac_methods[] = {
74	PLATFORMMETHOD(platform_probe, 		powermac_probe),
75	PLATFORMMETHOD(platform_attach,		powermac_attach),
76	PLATFORMMETHOD(platform_mem_regions,	powermac_mem_regions),
77	PLATFORMMETHOD(platform_timebase_freq,	powermac_timebase_freq),
78
79	PLATFORMMETHOD(platform_smp_first_cpu,	powermac_smp_first_cpu),
80	PLATFORMMETHOD(platform_smp_next_cpu,	powermac_smp_next_cpu),
81	PLATFORMMETHOD(platform_smp_get_bsp,	powermac_smp_get_bsp),
82	PLATFORMMETHOD(platform_smp_start_cpu,	powermac_smp_start_cpu),
83	PLATFORMMETHOD(platform_smp_timebase_sync, powermac_smp_timebase_sync),
84
85	PLATFORMMETHOD(platform_reset,		powermac_reset),
86	PLATFORMMETHOD(platform_sleep,		powermac_sleep),
87
88	PLATFORMMETHOD_END
89};
90
91static platform_def_t powermac_platform = {
92	"powermac",
93	powermac_methods,
94	0
95};
96
97PLATFORM_DEF(powermac_platform);
98
99static int
100powermac_probe(platform_t plat)
101{
102	char compat[255];
103	ssize_t compatlen;
104	char *curstr;
105	phandle_t root;
106
107	root = OF_peer(0);
108	if (root == 0)
109		return (ENXIO);
110
111	compatlen = OF_getprop(root, "compatible", compat, sizeof(compat));
112
113	for (curstr = compat; curstr < compat + compatlen;
114	    curstr += strlen(curstr) + 1) {
115		if (strncmp(curstr, "MacRISC", 7) == 0)
116			return (BUS_PROBE_SPECIFIC);
117	}
118
119	return (ENXIO);
120}
121
122void
123powermac_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
124    struct mem_region *avail, int *availsz)
125{
126	phandle_t memory;
127	cell_t memoryprop[PHYS_AVAIL_SZ * 2];
128	ssize_t propsize, i, j;
129	int physacells = 1;
130
131	memory = OF_finddevice("/memory");
132	if (memory == -1)
133		memory = OF_finddevice("/memory@0");
134
135	/* "reg" has variable #address-cells, but #size-cells is always 1 */
136	OF_getprop(OF_parent(memory), "#address-cells", &physacells,
137	    sizeof(physacells));
138
139	propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop));
140	propsize /= sizeof(cell_t);
141	for (i = 0, j = 0; i < propsize; i += physacells+1, j++) {
142		phys[j].mr_start = memoryprop[i];
143		if (physacells == 2) {
144#ifndef __powerpc64__
145			/* On 32-bit PPC, ignore regions starting above 4 GB */
146			if (memoryprop[i] != 0) {
147				j--;
148				continue;
149			}
150#else
151			phys[j].mr_start <<= 32;
152#endif
153			phys[j].mr_start |= memoryprop[i+1];
154		}
155		phys[j].mr_size = memoryprop[i + physacells];
156	}
157	*physsz = j;
158
159	/* "available" always has #address-cells = 1 */
160	propsize = OF_getprop(memory, "available", memoryprop,
161	    sizeof(memoryprop));
162	if (propsize <= 0) {
163		for (i = 0; i < *physsz; i++) {
164			avail[i].mr_start = phys[i].mr_start;
165			avail[i].mr_size = phys[i].mr_size;
166		}
167
168		*availsz = *physsz;
169	} else {
170		propsize /= sizeof(cell_t);
171		for (i = 0, j = 0; i < propsize; i += 2, j++) {
172			avail[j].mr_start = memoryprop[i];
173			avail[j].mr_size = memoryprop[i + 1];
174		}
175
176#ifdef __powerpc64__
177		/* Add in regions above 4 GB to the available list */
178		for (i = 0; i < *physsz; i++) {
179			if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
180				avail[j].mr_start = phys[i].mr_start;
181				avail[j].mr_size = phys[i].mr_size;
182				j++;
183			}
184		}
185#endif
186		*availsz = j;
187	}
188}
189
190static int
191powermac_attach(platform_t plat)
192{
193	phandle_t rootnode;
194	char model[32];
195
196
197	/*
198	 * Quiesce Open Firmware on PowerMac11,2 and 12,1. It is
199	 * necessary there to shut down a background thread doing fan
200	 * management, and is harmful on other machines (it will make OF
201	 * shut off power to various system components it had turned on).
202	 *
203	 * Note: we don't need to worry about which OF module we are
204	 * using since this is called only from very early boot, within
205	 * OF's boot context.
206	 */
207
208	rootnode = OF_finddevice("/");
209	if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
210		if (strcmp(model, "PowerMac11,2") == 0 ||
211		    strcmp(model, "PowerMac12,1") == 0) {
212			ofw_quiesce();
213		}
214	}
215
216	return (0);
217}
218
219static u_long
220powermac_timebase_freq(platform_t plat, struct cpuref *cpuref)
221{
222	phandle_t phandle;
223	int32_t ticks = -1;
224
225	phandle = cpuref->cr_hwref;
226
227	OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
228
229	if (ticks <= 0)
230		panic("Unable to determine timebase frequency!");
231
232	return (ticks);
233}
234
235
236static int
237powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu)
238{
239	cell_t cpuid;
240	int res;
241
242	cpuref->cr_hwref = cpu;
243	res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
244
245	/*
246	 * psim doesn't have a reg property, so assume 0 as for the
247	 * uniprocessor case in the CHRP spec.
248	 */
249	if (res < 0) {
250		cpuid = 0;
251	}
252
253	cpuref->cr_cpuid = cpuid & 0xff;
254	return (0);
255}
256
257static int
258powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
259{
260	char buf[8];
261	phandle_t cpu, dev, root;
262	int res;
263
264	root = OF_peer(0);
265
266	dev = OF_child(root);
267	while (dev != 0) {
268		res = OF_getprop(dev, "name", buf, sizeof(buf));
269		if (res > 0 && strcmp(buf, "cpus") == 0)
270			break;
271		dev = OF_peer(dev);
272	}
273	if (dev == 0) {
274		/*
275		 * psim doesn't have a name property on the /cpus node,
276		 * but it can be found directly
277		 */
278		dev = OF_finddevice("/cpus");
279		if (dev == -1)
280			return (ENOENT);
281	}
282
283	cpu = OF_child(dev);
284
285	while (cpu != 0) {
286		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
287		if (res > 0 && strcmp(buf, "cpu") == 0)
288			break;
289		cpu = OF_peer(cpu);
290	}
291	if (cpu == 0)
292		return (ENOENT);
293
294	return (powermac_smp_fill_cpuref(cpuref, cpu));
295}
296
297static int
298powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
299{
300	char buf[8];
301	phandle_t cpu;
302	int res;
303
304	cpu = OF_peer(cpuref->cr_hwref);
305	while (cpu != 0) {
306		res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
307		if (res > 0 && strcmp(buf, "cpu") == 0)
308			break;
309		cpu = OF_peer(cpu);
310	}
311	if (cpu == 0)
312		return (ENOENT);
313
314	return (powermac_smp_fill_cpuref(cpuref, cpu));
315}
316
317static int
318powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
319{
320	ihandle_t inst;
321	phandle_t bsp, chosen;
322	int res;
323
324	chosen = OF_finddevice("/chosen");
325	if (chosen == -1)
326		return (ENXIO);
327
328	res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
329	if (res < 0)
330		return (ENXIO);
331
332	bsp = OF_instance_to_package(inst);
333	return (powermac_smp_fill_cpuref(cpuref, bsp));
334}
335
336static int
337powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
338{
339#ifdef SMP
340	phandle_t cpu;
341	volatile uint8_t *rstvec;
342	static volatile uint8_t *rstvec_virtbase = NULL;
343	int res, reset, timeout;
344
345	cpu = pc->pc_hwref;
346	res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset));
347	if (res < 0) {
348		reset = 0x58;
349
350		switch (pc->pc_cpuid) {
351		case 0:
352			reset += 0x03;
353			break;
354		case 1:
355			reset += 0x04;
356			break;
357		case 2:
358			reset += 0x0f;
359			break;
360		case 3:
361			reset += 0x10;
362			break;
363		default:
364			return (ENXIO);
365		}
366	}
367
368	ap_pcpu = pc;
369
370	if (rstvec_virtbase == NULL)
371		rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE);
372
373	rstvec = rstvec_virtbase + reset;
374
375	*rstvec = 4;
376	powerpc_sync();
377	(void)(*rstvec);
378	powerpc_sync();
379	DELAY(1);
380	*rstvec = 0;
381	powerpc_sync();
382	(void)(*rstvec);
383	powerpc_sync();
384
385	timeout = 10000;
386	while (!pc->pc_awake && timeout--)
387		DELAY(100);
388
389	return ((pc->pc_awake) ? 0 : EBUSY);
390#else
391	/* No SMP support */
392	return (ENXIO);
393#endif
394}
395
396static void
397powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap)
398{
399
400	mttb(tb);
401}
402
403static void
404powermac_reset(platform_t platform)
405{
406	OF_reboot();
407}
408
409void
410powermac_sleep(platform_t platform)
411{
412
413	*(unsigned long *)0x80 = 0x100;
414	cpu_sleep();
415}
416
417