206 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Early serial console for 8250/16550 devices4 *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, in12 * 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,9600n818 * earlycon=uart8250,mmio,0xff5e0000,115200n819 * earlycon=uart8250,mmio32,0xff5e0000,115200n820 * or21 * console=uart8250,io,0x3f8,9600n822 * console=uart8250,mmio,0xff5e0000,115200n823 * console=uart8250,mmio32,0xff5e0000,115200n824 */25 26#include <linux/tty.h>27#include <linux/init.h>28#include <linux/console.h>29#include <linux/of.h>30#include <linux/serial_reg.h>31#include <linux/serial.h>32#include <linux/serial_8250.h>33#include <asm/io.h>34#include <asm/serial.h>35 36static unsigned int serial8250_early_in(struct uart_port *port, int offset)37{38 offset <<= port->regshift;39 40 switch (port->iotype) {41 case UPIO_MEM:42 return readb(port->membase + offset);43 case UPIO_MEM16:44 return readw(port->membase + offset);45 case UPIO_MEM32:46 return readl(port->membase + offset);47 case UPIO_MEM32BE:48 return ioread32be(port->membase + offset);49 case UPIO_PORT:50 return inb(port->iobase + offset);51 default:52 return 0;53 }54}55 56static void serial8250_early_out(struct uart_port *port, int offset, int value)57{58 offset <<= port->regshift;59 60 switch (port->iotype) {61 case UPIO_MEM:62 writeb(value, port->membase + offset);63 break;64 case UPIO_MEM16:65 writew(value, port->membase + offset);66 break;67 case UPIO_MEM32:68 writel(value, port->membase + offset);69 break;70 case UPIO_MEM32BE:71 iowrite32be(value, port->membase + offset);72 break;73 case UPIO_PORT:74 outb(value, port->iobase + offset);75 break;76 }77}78 79static void serial_putc(struct uart_port *port, unsigned char c)80{81 unsigned int status;82 83 serial8250_early_out(port, UART_TX, c);84 85 for (;;) {86 status = serial8250_early_in(port, UART_LSR);87 if (uart_lsr_tx_empty(status))88 break;89 cpu_relax();90 }91}92 93static void early_serial8250_write(struct console *console,94 const char *s, unsigned int count)95{96 struct earlycon_device *device = console->data;97 struct uart_port *port = &device->port;98 99 uart_console_write(port, s, count, serial_putc);100}101 102#ifdef CONFIG_CONSOLE_POLL103static int early_serial8250_read(struct console *console,104 char *s, unsigned int count)105{106 struct earlycon_device *device = console->data;107 struct uart_port *port = &device->port;108 unsigned int status;109 int num_read = 0;110 111 while (num_read < count) {112 status = serial8250_early_in(port, UART_LSR);113 if (!(status & UART_LSR_DR))114 break;115 s[num_read++] = serial8250_early_in(port, UART_RX);116 }117 118 return num_read;119}120#else121#define early_serial8250_read NULL122#endif123 124static void __init init_port(struct earlycon_device *device)125{126 struct uart_port *port = &device->port;127 unsigned int divisor;128 unsigned char c;129 unsigned int ier;130 131 serial8250_early_out(port, UART_LCR, UART_LCR_WLEN8); /* 8n1 */132 ier = serial8250_early_in(port, UART_IER);133 serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */134 serial8250_early_out(port, UART_FCR, 0); /* no fifo */135 serial8250_early_out(port, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);136 137 if (port->uartclk) {138 divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud);139 c = serial8250_early_in(port, UART_LCR);140 serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB);141 serial8250_early_out(port, UART_DLL, divisor & 0xff);142 serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff);143 serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB);144 }145}146 147int __init early_serial8250_setup(struct earlycon_device *device,148 const char *options)149{150 if (!(device->port.membase || device->port.iobase))151 return -ENODEV;152 153 if (!device->baud) {154 struct uart_port *port = &device->port;155 unsigned int ier;156 157 /* assume the device was initialized, only mask interrupts */158 ier = serial8250_early_in(port, UART_IER);159 serial8250_early_out(port, UART_IER, ier & UART_IER_UUE);160 } else161 init_port(device);162 163 device->con->write = early_serial8250_write;164 device->con->read = early_serial8250_read;165 return 0;166}167EARLYCON_DECLARE(uart8250, early_serial8250_setup);168EARLYCON_DECLARE(uart, early_serial8250_setup);169OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup);170OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup);171OF_EARLYCON_DECLARE(uart, "nvidia,tegra20-uart", early_serial8250_setup);172OF_EARLYCON_DECLARE(uart, "snps,dw-apb-uart", early_serial8250_setup);173 174static int __init early_serial8250_rs2_setup(struct earlycon_device *device,175 const char *options)176{177 device->port.regshift = 2;178 179 return early_serial8250_setup(device, options);180}181OF_EARLYCON_DECLARE(uart, "intel,xscale-uart", early_serial8250_rs2_setup);182OF_EARLYCON_DECLARE(uart, "mrvl,mmp-uart", early_serial8250_rs2_setup);183OF_EARLYCON_DECLARE(uart, "mrvl,pxa-uart", early_serial8250_rs2_setup);184 185#ifdef CONFIG_SERIAL_8250_OMAP186 187static int __init early_omap8250_setup(struct earlycon_device *device,188 const char *options)189{190 struct uart_port *port = &device->port;191 192 if (!(device->port.membase || device->port.iobase))193 return -ENODEV;194 195 port->regshift = 2;196 device->con->write = early_serial8250_write;197 return 0;198}199 200OF_EARLYCON_DECLARE(omap8250, "ti,omap2-uart", early_omap8250_setup);201OF_EARLYCON_DECLARE(omap8250, "ti,omap3-uart", early_omap8250_setup);202OF_EARLYCON_DECLARE(omap8250, "ti,omap4-uart", early_omap8250_setup);203OF_EARLYCON_DECLARE(omap8250, "ti,am654-uart", early_omap8250_setup);204 205#endif206