Path: blob/main/sys/powerpc/powernv/opal_console.c
39534 views
/*-1* Copyright (C) 2011,2015 by Nathan Whitehorn. All rights reserved.2*3* Redistribution and use in source and binary forms, with or without4* modification, are permitted provided that the following conditions5* are met:6* 1. Redistributions of source code must retain the above copyright7* notice, this list of conditions and the following disclaimer.8* 2. Redistributions in binary form must reproduce the above copyright9* notice, this list of conditions and the following disclaimer in the10* documentation and/or other materials provided with the distribution.11*12* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR13* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES14* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.15* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,16* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,17* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;18* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,19* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR20* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF21* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.22*/2324#include <sys/cdefs.h>25#include <sys/endian.h>26#include <sys/param.h>27#include <sys/conf.h>28#include <sys/cons.h>29#include <sys/kdb.h>30#include <sys/kernel.h>31#include <sys/lock.h>32#include <sys/module.h>33#include <sys/mutex.h>34#include <sys/priv.h>35#include <sys/proc.h>36#include <sys/systm.h>37#include <sys/tty.h>3839#include <vm/vm.h>40#include <vm/pmap.h>4142#include <machine/bus.h>4344#include <dev/ofw/openfirm.h>45#include <dev/ofw/ofw_bus.h>46#include <dev/ofw/ofw_bus_subr.h>47#include <dev/uart/uart.h>48#include <dev/uart/uart_cpu.h>49#include <dev/uart/uart_bus.h>5051#include "opal.h"52#include "uart_if.h"5354struct uart_opal_softc {55device_t dev;56phandle_t node;57int vtermid;5859struct tty *tp;60struct resource *irqres;61int irqrid;62struct callout callout;63void *sc_icookie;64int polltime;6566struct mtx sc_mtx;67int protocol;6869char opal_inbuf[16];70uint64_t inbuflen;71uint8_t outseqno;72#if defined(KDB)73int alt_break_state;74#endif75};7677static struct uart_opal_softc *console_sc = NULL;78static struct consdev *stdout_cp;7980enum {81OPAL_RAW, OPAL_HVSI82};8384#define VS_DATA_PACKET_HEADER 0xff85#define VS_CONTROL_PACKET_HEADER 0xfe86#define VSV_SET_MODEM_CTL 0x0187#define VSV_MODEM_CTL_UPDATE 0x0288#define VSV_RENEGOTIATE_CONNECTION 0x0389#define VS_QUERY_PACKET_HEADER 0xfd90#define VSV_SEND_VERSION_NUMBER 0x0191#define VSV_SEND_MODEM_CTL_STATUS 0x0292#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc9394static int uart_opal_probe(device_t dev);95static int uart_opal_attach(device_t dev);96static void uart_opal_intr(void *v);9798static device_method_t uart_opal_methods[] = {99/* Device interface */100DEVMETHOD(device_probe, uart_opal_probe),101DEVMETHOD(device_attach, uart_opal_attach),102103DEVMETHOD_END104};105106static driver_t uart_opal_driver = {107"uart",108uart_opal_methods,109sizeof(struct uart_opal_softc),110};111112DRIVER_MODULE(uart_opal, opalcons, uart_opal_driver, 0, 0);113114static int uart_opal_getc(struct uart_opal_softc *sc);115static cn_probe_t uart_opal_cnprobe;116static cn_init_t uart_opal_cninit;117static cn_term_t uart_opal_cnterm;118static cn_getc_t uart_opal_cngetc;119static cn_putc_t uart_opal_cnputc;120static cn_grab_t uart_opal_cngrab;121static cn_ungrab_t uart_opal_cnungrab;122123CONSOLE_DRIVER(uart_opal);124125static void uart_opal_ttyoutwakeup(struct tty *tp);126127static struct ttydevsw uart_opal_tty_class = {128.tsw_flags = TF_INITLOCK|TF_CALLOUT,129.tsw_outwakeup = uart_opal_ttyoutwakeup,130};131132static struct {133char tmpbuf[16];134uint64_t size;135struct mtx mtx;136} opalcons_buffer;137138static void139uart_opal_real_map_outbuffer(uint64_t *bufferp, uint64_t *lenp)140{141142if (!mtx_initialized(&opalcons_buffer.mtx))143mtx_init(&opalcons_buffer.mtx, "uart_opal", NULL,144MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);145146if (!pmap_bootstrapped)147return;148149mtx_lock_spin(&opalcons_buffer.mtx);150151opalcons_buffer.size = *(uint64_t *)(*lenp) =152min(sizeof(opalcons_buffer.tmpbuf), *(uint64_t *)(*lenp));153memcpy(opalcons_buffer.tmpbuf, (void *)(*bufferp),154*(uint64_t *)(*lenp));155*bufferp = (uint64_t)opalcons_buffer.tmpbuf;156*lenp = (uint64_t)&opalcons_buffer.size;157}158159static void160uart_opal_real_unmap_outbuffer(uint64_t *len)161{162163if (!pmap_bootstrapped)164return;165166mtx_assert(&opalcons_buffer.mtx, MA_OWNED);167*len = opalcons_buffer.size;168mtx_unlock_spin(&opalcons_buffer.mtx);169}170171static int64_t172uart_opal_console_write_buffer_space(int vtermid)173{174int64_t buffer_space_val = 0;175vm_paddr_t buffer_space_ptr;176177if (pmap_bootstrapped)178buffer_space_ptr = vtophys(&buffer_space_val);179else180buffer_space_ptr = (vm_paddr_t)&buffer_space_val;181182if (opal_call(OPAL_CONSOLE_WRITE_BUFFER_SPACE, vtermid,183buffer_space_ptr) != OPAL_SUCCESS)184return (-1);185186return (be64toh(buffer_space_val));187}188189static int190uart_opal_probe_node(struct uart_opal_softc *sc)191{192phandle_t node = sc->node;193uint32_t reg;194char buf[64];195196sc->inbuflen = 0;197sc->outseqno = 0;198199if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0)200return (ENXIO);201if (strcmp(buf, "serial") != 0)202return (ENXIO);203204reg = -1;205OF_getencprop(node, "reg", ®, sizeof(reg));206if (reg == -1)207return (ENXIO);208sc->vtermid = reg;209sc->node = node;210211if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0)212return (ENXIO);213if (strcmp(buf, "ibm,opal-console-raw") == 0) {214sc->protocol = OPAL_RAW;215return (0);216} else if (strcmp(buf, "ibm,opal-console-hvsi") == 0) {217sc->protocol = OPAL_HVSI;218return (0);219}220221return (ENXIO);222}223224static int225uart_opal_probe(device_t dev)226{227struct uart_opal_softc sc;228int err;229230sc.node = ofw_bus_get_node(dev);231err = uart_opal_probe_node(&sc);232if (err != 0)233return (err);234235device_set_desc(dev, "OPAL Serial Port");236237return (err);238}239240static void241uart_opal_cnprobe(struct consdev *cp)242{243char buf[64];244phandle_t input, chosen;245static struct uart_opal_softc sc;246247if (opal_check() != 0)248goto fail;249250if ((chosen = OF_finddevice("/chosen")) == -1)251goto fail;252253/* Check if OF has an active stdin/stdout */254if (OF_getprop(chosen, "linux,stdout-path", buf, sizeof(buf)) <= 0)255goto fail;256257input = OF_finddevice(buf);258if (input == -1)259goto fail;260261sc.node = input;262if (uart_opal_probe_node(&sc) != 0)263goto fail;264mtx_init(&sc.sc_mtx, "uart_opal", NULL, MTX_SPIN | MTX_QUIET |265MTX_NOWITNESS);266267cp->cn_pri = CN_NORMAL;268console_sc = ≻269cp->cn_arg = console_sc;270stdout_cp = cp;271return;272273fail:274cp->cn_pri = CN_DEAD;275return;276}277278static int279uart_opal_attach(device_t dev)280{281struct uart_opal_softc *sc;282int unit;283284sc = device_get_softc(dev);285sc->node = ofw_bus_get_node(dev);286uart_opal_probe_node(sc);287288unit = device_get_unit(dev);289mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL,290MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);291292if (console_sc != NULL && console_sc->vtermid == sc->vtermid) {293device_printf(dev, "console\n");294device_set_softc(dev, console_sc);295sc = console_sc;296sprintf(uart_opal_consdev.cn_name, "ttyu%r", unit);297}298sc->tp = tty_alloc(&uart_opal_tty_class, sc);299300if (console_sc == sc)301tty_init_console(sc->tp, 0);302303sc->dev = dev;304sc->irqrid = 0;305sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid,306RF_ACTIVE | RF_SHAREABLE);307if (sc->irqres != NULL) {308bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE,309NULL, uart_opal_intr, sc, &sc->sc_icookie);310} else {311callout_init(&sc->callout, CALLOUT_MPSAFE);312sc->polltime = hz / 20;313if (sc->polltime < 1)314sc->polltime = 1;315callout_reset(&sc->callout, sc->polltime, uart_opal_intr, sc);316}317318tty_makedev(sc->tp, NULL, "u%r", unit);319320return (0);321}322323static void324uart_opal_cninit(struct consdev *cp)325{326327strcpy(cp->cn_name, "opalcons");328}329330static void331uart_opal_cnterm(struct consdev *cp)332{333}334335static int336uart_opal_get(struct uart_opal_softc *sc, void *buffer, size_t bufsize)337{338int err;339int hdr = 0;340341if (sc->protocol == OPAL_RAW) {342uint64_t len = htobe64(bufsize);343uint64_t olen = (uint64_t)&len;344uint64_t obuf = (uint64_t)buffer;345346if (pmap_bootstrapped) {347olen = vtophys(&len);348obuf = vtophys(buffer);349}350351err = opal_call(OPAL_CONSOLE_READ, sc->vtermid, olen, obuf);352if (err != OPAL_SUCCESS)353return (-1);354355bufsize = be64toh(len);356} else {357uart_lock(&sc->sc_mtx);358if (sc->inbuflen == 0) {359err = opal_call(OPAL_CONSOLE_READ, sc->vtermid,360&sc->inbuflen, sc->opal_inbuf);361if (err != OPAL_SUCCESS) {362uart_unlock(&sc->sc_mtx);363return (-1);364}365hdr = 1;366sc->inbuflen = be64toh(sc->inbuflen);367}368369if (sc->inbuflen == 0) {370uart_unlock(&sc->sc_mtx);371return (0);372}373374if (bufsize > sc->inbuflen)375bufsize = sc->inbuflen;376377if (hdr == 1) {378sc->inbuflen = sc->inbuflen - 4;379/* The HVSI protocol has a 4 byte header, skip it */380memmove(&sc->opal_inbuf[0], &sc->opal_inbuf[4],381sc->inbuflen);382}383384memcpy(buffer, sc->opal_inbuf, bufsize);385sc->inbuflen -= bufsize;386if (sc->inbuflen > 0)387memmove(&sc->opal_inbuf[0], &sc->opal_inbuf[bufsize],388sc->inbuflen);389390uart_unlock(&sc->sc_mtx);391}392393return (bufsize);394}395396static int397uart_opal_put(struct uart_opal_softc *sc, void *buffer, size_t bufsize)398{399uint16_t seqno;400uint64_t len;401char cbuf[16];402int err;403uint64_t olen = (uint64_t)&len;404uint64_t obuf = (uint64_t)cbuf;405406if (sc->protocol == OPAL_RAW) {407obuf = (uint64_t)buffer;408len = bufsize;409410uart_opal_real_map_outbuffer(&obuf, &olen);411*(uint64_t*)olen = htobe64(*(uint64_t*)olen);412err = opal_call(OPAL_CONSOLE_WRITE, sc->vtermid, olen, obuf);413*(uint64_t*)olen = be64toh(*(uint64_t*)olen);414uart_opal_real_unmap_outbuffer(&len);415} else {416uart_lock(&sc->sc_mtx);417if (bufsize > 12)418bufsize = 12;419seqno = sc->outseqno++;420cbuf[0] = VS_DATA_PACKET_HEADER;421cbuf[1] = 4 + bufsize; /* total length */422cbuf[2] = (seqno >> 8) & 0xff;423cbuf[3] = seqno & 0xff;424memcpy(&cbuf[4], buffer, bufsize);425len = 4 + bufsize;426427uart_opal_real_map_outbuffer(&obuf, &olen);428*(uint64_t*)olen = htobe64(*(uint64_t*)olen);429err = opal_call(OPAL_CONSOLE_WRITE, sc->vtermid, olen, obuf);430*(uint64_t*)olen = be64toh(*(uint64_t*)olen);431uart_opal_real_unmap_outbuffer(&len);432433uart_unlock(&sc->sc_mtx);434435len -= 4;436}437438if (err == OPAL_SUCCESS)439return (len);440else if (err == OPAL_BUSY_EVENT)441return(0);442443return (-1);444}445446static int447uart_opal_cngetc(struct consdev *cp)448{449return (uart_opal_getc(cp->cn_arg));450}451452static int453uart_opal_getc(struct uart_opal_softc *sc)454{455unsigned char c;456int retval;457458retval = uart_opal_get(sc, &c, 1);459if (retval != 1)460return (-1);461#if defined(KDB)462kdb_alt_break(c, &sc->alt_break_state);463#endif464465return (c);466}467468static void469uart_opal_cnputc(struct consdev *cp, int c)470{471unsigned char ch = c;472int a;473474if (1) {475/* Clear FIFO if needed. Must be repeated few times. */476for (a = 0; a < 20; a++) {477opal_call(OPAL_POLL_EVENTS, NULL);478}479}480uart_opal_put(cp->cn_arg, &ch, 1);481}482483static void484uart_opal_cngrab(struct consdev *cp)485{486}487488static void489uart_opal_cnungrab(struct consdev *cp)490{491}492493static void494uart_opal_ttyoutwakeup(struct tty *tp)495{496struct uart_opal_softc *sc;497char buffer[8];498int len;499int64_t buffer_space;500501sc = tty_softc(tp);502503while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0) {504int bytes_written = 0;505while (bytes_written == 0) {506buffer_space = uart_opal_console_write_buffer_space(sc->vtermid);507if (buffer_space == -1)508/* OPAL failure or invalid terminal */509break;510else if (buffer_space >= len)511bytes_written = uart_opal_put(sc, buffer, len);512513if (bytes_written == 0)514/* OPAL must be busy, poll and retry */515opal_call(OPAL_POLL_EVENTS, NULL);516else if (bytes_written == -1)517/* OPAL failure or invalid terminal */518break;519}520}521}522523static void524uart_opal_intr(void *v)525{526struct uart_opal_softc *sc = v;527struct tty *tp = sc->tp;528int c;529530tty_lock(tp);531while ((c = uart_opal_getc(sc)) > 0)532ttydisc_rint(tp, c, 0);533ttydisc_rint_done(tp);534tty_unlock(tp);535536opal_call(OPAL_POLL_EVENTS, NULL);537538if (sc->irqres == NULL)539callout_reset(&sc->callout, sc->polltime, uart_opal_intr, sc);540}541542static int543opalcons_probe(device_t dev)544{545const char *name;546547name = ofw_bus_get_name(dev);548if (name == NULL || strcmp(name, "consoles") != 0)549return (ENXIO);550551device_set_desc(dev, "OPAL Consoles");552return (BUS_PROBE_SPECIFIC);553}554555static int556opalcons_attach(device_t dev)557{558phandle_t child;559device_t cdev;560struct ofw_bus_devinfo *dinfo;561562for (child = OF_child(ofw_bus_get_node(dev)); child != 0;563child = OF_peer(child)) {564dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);565if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) {566free(dinfo, M_DEVBUF);567continue;568}569cdev = device_add_child(dev, NULL, DEVICE_UNIT_ANY);570if (cdev == NULL) {571device_printf(dev, "<%s>: device_add_child failed\n",572dinfo->obd_name);573ofw_bus_gen_destroy_devinfo(dinfo);574free(dinfo, M_DEVBUF);575continue;576}577device_set_ivars(cdev, dinfo);578}579580bus_attach_children(dev);581return (0);582}583584static const struct ofw_bus_devinfo *585opalcons_get_devinfo(device_t dev, device_t child)586{587return (device_get_ivars(child));588}589590static device_method_t opalcons_methods[] = {591/* Device interface */592DEVMETHOD(device_probe, opalcons_probe),593DEVMETHOD(device_attach, opalcons_attach),594595/* ofw_bus interface */596DEVMETHOD(ofw_bus_get_devinfo, opalcons_get_devinfo),597DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),598DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),599DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),600DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),601DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),602603DEVMETHOD_END604};605606static driver_t opalcons_driver = {607"opalcons",608opalcons_methods,6090610};611612DRIVER_MODULE(opalcons, opal, opalcons_driver, 0, 0);613614615