linux/drivers/tty/serial/8250/8250_early.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Early serial console for 8250/16550 devices
   4 *
   5 * (c) Copyright 2004 Hewlett-Packard Development Company, L.P.
   6 *      Bjorn Helgaas <bjorn.helgaas@hp.com>
   7 *
   8 * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King,
   9 * and on early_printk.c by Andi Kleen.
  10 *
  11 * This is for use before the serial driver has initialized, in
  12 * particular, before the UARTs have been discovered and named.
  13 * Instead of specifying the console device as, e.g., "ttyS0",
  14 * we locate the device directly by its MMIO or I/O port address.
  15 *
  16 * The user can specify the device directly, e.g.,
  17 *      earlycon=uart8250,io,0x3f8,9600n8
  18 *      earlycon=uart8250,mmio,0xff5e0000,115200n8
  19 *      earlycon=uart8250,mmio32,0xff5e0000,115200n8
  20 * or
  21 *      console=uart8250,io,0x3f8,9600n8
  22 *      console=uart8250,mmio,0xff5e0000,115200n8
  23 *      console=uart8250,mmio32,0xff5e0000,115200n8
  24 */
  25
  26#include <linux/tty.h>
  27#include <linux/init.h>
  28#include <linux/console.h>
  29#include <linux/of.h>
  30#include <linux/of_device.h>
  31#include <linux/serial_reg.h>
  32#include <linux/serial.h>
  33#include <linux/serial_8250.h>
  34#include <asm/io.h>
  35#include <asm/serial.h>
  36
  37static unsigned int serial8250_early_in(struct uart_port *port, int offset)
  38{
  39        int reg_offset = offset;
  40        offset <<= port->regshift;
  41
  42        switch (port->iotype) {
  43        case UPIO_MEM:
  44                return readb(port->membase + offset);
  45        case UPIO_MEM16:
  46                return readw(port->membase + offset);
  47        case UPIO_MEM32:
  48                return readl(port->membase + offset);
  49        case UPIO_MEM32BE:
  50                return ioread32be(port->membase + offset);
  51        case UPIO_PORT:
  52                return inb(port->iobase + offset);
  53        case UPIO_AU:
  54                return port->serial_in(port, reg_offset);
  55        default:
  56                return 0;
  57        }
  58}
  59
  60static void serial8250_early_out(struct uart_port *port, int offset, int value)
  61{
  62        int reg_offset = offset;
  63        offset <<= port->regshift;
  64
  65        switch (port->iotype) {
  66        case UPIO_MEM:
  67                writeb(value, port->membase + offset);
  68                break;
  69        case UPIO_MEM16:
  70                writew(value, port->membase + offset);
  71                break;
  72        case UPIO_MEM32:
  73                writel(value, port->membase + offset);
  74                break;
  75        case UPIO_MEM32BE:
  76                iowrite32be(value, port->membase + offset);
  77                break;
  78        case UPIO_PORT:
  79                outb(value, port->iobase + offset);
  80                break;
  81        case UPIO_AU:
  82                port->serial_out(port, reg_offset, value);
  83                break;
  84        }
  85}
  86
  87#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
  88
  89static void serial_putc(struct uart_port *port, int c)
  90{
  91        unsigned int status;
  92
  93        serial8250_early_out(port, UART_TX, c);
  94
  95        for (;;) {
  96                status = serial8250_early_in(port, UART_LSR);
  97                if ((status & BOTH_EMPTY) == BOTH_EMPTY)
  98                        break;
  99                cpu_relax();
 100        }
 101}
 102
 103static void early_serial8250_write(struct console *console,
 104                                        const char *s, unsigned int count)
 105{
 106        struct earlycon_device *device = console->data;
 107        struct uart_port *port = &device->port;
 108
 109        uart_console_write(port, s, count, serial_putc);
 110}
 111
 112static void __init init_port(struct earlycon_device *device)
 113{
 114        struct uart_port *port = &device->port;
 115        unsigned int divisor;
 116        unsigned char c;
 117        unsigned int ier;
 118
 119        serial8250_early_out(port, UART_LCR, 0x3);      /* 8n1 */
 120        ier = serial8250_early_in(port, UART_IER);
 121        serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */
 122        serial8250_early_out(port, UART_FCR, 0);        /* no fifo */
 123        serial8250_early_out(port, UART_MCR, 0x3);      /* DTR + RTS */
 124
 125        if (port->uartclk) {
 126                divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);
 127                c = serial8250_early_in(port, UART_LCR);
 128                serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB);
 129                serial8250_early_out(port, UART_DLL, divisor & 0xff);
 130                serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff);
 131                serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB);
 132        }
 133}
 134
 135int __init early_serial8250_setup(struct earlycon_device *device,
 136                                         const char *options)
 137{
 138        if (!(device->port.membase || device->port.iobase))
 139                return -ENODEV;
 140
 141        if (!device->baud) {
 142                struct uart_port *port = &device->port;
 143                unsigned int ier;
 144
 145                /* assume the device was initialized, only mask interrupts */
 146                ier = serial8250_early_in(port, UART_IER);
 147                serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);
 148        } else
 149                init_port(device);
 150
 151        device->con->write = early_serial8250_write;
 152        return 0;
 153}
 154EARLYCON_DECLARE(uart8250, early_serial8250_setup);
 155EARLYCON_DECLARE(uart, early_serial8250_setup);
 156OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup);
 157OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup);
 158OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup);
 159OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup);
 160
 161#ifdef CONFIG_SERIAL_8250_OMAP
 162
 163static int __init early_omap8250_setup(struct earlycon_device *device,
 164                                       const char *options)
 165{
 166        struct uart_port *port = &device->port;
 167
 168        if (!(device->port.membase || device->port.iobase))
 169                return -ENODEV;
 170
 171        port->regshift = 2;
 172        device->con->write = early_serial8250_write;
 173        return 0;
 174}
 175
 176OF_EARLYCON_DECLARE(omap8250, "ti,omap2-uart", early_omap8250_setup);
 177OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup);
 178OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);
 179
 180#endif
 181
 182#ifdef CONFIG_SERIAL_8250_RT288X
 183
 184unsigned int au_serial_in(struct uart_port *p, int offset);
 185void au_serial_out(struct uart_port *p, int offset, int value);
 186
 187static int __init early_au_setup(struct earlycon_device *dev, const char *opt)
 188{
 189        dev->port.serial_in = au_serial_in;
 190        dev->port.serial_out = au_serial_out;
 191        dev->port.iotype = UPIO_AU;
 192        dev->con->write = early_serial8250_write;
 193        return 0;
 194}
 195OF_EARLYCON_DECLARE(palmchip, "ralink,rt2880-uart", early_au_setup);
 196
 197#endif
 198