linux/drivers/tty/serial/8250/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
  39static struct earlycon_device *early_device;
  40
  41unsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset)
  42{
  43        switch (port->iotype) {
  44        case UPIO_MEM:
  45                return readb(port->membase + offset);
  46        case UPIO_MEM32:
  47                return readl(port->membase + (offset << 2));
  48        case UPIO_PORT:
  49                return inb(port->iobase + offset);
  50        default:
  51                return 0;
  52        }
  53}
  54
  55void __weak __init serial8250_early_out(struct uart_port *port, int offset, int value)
  56{
  57        switch (port->iotype) {
  58        case UPIO_MEM:
  59                writeb(value, port->membase + offset);
  60                break;
  61        case UPIO_MEM32:
  62                writel(value, port->membase + (offset << 2));
  63                break;
  64        case UPIO_PORT:
  65                outb(value, port->iobase + offset);
  66                break;
  67        }
  68}
  69
  70#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
  71
  72static void __init wait_for_xmitr(struct uart_port *port)
  73{
  74        unsigned int status;
  75
  76        for (;;) {
  77                status = serial8250_early_in(port, UART_LSR);
  78                if ((status & BOTH_EMPTY) == BOTH_EMPTY)
  79                        return;
  80                cpu_relax();
  81        }
  82}
  83
  84static void __init serial_putc(struct uart_port *port, int c)
  85{
  86        wait_for_xmitr(port);
  87        serial8250_early_out(port, UART_TX, c);
  88}
  89
  90static void __init early_serial8250_write(struct console *console,
  91                                        const char *s, unsigned int count)
  92{
  93        struct uart_port *port = &early_device->port;
  94        unsigned int ier;
  95
  96        /* Save the IER and disable interrupts */
  97        ier = serial8250_early_in(port, UART_IER);
  98        serial8250_early_out(port, UART_IER, 0);
  99
 100        uart_console_write(port, s, count, serial_putc);
 101
 102        /* Wait for transmitter to become empty and restore the IER */
 103        wait_for_xmitr(port);
 104        serial8250_early_out(port, UART_IER, ier);
 105}
 106
 107static unsigned int __init probe_baud(struct uart_port *port)
 108{
 109        unsigned char lcr, dll, dlm;
 110        unsigned int quot;
 111
 112        lcr = serial8250_early_in(port, UART_LCR);
 113        serial8250_early_out(port, UART_LCR, lcr | UART_LCR_DLAB);
 114        dll = serial8250_early_in(port, UART_DLL);
 115        dlm = serial8250_early_in(port, UART_DLM);
 116        serial8250_early_out(port, UART_LCR, lcr);
 117
 118        quot = (dlm << 8) | dll;
 119        return (port->uartclk / 16) / quot;
 120}
 121
 122static void __init init_port(struct earlycon_device *device)
 123{
 124        struct uart_port *port = &device->port;
 125        unsigned int divisor;
 126        unsigned char c;
 127
 128        serial8250_early_out(port, UART_LCR, 0x3);      /* 8n1 */
 129        serial8250_early_out(port, UART_IER, 0);        /* no interrupt */
 130        serial8250_early_out(port, UART_FCR, 0);        /* no fifo */
 131        serial8250_early_out(port, UART_MCR, 0x3);      /* DTR + RTS */
 132
 133        divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);
 134        c = serial8250_early_in(port, UART_LCR);
 135        serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB);
 136        serial8250_early_out(port, UART_DLL, divisor & 0xff);
 137        serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff);
 138        serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB);
 139}
 140
 141static int __init early_serial8250_setup(struct earlycon_device *device,
 142                                         const char *options)
 143{
 144        if (!(device->port.membase || device->port.iobase))
 145                return 0;
 146
 147        if (!device->baud) {
 148                device->baud = probe_baud(&device->port);
 149                snprintf(device->options, sizeof(device->options), "%u",
 150                         device->baud);
 151        }
 152
 153        init_port(device);
 154
 155        early_device = device;
 156        device->con->write = early_serial8250_write;
 157        return 0;
 158}
 159EARLYCON_DECLARE(uart8250, early_serial8250_setup);
 160EARLYCON_DECLARE(uart, early_serial8250_setup);
 161
 162int __init setup_early_serial8250_console(char *cmdline)
 163{
 164        char match[] = "uart8250";
 165
 166        if (cmdline && cmdline[4] == ',')
 167                match[4] = '\0';
 168
 169        return setup_earlycon(cmdline, match, early_serial8250_setup);
 170}
 171
 172int serial8250_find_port_for_earlycon(void)
 173{
 174        struct earlycon_device *device = early_device;
 175        struct uart_port *port = device ? &device->port : NULL;
 176        int line;
 177        int ret;
 178
 179        if (!port || (!port->membase && !port->iobase))
 180                return -ENODEV;
 181
 182        line = serial8250_find_port(port);
 183        if (line < 0)
 184                return -ENODEV;
 185
 186        ret = update_console_cmdline("uart", 8250,
 187                             "ttyS", line, device->options);
 188        if (ret < 0)
 189                ret = update_console_cmdline("uart", 0,
 190                                     "ttyS", line, device->options);
 191
 192        return ret;
 193}
 194