Path: blob/main/sys/powerpc/powermac/platform_powermac.c
39562 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2008 Marcel Moolenaar4* Copyright (c) 2009 Nathan Whitehorn5* All rights reserved.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10*11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16*17* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR18* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES19* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.20* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,21* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT22* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF26* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.27*/2829#include <sys/param.h>30#include <sys/systm.h>31#include <sys/kernel.h>32#include <sys/bus.h>33#include <sys/pcpu.h>34#include <sys/proc.h>35#include <sys/smp.h>36#include <vm/vm.h>37#include <vm/pmap.h>3839#include <machine/altivec.h> /* For save_vec() */40#include <machine/bus.h>41#include <machine/cpu.h>42#include <machine/fpu.h> /* For save_fpu() */43#include <machine/hid.h>44#include <machine/platformvar.h>45#include <machine/setjmp.h>46#include <machine/smp.h>47#include <machine/spr.h>4849#include <dev/ofw/openfirm.h>50#include <machine/ofw_machdep.h>5152#include <powerpc/powermac/platform_powermac.h>5354#include "platform_if.h"5556extern volatile void *ap_pcpu;5758static void dummy_timebase(device_t, bool);59static device_t powermac_tb_dev;60static void (*freeze_timebase)(device_t, bool) = dummy_timebase;6162static int powermac_probe(platform_t);63static int powermac_attach(platform_t);64void powermac_mem_regions(platform_t, struct mem_region *phys, int *physsz,65struct mem_region *avail, int *availsz);66static u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref);67static int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref);68static int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref);69static int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref);70static int powermac_smp_start_cpu(platform_t, struct pcpu *cpu);71static void powermac_smp_timebase_sync(platform_t, u_long tb, int ap);72static void powermac_reset(platform_t);73#ifndef __powerpc64__74static void powermac_sleep(platform_t);75#endif7677static platform_method_t powermac_methods[] = {78PLATFORMMETHOD(platform_probe, powermac_probe),79PLATFORMMETHOD(platform_attach, powermac_attach),80PLATFORMMETHOD(platform_mem_regions, powermac_mem_regions),81PLATFORMMETHOD(platform_timebase_freq, powermac_timebase_freq),8283PLATFORMMETHOD(platform_smp_first_cpu, powermac_smp_first_cpu),84PLATFORMMETHOD(platform_smp_next_cpu, powermac_smp_next_cpu),85PLATFORMMETHOD(platform_smp_get_bsp, powermac_smp_get_bsp),86PLATFORMMETHOD(platform_smp_start_cpu, powermac_smp_start_cpu),87PLATFORMMETHOD(platform_smp_timebase_sync, powermac_smp_timebase_sync),8889PLATFORMMETHOD(platform_reset, powermac_reset),90#ifndef __powerpc64__91PLATFORMMETHOD(platform_sleep, powermac_sleep),92#endif9394PLATFORMMETHOD_END95};9697static platform_def_t powermac_platform = {98"powermac",99powermac_methods,1000101};102103PLATFORM_DEF(powermac_platform);104105static int106powermac_probe(platform_t plat)107{108char compat[255];109ssize_t compatlen;110char *curstr;111phandle_t root;112113root = OF_peer(0);114if (root == 0)115return (ENXIO);116117compatlen = OF_getprop(root, "compatible", compat, sizeof(compat));118119for (curstr = compat; curstr < compat + compatlen;120curstr += strlen(curstr) + 1) {121if (strncmp(curstr, "MacRISC", 7) == 0)122return (BUS_PROBE_SPECIFIC);123}124125return (ENXIO);126}127128void129powermac_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,130struct mem_region *avail, int *availsz)131{132phandle_t memory;133cell_t memoryprop[PHYS_AVAIL_SZ * 2];134ssize_t propsize, i, j;135int physacells = 1;136137memory = OF_finddevice("/memory");138if (memory == -1)139memory = OF_finddevice("/memory@0");140141/* "reg" has variable #address-cells, but #size-cells is always 1 */142OF_getprop(OF_parent(memory), "#address-cells", &physacells,143sizeof(physacells));144145propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop));146propsize /= sizeof(cell_t);147for (i = 0, j = 0; i < propsize; i += physacells+1, j++) {148phys[j].mr_start = memoryprop[i];149if (physacells == 2) {150#ifndef __powerpc64__151/* On 32-bit PPC, ignore regions starting above 4 GB */152if (memoryprop[i] != 0) {153j--;154continue;155}156#else157phys[j].mr_start <<= 32;158#endif159phys[j].mr_start |= memoryprop[i+1];160}161phys[j].mr_size = memoryprop[i + physacells];162}163*physsz = j;164165/* "available" always has #address-cells = 1 */166propsize = OF_getprop(memory, "available", memoryprop,167sizeof(memoryprop));168if (propsize <= 0) {169for (i = 0; i < *physsz; i++) {170avail[i].mr_start = phys[i].mr_start;171avail[i].mr_size = phys[i].mr_size;172}173174*availsz = *physsz;175} else {176propsize /= sizeof(cell_t);177for (i = 0, j = 0; i < propsize; i += 2, j++) {178avail[j].mr_start = memoryprop[i];179avail[j].mr_size = memoryprop[i + 1];180}181182#ifdef __powerpc64__183/* Add in regions above 4 GB to the available list */184for (i = 0; i < *physsz; i++) {185if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {186avail[j].mr_start = phys[i].mr_start;187avail[j].mr_size = phys[i].mr_size;188j++;189}190}191#endif192*availsz = j;193}194}195196static int197powermac_attach(platform_t plat)198{199phandle_t rootnode;200char model[32];201202/*203* Quiesce Open Firmware on PowerMac11,2 and 12,1. It is204* necessary there to shut down a background thread doing fan205* management, and is harmful on other machines (it will make OF206* shut off power to various system components it had turned on).207*208* Note: we don't need to worry about which OF module we are209* using since this is called only from very early boot, within210* OF's boot context.211*/212213rootnode = OF_finddevice("/");214if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {215if (strcmp(model, "PowerMac11,2") == 0 ||216strcmp(model, "PowerMac12,1") == 0) {217ofw_quiesce();218}219}220221return (0);222}223224static u_long225powermac_timebase_freq(platform_t plat, struct cpuref *cpuref)226{227phandle_t phandle;228int32_t ticks = -1;229230phandle = cpuref->cr_hwref;231232OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));233234if (ticks <= 0)235panic("Unable to determine timebase frequency!");236237return (ticks);238}239240static int241powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu)242{243cell_t cpuid;244int res;245246cpuref->cr_hwref = cpu;247res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));248249/*250* psim doesn't have a reg property, so assume 0 as for the251* uniprocessor case in the CHRP spec.252*/253if (res < 0) {254cpuid = 0;255}256257cpuref->cr_cpuid = cpuid & 0xff;258return (0);259}260261static int262powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref)263{264char buf[8];265phandle_t cpu, dev, root;266int res;267268root = OF_peer(0);269270dev = OF_child(root);271while (dev != 0) {272res = OF_getprop(dev, "name", buf, sizeof(buf));273if (res > 0 && strcmp(buf, "cpus") == 0)274break;275dev = OF_peer(dev);276}277if (dev == 0) {278/*279* psim doesn't have a name property on the /cpus node,280* but it can be found directly281*/282dev = OF_finddevice("/cpus");283if (dev == -1)284return (ENOENT);285}286287cpu = OF_child(dev);288289while (cpu != 0) {290res = OF_getprop(cpu, "device_type", buf, sizeof(buf));291if (res > 0 && strcmp(buf, "cpu") == 0)292break;293cpu = OF_peer(cpu);294}295if (cpu == 0)296return (ENOENT);297298return (powermac_smp_fill_cpuref(cpuref, cpu));299}300301static int302powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref)303{304char buf[8];305phandle_t cpu;306int res;307308cpu = OF_peer(cpuref->cr_hwref);309while (cpu != 0) {310res = OF_getprop(cpu, "device_type", buf, sizeof(buf));311if (res > 0 && strcmp(buf, "cpu") == 0)312break;313cpu = OF_peer(cpu);314}315if (cpu == 0)316return (ENOENT);317318return (powermac_smp_fill_cpuref(cpuref, cpu));319}320321static int322powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref)323{324ihandle_t inst;325phandle_t bsp, chosen;326int res;327328chosen = OF_finddevice("/chosen");329if (chosen == -1)330return (ENXIO);331332res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));333if (res < 0)334return (ENXIO);335336bsp = OF_instance_to_package(inst);337return (powermac_smp_fill_cpuref(cpuref, bsp));338}339340static int341powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)342{343#ifdef SMP344phandle_t cpu;345volatile uint8_t *rstvec;346static volatile uint8_t *rstvec_virtbase = NULL;347int res, reset, timeout;348349cpu = pc->pc_hwref;350res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset));351if (res < 0) {352reset = 0x58;353354switch (pc->pc_cpuid) {355case 0:356reset += 0x03;357break;358case 1:359reset += 0x04;360break;361case 2:362reset += 0x0f;363break;364case 3:365reset += 0x10;366break;367default:368return (ENXIO);369}370}371372ap_pcpu = pc;373374if (rstvec_virtbase == NULL)375rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE);376377rstvec = rstvec_virtbase + reset;378379*rstvec = 4;380powerpc_sync();381(void)(*rstvec);382powerpc_sync();383DELAY(1);384*rstvec = 0;385powerpc_sync();386(void)(*rstvec);387powerpc_sync();388389timeout = 10000;390while (!pc->pc_awake && timeout--)391DELAY(100);392393return ((pc->pc_awake) ? 0 : EBUSY);394#else395/* No SMP support */396return (ENXIO);397#endif398}399400void401powermac_register_timebase(device_t dev, powermac_tb_disable_t cb)402{403powermac_tb_dev = dev;404freeze_timebase = cb;405}406407static void408powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap)409{410static volatile bool tb_ready;411static volatile int cpu_done;412413/*414* XXX Temporary fallback for platforms we don't know how to freeze.415*416* This needs to be replaced with a cpu-to-cpu software sync417* protocol, because this is not a consistent way to sync timebase.418*/419mttb(tb);420if (freeze_timebase == dummy_timebase)421return;422423if (ap) {424/* APs. Hold off until we get a stable timebase. */425critical_enter();426while (!tb_ready)427atomic_thread_fence_seq_cst();428mttb(tb);429atomic_add_int(&cpu_done, 1);430while (cpu_done < mp_ncpus)431atomic_thread_fence_seq_cst();432critical_exit();433} else {434/* BSP */435critical_enter();436/* Ensure cpu_done is zeroed so we can resync at runtime */437atomic_set_int(&cpu_done, 0);438freeze_timebase(powermac_tb_dev, true);439tb_ready = true;440mttb(tb);441atomic_add_int(&cpu_done, 1);442while (cpu_done < mp_ncpus)443atomic_thread_fence_seq_cst();444freeze_timebase(powermac_tb_dev, false);445/* Reset tb_ready so we can resync at runtime */446tb_ready = false;447critical_exit();448}449}450451/* Fallback freeze. In case no real handler is found in the device tree. */452static void453dummy_timebase(device_t dev, bool freeze)454{455/* Nothing to do here, move along. */456}457458static void459powermac_reset(platform_t platform)460{461OF_reboot();462}463464#ifndef __powerpc64__465void466powermac_sleep(platform_t platform)467{468/* Only supports MPC745x for now. */469if (!MPC745X_P(mfspr(SPR_PVR) >> 16)) {470printf("sleep only supported for G4 PowerMac hardware.\n");471return;472}473474*(unsigned long *)0x80 = 0x100;475mpc745x_sleep();476}477#endif478479480