diff options
author | nwhitehorn <nwhitehorn@FreeBSD.org> | 2010-08-31 15:27:46 +0000 |
---|---|---|
committer | nwhitehorn <nwhitehorn@FreeBSD.org> | 2010-08-31 15:27:46 +0000 |
commit | 655a96888d55c63b6496de2c02332278739d62f6 (patch) | |
tree | 6d62f76674112f0f46965b70b4629824076f5094 /sys/powerpc/powermac | |
parent | 97e3e3670826f9a28781351eebc6280670c370a5 (diff) | |
download | FreeBSD-src-655a96888d55c63b6496de2c02332278739d62f6.zip FreeBSD-src-655a96888d55c63b6496de2c02332278739d62f6.tar.gz |
Restructure how reset and poweroff are handled on PowerPC systems, since
the existing code was very platform specific, and broken for SMP systems
trying to reboot from KDB.
- Add a new PLATFORM_RESET() method to the platform KOBJ interface, and
migrate existing reset functions into platform modules.
- Modify the OF_reboot() routine to submit the request by hand to avoid
the IPIs involved in the regular openfirmware() routine. This fixes
reboot from KDB on SMP machines.
- Move non-KDB reset and poweroff functions on the Powermac platform
into the relevant power control drivers (cuda, pmu, smu), instead of
using them through the Open Firmware backdoor.
- Rename platform_chrp to platform_powermac since it has become
increasingly Powermac specific. When we gain support for IBM systems,
we will grow a new platform_chrp.
Diffstat (limited to 'sys/powerpc/powermac')
-rw-r--r-- | sys/powerpc/powermac/cuda.c | 18 | ||||
-rw-r--r-- | sys/powerpc/powermac/platform_powermac.c | 288 | ||||
-rw-r--r-- | sys/powerpc/powermac/pmu.c | 26 | ||||
-rw-r--r-- | sys/powerpc/powermac/smu.c | 26 |
4 files changed, 358 insertions, 0 deletions
diff --git a/sys/powerpc/powermac/cuda.c b/sys/powerpc/powermac/cuda.c index 0499352..99a0ea9 100644 --- a/sys/powerpc/powermac/cuda.c +++ b/sys/powerpc/powermac/cuda.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <sys/conf.h> #include <sys/kernel.h> #include <sys/clock.h> +#include <sys/reboot.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/openfirm.h> @@ -72,6 +73,7 @@ static u_int cuda_adb_autopoll(device_t dev, uint16_t mask); static u_int cuda_poll(device_t dev); static void cuda_send_inbound(struct cuda_softc *sc); static void cuda_send_outbound(struct cuda_softc *sc); +static void cuda_shutdown(void *xsc, int howto); /* * Clock interface @@ -249,6 +251,8 @@ cuda_attach(device_t dev) } clock_register(dev, 1000); + EVENTHANDLER_REGISTER(shutdown_final, cuda_shutdown, sc, + SHUTDOWN_PRI_LAST); return (bus_generic_attach(dev)); } @@ -739,6 +743,20 @@ cuda_adb_autopoll(device_t dev, uint16_t mask) { return (0); } +static void +cuda_shutdown(void *xsc, int howto) +{ + struct cuda_softc *sc = xsc; + uint8_t cmd[] = {CUDA_PSEUDO, 0}; + + cmd[1] = (howto & RB_HALT) ? CMD_POWEROFF : CMD_RESET; + cuda_poll(sc->sc_dev); + cuda_send(sc, 1, 2, cmd); + + while (1) + cuda_poll(sc->sc_dev); +} + #define DIFF19041970 2082844800 static int diff --git a/sys/powerpc/powermac/platform_powermac.c b/sys/powerpc/powermac/platform_powermac.c new file mode 100644 index 0000000..d495065 --- /dev/null +++ b/sys/powerpc/powermac/platform_powermac.c @@ -0,0 +1,288 @@ +/*- + * Copyright (c) 2008 Marcel Moolenaar + * Copyright (c) 2009 Nathan Whitehorn + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/bus.h> +#include <sys/pcpu.h> +#include <sys/proc.h> +#include <sys/smp.h> +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/hid.h> +#include <machine/platformvar.h> +#include <machine/pmap.h> +#include <machine/smp.h> +#include <machine/spr.h> + +#include <dev/ofw/openfirm.h> +#include <machine/ofw_machdep.h> + +#include "platform_if.h" + +#ifdef SMP +extern void *ap_pcpu; +#endif + +static int powermac_probe(platform_t); +void powermac_mem_regions(platform_t, struct mem_region **phys, int *physsz, + struct mem_region **avail, int *availsz); +static u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref); +static int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref); +static int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref); +static int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref); +static int powermac_smp_start_cpu(platform_t, struct pcpu *cpu); +static void powermac_reset(platform_t); + +static platform_method_t powermac_methods[] = { + PLATFORMMETHOD(platform_probe, powermac_probe), + PLATFORMMETHOD(platform_mem_regions, powermac_mem_regions), + PLATFORMMETHOD(platform_timebase_freq, powermac_timebase_freq), + + PLATFORMMETHOD(platform_smp_first_cpu, powermac_smp_first_cpu), + PLATFORMMETHOD(platform_smp_next_cpu, powermac_smp_next_cpu), + PLATFORMMETHOD(platform_smp_get_bsp, powermac_smp_get_bsp), + PLATFORMMETHOD(platform_smp_start_cpu, powermac_smp_start_cpu), + + PLATFORMMETHOD(platform_reset, powermac_reset), + + { 0, 0 } +}; + +static platform_def_t powermac_platform = { + "powermac", + powermac_methods, + 0 +}; + +PLATFORM_DEF(powermac_platform); + +static int +powermac_probe(platform_t plat) +{ + if (OF_finddevice("/memory") != -1 || OF_finddevice("/memory@0") != -1) + return (BUS_PROBE_GENERIC); + + return (ENXIO); +} + +void +powermac_mem_regions(platform_t plat, struct mem_region **phys, int *physsz, + struct mem_region **avail, int *availsz) +{ + ofw_mem_regions(phys,physsz,avail,availsz); +} + +static u_long +powermac_timebase_freq(platform_t plat, struct cpuref *cpuref) +{ + phandle_t phandle; + int32_t ticks = -1; + + phandle = cpuref->cr_hwref; + + OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks)); + + if (ticks <= 0) + panic("Unable to determine timebase frequency!"); + + return (ticks); +} + + +static int +powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu) +{ + cell_t cpuid, res; + + cpuref->cr_hwref = cpu; + res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); + + /* + * psim doesn't have a reg property, so assume 0 as for the + * uniprocessor case in the CHRP spec. + */ + if (res < 0) { + cpuid = 0; + } + + cpuref->cr_cpuid = cpuid & 0xff; + return (0); +} + +static int +powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref) +{ + char buf[8]; + phandle_t cpu, dev, root; + int res; + + root = OF_peer(0); + + dev = OF_child(root); + while (dev != 0) { + res = OF_getprop(dev, "name", buf, sizeof(buf)); + if (res > 0 && strcmp(buf, "cpus") == 0) + break; + dev = OF_peer(dev); + } + if (dev == 0) { + /* + * psim doesn't have a name property on the /cpus node, + * but it can be found directly + */ + dev = OF_finddevice("/cpus"); + if (dev == 0) + return (ENOENT); + } + + cpu = OF_child(dev); + + while (cpu != 0) { + res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); + if (res > 0 && strcmp(buf, "cpu") == 0) + break; + cpu = OF_peer(cpu); + } + if (cpu == 0) + return (ENOENT); + + return (powermac_smp_fill_cpuref(cpuref, cpu)); +} + +static int +powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref) +{ + char buf[8]; + phandle_t cpu; + int res; + + cpu = OF_peer(cpuref->cr_hwref); + while (cpu != 0) { + res = OF_getprop(cpu, "device_type", buf, sizeof(buf)); + if (res > 0 && strcmp(buf, "cpu") == 0) + break; + cpu = OF_peer(cpu); + } + if (cpu == 0) + return (ENOENT); + + return (powermac_smp_fill_cpuref(cpuref, cpu)); +} + +static int +powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref) +{ + ihandle_t inst; + phandle_t bsp, chosen; + int res; + + chosen = OF_finddevice("/chosen"); + if (chosen == 0) + return (ENXIO); + + res = OF_getprop(chosen, "cpu", &inst, sizeof(inst)); + if (res < 0) + return (ENXIO); + + bsp = OF_instance_to_package(inst); + return (powermac_smp_fill_cpuref(cpuref, bsp)); +} + +static int +powermac_smp_start_cpu(platform_t plat, struct pcpu *pc) +{ +#ifdef SMP + phandle_t cpu; + volatile uint8_t *rstvec; + static volatile uint8_t *rstvec_virtbase = NULL; + int res, reset, timeout; + + cpu = pc->pc_hwref; + res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset)); + if (res < 0) { + reset = 0x58; + + switch (pc->pc_cpuid) { + case 0: + reset += 0x03; + break; + case 1: + reset += 0x04; + break; + case 2: + reset += 0x0f; + break; + case 3: + reset += 0x10; + break; + default: + return (ENXIO); + } + } + + ap_pcpu = pc; + + if (rstvec_virtbase == NULL) + rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE); + + rstvec = rstvec_virtbase + reset; + + *rstvec = 4; + powerpc_sync(); + (void)(*rstvec); + powerpc_sync(); + DELAY(1); + *rstvec = 0; + powerpc_sync(); + (void)(*rstvec); + powerpc_sync(); + + timeout = 10000; + while (!pc->pc_awake && timeout--) + DELAY(100); + + return ((pc->pc_awake) ? 0 : EBUSY); +#else + /* No SMP support */ + return (ENXIO); +#endif +} + +static void +powermac_reset(platform_t platform) +{ + OF_reboot(); +} + diff --git a/sys/powerpc/powermac/pmu.c b/sys/powerpc/powermac/pmu.c index 1fa101d..d641c9c 100644 --- a/sys/powerpc/powermac/pmu.c +++ b/sys/powerpc/powermac/pmu.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/conf.h> #include <sys/kernel.h> #include <sys/clock.h> +#include <sys/reboot.h> #include <sys/sysctl.h> #include <dev/ofw/ofw_bus.h> @@ -81,6 +82,11 @@ static u_int pmu_adb_send(device_t dev, u_char command_byte, int len, static u_int pmu_adb_autopoll(device_t dev, uint16_t mask); static u_int pmu_poll(device_t dev); +/* + * Power interface + */ + +static void pmu_shutdown(void *xsc, int howto); static void pmu_set_sleepled(void *xsc, int onoff); static int pmu_server_mode(SYSCTL_HANDLER_ARGS); static int pmu_acline_state(SYSCTL_HANDLER_ARGS); @@ -474,6 +480,12 @@ pmu_attach(device_t dev) clock_register(dev, 1000); + /* + * Register power control handler + */ + EVENTHANDLER_REGISTER(shutdown_final, pmu_shutdown, sc, + SHUTDOWN_PRI_LAST); + return (bus_generic_attach(dev)); } @@ -751,6 +763,20 @@ pmu_adb_autopoll(device_t dev, uint16_t mask) } static void +pmu_shutdown(void *xsc, int howto) +{ + struct pmu_softc *sc = xsc; + uint8_t cmd[] = {'M', 'A', 'T', 'T'}; + + if (howto & RB_HALT) + pmu_send(sc, PMU_POWER_OFF, 4, cmd, 0, NULL); + else + pmu_send(sc, PMU_RESET_CPU, 0, NULL, 0, NULL); + + for (;;); +} + +static void pmu_set_sleepled(void *xsc, int onoff) { struct pmu_softc *sc = xsc; diff --git a/sys/powerpc/powermac/smu.c b/sys/powerpc/powermac/smu.c index 7a4fac8..2894904 100644 --- a/sys/powerpc/powermac/smu.c +++ b/sys/powerpc/powermac/smu.c @@ -165,6 +165,7 @@ static void smu_manage_fans(device_t smu); static void smu_set_sleepled(void *xdev, int onoff); static int smu_server_mode(SYSCTL_HANDLER_ARGS); static void smu_doorbell_intr(void *xdev); +static void smu_shutdown(void *xdev, int howto); /* where to find the doorbell GPIO */ @@ -391,6 +392,12 @@ smu_attach(device_t dev) */ clock_register(dev, 1000); + /* + * Learn about shutdown events + */ + EVENTHANDLER_REGISTER(shutdown_final, smu_shutdown, dev, + SHUTDOWN_PRI_LAST); + return (bus_generic_attach(dev)); } @@ -1115,6 +1122,25 @@ smu_server_mode(SYSCTL_HANDLER_ARGS) return (smu_run_cmd(smu, &cmd, 1)); } +static void +smu_shutdown(void *xdev, int howto) +{ + device_t smu = xdev; + struct smu_cmd cmd; + + cmd.cmd = SMU_POWER; + if (howto & RB_HALT) + strcpy(cmd.data, "SHUTDOWN"); + else + strcpy(cmd.data, "RESTART"); + + cmd.len = strlen(cmd.data); + + smu_run_cmd(smu, &cmd, 1); + + for (;;); +} + static int smu_gettime(device_t dev, struct timespec *ts) { |