654 lines · c
1// SPDX-License-Identifier: GPL-2.02/* sunhv.c: Serial driver for SUN4V hypervisor console.3 *4 * Copyright (C) 2006, 2007 David S. Miller (davem@davemloft.net)5 */6 7#include <linux/kernel.h>8#include <linux/errno.h>9#include <linux/tty.h>10#include <linux/tty_flip.h>11#include <linux/major.h>12#include <linux/circ_buf.h>13#include <linux/serial.h>14#include <linux/sysrq.h>15#include <linux/console.h>16#include <linux/spinlock.h>17#include <linux/slab.h>18#include <linux/delay.h>19#include <linux/init.h>20#include <linux/of.h>21#include <linux/platform_device.h>22 23#include <asm/hypervisor.h>24#include <asm/spitfire.h>25#include <asm/irq.h>26#include <asm/setup.h>27 28#include <linux/serial_core.h>29#include <linux/sunserialcore.h>30 31#define CON_BREAK ((long)-1)32#define CON_HUP ((long)-2)33 34#define IGNORE_BREAK 0x135#define IGNORE_ALL 0x236 37static char *con_write_page;38static char *con_read_page;39 40static int hung_up = 0;41 42static void transmit_chars_putchar(struct uart_port *port,43 struct tty_port *tport)44{45 unsigned char ch;46 47 while (kfifo_peek(&tport->xmit_fifo, &ch)) {48 long status = sun4v_con_putchar(ch);49 50 if (status != HV_EOK)51 break;52 53 uart_xmit_advance(port, 1);54 }55}56 57static void transmit_chars_write(struct uart_port *port, struct tty_port *tport)58{59 while (!kfifo_is_empty(&tport->xmit_fifo)) {60 unsigned long len, ra, status, sent;61 unsigned char *tail;62 63 len = kfifo_out_linear_ptr(&tport->xmit_fifo, &tail,64 UART_XMIT_SIZE);65 ra = __pa(tail);66 67 status = sun4v_con_write(ra, len, &sent);68 if (status != HV_EOK)69 break;70 uart_xmit_advance(port, sent);71 }72}73 74static int receive_chars_getchar(struct uart_port *port)75{76 int saw_console_brk = 0;77 int limit = 10000;78 79 while (limit-- > 0) {80 long status;81 long c = sun4v_con_getchar(&status);82 83 if (status == HV_EWOULDBLOCK)84 break;85 86 if (c == CON_BREAK) {87 if (uart_handle_break(port))88 continue;89 saw_console_brk = 1;90 c = 0;91 }92 93 if (c == CON_HUP) {94 hung_up = 1;95 uart_handle_dcd_change(port, false);96 } else if (hung_up) {97 hung_up = 0;98 uart_handle_dcd_change(port, true);99 }100 101 if (port->state == NULL) {102 uart_handle_sysrq_char(port, c);103 continue;104 }105 106 port->icount.rx++;107 108 if (uart_handle_sysrq_char(port, c))109 continue;110 111 tty_insert_flip_char(&port->state->port, c, TTY_NORMAL);112 }113 114 return saw_console_brk;115}116 117static int receive_chars_read(struct uart_port *port)118{119 static int saw_console_brk;120 int limit = 10000;121 122 while (limit-- > 0) {123 unsigned long ra = __pa(con_read_page);124 unsigned long bytes_read, i;125 long stat = sun4v_con_read(ra, PAGE_SIZE, &bytes_read);126 127 if (stat != HV_EOK) {128 bytes_read = 0;129 130 if (stat == CON_BREAK) {131 if (saw_console_brk)132 sun_do_break();133 134 if (uart_handle_break(port))135 continue;136 saw_console_brk = 1;137 *con_read_page = 0;138 bytes_read = 1;139 } else if (stat == CON_HUP) {140 hung_up = 1;141 uart_handle_dcd_change(port, false);142 continue;143 } else {144 /* HV_EWOULDBLOCK, etc. */145 break;146 }147 }148 149 if (hung_up) {150 hung_up = 0;151 uart_handle_dcd_change(port, true);152 }153 154 if (port->sysrq != 0 && *con_read_page) {155 for (i = 0; i < bytes_read; i++)156 uart_handle_sysrq_char(port, con_read_page[i]);157 saw_console_brk = 0;158 }159 160 if (port->state == NULL)161 continue;162 163 port->icount.rx += bytes_read;164 165 tty_insert_flip_string(&port->state->port, con_read_page,166 bytes_read);167 }168 169 return saw_console_brk;170}171 172struct sunhv_ops {173 void (*transmit_chars)(struct uart_port *port, struct tty_port *tport);174 int (*receive_chars)(struct uart_port *port);175};176 177static const struct sunhv_ops bychar_ops = {178 .transmit_chars = transmit_chars_putchar,179 .receive_chars = receive_chars_getchar,180};181 182static const struct sunhv_ops bywrite_ops = {183 .transmit_chars = transmit_chars_write,184 .receive_chars = receive_chars_read,185};186 187static const struct sunhv_ops *sunhv_ops = &bychar_ops;188 189static struct tty_port *receive_chars(struct uart_port *port)190{191 struct tty_port *tport = NULL;192 193 if (port->state != NULL) /* Unopened serial console */194 tport = &port->state->port;195 196 if (sunhv_ops->receive_chars(port))197 sun_do_break();198 199 return tport;200}201 202static void transmit_chars(struct uart_port *port)203{204 struct tty_port *tport;205 206 if (!port->state)207 return;208 209 tport = &port->state->port;210 if (kfifo_is_empty(&tport->xmit_fifo) || uart_tx_stopped(port))211 return;212 213 sunhv_ops->transmit_chars(port, tport);214 215 if (kfifo_len(&tport->xmit_fifo) < WAKEUP_CHARS)216 uart_write_wakeup(port);217}218 219static irqreturn_t sunhv_interrupt(int irq, void *dev_id)220{221 struct uart_port *port = dev_id;222 struct tty_port *tport;223 unsigned long flags;224 225 uart_port_lock_irqsave(port, &flags);226 tport = receive_chars(port);227 transmit_chars(port);228 uart_port_unlock_irqrestore(port, flags);229 230 if (tport)231 tty_flip_buffer_push(tport);232 233 return IRQ_HANDLED;234}235 236/* port->lock is not held. */237static unsigned int sunhv_tx_empty(struct uart_port *port)238{239 /* Transmitter is always empty for us. If the circ buffer240 * is non-empty or there is an x_char pending, our caller241 * will do the right thing and ignore what we return here.242 */243 return TIOCSER_TEMT;244}245 246/* port->lock held by caller. */247static void sunhv_set_mctrl(struct uart_port *port, unsigned int mctrl)248{249 return;250}251 252/* port->lock is held by caller and interrupts are disabled. */253static unsigned int sunhv_get_mctrl(struct uart_port *port)254{255 return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS;256}257 258/* port->lock held by caller. */259static void sunhv_stop_tx(struct uart_port *port)260{261 return;262}263 264/* port->lock held by caller. */265static void sunhv_start_tx(struct uart_port *port)266{267 transmit_chars(port);268}269 270/* port->lock is not held. */271static void sunhv_send_xchar(struct uart_port *port, char ch)272{273 unsigned long flags;274 int limit = 10000;275 276 if (ch == __DISABLED_CHAR)277 return;278 279 uart_port_lock_irqsave(port, &flags);280 281 while (limit-- > 0) {282 long status = sun4v_con_putchar(ch);283 if (status == HV_EOK)284 break;285 udelay(1);286 }287 288 uart_port_unlock_irqrestore(port, flags);289}290 291/* port->lock held by caller. */292static void sunhv_stop_rx(struct uart_port *port)293{294}295 296/* port->lock is not held. */297static void sunhv_break_ctl(struct uart_port *port, int break_state)298{299 if (break_state) {300 unsigned long flags;301 int limit = 10000;302 303 uart_port_lock_irqsave(port, &flags);304 305 while (limit-- > 0) {306 long status = sun4v_con_putchar(CON_BREAK);307 if (status == HV_EOK)308 break;309 udelay(1);310 }311 312 uart_port_unlock_irqrestore(port, flags);313 }314}315 316/* port->lock is not held. */317static int sunhv_startup(struct uart_port *port)318{319 return 0;320}321 322/* port->lock is not held. */323static void sunhv_shutdown(struct uart_port *port)324{325}326 327/* port->lock is not held. */328static void sunhv_set_termios(struct uart_port *port, struct ktermios *termios,329 const struct ktermios *old)330{331 unsigned int baud = uart_get_baud_rate(port, termios, old, 0, 4000000);332 unsigned int quot = uart_get_divisor(port, baud);333 unsigned int iflag, cflag;334 unsigned long flags;335 336 uart_port_lock_irqsave(port, &flags);337 338 iflag = termios->c_iflag;339 cflag = termios->c_cflag;340 341 port->ignore_status_mask = 0;342 if (iflag & IGNBRK)343 port->ignore_status_mask |= IGNORE_BREAK;344 if ((cflag & CREAD) == 0)345 port->ignore_status_mask |= IGNORE_ALL;346 347 /* XXX */348 uart_update_timeout(port, cflag,349 (port->uartclk / (16 * quot)));350 351 uart_port_unlock_irqrestore(port, flags);352}353 354static const char *sunhv_type(struct uart_port *port)355{356 return "SUN4V HCONS";357}358 359static void sunhv_release_port(struct uart_port *port)360{361}362 363static int sunhv_request_port(struct uart_port *port)364{365 return 0;366}367 368static void sunhv_config_port(struct uart_port *port, int flags)369{370}371 372static int sunhv_verify_port(struct uart_port *port, struct serial_struct *ser)373{374 return -EINVAL;375}376 377static const struct uart_ops sunhv_pops = {378 .tx_empty = sunhv_tx_empty,379 .set_mctrl = sunhv_set_mctrl,380 .get_mctrl = sunhv_get_mctrl,381 .stop_tx = sunhv_stop_tx,382 .start_tx = sunhv_start_tx,383 .send_xchar = sunhv_send_xchar,384 .stop_rx = sunhv_stop_rx,385 .break_ctl = sunhv_break_ctl,386 .startup = sunhv_startup,387 .shutdown = sunhv_shutdown,388 .set_termios = sunhv_set_termios,389 .type = sunhv_type,390 .release_port = sunhv_release_port,391 .request_port = sunhv_request_port,392 .config_port = sunhv_config_port,393 .verify_port = sunhv_verify_port,394};395 396static struct uart_driver sunhv_reg = {397 .owner = THIS_MODULE,398 .driver_name = "sunhv",399 .dev_name = "ttyHV",400 .major = TTY_MAJOR,401};402 403static struct uart_port *sunhv_port;404 405void sunhv_migrate_hvcons_irq(int cpu)406{407 /* Migrate hvcons irq to param cpu */408 irq_force_affinity(sunhv_port->irq, cpumask_of(cpu));409}410 411/* Copy 's' into the con_write_page, decoding "\n" into412 * "\r\n" along the way. We have to return two lengths413 * because the caller needs to know how much to advance414 * 's' and also how many bytes to output via con_write_page.415 */416static int fill_con_write_page(const char *s, unsigned int n,417 unsigned long *page_bytes)418{419 const char *orig_s = s;420 char *p = con_write_page;421 int left = PAGE_SIZE;422 423 while (n--) {424 if (*s == '\n') {425 if (left < 2)426 break;427 *p++ = '\r';428 left--;429 } else if (left < 1)430 break;431 *p++ = *s++;432 left--;433 }434 *page_bytes = p - con_write_page;435 return s - orig_s;436}437 438static void sunhv_console_write_paged(struct console *con, const char *s, unsigned n)439{440 struct uart_port *port = sunhv_port;441 unsigned long flags;442 int locked = 1;443 444 if (port->sysrq || oops_in_progress)445 locked = uart_port_trylock_irqsave(port, &flags);446 else447 uart_port_lock_irqsave(port, &flags);448 449 while (n > 0) {450 unsigned long ra = __pa(con_write_page);451 unsigned long page_bytes;452 unsigned int cpy = fill_con_write_page(s, n,453 &page_bytes);454 455 n -= cpy;456 s += cpy;457 while (page_bytes > 0) {458 unsigned long written;459 int limit = 1000000;460 461 while (limit--) {462 unsigned long stat;463 464 stat = sun4v_con_write(ra, page_bytes,465 &written);466 if (stat == HV_EOK)467 break;468 udelay(1);469 }470 if (limit < 0)471 break;472 page_bytes -= written;473 ra += written;474 }475 }476 477 if (locked)478 uart_port_unlock_irqrestore(port, flags);479}480 481static inline void sunhv_console_putchar(struct uart_port *port, char c)482{483 int limit = 1000000;484 485 while (limit-- > 0) {486 long status = sun4v_con_putchar(c);487 if (status == HV_EOK)488 break;489 udelay(1);490 }491}492 493static void sunhv_console_write_bychar(struct console *con, const char *s, unsigned n)494{495 struct uart_port *port = sunhv_port;496 unsigned long flags;497 int i, locked = 1;498 499 if (port->sysrq || oops_in_progress)500 locked = uart_port_trylock_irqsave(port, &flags);501 else502 uart_port_lock_irqsave(port, &flags);503 504 for (i = 0; i < n; i++) {505 if (*s == '\n')506 sunhv_console_putchar(port, '\r');507 sunhv_console_putchar(port, *s++);508 }509 510 if (locked)511 uart_port_unlock_irqrestore(port, flags);512}513 514static struct console sunhv_console = {515 .name = "ttyHV",516 .write = sunhv_console_write_bychar,517 .device = uart_console_device,518 .flags = CON_PRINTBUFFER,519 .index = -1,520 .data = &sunhv_reg,521};522 523static int hv_probe(struct platform_device *op)524{525 struct uart_port *port;526 unsigned long minor;527 int err;528 529 if (op->archdata.irqs[0] == 0xffffffff)530 return -ENODEV;531 532 port = kzalloc(sizeof(struct uart_port), GFP_KERNEL);533 if (unlikely(!port))534 return -ENOMEM;535 536 minor = 1;537 if (sun4v_hvapi_register(HV_GRP_CORE, 1, &minor) == 0 &&538 minor >= 1) {539 err = -ENOMEM;540 con_write_page = kzalloc(PAGE_SIZE, GFP_KERNEL);541 if (!con_write_page)542 goto out_free_port;543 544 con_read_page = kzalloc(PAGE_SIZE, GFP_KERNEL);545 if (!con_read_page)546 goto out_free_con_write_page;547 548 sunhv_console.write = sunhv_console_write_paged;549 sunhv_ops = &bywrite_ops;550 }551 552 sunhv_port = port;553 554 port->has_sysrq = 1;555 port->line = 0;556 port->ops = &sunhv_pops;557 port->type = PORT_SUNHV;558 port->uartclk = ( 29491200 / 16 ); /* arbitrary */559 560 port->membase = (unsigned char __iomem *) __pa(port);561 562 port->irq = op->archdata.irqs[0];563 564 port->dev = &op->dev;565 566 err = sunserial_register_minors(&sunhv_reg, 1);567 if (err)568 goto out_free_con_read_page;569 570 sunserial_console_match(&sunhv_console, op->dev.of_node,571 &sunhv_reg, port->line, false);572 573 err = uart_add_one_port(&sunhv_reg, port);574 if (err)575 goto out_unregister_driver;576 577 err = request_irq(port->irq, sunhv_interrupt, 0, "hvcons", port);578 if (err)579 goto out_remove_port;580 581 platform_set_drvdata(op, port);582 583 return 0;584 585out_remove_port:586 uart_remove_one_port(&sunhv_reg, port);587 588out_unregister_driver:589 sunserial_unregister_minors(&sunhv_reg, 1);590 591out_free_con_read_page:592 kfree(con_read_page);593 594out_free_con_write_page:595 kfree(con_write_page);596 597out_free_port:598 kfree(port);599 sunhv_port = NULL;600 return err;601}602 603static void hv_remove(struct platform_device *dev)604{605 struct uart_port *port = platform_get_drvdata(dev);606 607 free_irq(port->irq, port);608 609 uart_remove_one_port(&sunhv_reg, port);610 611 sunserial_unregister_minors(&sunhv_reg, 1);612 kfree(con_read_page);613 kfree(con_write_page);614 kfree(port);615 sunhv_port = NULL;616}617 618static const struct of_device_id hv_match[] = {619 {620 .name = "console",621 .compatible = "qcn",622 },623 {624 .name = "console",625 .compatible = "SUNW,sun4v-console",626 },627 {},628};629 630static struct platform_driver hv_driver = {631 .driver = {632 .name = "hv",633 .of_match_table = hv_match,634 },635 .probe = hv_probe,636 .remove_new = hv_remove,637};638 639static int __init sunhv_init(void)640{641 if (tlb_type != hypervisor)642 return -ENODEV;643 644 return platform_driver_register(&hv_driver);645}646device_initcall(sunhv_init);647 648#if 0 /* ...def MODULE ; never supported as such */649MODULE_AUTHOR("David S. Miller");650MODULE_DESCRIPTION("SUN4V Hypervisor console driver");651MODULE_VERSION("2.0");652MODULE_LICENSE("GPL");653#endif654