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