Index: head/sys/powerpc/powernv/opal.h =================================================================== --- head/sys/powerpc/powernv/opal.h (revision 327872) +++ head/sys/powerpc/powernv/opal.h (revision 327873) @@ -1,77 +1,80 @@ /*- * 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_RTC_READ 3 +#define OPAL_RTC_WRITE 4 #define OPAL_CEC_POWER_DOWN 5 #define OPAL_CEC_REBOOT 6 #define OPAL_HANDLE_INTERRUPT 9 +#define OPAL_POLL_EVENTS 10 #define OPAL_PCI_CONFIG_READ_BYTE 13 #define OPAL_PCI_CONFIG_READ_HALF_WORD 14 #define OPAL_PCI_CONFIG_READ_WORD 15 #define OPAL_PCI_CONFIG_WRITE_BYTE 16 #define OPAL_PCI_CONFIG_WRITE_HALF_WORD 17 #define OPAL_PCI_CONFIG_WRITE_WORD 18 #define OPAL_PCI_EEH_FREEZE_CLEAR 26 #define OPAL_PCI_PHB_MMIO_ENABLE 27 #define OPAL_PCI_SET_PHB_MEM_WINDOW 28 #define OPAL_PCI_MAP_PE_MMIO_WINDOW 29 #define OPAL_PCI_SET_XIVE_PE 37 #define OPAL_PCI_RESET 49 #define OPAL_PCI_POLL 62 #define OPAL_PCI_SET_PE 31 -#define OPAL_START_CPU 41 #define OPAL_GET_MSI_32 39 #define OPAL_GET_MSI_64 40 #define OPAL_PCI_MSI_EOI 63 +#define OPAL_START_CPU 41 #define OPAL_PCI_MAP_PE_DMA_WINDOW_REAL 45 #define OPAL_RETURN_CPU 69 #define OPAL_REINIT_CPUS 70 /* For OPAL_PCI_SET_PE */ #define OPAL_UNMAP_PE 0 #define OPAL_MAP_PE 1 #define OPAL_SUCCESS 0 #define OPAL_PARAMETER -1 #define OPAL_CLOSED -5 #define OPAL_BUSY_EVENT -12 #endif Index: head/sys/powerpc/powernv/opal_console.c =================================================================== --- head/sys/powerpc/powernv/opal_console.c (revision 327872) +++ head/sys/powerpc/powernv/opal_console.c (revision 327873) @@ -1,558 +1,559 @@ /*- * 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 #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 struct { char tmpbuf[16]; uint64_t size; struct mtx mtx; -} escapehatch; +} opalcons_buffer; static void uart_opal_real_map_outbuffer(uint64_t *bufferp, uint64_t *lenp) { - if (!mtx_initialized(&escapehatch.mtx)) - mtx_init(&escapehatch.mtx, "uart_opal", NULL, MTX_SPIN | MTX_QUIET | - MTX_NOWITNESS); + if (!mtx_initialized(&opalcons_buffer.mtx)) + mtx_init(&opalcons_buffer.mtx, "uart_opal", NULL, + MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); if (!pmap_bootstrapped) return; - if (TD_IS_IDLETHREAD(curthread)) { - escapehatch.size = *(uint64_t *)(*lenp) = - min(sizeof(escapehatch.tmpbuf), *(uint64_t *)(*lenp)); - mtx_lock_spin(&escapehatch.mtx); - memcpy(escapehatch.tmpbuf, (void *)(*bufferp), *(uint64_t *)(*lenp)); - *bufferp = (uint64_t)escapehatch.tmpbuf; - *lenp = (uint64_t)&escapehatch.size; - } + mtx_lock_spin(&opalcons_buffer.mtx); - *bufferp = vtophys(*bufferp); - *lenp = vtophys(*lenp); + opalcons_buffer.size = *(uint64_t *)(*lenp) = + min(sizeof(opalcons_buffer.tmpbuf), *(uint64_t *)(*lenp)); + memcpy(opalcons_buffer.tmpbuf, (void *)(*bufferp), + *(uint64_t *)(*lenp)); + *bufferp = (uint64_t)opalcons_buffer.tmpbuf; + *lenp = (uint64_t)&opalcons_buffer.size; } static void -uart_opal_real_unmap_outbuffer(uint64_t lenp, uint64_t *origlen) +uart_opal_real_unmap_outbuffer(uint64_t *len) { - if (!pmap_bootstrapped || !TD_IS_IDLETHREAD(curthread)) + if (!pmap_bootstrapped) return; - mtx_assert(&escapehatch.mtx, MA_OWNED); - *origlen = escapehatch.size; - mtx_unlock_spin(&escapehatch.mtx); + mtx_assert(&opalcons_buffer.mtx, MA_OWNED); + *len = opalcons_buffer.size; + mtx_unlock_spin(&opalcons_buffer.mtx); } 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)); + OF_getencprop(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) { device_printf(dev, "console\n"); 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; char cbuf[16]; int err; uint64_t olen = (uint64_t)&len; uint64_t obuf = (uint64_t)cbuf; if (sc->protocol == OPAL_RAW) { obuf = (uint64_t)buffer; len = bufsize; uart_opal_real_map_outbuffer(&obuf, &olen); err = opal_call(OPAL_CONSOLE_WRITE, sc->vtermid, olen, obuf); - uart_opal_real_unmap_outbuffer(olen, &len); + uart_opal_real_unmap_outbuffer(&len); } else { 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; uart_opal_real_map_outbuffer(&obuf, &olen); err = opal_call(OPAL_CONSOLE_WRITE, sc->vtermid, olen, obuf); - uart_opal_real_unmap_outbuffer(olen, &len); + uart_opal_real_unmap_outbuffer(&len); 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) { + static uint64_t events; unsigned char ch = c; + + if (cold) + opal_call(OPAL_POLL_EVENTS, &events); /* Clear FIFO if needed */ 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 (revision 327872) +++ head/sys/powerpc/powernv/opal_dev.c (revision 327873) @@ -1,162 +1,262 @@ /*- * 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 #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 void opal_intr(void *); 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) { + phandle_t iparent; + pcell_t *irqs; + int i, n_irqs; if (!ofw_bus_is_compatible(dev, "ibm,opal-v3")) return (ENXIO); if (opal_check() != 0) return (ENXIO); device_set_desc(dev, "OPAL Abstraction Firmware"); + + /* Manually add IRQs before attaching */ + if (OF_hasprop(ofw_bus_get_node(dev), "opal-interrupts")) { + iparent = OF_finddevice("/interrupt-controller@0"); + iparent = OF_xref_from_node(iparent); + + n_irqs = OF_getproplen(ofw_bus_get_node(dev), + "opal-interrupts") / sizeof(*irqs); + irqs = malloc(n_irqs * sizeof(*irqs), M_DEVBUF, M_WAITOK); + OF_getencprop(ofw_bus_get_node(dev), "opal-interrupts", irqs, + n_irqs * sizeof(*irqs)); + for (i = 0; i < n_irqs; i++) + bus_set_resource(dev, SYS_RES_IRQ, i, + ofw_bus_map_intr(dev, iparent, 1, &irqs[i]), 1); + free(irqs, M_DEVBUF); + } + + return (BUS_PROBE_SPECIFIC); } static int opaldev_attach(device_t dev) { phandle_t child; device_t cdev; + uint64_t junk; + int i, rv; struct ofw_bus_devinfo *dinfo; + struct resource *irq; - if (0 /* XXX NOT YET TEST FOR RTC */) + /* Test for RTC support and register clock if it works */ + rv = opal_call(OPAL_RTC_READ, vtophys(&junk), vtophys(&junk)); + do { + rv = opal_call(OPAL_RTC_READ, vtophys(&junk), vtophys(&junk)); + if (rv == OPAL_BUSY_EVENT) + rv = opal_call(OPAL_POLL_EVENTS, 0); + } while (rv == OPAL_BUSY_EVENT); + + if (rv == OPAL_SUCCESS) clock_register(dev, 2000); EVENTHANDLER_REGISTER(shutdown_final, opal_shutdown, NULL, SHUTDOWN_PRI_LAST); + /* Bind to interrupts */ + for (i = 0; (irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, + RF_ACTIVE)) != NULL; i++) + bus_setup_intr(dev, irq, INTR_TYPE_TTY | INTR_MPSAFE | + INTR_ENTROPY, NULL, opal_intr, (void *)rman_get_start(irq), + NULL); + 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); +bcd2bin32(int bcd) +{ + int out = 0; + + out += bcd2bin(bcd & 0xff); + out += 100*bcd2bin((bcd & 0x0000ff00) >> 8); + out += 10000*bcd2bin((bcd & 0x00ff0000) >> 16); + out += 1000000*bcd2bin((bcd & 0xffff0000) >> 24); + + return (out); } static int +opal_gettime(device_t dev, struct timespec *ts) +{ + int rv; + struct clocktime ct; + uint32_t ymd; + uint64_t hmsm; + + do { + rv = opal_call(OPAL_RTC_READ, vtophys(&ymd), vtophys(&hmsm)); + if (rv == OPAL_BUSY_EVENT) { + rv = opal_call(OPAL_POLL_EVENTS, 0); + pause("opalrtc", 1); + } + } while (rv == OPAL_BUSY_EVENT); + + if (rv != OPAL_SUCCESS) + return (ENXIO); + + hmsm = be64toh(hmsm); + ymd = be32toh(ymd); + + ct.nsec = bcd2bin32((hmsm & 0x000000ffffff0000) >> 16) * 1000; + ct.sec = bcd2bin((hmsm & 0x0000ff0000000000) >> 40); + ct.min = bcd2bin((hmsm & 0x00ff000000000000) >> 48); + ct.hour = bcd2bin((hmsm & 0xff00000000000000) >> 56); + + ct.day = bcd2bin((ymd & 0x000000ff) >> 0); + ct.mon = bcd2bin((ymd & 0x0000ff00) >> 8); + ct.year = bcd2bin32((ymd & 0xffff0000) >> 16); + + return (clock_ct_to_ts(&ct, ts)); +} + +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); + + opal_call(OPAL_RETURN_CPU); +} + +static void +opal_intr(void *xintr) +{ + uint64_t events = 0; + + opal_call(OPAL_HANDLE_INTERRUPT, (uint32_t)(uint64_t)xintr, + vtophys(&events)); + /* XXX: do something useful with this information */ + } Index: head/sys/powerpc/powernv/platform_powernv.c =================================================================== --- head/sys/powerpc/powernv/platform_powernv.c (revision 327872) +++ head/sys/powerpc/powernv/platform_powernv.c (revision 327873) @@ -1,341 +1,436 @@ /*- * 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 #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 void powernv_cpu_idle(sbintime_t sbt); 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_boot_pir; + static int powernv_probe(platform_t plat) { if (opal_check() == 0) return (BUS_PROBE_SPECIFIC); return (ENXIO); } static int powernv_attach(platform_t plat) { + uint32_t nptlp, shift = 0, slb_encoding = 0; + int32_t lp_size, lp_encoding; + char buf[255]; + pcell_t prop; + phandle_t cpu; + int res, len, node, idx; + /* Ping OPAL again just to make sure */ opal_check(); cpu_idle_hook = powernv_cpu_idle; + powernv_boot_pir = mfspr(SPR_PIR); - /* Direct interrupts to SRR instead of HSRR */ - mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_LPES); + /* Init CPU bits */ + powernv_smp_ap_init(plat); + /* Set SLB count from device tree */ + cpu = OF_peer(0); + cpu = OF_child(cpu); + while (cpu != 0) { + res = OF_getprop(cpu, "name", buf, sizeof(buf)); + if (res > 0 && strcmp(buf, "cpus") == 0) + break; + cpu = OF_peer(cpu); + } + if (cpu == 0) + goto out; + + cpu = OF_child(cpu); + 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) + goto out; + + res = OF_getencprop(cpu, "ibm,slb-size", &prop, sizeof(prop)); + if (res > 0) + n_slbs = prop; + + /* + * Scan the large page size property for PAPR compatible machines. + * See PAPR D.5 Changes to Section 5.1.4, 'CPU Node Properties' + * for the encoding of the property. + */ + + len = OF_getproplen(node, "ibm,segment-page-sizes"); + if (len > 0) { + /* + * We have to use a variable length array on the stack + * since we have very limited stack space. + */ + pcell_t arr[len/sizeof(cell_t)]; + res = OF_getencprop(cpu, "ibm,segment-page-sizes", arr, + sizeof(arr)); + len /= 4; + idx = 0; + while (len > 0) { + shift = arr[idx]; + slb_encoding = arr[idx + 1]; + nptlp = arr[idx + 2]; + idx += 3; + len -= 3; + while (len > 0 && nptlp) { + lp_size = arr[idx]; + lp_encoding = arr[idx+1]; + if (slb_encoding == SLBV_L && lp_encoding == 0) + break; + + idx += 2; + len -= 2; + nptlp--; + } + if (nptlp && slb_encoding == SLBV_L && lp_encoding == 0) + break; + } + + if (len == 0) + panic("Standard large pages (SLB[L] = 1, PTE[LP] = 0) " + "not supported by this system."); + + moea64_large_page_shift = shift; + moea64_large_page_size = 1ULL << lp_size; + } + +out: 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)); + OF_getencprop(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, + res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) - res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); + res = OF_getencprop(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", + OF_getencprop(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, + res = OF_getencprop(cpu, "ibm,ppc-interrupt-server#s", &cpuid, sizeof(cpuid)); if (res <= 0) - res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid)); + res = OF_getencprop(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); + /* XXX: FDT from kexec lies sometimes. PIR seems not to. */ + if (cpuid == 0) + cpuid = powernv_boot_pir; + 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; ap_pcpu = pc; powerpc_sync(); result = opal_call(OPAL_START_CPU, pc->pc_cpuid, EXC_RST); if (result != OPAL_SUCCESS) { printf("OPAL error (%d): unable to start AP %d\n", result, pc->pc_cpuid); return (ENXIO); } return (0); } 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++) { + CPU_FOREACH(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()); +#ifdef NOTYET /* smp_topo_1level() fails with non-consecutive CPU IDs */ return (smp_topo_1level(CG_SHARE_L1, ncpus / ncores, CG_FLAG_SMT)); +#else + return (smp_topo_none()); +#endif } #endif static void powernv_reset(platform_t platform) { opal_call(OPAL_CEC_REBOOT); } static void powernv_smp_ap_init(platform_t platform) { + + /* Direct interrupts to SRR instead of HSRR and reset LPCR otherwise */ + mtspr(SPR_LPCR, LPCR_LPES); } static void powernv_cpu_idle(sbintime_t sbt) { }