linux/drivers/tty/serial/8250_early.c
<<
>>
Prefs
   1/*
   2 * Early serial console for 8250/16550 devices
   3 *
   4 * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
   5 *      Bjorn Helgaas <bjorn.helgaas@hp.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 *
  11 * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King,
  12 * and on early_printk.c by Andi Kleen.
  13 *
  14 * This is for use before the serial driver has initialized, in
  15 * particular, before the UARTs have been discovered and named.
  16 * Instead of specifying the console device as, e.g., "ttyS0",
  17 * we locate the device directly by its MMIO or I/O port address.
  18 *
  19 * The user can specify the device directly, e.g.,
  20 *      earlycon=uart8250,io,0x3f8,9600n8
  21 *      earlycon=uart8250,mmio,0xff5e0000,115200n8
  22 *      earlycon=uart8250,mmio32,0xff5e0000,115200n8
  23 * or
  24 *      console=uart8250,io,0x3f8,9600n8
  25 *      console=uart8250,mmio,0xff5e0000,115200n8
  26 *      console=uart8250,mmio32,0xff5e0000,115200n8
  27 */
  28
  29#include <linux/tty.h>
  30#include <linux/init.h>
  31#include <linux/console.h>
  32#include <linux/serial_core.h>
  33#include <linux/serial_reg.h>
  34#include <linux/serial.h>
  35#include <linux/serial_8250.h>
  36#include <asm/io.h>
  37#include <asm/serial.h>
  38#ifdef CONFIG_FIX_EARLYCON_MEM
  39#include <asm/pgtable.h>
  40#include <asm/fixmap.h>
  41#endif
  42
  43struct early_serial8250_device {
  44        struct uart_port port;
  45        char options[16];               /* e.g., 115200n8 */
  46        unsigned int baud;
  47};
  48
  49static struct early_serial8250_device early_device;
  50
  51static unsigned int __init serial_in(struct uart_port *port, int offset)
  52{
  53        switch (port->iotype) {
  54        case UPIO_MEM:
  55                return readb(port->membase + offset);
  56        case UPIO_MEM32:
  57                return readl(port->membase + (offset << 2));
  58        case UPIO_PORT:
  59                return inb(port->iobase + offset);
  60        default:
  61                return 0;
  62        }
  63}
  64
  65static void __init serial_out(struct uart_port *port, int offset, int value)
  66{
  67        switch (port->iotype) {
  68        case UPIO_MEM:
  69                writeb(value, port->membase + offset);
  70                break;
  71        case UPIO_MEM32:
  72                writel(value, port->membase + (offset << 2));
  73                break;
  74        case UPIO_PORT:
  75                outb(value, port->iobase + offset);
  76                break;
  77        }
  78}
  79
  80#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
  81
  82static void __init wait_for_xmitr(struct uart_port *port)
  83{
  84        unsigned int status;
  85
  86        for (;;) {
  87                status = serial_in(port, UART_LSR);
  88                if ((status & BOTH_EMPTY) == BOTH_EMPTY)
  89                        return;
  90                cpu_relax();
  91        }
  92}
  93
  94static void __init serial_putc(struct uart_port *port, int c)
  95{
  96        wait_for_xmitr(port);
  97        serial_out(port, UART_TX, c);
  98}
  99
 100static void __init early_serial8250_write(struct console *console,
 101                                        const char *s, unsigned int count)
 102{
 103        struct uart_port *port = &early_device.port;
 104        unsigned int ier;
 105
 106        /* Save the IER and disable interrupts */
 107        ier = serial_in(port, UART_IER);
 108        serial_out(port, UART_IER, 0);
 109
 110        uart_console_write(port, s, count, serial_putc);
 111
 112        /* Wait for transmitter to become empty and restore the IER */
 113        wait_for_xmitr(port);
 114        serial_out(port, UART_IER, ier);
 115}
 116
 117static unsigned int __init probe_baud(struct uart_port *port)
 118{
 119        unsigned char lcr, dll, dlm;
 120        unsigned int quot;
 121
 122        lcr = serial_in(port, UART_LCR);
 123        serial_out(port, UART_LCR, lcr | UART_LCR_DLAB);
 124        dll = serial_in(port, UART_DLL);
 125        dlm = serial_in(port, UART_DLM);
 126        serial_out(port, UART_LCR, lcr);
 127
 128        quot = (dlm << 8) | dll;
 129        return (port->uartclk / 16) / quot;
 130}
 131
 132static void __init init_port(struct early_serial8250_device *device)
 133{
 134        struct uart_port *port = &device->port;
 135        unsigned int divisor;
 136        unsigned char c;
 137
 138        serial_out(port, UART_LCR, 0x3);        /* 8n1 */
 139        serial_out(port, UART_IER, 0);          /* no interrupt */
 140        serial_out(port, UART_FCR, 0);          /* no fifo */
 141        serial_out(port, UART_MCR, 0x3);        /* DTR + RTS */
 142
 143        divisor = port->uartclk / (16 * device->baud);
 144        c = serial_in(port, UART_LCR);
 145        serial_out(port, UART_LCR, c | UART_LCR_DLAB);
 146        serial_out(port, UART_DLL, divisor & 0xff);
 147        serial_out(port, UART_DLM, (divisor >> 8) & 0xff);
 148        serial_out(port, UART_LCR, c & ~UART_LCR_DLAB);
 149}
 150
 151static int __init parse_options(struct early_serial8250_device *device,
 152                                                                char *options)
 153{
 154        struct uart_port *port = &device->port;
 155        int mmio, mmio32, length;
 156
 157        if (!options)
 158                return -ENODEV;
 159
 160        port->uartclk = BASE_BAUD * 16;
 161
 162        mmio = !strncmp(options, "mmio,", 5);
 163        mmio32 = !strncmp(options, "mmio32,", 7);
 164        if (mmio || mmio32) {
 165                port->iotype = (mmio ? UPIO_MEM : UPIO_MEM32);
 166                port->mapbase = simple_strtoul(options + (mmio ? 5 : 7),
 167                                               &options, 0);
 168                if (mmio32)
 169                        port->regshift = 2;
 170#ifdef CONFIG_FIX_EARLYCON_MEM
 171                set_fixmap_nocache(FIX_EARLYCON_MEM_BASE,
 172                                        port->mapbase & PAGE_MASK);
 173                port->membase =
 174                        (void __iomem *)__fix_to_virt(FIX_EARLYCON_MEM_BASE);
 175                port->membase += port->mapbase & ~PAGE_MASK;
 176#else
 177                port->membase = ioremap_nocache(port->mapbase, 64);
 178                if (!port->membase) {
 179                        printk(KERN_ERR "%s: Couldn't ioremap 0x%llx\n",
 180                                __func__,
 181                               (unsigned long long) port->mapbase);
 182                        return -ENOMEM;
 183                }
 184#endif
 185        } else if (!strncmp(options, "io,", 3)) {
 186                port->iotype = UPIO_PORT;
 187                port->iobase = simple_strtoul(options + 3, &options, 0);
 188                mmio = 0;
 189        } else
 190                return -EINVAL;
 191
 192        options = strchr(options, ',');
 193        if (options) {
 194                options++;
 195                device->baud = simple_strtoul(options, NULL, 0);
 196                length = min(strcspn(options, " "), sizeof(device->options));
 197                strncpy(device->options, options, length);
 198        } else {
 199                device->baud = probe_baud(port);
 200                snprintf(device->options, sizeof(device->options), "%u",
 201                        device->baud);
 202        }
 203
 204        if (mmio || mmio32)
 205                printk(KERN_INFO
 206                       "Early serial console at MMIO%s 0x%llx (options '%s')\n",
 207                        mmio32 ? "32" : "",
 208                        (unsigned long long)port->mapbase,
 209                        device->options);
 210        else
 211                printk(KERN_INFO
 212                      "Early serial console at I/O port 0x%lx (options '%s')\n",
 213                        port->iobase,
 214                        device->options);
 215
 216        return 0;
 217}
 218
 219static struct console early_serial8250_console __initdata = {
 220        .name   = "uart",
 221        .write  = early_serial8250_write,
 222        .flags  = CON_PRINTBUFFER | CON_BOOT,
 223        .index  = -1,
 224};
 225
 226static int __init early_serial8250_setup(char *options)
 227{
 228        struct early_serial8250_device *device = &early_device;
 229        int err;
 230
 231        if (device->port.membase || device->port.iobase)
 232                return 0;
 233
 234        err = parse_options(device, options);
 235        if (err < 0)
 236                return err;
 237
 238        init_port(device);
 239        return 0;
 240}
 241
 242int __init setup_early_serial8250_console(char *cmdline)
 243{
 244        char *options;
 245        int err;
 246
 247        options = strstr(cmdline, "uart8250,");
 248        if (!options) {
 249                options = strstr(cmdline, "uart,");
 250                if (!options)
 251                        return 0;
 252        }
 253
 254        options = strchr(cmdline, ',') + 1;
 255        err = early_serial8250_setup(options);
 256        if (err < 0)
 257                return err;
 258
 259        register_console(&early_serial8250_console);
 260
 261        return 0;
 262}
 263
 264int serial8250_find_port_for_earlycon(void)
 265{
 266        struct early_serial8250_device *device = &early_device;
 267        struct uart_port *port = &device->port;
 268        int line;
 269        int ret;
 270
 271        if (!device->port.membase && !device->port.iobase)
 272                return -ENODEV;
 273
 274        line = serial8250_find_port(port);
 275        if (line < 0)
 276                return -ENODEV;
 277
 278        ret = update_console_cmdline("uart", 8250,
 279                             "ttyS", line, device->options);
 280        if (ret < 0)
 281                ret = update_console_cmdline("uart", 0,
 282                                     "ttyS", line, device->options);
 283
 284        return ret;
 285}
 286
 287early_param("earlycon", setup_early_serial8250_console);
 288