Index: head/sys/powerpc/powernv/opal.h =================================================================== --- head/sys/powerpc/powernv/opal.h (revision 327811) +++ head/sys/powerpc/powernv/opal.h (revision 327812) @@ -1,48 +1,50 @@ /*- * Copyright (c) 2015 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. * * $FreeBSD$ */ #ifndef _POWERNV_OPAL_H #define _POWERNV_OPAL_H #include #include /* Check if OPAL is correctly instantiated. Will try to instantiate it. */ int opal_check(void); /* Call an OPAL method. Any pointers passed must be real-mode accessible! */ int opal_call(uint64_t token, ...); #define OPAL_CONSOLE_WRITE 1 #define OPAL_CONSOLE_READ 2 +#define OPAL_CEC_POWER_DOWN 5 +#define OPAL_CEC_REBOOT 6 #define OPAL_START_CPU 41 #define OPAL_SUCCESS 0 #define OPAL_BUSY_EVENT -12 #endif Index: head/sys/powerpc/powernv/opal_console.c =================================================================== --- head/sys/powerpc/powernv/opal_console.c (revision 327811) +++ head/sys/powerpc/powernv/opal_console.c (revision 327812) @@ -1,441 +1,514 @@ /*- * Copyright (C) 2011,2015 by 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 TOOLS GMBH 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "opal.h" #include "uart_if.h" struct uart_opal_softc { device_t dev; phandle_t node; int vtermid; struct tty *tp; struct resource *irqres; int irqrid; struct callout callout; void *sc_icookie; int polltime; struct mtx sc_mtx; int protocol; char opal_inbuf[16]; uint64_t inbuflen; uint8_t outseqno; }; static struct uart_opal_softc *console_sc = NULL; #if defined(KDB) static int alt_break_state; #endif enum { OPAL_RAW, OPAL_HVSI }; #define VS_DATA_PACKET_HEADER 0xff #define VS_CONTROL_PACKET_HEADER 0xfe #define VSV_SET_MODEM_CTL 0x01 #define VSV_MODEM_CTL_UPDATE 0x02 #define VSV_RENEGOTIATE_CONNECTION 0x03 #define VS_QUERY_PACKET_HEADER 0xfd #define VSV_SEND_VERSION_NUMBER 0x01 #define VSV_SEND_MODEM_CTL_STATUS 0x02 #define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc static int uart_opal_probe(device_t dev); static int uart_opal_attach(device_t dev); static void uart_opal_intr(void *v); static device_method_t uart_opal_methods[] = { /* Device interface */ DEVMETHOD(device_probe, uart_opal_probe), DEVMETHOD(device_attach, uart_opal_attach), DEVMETHOD_END }; static driver_t uart_opal_driver = { "uart", uart_opal_methods, sizeof(struct uart_opal_softc), }; DRIVER_MODULE(uart_opal, opalcons, uart_opal_driver, uart_devclass, 0, 0); static cn_probe_t uart_opal_cnprobe; static cn_init_t uart_opal_cninit; static cn_term_t uart_opal_cnterm; static cn_getc_t uart_opal_cngetc; static cn_putc_t uart_opal_cnputc; static cn_grab_t uart_opal_cngrab; static cn_ungrab_t uart_opal_cnungrab; CONSOLE_DRIVER(uart_opal); static void uart_opal_ttyoutwakeup(struct tty *tp); static struct ttydevsw uart_opal_tty_class = { .tsw_flags = TF_INITLOCK|TF_CALLOUT, .tsw_outwakeup = uart_opal_ttyoutwakeup, }; static int uart_opal_probe_node(struct uart_opal_softc *sc) { phandle_t node = sc->node; uint32_t reg; char buf[64]; sc->inbuflen = 0; sc->outseqno = 0; if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0) return (ENXIO); if (strcmp(buf, "serial") != 0) return (ENXIO); reg = -1; OF_getprop(node, "reg", ®, sizeof(reg)); if (reg == -1) return (ENXIO); sc->vtermid = reg; sc->node = node; if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0) return (ENXIO); if (strcmp(buf, "ibm,opal-console-raw") == 0) { sc->protocol = OPAL_RAW; return (0); } else if (strcmp(buf, "ibm,opal-console-hvsi") == 0) { sc->protocol = OPAL_HVSI; return (0); } return (ENXIO); } static int uart_opal_probe(device_t dev) { struct uart_opal_softc sc; int err; sc.node = ofw_bus_get_node(dev); err = uart_opal_probe_node(&sc); if (err != 0) return (err); device_set_desc(dev, "OPAL Serial Port"); return (err); } static void uart_opal_cnprobe(struct consdev *cp) { char buf[64]; phandle_t input, chosen; static struct uart_opal_softc sc; if (opal_check() != 0) goto fail; if ((chosen = OF_finddevice("/chosen")) == -1) goto fail; /* Check if OF has an active stdin/stdout */ if (OF_getprop(chosen, "linux,stdout-path", buf, sizeof(buf)) <= 0) goto fail; input = OF_finddevice(buf); if (input == -1) goto fail; sc.node = input; if (uart_opal_probe_node(&sc) != 0) goto fail; mtx_init(&sc.sc_mtx, "uart_opal", NULL, MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); cp->cn_pri = CN_NORMAL; console_sc = ≻ return; fail: cp->cn_pri = CN_DEAD; return; } static int uart_opal_attach(device_t dev) { struct uart_opal_softc *sc; int unit; sc = device_get_softc(dev); sc->dev = dev; sc->node = ofw_bus_get_node(dev); uart_opal_probe_node(sc); unit = device_get_unit(dev); sc->tp = tty_alloc(&uart_opal_tty_class, sc); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); if (console_sc != NULL && console_sc->vtermid == sc->vtermid) { sc->outseqno = console_sc->outseqno; console_sc = sc; sprintf(uart_opal_consdev.cn_name, "ttyu%r", unit); tty_init_console(sc->tp, 0); } sc->irqrid = 0; sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid, RF_ACTIVE | RF_SHAREABLE); if (sc->irqres != NULL) { bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE, NULL, uart_opal_intr, sc, &sc->sc_icookie); } else { callout_init(&sc->callout, CALLOUT_MPSAFE); sc->polltime = hz / 20; if (sc->polltime < 1) sc->polltime = 1; callout_reset(&sc->callout, sc->polltime, uart_opal_intr, sc); } tty_makedev(sc->tp, NULL, "u%r", unit); return (0); } static void uart_opal_cninit(struct consdev *cp) { strcpy(cp->cn_name, "opalcons"); } static void uart_opal_cnterm(struct consdev *cp) { } static int uart_opal_get(struct uart_opal_softc *sc, void *buffer, size_t bufsize) { int err; int hdr = 0; if (sc->protocol == OPAL_RAW) { uint64_t len = bufsize; uint64_t olen = (uint64_t)&len; uint64_t obuf = (uint64_t)buffer; if (pmap_bootstrapped) { olen = vtophys(&len); obuf = vtophys(buffer); } err = opal_call(OPAL_CONSOLE_READ, sc->vtermid, olen, obuf); if (err != OPAL_SUCCESS) return (-1); bufsize = len; } else { uart_lock(&sc->sc_mtx); if (sc->inbuflen == 0) { err = opal_call(OPAL_CONSOLE_READ, sc->vtermid, &sc->inbuflen, sc->opal_inbuf); if (err != OPAL_SUCCESS) { uart_unlock(&sc->sc_mtx); return (-1); } hdr = 1; } if (sc->inbuflen == 0) { uart_unlock(&sc->sc_mtx); return (0); } if (bufsize > sc->inbuflen) bufsize = sc->inbuflen; if (hdr == 1) { sc->inbuflen = sc->inbuflen - 4; /* The HVSI protocol has a 4 byte header, skip it */ memmove(&sc->opal_inbuf[0], &sc->opal_inbuf[4], sc->inbuflen); } memcpy(buffer, sc->opal_inbuf, bufsize); sc->inbuflen -= bufsize; if (sc->inbuflen > 0) memmove(&sc->opal_inbuf[0], &sc->opal_inbuf[bufsize], sc->inbuflen); uart_unlock(&sc->sc_mtx); } return (bufsize); } static int uart_opal_put(struct uart_opal_softc *sc, void *buffer, size_t bufsize) { uint16_t seqno; uint64_t len = bufsize; char cbuf[16]; int err; uint64_t olen = (uint64_t)&len; uint64_t obuf = (uint64_t)cbuf; if (pmap_bootstrapped) olen = vtophys(&len); if (sc->protocol == OPAL_RAW) { if (pmap_bootstrapped) obuf = vtophys(buffer); else obuf = (uint64_t)(buffer); err = opal_call(OPAL_CONSOLE_WRITE, sc->vtermid, olen, obuf); } else { if (pmap_bootstrapped) obuf = vtophys(cbuf); uart_lock(&sc->sc_mtx); if (bufsize > 12) bufsize = 12; seqno = sc->outseqno++; cbuf[0] = VS_DATA_PACKET_HEADER; cbuf[1] = 4 + bufsize; /* total length */ cbuf[2] = (seqno >> 8) & 0xff; cbuf[3] = seqno & 0xff; memcpy(&cbuf[4], buffer, bufsize); len = 4 + bufsize; err = opal_call(OPAL_CONSOLE_WRITE, sc->vtermid, olen, obuf); uart_unlock(&sc->sc_mtx); len -= 4; } #if 0 if (err != OPAL_SUCCESS) len = 0; #endif return (len); } static int uart_opal_cngetc(struct consdev *cp) { unsigned char c; int retval; retval = uart_opal_get(console_sc, &c, 1); if (retval != 1) return (-1); #if defined(KDB) kdb_alt_break(c, &alt_break_state); #endif return (c); } static void uart_opal_cnputc(struct consdev *cp, int c) { unsigned char ch = c; uart_opal_put(console_sc, &ch, 1); } static void uart_opal_cngrab(struct consdev *cp) { } static void uart_opal_cnungrab(struct consdev *cp) { } static void uart_opal_ttyoutwakeup(struct tty *tp) { struct uart_opal_softc *sc; char buffer[8]; int len; sc = tty_softc(tp); while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0) uart_opal_put(sc, buffer, len); } static void uart_opal_intr(void *v) { struct uart_opal_softc *sc = v; struct tty *tp = sc->tp; unsigned char c; int len; tty_lock(tp); while ((len = uart_opal_get(sc, &c, 1)) > 0) ttydisc_rint(tp, c, 0); ttydisc_rint_done(tp); tty_unlock(tp); if (sc->irqres == NULL) callout_reset(&sc->callout, sc->polltime, uart_opal_intr, sc); } +static int +opalcons_probe(device_t dev) +{ + const char *name; + + name = ofw_bus_get_name(dev); + if (name == NULL || strcmp(name, "consoles") != 0) + return (ENXIO); + + device_set_desc(dev, "OPAL Consoles"); + return (BUS_PROBE_SPECIFIC); +} + +static int +opalcons_attach(device_t dev) +{ + phandle_t child; + device_t cdev; + struct ofw_bus_devinfo *dinfo; + + for (child = OF_child(ofw_bus_get_node(dev)); child != 0; + child = OF_peer(child)) { + dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); + if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) { + free(dinfo, M_DEVBUF); + continue; + } + cdev = device_add_child(dev, NULL, -1); + if (cdev == NULL) { + device_printf(dev, "<%s>: device_add_child failed\n", + dinfo->obd_name); + ofw_bus_gen_destroy_devinfo(dinfo); + free(dinfo, M_DEVBUF); + continue; + } + device_set_ivars(cdev, dinfo); + } + + return (bus_generic_attach(dev)); +} + +static const struct ofw_bus_devinfo * +opalcons_get_devinfo(device_t dev, device_t child) +{ + return (device_get_ivars(child)); +} + +static device_method_t opalcons_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, opalcons_probe), + DEVMETHOD(device_attach, opalcons_attach), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, opalcons_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +static driver_t opalcons_driver = { + "opalcons", + opalcons_methods, + 0 +}; + +static devclass_t opalcons_devclass; + +DRIVER_MODULE(opalcons, opal, opalcons_driver, opalcons_devclass, 0, 0); + Index: head/sys/powerpc/powernv/opal_dev.c =================================================================== --- head/sys/powerpc/powernv/opal_dev.c (nonexistent) +++ head/sys/powerpc/powernv/opal_dev.c (revision 327812) @@ -0,0 +1,162 @@ +/*- + * Copyright (c) 2015 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "clock_if.h" +#include "opal.h" + +static int opaldev_probe(device_t); +static int opaldev_attach(device_t); +/* clock interface */ +static int opal_gettime(device_t dev, struct timespec *ts); +static int opal_settime(device_t dev, struct timespec *ts); +/* ofw bus interface */ +static const struct ofw_bus_devinfo *opaldev_get_devinfo(device_t dev, + device_t child); + +static void opal_shutdown(void *arg, int howto); + +static device_method_t opaldev_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, opaldev_probe), + DEVMETHOD(device_attach, opaldev_attach), + + /* clock interface */ + DEVMETHOD(clock_gettime, opal_gettime), + DEVMETHOD(clock_settime, opal_settime), + + /* ofw_bus interface */ + DEVMETHOD(ofw_bus_get_devinfo, opaldev_get_devinfo), + DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), + DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), + DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), + DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), + DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), + + DEVMETHOD_END +}; + +static driver_t opaldev_driver = { + "opal", + opaldev_methods, + 0 +}; + +static devclass_t opaldev_devclass; + +DRIVER_MODULE(opaldev, ofwbus, opaldev_driver, opaldev_devclass, 0, 0); + +static int +opaldev_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "ibm,opal-v3")) + return (ENXIO); + if (opal_check() != 0) + return (ENXIO); + + device_set_desc(dev, "OPAL Abstraction Firmware"); + return (BUS_PROBE_SPECIFIC); +} + +static int +opaldev_attach(device_t dev) +{ + phandle_t child; + device_t cdev; + struct ofw_bus_devinfo *dinfo; + + if (0 /* XXX NOT YET TEST FOR RTC */) + clock_register(dev, 2000); + + EVENTHANDLER_REGISTER(shutdown_final, opal_shutdown, NULL, + SHUTDOWN_PRI_LAST); + + for (child = OF_child(ofw_bus_get_node(dev)); child != 0; + child = OF_peer(child)) { + dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); + if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) { + free(dinfo, M_DEVBUF); + continue; + } + cdev = device_add_child(dev, NULL, -1); + if (cdev == NULL) { + device_printf(dev, "<%s>: device_add_child failed\n", + dinfo->obd_name); + ofw_bus_gen_destroy_devinfo(dinfo); + free(dinfo, M_DEVBUF); + continue; + } + device_set_ivars(cdev, dinfo); + } + + return (bus_generic_attach(dev)); +} + +static int +opal_gettime(device_t dev, struct timespec *ts) { + return (ENXIO); +} + +static int +opal_settime(device_t dev, struct timespec *ts) +{ + return (0); +} + +static const struct ofw_bus_devinfo * +opaldev_get_devinfo(device_t dev, device_t child) +{ + return (device_get_ivars(child)); +} + +static void +opal_shutdown(void *arg, int howto) +{ + + if (howto & RB_HALT) + opal_call(OPAL_CEC_POWER_DOWN, 0 /* Normal power off */); + else + opal_call(OPAL_CEC_REBOOT); +} + Property changes on: head/sys/powerpc/powernv/opal_dev.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: head/sys/powerpc/powernv/platform_powernv.c =================================================================== --- head/sys/powerpc/powernv/platform_powernv.c (revision 327811) +++ head/sys/powerpc/powernv/platform_powernv.c (revision 327812) @@ -1,333 +1,335 @@ /*- * Copyright (c) 2015 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" #include "opal.h" #ifdef SMP extern void *ap_pcpu; #endif static int powernv_probe(platform_t); static int powernv_attach(platform_t); void powernv_mem_regions(platform_t, struct mem_region *phys, int *physsz, struct mem_region *avail, int *availsz); static u_long powernv_timebase_freq(platform_t, struct cpuref *cpuref); static int powernv_smp_first_cpu(platform_t, struct cpuref *cpuref); static int powernv_smp_next_cpu(platform_t, struct cpuref *cpuref); static int powernv_smp_get_bsp(platform_t, struct cpuref *cpuref); static void powernv_smp_ap_init(platform_t); #ifdef SMP static int powernv_smp_start_cpu(platform_t, struct pcpu *cpu); static struct cpu_group *powernv_smp_topo(platform_t plat); #endif static void powernv_reset(platform_t); static platform_method_t powernv_methods[] = { PLATFORMMETHOD(platform_probe, powernv_probe), PLATFORMMETHOD(platform_attach, powernv_attach), PLATFORMMETHOD(platform_mem_regions, powernv_mem_regions), PLATFORMMETHOD(platform_timebase_freq, powernv_timebase_freq), PLATFORMMETHOD(platform_smp_ap_init, powernv_smp_ap_init), PLATFORMMETHOD(platform_smp_first_cpu, powernv_smp_first_cpu), PLATFORMMETHOD(platform_smp_next_cpu, powernv_smp_next_cpu), PLATFORMMETHOD(platform_smp_get_bsp, powernv_smp_get_bsp), #ifdef SMP PLATFORMMETHOD(platform_smp_start_cpu, powernv_smp_start_cpu), PLATFORMMETHOD(platform_smp_topo, powernv_smp_topo), #endif PLATFORMMETHOD(platform_reset, powernv_reset), { 0, 0 } }; static platform_def_t powernv_platform = { "powernv", powernv_methods, 0 }; PLATFORM_DEF(powernv_platform); static int powernv_probe(platform_t plat) { if (opal_check() == 0) return (BUS_PROBE_SPECIFIC); return (ENXIO); } static int powernv_attach(platform_t plat) { /* Ping OPAL again just to make sure */ opal_check(); return (0); } void powernv_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 powernv_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 powernv_smp_first_cpu(platform_t plat, struct cpuref *cpuref) { char buf[8]; phandle_t cpu, dev, root; int res, cpuid; 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); cpuref->cr_hwref = cpu; res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); if (res <= 0) cpuid = 0; cpuref->cr_cpuid = cpuid; return (0); } static int powernv_smp_next_cpu(platform_t plat, struct cpuref *cpuref) { char buf[8]; phandle_t cpu; int i, res, cpuid; /* Check for whether it should be the next thread */ res = OF_getproplen(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s"); if (res > 0) { cell_t interrupt_servers[res/sizeof(cell_t)]; OF_getprop(cpuref->cr_hwref, "ibm,ppc-interrupt-server#s", interrupt_servers, res); for (i = 0; i < res/sizeof(cell_t) - 1; i++) { if (interrupt_servers[i] == cpuref->cr_cpuid) { cpuref->cr_cpuid = interrupt_servers[i+1]; return (0); } } } /* Next CPU core/package */ 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); cpuref->cr_hwref = cpu; res = OF_getprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); if (res <= 0) cpuid = 0; cpuref->cr_cpuid = cpuid; return (0); } static int powernv_smp_get_bsp(platform_t plat, struct cpuref *cpuref) { phandle_t chosen; int cpuid, res; struct cpuref i; chosen = OF_finddevice("/chosen"); if (chosen == 0) return (ENOENT); res = OF_getencprop(chosen, "fdtbootcpu", &cpuid, sizeof(cpuid)); if (res < 0) return (ENOENT); cpuref->cr_cpuid = cpuid; if (powernv_smp_first_cpu(plat, &i) != 0) return (ENOENT); cpuref->cr_hwref = i.cr_hwref; do { if (i.cr_cpuid == cpuid) { cpuref->cr_hwref = i.cr_hwref; break; } } while (powernv_smp_next_cpu(plat, &i) == 0); return (0); } #ifdef SMP static int powernv_smp_start_cpu(platform_t plat, struct pcpu *pc) { int result, err, timeout; ap_pcpu = pc; powerpc_sync(); result = opal_call(OPAL_START_CPU, pc->pc_cpuid, EXC_RST); if (result < 0 || err != 0) { printf("OPAL error (%d/%d): unable to start AP %d\n", result, err, pc->pc_cpuid); return (ENXIO); } timeout = 10000; while (!pc->pc_awake && timeout--) DELAY(100); return ((pc->pc_awake) ? 0 : EBUSY); } static struct cpu_group * powernv_smp_topo(platform_t plat) { struct pcpu *pc, *last_pc; int i, ncores, ncpus; ncores = ncpus = 0; last_pc = NULL; for (i = 0; i <= mp_maxid; i++) { pc = pcpu_find(i); if (pc == NULL) continue; if (last_pc == NULL || pc->pc_hwref != last_pc->pc_hwref) ncores++; last_pc = pc; ncpus++; } if (ncpus % ncores != 0) { printf("WARNING: Irregular SMP topology. Performance may be " "suboptimal (%d CPUS, %d cores)\n", ncpus, ncores); return (smp_topo_none()); } /* Don't do anything fancier for non-threaded SMP */ if (ncpus == ncores) return (smp_topo_none()); return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT)); } #endif static void powernv_reset(platform_t platform) { + + opal_call(OPAL_CEC_REBOOT); } static void powernv_smp_ap_init(platform_t platform) { }