PowerNV: initial support for OPAL
OPAL is a dedicated firmware acting as a hypervisor. Add generic functions to provide all access. Created by: Nathan Whitehorn <nw@freebsd.org> Submitted by: Wojciech Macek <wma@freebsd.org>
This commit is contained in:
parent
8fb95dc265
commit
fb3855e0e7
@ -183,6 +183,10 @@ powerpc/powermac/smusat.c optional powermac smu
|
||||
powerpc/powermac/uninorth.c optional powermac
|
||||
powerpc/powermac/uninorthpci.c optional powermac pci
|
||||
powerpc/powermac/vcoregpio.c optional powermac
|
||||
powerpc/powernv/opal.c optional powernv
|
||||
powerpc/powernv/opal_console.c optional powernv
|
||||
powerpc/powernv/opalcall.S optional powernv
|
||||
powerpc/powernv/platform_powernv.c optional powernv
|
||||
powerpc/powerpc/altivec.c optional powerpc | powerpc64
|
||||
powerpc/powerpc/autoconf.c standard
|
||||
powerpc/powerpc/bcopy.c standard
|
||||
|
@ -23,6 +23,7 @@ MPC85XX opt_platform.h
|
||||
POWERMAC opt_platform.h
|
||||
PS3 opt_platform.h
|
||||
MAMBO
|
||||
POWERNV
|
||||
PSERIES
|
||||
PSIM
|
||||
|
||||
|
@ -31,6 +31,7 @@ options POWERMAC #NewWorld Apple PowerMacs
|
||||
options PS3 #Sony Playstation 3
|
||||
options MAMBO #IBM Mambo Full System Simulator
|
||||
options PSERIES #PAPR-compliant systems (e.g. IBM p)
|
||||
options POWERNV #Non-virtualized OpenPOWER systems
|
||||
|
||||
options FDT #Flattened Device Tree
|
||||
options SCHED_ULE #ULE scheduler
|
||||
|
66
sys/powerpc/powernv/opal.c
Normal file
66
sys/powerpc/powernv/opal.c
Normal file
@ -0,0 +1,66 @@
|
||||
/*-
|
||||
* 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 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
|
||||
#include "opal.h"
|
||||
|
||||
extern uint64_t opal_entrypoint;
|
||||
extern uint64_t opal_data;
|
||||
extern uint64_t opal_msr;
|
||||
|
||||
static int opal_initialized = 0;
|
||||
|
||||
int
|
||||
opal_check(void)
|
||||
{
|
||||
phandle_t opal;
|
||||
cell_t val[2];
|
||||
|
||||
if (opal_initialized)
|
||||
return (0);
|
||||
|
||||
opal = OF_finddevice("/ibm,opal");
|
||||
if (opal == -1)
|
||||
return (ENOENT);
|
||||
|
||||
if (!OF_hasprop(opal, "opal-base-address") ||
|
||||
!OF_hasprop(opal, "opal-entry-address"))
|
||||
return (ENOENT);
|
||||
|
||||
OF_getencprop(opal, "opal-base-address", val, sizeof(val));
|
||||
opal_data = ((uint64_t)val[0] << 32) | val[1];
|
||||
OF_getencprop(opal, "opal-entry-address", val, sizeof(val));
|
||||
opal_entrypoint = ((uint64_t)val[0] << 32) | val[1];
|
||||
|
||||
opal_msr = mfmsr() & ~(PSL_EE | PSL_IR | PSL_DR | PSL_SE);
|
||||
|
||||
opal_initialized = 1;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
48
sys/powerpc/powernv/opal.h
Normal file
48
sys/powerpc/powernv/opal.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*-
|
||||
* 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 <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* 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_START_CPU 41
|
||||
|
||||
#define OPAL_SUCCESS 0
|
||||
#define OPAL_BUSY_EVENT -12
|
||||
|
||||
#endif
|
441
sys/powerpc/powernv/opal_console.c
Normal file
441
sys/powerpc/powernv/opal_console.c
Normal file
@ -0,0 +1,441 @@
|
||||
/*-
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kdb.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/priv.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/cons.h>
|
||||
#include <sys/tty.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <dev/ofw/ofw_bus.h>
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
#include <dev/uart/uart.h>
|
||||
#include <dev/uart/uart_cpu.h>
|
||||
#include <dev/uart/uart_bus.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
99
sys/powerpc/powernv/opalcall.S
Normal file
99
sys/powerpc/powernv/opalcall.S
Normal file
@ -0,0 +1,99 @@
|
||||
/*-
|
||||
* 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 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <machine/asm.h>
|
||||
|
||||
GLOBAL(opal_entrypoint)
|
||||
.llong 0
|
||||
GLOBAL(opal_data)
|
||||
.llong 0
|
||||
GLOBAL(opal_msr)
|
||||
.llong 0
|
||||
|
||||
TOC_ENTRY(opal_entrypoint)
|
||||
TOC_ENTRY(opal_data)
|
||||
TOC_ENTRY(opal_msr)
|
||||
|
||||
ASENTRY(opal_call)
|
||||
/* Args:
|
||||
* r3: opal token
|
||||
* r4-r10 opal arguments
|
||||
*/
|
||||
|
||||
/* Save call stuff on stack */
|
||||
mflr %r0
|
||||
std %r0,16(%r1)
|
||||
std %r2,-16(%r1)
|
||||
mfcr %r0
|
||||
std %r0,8(%r1)
|
||||
|
||||
/* Load OPAL entry information */
|
||||
mr %r0,%r3
|
||||
ld %r3,TOC_REF(opal_entrypoint)(%r2)
|
||||
ld %r3,0(%r3)
|
||||
mtctr %r3
|
||||
|
||||
/* Save MSR in non-volatile scratch register and turn off translation */
|
||||
std %r31,-8(%r1)
|
||||
mfmsr %r31
|
||||
|
||||
/* Load last bits from the TOC */
|
||||
ld %r3,TOC_REF(opal_msr)(%r2)
|
||||
ld %r3,0(%r3)
|
||||
ld %r2,TOC_REF(opal_data)(%r2)
|
||||
ld %r2,0(%r2)
|
||||
|
||||
mtmsrd %r3
|
||||
isync
|
||||
|
||||
/* Shift registers over */
|
||||
mr %r3,%r4
|
||||
mr %r4,%r5
|
||||
mr %r5,%r6
|
||||
mr %r6,%r7
|
||||
mr %r7,%r8
|
||||
mr %r8,%r9
|
||||
mr %r9,%r10
|
||||
|
||||
/* Call OPAL */
|
||||
bctrl
|
||||
|
||||
/* Restore MSR */
|
||||
mtmsrd %r31
|
||||
isync
|
||||
ld %r31,-8(%r1)
|
||||
|
||||
/* Restore call stuff from stack */
|
||||
ld %r0,16(%r1)
|
||||
mtlr %r0
|
||||
ld %r2,-16(%r1)
|
||||
ld %r0,8(%r1)
|
||||
mtcr %r0
|
||||
|
||||
/* And return */
|
||||
blr
|
||||
|
333
sys/powerpc/powernv/platform_powernv.c
Normal file
333
sys/powerpc/powernv/platform_powernv.c
Normal file
@ -0,0 +1,333 @@
|
||||
/*-
|
||||
* 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 <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/rtas.h>
|
||||
#include <machine/smp.h>
|
||||
#include <machine/spr.h>
|
||||
#include <machine/trap.h>
|
||||
|
||||
#include <dev/ofw/openfirm.h>
|
||||
#include <machine/ofw_machdep.h>
|
||||
|
||||
#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)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
powernv_smp_ap_init(platform_t platform)
|
||||
{
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user