phyp_console.c revision 266019
1129198Scognet/*- 2129198Scognet * Copyright (C) 2011 by Nathan Whitehorn. All rights reserved. 3139735Simp * 4129198Scognet * Redistribution and use in source and binary forms, with or without 5129198Scognet * modification, are permitted provided that the following conditions 6129198Scognet * are met: 7129198Scognet * 1. Redistributions of source code must retain the above copyright 8129198Scognet * notice, this list of conditions and the following disclaimer. 9129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 10129198Scognet * notice, this list of conditions and the following disclaimer in the 11129198Scognet * documentation and/or other materials provided with the distribution. 12129198Scognet * 13129198Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14129198Scognet * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15129198Scognet * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16129198Scognet * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 17129198Scognet * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18129198Scognet * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 19129198Scognet * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 20129198Scognet * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 21129198Scognet * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 22129198Scognet * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23129198Scognet */ 24129198Scognet 25129198Scognet#include <sys/cdefs.h> 26129198Scognet__FBSDID("$FreeBSD: stable/10/sys/powerpc/pseries/phyp_console.c 266019 2014-05-14 14:08:45Z ian $"); 27129198Scognet 28129198Scognet#include <sys/param.h> 29129198Scognet#include <sys/kdb.h> 30129198Scognet#include <sys/kernel.h> 31129198Scognet#include <sys/priv.h> 32129198Scognet#include <sys/systm.h> 33129198Scognet#include <sys/module.h> 34129198Scognet#include <sys/types.h> 35129198Scognet#include <sys/conf.h> 36129198Scognet#include <sys/cons.h> 37129198Scognet#include <sys/tty.h> 38129198Scognet#include <machine/bus.h> 39129198Scognet 40129198Scognet#include <dev/ofw/openfirm.h> 41129198Scognet#include <dev/ofw/ofw_bus.h> 42129198Scognet#include <dev/ofw/ofw_bus_subr.h> 43129198Scognet#include <dev/uart/uart.h> 44129198Scognet#include <dev/uart/uart_cpu.h> 45129198Scognet#include <dev/uart/uart_bus.h> 46129198Scognet 47129198Scognet#include "phyp-hvcall.h" 48129198Scognet#include "uart_if.h" 49129198Scognet 50129198Scognetstruct uart_phyp_softc { 51129198Scognet device_t dev; 52135652Scognet phandle_t node; 53135652Scognet int vtermid; 54129198Scognet 55129198Scognet struct tty *tp; 56129198Scognet struct resource *irqres; 57129198Scognet int irqrid; 58129198Scognet struct callout callout; 59129198Scognet void *sc_icookie; 60135652Scognet int polltime; 61135652Scognet 62129198Scognet struct mtx sc_mtx; 63129198Scognet int protocol; 64129198Scognet 65129198Scognet union { 66129198Scognet uint64_t u64[2]; 67129198Scognet char str[16]; 68129198Scognet } phyp_inbuf; 69129198Scognet uint64_t inbuflen; 70129198Scognet uint8_t outseqno; 71129198Scognet}; 72129198Scognet 73129198Scognetstatic struct uart_phyp_softc *console_sc = NULL; 74129198Scognet#if defined(KDB) 75129198Scognetstatic int alt_break_state; 76129198Scognet#endif 77129198Scognet 78129198Scognetenum { 79129198Scognet HVTERM1, HVTERMPROT 80129198Scognet}; 81129198Scognet 82129198Scognet#define VS_DATA_PACKET_HEADER 0xff 83129198Scognet#define VS_CONTROL_PACKET_HEADER 0xfe 84129198Scognet#define VSV_SET_MODEM_CTL 0x01 85129198Scognet#define VSV_MODEM_CTL_UPDATE 0x02 86129198Scognet#define VSV_RENEGOTIATE_CONNECTION 0x03 87129198Scognet#define VS_QUERY_PACKET_HEADER 0xfd 88129198Scognet#define VSV_SEND_VERSION_NUMBER 0x01 89129198Scognet#define VSV_SEND_MODEM_CTL_STATUS 0x02 90129198Scognet#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc 91129198Scognet 92129198Scognetstatic int uart_phyp_probe(device_t dev); 93129198Scognetstatic int uart_phyp_attach(device_t dev); 94129198Scognetstatic void uart_phyp_intr(void *v); 95129198Scognet 96129198Scognetstatic device_method_t uart_phyp_methods[] = { 97129198Scognet /* Device interface */ 98129198Scognet DEVMETHOD(device_probe, uart_phyp_probe), 99129198Scognet DEVMETHOD(device_attach, uart_phyp_attach), 100129198Scognet 101129198Scognet DEVMETHOD_END 102129198Scognet}; 103129198Scognet 104129198Scognetstatic driver_t uart_phyp_driver = { 105129198Scognet "uart", 106129198Scognet uart_phyp_methods, 107129198Scognet sizeof(struct uart_phyp_softc), 108129198Scognet}; 109129198Scognet 110129198ScognetDRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, uart_devclass, 0, 0); 111129198Scognet 112129198Scognetstatic cn_probe_t uart_phyp_cnprobe; 113129198Scognetstatic cn_init_t uart_phyp_cninit; 114129198Scognetstatic cn_term_t uart_phyp_cnterm; 115129198Scognetstatic cn_getc_t uart_phyp_cngetc; 116129198Scognetstatic cn_putc_t uart_phyp_cnputc; 117129198Scognetstatic cn_grab_t uart_phyp_cngrab; 118129198Scognetstatic cn_ungrab_t uart_phyp_cnungrab; 119129198Scognet 120129198ScognetCONSOLE_DRIVER(uart_phyp); 121129198Scognet 122129198Scognetstatic void uart_phyp_ttyoutwakeup(struct tty *tp); 123129198Scognet 124129198Scognetstatic struct ttydevsw uart_phyp_tty_class = { 125129198Scognet .tsw_flags = TF_INITLOCK|TF_CALLOUT, 126129198Scognet .tsw_outwakeup = uart_phyp_ttyoutwakeup, 127129198Scognet}; 128129198Scognet 129164080Scognetstatic int 130164080Scognetuart_phyp_probe_node(struct uart_phyp_softc *sc) 131164080Scognet{ 132164080Scognet phandle_t node = sc->node; 133164080Scognet uint32_t reg; 134164080Scognet char buf[64]; 135164080Scognet 136129198Scognet sc->inbuflen = 0; 137129198Scognet sc->outseqno = 0; 138129198Scognet 139129198Scognet if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0) 140129198Scognet return (ENXIO); 141129198Scognet if (strcmp(buf, "vty") != 0) 142129198Scognet return (ENXIO); 143129198Scognet 144164423Ssam if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0) 145129198Scognet return (ENXIO); 146129198Scognet if (strcmp(buf, "serial") != 0) 147129198Scognet return (ENXIO); 148129198Scognet 149129198Scognet reg = -1; 150129198Scognet OF_getprop(node, "reg", ®, sizeof(reg)); 151129198Scognet if (reg == -1) 152129198Scognet return (ENXIO); 153129198Scognet sc->vtermid = reg; 154129198Scognet sc->node = node; 155129198Scognet 156129198Scognet if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0) 157129198Scognet return (ENXIO); 158129198Scognet if (strcmp(buf, "hvterm1") == 0) { 159129198Scognet sc->protocol = HVTERM1; 160129198Scognet return (0); 161129198Scognet } else if (strcmp(buf, "hvterm-protocol") == 0) { 162129198Scognet sc->protocol = HVTERMPROT; 163129198Scognet return (0); 164129198Scognet } 165129198Scognet 166129198Scognet return (ENXIO); 167129198Scognet} 168129198Scognet 169129198Scognetstatic int 170129198Scognetuart_phyp_probe(device_t dev) 171129198Scognet{ 172129198Scognet const char *name; 173129198Scognet struct uart_phyp_softc sc; 174129198Scognet int err; 175129198Scognet 176129198Scognet name = ofw_bus_get_name(dev); 177129198Scognet if (name == NULL || strcmp(name, "vty") != 0) 178129198Scognet return (ENXIO); 179129198Scognet 180129198Scognet sc.node = ofw_bus_get_node(dev); 181129198Scognet err = uart_phyp_probe_node(&sc); 182129198Scognet if (err != 0) 183129198Scognet return (err); 184129198Scognet 185129198Scognet device_set_desc(dev, "POWER Hypervisor Virtual Serial Port"); 186129198Scognet 187129198Scognet return (err); 188129198Scognet} 189129198Scognet 190129198Scognetstatic void 191129198Scognetuart_phyp_cnprobe(struct consdev *cp) 192129198Scognet{ 193129198Scognet char buf[64]; 194129198Scognet ihandle_t stdout; 195129198Scognet phandle_t input, chosen; 196129198Scognet static struct uart_phyp_softc sc; 197152653Scognet 198152653Scognet if ((chosen = OF_finddevice("/chosen")) == -1) 199129198Scognet goto fail; 200129198Scognet 201129198Scognet /* Check if OF has an active stdin/stdout */ 202129198Scognet input = -1; 203129198Scognet if (OF_getprop(chosen, "stdout", &stdout, 204129198Scognet sizeof(stdout)) == sizeof(stdout) && stdout != 0) 205129198Scognet input = OF_instance_to_package(stdout); 206129198Scognet if (input == -1) 207129198Scognet goto fail; 208129198Scognet 209129198Scognet if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1) 210129198Scognet goto fail; 211129198Scognet if (strcmp(buf, "serial") != 0) 212129198Scognet goto fail; 213129198Scognet 214129198Scognet sc.node = input; 215129198Scognet if (uart_phyp_probe_node(&sc) != 0) 216129198Scognet goto fail; 217129198Scognet mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET | 218129198Scognet MTX_NOWITNESS); 219129198Scognet 220129198Scognet cp->cn_pri = CN_NORMAL; 221129198Scognet console_sc = ≻ 222129198Scognet return; 223129198Scognet 224129198Scognetfail: 225129198Scognet cp->cn_pri = CN_DEAD; 226129198Scognet return; 227129198Scognet} 228129198Scognet 229129198Scognetstatic int 230129198Scognetuart_phyp_attach(device_t dev) 231129198Scognet{ 232129198Scognet struct uart_phyp_softc *sc; 233129198Scognet int unit; 234129198Scognet 235129198Scognet sc = device_get_softc(dev); 236129198Scognet sc->dev = dev; 237129198Scognet sc->node = ofw_bus_get_node(dev); 238129198Scognet uart_phyp_probe_node(sc); 239164080Scognet 240164080Scognet unit = device_get_unit(dev); 241164080Scognet sc->tp = tty_alloc(&uart_phyp_tty_class, sc); 242161592Scognet mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, 243161592Scognet MTX_SPIN | MTX_QUIET | MTX_NOWITNESS); 244161592Scognet 245161592Scognet if (console_sc != NULL && console_sc->vtermid == sc->vtermid) { 246161592Scognet sc->outseqno = console_sc->outseqno; 247161592Scognet console_sc = sc; 248129198Scognet sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit); 249129198Scognet tty_init_console(sc->tp, 0); 250129198Scognet } 251129198Scognet 252129198Scognet sc->irqrid = 0; 253129198Scognet sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid, 254129198Scognet RF_ACTIVE | RF_SHAREABLE); 255129198Scognet if (sc->irqres != NULL) { 256129198Scognet bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE, 257129198Scognet NULL, uart_phyp_intr, sc, &sc->sc_icookie); 258129198Scognet } else { 259129198Scognet callout_init(&sc->callout, CALLOUT_MPSAFE); 260129198Scognet sc->polltime = hz / 20; 261129198Scognet if (sc->polltime < 1) 262129198Scognet sc->polltime = 1; 263129198Scognet callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); 264129198Scognet } 265129198Scognet 266129198Scognet tty_makedev(sc->tp, NULL, "u%r", unit); 267129198Scognet 268129198Scognet return (0); 269129198Scognet} 270129198Scognet 271129198Scognetstatic void 272129198Scognetuart_phyp_cninit(struct consdev *cp) 273129198Scognet{ 274129198Scognet 275129198Scognet strcpy(cp->cn_name, "phypcons"); 276129198Scognet} 277129198Scognet 278129198Scognetstatic void 279129198Scognetuart_phyp_cnterm(struct consdev *cp) 280129198Scognet{ 281129198Scognet} 282129198Scognet 283129198Scognetstatic int 284129198Scognetuart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) 285155242Simp{ 286129198Scognet int err; 287129198Scognet int hdr = 0; 288129198Scognet 289129198Scognet uart_lock(&sc->sc_mtx); 290129198Scognet if (sc->inbuflen == 0) { 291129198Scognet err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid, 292129198Scognet 0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0], 293129198Scognet &sc->phyp_inbuf.u64[1]); 294129198Scognet if (err != H_SUCCESS) { 295129198Scognet uart_unlock(&sc->sc_mtx); 296129198Scognet return (-1); 297129198Scognet } 298129198Scognet hdr = 1; 299129198Scognet } 300129198Scognet 301129198Scognet if (sc->inbuflen == 0) { 302129198Scognet uart_unlock(&sc->sc_mtx); 303129198Scognet return (0); 304129198Scognet } 305129198Scognet 306129198Scognet if (bufsize > sc->inbuflen) 307129198Scognet bufsize = sc->inbuflen; 308129198Scognet 309129198Scognet if ((sc->protocol == HVTERMPROT) && (hdr == 1)) { 310129198Scognet sc->inbuflen = sc->inbuflen - 4; 311129198Scognet /* The VTERM protocol has a 4 byte header, skip it here. */ 312129198Scognet memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[4], 313129198Scognet sc->inbuflen); 314129198Scognet } 315129198Scognet 316129198Scognet memcpy(buffer, sc->phyp_inbuf.str, bufsize); 317153940Snetchild sc->inbuflen -= bufsize; 318153940Snetchild if (sc->inbuflen > 0) 319153940Snetchild memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize], 320153940Snetchild sc->inbuflen); 321153940Snetchild 322153940Snetchild uart_unlock(&sc->sc_mtx); 323153940Snetchild return (bufsize); 324153940Snetchild} 325153940Snetchild 326129198Scognetstatic int 327129198Scognetuart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize) 328129198Scognet{ 329129198Scognet uint16_t seqno; 330129198Scognet uint64_t len = 0; 331129198Scognet int err; 332129198Scognet 333129198Scognet union { 334129198Scognet uint64_t u64[2]; 335129198Scognet char bytes[16]; 336129198Scognet } cbuf; 337129198Scognet 338129198Scognet uart_lock(&sc->sc_mtx); 339129198Scognet switch (sc->protocol) { 340129198Scognet case HVTERM1: 341129198Scognet if (bufsize > 16) 342129198Scognet bufsize = 16; 343129198Scognet memcpy(&cbuf, buffer, bufsize); 344155242Simp len = bufsize; 345129198Scognet break; 346129198Scognet case HVTERMPROT: 347129198Scognet if (bufsize > 12) 348129198Scognet bufsize = 12; 349129198Scognet seqno = sc->outseqno++; 350129198Scognet cbuf.bytes[0] = VS_DATA_PACKET_HEADER; 351129198Scognet cbuf.bytes[1] = 4 + bufsize; /* total length, max 16 bytes */ 352129198Scognet cbuf.bytes[2] = (seqno >> 8) & 0xff; 353129198Scognet cbuf.bytes[3] = seqno & 0xff; 354155242Simp memcpy(&cbuf.bytes[4], buffer, bufsize); 355129198Scognet len = 4 + bufsize; 356129198Scognet break; 357129198Scognet } 358129198Scognet 359129198Scognet do { 360129198Scognet err = phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, cbuf.u64[0], 361129198Scognet cbuf.u64[1]); 362129198Scognet DELAY(100); 363129198Scognet } while (err == H_BUSY); 364129198Scognet 365129198Scognet uart_unlock(&sc->sc_mtx); 366129198Scognet 367129198Scognet return (bufsize); 368129198Scognet} 369129198Scognet 370129198Scognetstatic int 371129198Scognetuart_phyp_cngetc(struct consdev *cp) 372129198Scognet{ 373129198Scognet unsigned char c; 374129198Scognet int retval; 375129198Scognet 376129198Scognet retval = uart_phyp_get(console_sc, &c, 1); 377171625Scognet if (retval != 1) 378171625Scognet return (-1); 379171625Scognet#if defined(KDB) 380171625Scognet kdb_alt_break(c, &alt_break_state); 381171625Scognet#endif 382171625Scognet 383129198Scognet return (c); 384129198Scognet} 385129198Scognet 386129198Scognetstatic void 387129198Scognetuart_phyp_cnputc(struct consdev *cp, int c) 388129198Scognet{ 389129198Scognet unsigned char ch = c; 390129198Scognet uart_phyp_put(console_sc, &ch, 1); 391129198Scognet} 392129198Scognet 393129198Scognetstatic void 394129198Scognetuart_phyp_cngrab(struct consdev *cp) 395129198Scognet{ 396129198Scognet} 397129198Scognet 398129198Scognetstatic void 399129198Scognetuart_phyp_cnungrab(struct consdev *cp) 400155242Simp{ 401137272Scognet} 402137272Scognet 403137272Scognetstatic void 404137272Scognetuart_phyp_ttyoutwakeup(struct tty *tp) 405137272Scognet{ 406155242Simp struct uart_phyp_softc *sc; 407137272Scognet char buffer[8]; 408137272Scognet int len; 409137272Scognet 410137272Scognet sc = tty_softc(tp); 411155242Simp 412137272Scognet while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0) 413137272Scognet uart_phyp_put(sc, buffer, len); 414155242Simp} 415137272Scognet 416137272Scognetstatic void 417137272Scognetuart_phyp_intr(void *v) 418137272Scognet{ 419129198Scognet struct uart_phyp_softc *sc = v; 420129198Scognet struct tty *tp = sc->tp; 421 unsigned char c; 422 int len; 423 424 tty_lock(tp); 425 while ((len = uart_phyp_get(sc, &c, 1)) > 0) 426 ttydisc_rint(tp, c, 0); 427 ttydisc_rint_done(tp); 428 tty_unlock(tp); 429 430 if (sc->irqres == NULL) 431 callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc); 432} 433 434