Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/powermac/platform_powermac.c
39562 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
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/param.h>
31
#include <sys/systm.h>
32
#include <sys/kernel.h>
33
#include <sys/bus.h>
34
#include <sys/pcpu.h>
35
#include <sys/proc.h>
36
#include <sys/smp.h>
37
#include <vm/vm.h>
38
#include <vm/pmap.h>
39
40
#include <machine/altivec.h> /* For save_vec() */
41
#include <machine/bus.h>
42
#include <machine/cpu.h>
43
#include <machine/fpu.h> /* For save_fpu() */
44
#include <machine/hid.h>
45
#include <machine/platformvar.h>
46
#include <machine/setjmp.h>
47
#include <machine/smp.h>
48
#include <machine/spr.h>
49
50
#include <dev/ofw/openfirm.h>
51
#include <machine/ofw_machdep.h>
52
53
#include <powerpc/powermac/platform_powermac.h>
54
55
#include "platform_if.h"
56
57
extern volatile void *ap_pcpu;
58
59
static void dummy_timebase(device_t, bool);
60
static device_t powermac_tb_dev;
61
static void (*freeze_timebase)(device_t, bool) = dummy_timebase;
62
63
static int powermac_probe(platform_t);
64
static int powermac_attach(platform_t);
65
void powermac_mem_regions(platform_t, struct mem_region *phys, int *physsz,
66
struct mem_region *avail, int *availsz);
67
static u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref);
68
static int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref);
69
static int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref);
70
static int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref);
71
static int powermac_smp_start_cpu(platform_t, struct pcpu *cpu);
72
static void powermac_smp_timebase_sync(platform_t, u_long tb, int ap);
73
static void powermac_reset(platform_t);
74
#ifndef __powerpc64__
75
static void powermac_sleep(platform_t);
76
#endif
77
78
static platform_method_t powermac_methods[] = {
79
PLATFORMMETHOD(platform_probe, powermac_probe),
80
PLATFORMMETHOD(platform_attach, powermac_attach),
81
PLATFORMMETHOD(platform_mem_regions, powermac_mem_regions),
82
PLATFORMMETHOD(platform_timebase_freq, powermac_timebase_freq),
83
84
PLATFORMMETHOD(platform_smp_first_cpu, powermac_smp_first_cpu),
85
PLATFORMMETHOD(platform_smp_next_cpu, powermac_smp_next_cpu),
86
PLATFORMMETHOD(platform_smp_get_bsp, powermac_smp_get_bsp),
87
PLATFORMMETHOD(platform_smp_start_cpu, powermac_smp_start_cpu),
88
PLATFORMMETHOD(platform_smp_timebase_sync, powermac_smp_timebase_sync),
89
90
PLATFORMMETHOD(platform_reset, powermac_reset),
91
#ifndef __powerpc64__
92
PLATFORMMETHOD(platform_sleep, powermac_sleep),
93
#endif
94
95
PLATFORMMETHOD_END
96
};
97
98
static platform_def_t powermac_platform = {
99
"powermac",
100
powermac_methods,
101
0
102
};
103
104
PLATFORM_DEF(powermac_platform);
105
106
static int
107
powermac_probe(platform_t plat)
108
{
109
char compat[255];
110
ssize_t compatlen;
111
char *curstr;
112
phandle_t root;
113
114
root = OF_peer(0);
115
if (root == 0)
116
return (ENXIO);
117
118
compatlen = OF_getprop(root, "compatible", compat, sizeof(compat));
119
120
for (curstr = compat; curstr < compat + compatlen;
121
curstr += strlen(curstr) + 1) {
122
if (strncmp(curstr, "MacRISC", 7) == 0)
123
return (BUS_PROBE_SPECIFIC);
124
}
125
126
return (ENXIO);
127
}
128
129
void
130
powermac_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
131
struct mem_region *avail, int *availsz)
132
{
133
phandle_t memory;
134
cell_t memoryprop[PHYS_AVAIL_SZ * 2];
135
ssize_t propsize, i, j;
136
int physacells = 1;
137
138
memory = OF_finddevice("/memory");
139
if (memory == -1)
140
memory = OF_finddevice("/memory@0");
141
142
/* "reg" has variable #address-cells, but #size-cells is always 1 */
143
OF_getprop(OF_parent(memory), "#address-cells", &physacells,
144
sizeof(physacells));
145
146
propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop));
147
propsize /= sizeof(cell_t);
148
for (i = 0, j = 0; i < propsize; i += physacells+1, j++) {
149
phys[j].mr_start = memoryprop[i];
150
if (physacells == 2) {
151
#ifndef __powerpc64__
152
/* On 32-bit PPC, ignore regions starting above 4 GB */
153
if (memoryprop[i] != 0) {
154
j--;
155
continue;
156
}
157
#else
158
phys[j].mr_start <<= 32;
159
#endif
160
phys[j].mr_start |= memoryprop[i+1];
161
}
162
phys[j].mr_size = memoryprop[i + physacells];
163
}
164
*physsz = j;
165
166
/* "available" always has #address-cells = 1 */
167
propsize = OF_getprop(memory, "available", memoryprop,
168
sizeof(memoryprop));
169
if (propsize <= 0) {
170
for (i = 0; i < *physsz; i++) {
171
avail[i].mr_start = phys[i].mr_start;
172
avail[i].mr_size = phys[i].mr_size;
173
}
174
175
*availsz = *physsz;
176
} else {
177
propsize /= sizeof(cell_t);
178
for (i = 0, j = 0; i < propsize; i += 2, j++) {
179
avail[j].mr_start = memoryprop[i];
180
avail[j].mr_size = memoryprop[i + 1];
181
}
182
183
#ifdef __powerpc64__
184
/* Add in regions above 4 GB to the available list */
185
for (i = 0; i < *physsz; i++) {
186
if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
187
avail[j].mr_start = phys[i].mr_start;
188
avail[j].mr_size = phys[i].mr_size;
189
j++;
190
}
191
}
192
#endif
193
*availsz = j;
194
}
195
}
196
197
static int
198
powermac_attach(platform_t plat)
199
{
200
phandle_t rootnode;
201
char model[32];
202
203
/*
204
* Quiesce Open Firmware on PowerMac11,2 and 12,1. It is
205
* necessary there to shut down a background thread doing fan
206
* management, and is harmful on other machines (it will make OF
207
* shut off power to various system components it had turned on).
208
*
209
* Note: we don't need to worry about which OF module we are
210
* using since this is called only from very early boot, within
211
* OF's boot context.
212
*/
213
214
rootnode = OF_finddevice("/");
215
if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
216
if (strcmp(model, "PowerMac11,2") == 0 ||
217
strcmp(model, "PowerMac12,1") == 0) {
218
ofw_quiesce();
219
}
220
}
221
222
return (0);
223
}
224
225
static u_long
226
powermac_timebase_freq(platform_t plat, struct cpuref *cpuref)
227
{
228
phandle_t phandle;
229
int32_t ticks = -1;
230
231
phandle = cpuref->cr_hwref;
232
233
OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
234
235
if (ticks <= 0)
236
panic("Unable to determine timebase frequency!");
237
238
return (ticks);
239
}
240
241
static int
242
powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu)
243
{
244
cell_t cpuid;
245
int res;
246
247
cpuref->cr_hwref = cpu;
248
res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
249
250
/*
251
* psim doesn't have a reg property, so assume 0 as for the
252
* uniprocessor case in the CHRP spec.
253
*/
254
if (res < 0) {
255
cpuid = 0;
256
}
257
258
cpuref->cr_cpuid = cpuid & 0xff;
259
return (0);
260
}
261
262
static int
263
powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
264
{
265
char buf[8];
266
phandle_t cpu, dev, root;
267
int res;
268
269
root = OF_peer(0);
270
271
dev = OF_child(root);
272
while (dev != 0) {
273
res = OF_getprop(dev, "name", buf, sizeof(buf));
274
if (res > 0 && strcmp(buf, "cpus") == 0)
275
break;
276
dev = OF_peer(dev);
277
}
278
if (dev == 0) {
279
/*
280
* psim doesn't have a name property on the /cpus node,
281
* but it can be found directly
282
*/
283
dev = OF_finddevice("/cpus");
284
if (dev == -1)
285
return (ENOENT);
286
}
287
288
cpu = OF_child(dev);
289
290
while (cpu != 0) {
291
res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
292
if (res > 0 && strcmp(buf, "cpu") == 0)
293
break;
294
cpu = OF_peer(cpu);
295
}
296
if (cpu == 0)
297
return (ENOENT);
298
299
return (powermac_smp_fill_cpuref(cpuref, cpu));
300
}
301
302
static int
303
powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
304
{
305
char buf[8];
306
phandle_t cpu;
307
int res;
308
309
cpu = OF_peer(cpuref->cr_hwref);
310
while (cpu != 0) {
311
res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
312
if (res > 0 && strcmp(buf, "cpu") == 0)
313
break;
314
cpu = OF_peer(cpu);
315
}
316
if (cpu == 0)
317
return (ENOENT);
318
319
return (powermac_smp_fill_cpuref(cpuref, cpu));
320
}
321
322
static int
323
powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
324
{
325
ihandle_t inst;
326
phandle_t bsp, chosen;
327
int res;
328
329
chosen = OF_finddevice("/chosen");
330
if (chosen == -1)
331
return (ENXIO);
332
333
res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
334
if (res < 0)
335
return (ENXIO);
336
337
bsp = OF_instance_to_package(inst);
338
return (powermac_smp_fill_cpuref(cpuref, bsp));
339
}
340
341
static int
342
powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
343
{
344
#ifdef SMP
345
phandle_t cpu;
346
volatile uint8_t *rstvec;
347
static volatile uint8_t *rstvec_virtbase = NULL;
348
int res, reset, timeout;
349
350
cpu = pc->pc_hwref;
351
res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset));
352
if (res < 0) {
353
reset = 0x58;
354
355
switch (pc->pc_cpuid) {
356
case 0:
357
reset += 0x03;
358
break;
359
case 1:
360
reset += 0x04;
361
break;
362
case 2:
363
reset += 0x0f;
364
break;
365
case 3:
366
reset += 0x10;
367
break;
368
default:
369
return (ENXIO);
370
}
371
}
372
373
ap_pcpu = pc;
374
375
if (rstvec_virtbase == NULL)
376
rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE);
377
378
rstvec = rstvec_virtbase + reset;
379
380
*rstvec = 4;
381
powerpc_sync();
382
(void)(*rstvec);
383
powerpc_sync();
384
DELAY(1);
385
*rstvec = 0;
386
powerpc_sync();
387
(void)(*rstvec);
388
powerpc_sync();
389
390
timeout = 10000;
391
while (!pc->pc_awake && timeout--)
392
DELAY(100);
393
394
return ((pc->pc_awake) ? 0 : EBUSY);
395
#else
396
/* No SMP support */
397
return (ENXIO);
398
#endif
399
}
400
401
void
402
powermac_register_timebase(device_t dev, powermac_tb_disable_t cb)
403
{
404
powermac_tb_dev = dev;
405
freeze_timebase = cb;
406
}
407
408
static void
409
powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap)
410
{
411
static volatile bool tb_ready;
412
static volatile int cpu_done;
413
414
/*
415
* XXX Temporary fallback for platforms we don't know how to freeze.
416
*
417
* This needs to be replaced with a cpu-to-cpu software sync
418
* protocol, because this is not a consistent way to sync timebase.
419
*/
420
mttb(tb);
421
if (freeze_timebase == dummy_timebase)
422
return;
423
424
if (ap) {
425
/* APs. Hold off until we get a stable timebase. */
426
critical_enter();
427
while (!tb_ready)
428
atomic_thread_fence_seq_cst();
429
mttb(tb);
430
atomic_add_int(&cpu_done, 1);
431
while (cpu_done < mp_ncpus)
432
atomic_thread_fence_seq_cst();
433
critical_exit();
434
} else {
435
/* BSP */
436
critical_enter();
437
/* Ensure cpu_done is zeroed so we can resync at runtime */
438
atomic_set_int(&cpu_done, 0);
439
freeze_timebase(powermac_tb_dev, true);
440
tb_ready = true;
441
mttb(tb);
442
atomic_add_int(&cpu_done, 1);
443
while (cpu_done < mp_ncpus)
444
atomic_thread_fence_seq_cst();
445
freeze_timebase(powermac_tb_dev, false);
446
/* Reset tb_ready so we can resync at runtime */
447
tb_ready = false;
448
critical_exit();
449
}
450
}
451
452
/* Fallback freeze. In case no real handler is found in the device tree. */
453
static void
454
dummy_timebase(device_t dev, bool freeze)
455
{
456
/* Nothing to do here, move along. */
457
}
458
459
static void
460
powermac_reset(platform_t platform)
461
{
462
OF_reboot();
463
}
464
465
#ifndef __powerpc64__
466
void
467
powermac_sleep(platform_t platform)
468
{
469
/* Only supports MPC745x for now. */
470
if (!MPC745X_P(mfspr(SPR_PVR) >> 16)) {
471
printf("sleep only supported for G4 PowerMac hardware.\n");
472
return;
473
}
474
475
*(unsigned long *)0x80 = 0x100;
476
mpc745x_sleep();
477
}
478
#endif
479
480