19bef3d41SPaul Gortmaker /* 29bef3d41SPaul Gortmaker * Early serial console for 8250/16550 devices 39bef3d41SPaul Gortmaker * 49bef3d41SPaul Gortmaker * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. 59bef3d41SPaul Gortmaker * Bjorn Helgaas <bjorn.helgaas@hp.com> 69bef3d41SPaul Gortmaker * 79bef3d41SPaul Gortmaker * This program is free software; you can redistribute it and/or modify 89bef3d41SPaul Gortmaker * it under the terms of the GNU General Public License version 2 as 99bef3d41SPaul Gortmaker * published by the Free Software Foundation. 109bef3d41SPaul Gortmaker * 119bef3d41SPaul Gortmaker * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King, 129bef3d41SPaul Gortmaker * and on early_printk.c by Andi Kleen. 139bef3d41SPaul Gortmaker * 149bef3d41SPaul Gortmaker * This is for use before the serial driver has initialized, in 159bef3d41SPaul Gortmaker * particular, before the UARTs have been discovered and named. 169bef3d41SPaul Gortmaker * Instead of specifying the console device as, e.g., "ttyS0", 179bef3d41SPaul Gortmaker * we locate the device directly by its MMIO or I/O port address. 189bef3d41SPaul Gortmaker * 199bef3d41SPaul Gortmaker * The user can specify the device directly, e.g., 209bef3d41SPaul Gortmaker * earlycon=uart8250,io,0x3f8,9600n8 219bef3d41SPaul Gortmaker * earlycon=uart8250,mmio,0xff5e0000,115200n8 229bef3d41SPaul Gortmaker * earlycon=uart8250,mmio32,0xff5e0000,115200n8 239bef3d41SPaul Gortmaker * or 249bef3d41SPaul Gortmaker * console=uart8250,io,0x3f8,9600n8 259bef3d41SPaul Gortmaker * console=uart8250,mmio,0xff5e0000,115200n8 269bef3d41SPaul Gortmaker * console=uart8250,mmio32,0xff5e0000,115200n8 279bef3d41SPaul Gortmaker */ 289bef3d41SPaul Gortmaker 299bef3d41SPaul Gortmaker #include <linux/tty.h> 309bef3d41SPaul Gortmaker #include <linux/init.h> 319bef3d41SPaul Gortmaker #include <linux/console.h> 32d05f1570SScott Wood #include <linux/of.h> 33d05f1570SScott Wood #include <linux/of_device.h> 349bef3d41SPaul Gortmaker #include <linux/serial_reg.h> 359bef3d41SPaul Gortmaker #include <linux/serial.h> 369bef3d41SPaul Gortmaker #include <linux/serial_8250.h> 379bef3d41SPaul Gortmaker #include <asm/io.h> 389bef3d41SPaul Gortmaker #include <asm/serial.h> 399bef3d41SPaul Gortmaker 40f3fb7ef3SVineet Gupta static unsigned int __init serial8250_early_in(struct uart_port *port, int offset) 419bef3d41SPaul Gortmaker { 429bef3d41SPaul Gortmaker switch (port->iotype) { 439bef3d41SPaul Gortmaker case UPIO_MEM: 449bef3d41SPaul Gortmaker return readb(port->membase + offset); 45bd94c407SMasahiro Yamada case UPIO_MEM16: 46bd94c407SMasahiro Yamada return readw(port->membase + (offset << 1)); 479bef3d41SPaul Gortmaker case UPIO_MEM32: 489bef3d41SPaul Gortmaker return readl(port->membase + (offset << 2)); 49c627f2ceSKevin Cernekee case UPIO_MEM32BE: 50c627f2ceSKevin Cernekee return ioread32be(port->membase + (offset << 2)); 519bef3d41SPaul Gortmaker case UPIO_PORT: 529bef3d41SPaul Gortmaker return inb(port->iobase + offset); 539bef3d41SPaul Gortmaker default: 549bef3d41SPaul Gortmaker return 0; 559bef3d41SPaul Gortmaker } 569bef3d41SPaul Gortmaker } 579bef3d41SPaul Gortmaker 58f3fb7ef3SVineet Gupta static void __init serial8250_early_out(struct uart_port *port, int offset, int value) 599bef3d41SPaul Gortmaker { 609bef3d41SPaul Gortmaker switch (port->iotype) { 619bef3d41SPaul Gortmaker case UPIO_MEM: 629bef3d41SPaul Gortmaker writeb(value, port->membase + offset); 639bef3d41SPaul Gortmaker break; 64bd94c407SMasahiro Yamada case UPIO_MEM16: 65bd94c407SMasahiro Yamada writew(value, port->membase + (offset << 1)); 66bd94c407SMasahiro Yamada break; 679bef3d41SPaul Gortmaker case UPIO_MEM32: 689bef3d41SPaul Gortmaker writel(value, port->membase + (offset << 2)); 699bef3d41SPaul Gortmaker break; 70c627f2ceSKevin Cernekee case UPIO_MEM32BE: 71c627f2ceSKevin Cernekee iowrite32be(value, port->membase + (offset << 2)); 72c627f2ceSKevin Cernekee break; 739bef3d41SPaul Gortmaker case UPIO_PORT: 749bef3d41SPaul Gortmaker outb(value, port->iobase + offset); 759bef3d41SPaul Gortmaker break; 769bef3d41SPaul Gortmaker } 779bef3d41SPaul Gortmaker } 789bef3d41SPaul Gortmaker 799bef3d41SPaul Gortmaker #define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) 809bef3d41SPaul Gortmaker 81004e2ed5SMasahiro Yamada static void __init serial_putc(struct uart_port *port, int c) 829bef3d41SPaul Gortmaker { 839bef3d41SPaul Gortmaker unsigned int status; 849bef3d41SPaul Gortmaker 85004e2ed5SMasahiro Yamada serial8250_early_out(port, UART_TX, c); 86004e2ed5SMasahiro Yamada 879bef3d41SPaul Gortmaker for (;;) { 88ed71871bSNoam Camus status = serial8250_early_in(port, UART_LSR); 899bef3d41SPaul Gortmaker if ((status & BOTH_EMPTY) == BOTH_EMPTY) 90004e2ed5SMasahiro Yamada break; 919bef3d41SPaul Gortmaker cpu_relax(); 929bef3d41SPaul Gortmaker } 939bef3d41SPaul Gortmaker } 949bef3d41SPaul Gortmaker 959bef3d41SPaul Gortmaker static void __init early_serial8250_write(struct console *console, 969bef3d41SPaul Gortmaker const char *s, unsigned int count) 979bef3d41SPaul Gortmaker { 98d0d654ceSPeter Hurley struct earlycon_device *device = console->data; 99d0d654ceSPeter Hurley struct uart_port *port = &device->port; 1009bef3d41SPaul Gortmaker 1019bef3d41SPaul Gortmaker uart_console_write(port, s, count, serial_putc); 1029bef3d41SPaul Gortmaker } 1039bef3d41SPaul Gortmaker 104d2fd6810SRob Herring static void __init init_port(struct earlycon_device *device) 1059bef3d41SPaul Gortmaker { 1069bef3d41SPaul Gortmaker struct uart_port *port = &device->port; 1079bef3d41SPaul Gortmaker unsigned int divisor; 1089bef3d41SPaul Gortmaker unsigned char c; 109a4c639b0SRob Herring unsigned int ier; 1109bef3d41SPaul Gortmaker 111ed71871bSNoam Camus serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */ 112a4c639b0SRob Herring ier = serial8250_early_in(port, UART_IER); 113a4c639b0SRob Herring serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); /* no interrupt */ 114ed71871bSNoam Camus serial8250_early_out(port, UART_FCR, 0); /* no fifo */ 115ed71871bSNoam Camus serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ 1169bef3d41SPaul Gortmaker 117b15d5380SAlexey Brodkin divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); 118ed71871bSNoam Camus c = serial8250_early_in(port, UART_LCR); 119ed71871bSNoam Camus serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB); 120ed71871bSNoam Camus serial8250_early_out(port, UART_DLL, divisor & 0xff); 121ed71871bSNoam Camus serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff); 122ed71871bSNoam Camus serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); 1239bef3d41SPaul Gortmaker } 1249bef3d41SPaul Gortmaker 1251c5841e8SEddie Huang int __init early_serial8250_setup(struct earlycon_device *device, 126d2fd6810SRob Herring const char *options) 1279bef3d41SPaul Gortmaker { 128d2fd6810SRob Herring if (!(device->port.membase || device->port.iobase)) 129cd385e9aSPeter Hurley return -ENODEV; 1309bef3d41SPaul Gortmaker 13160efcf04SRob Herring if (!device->baud) { 1320e3e143eSPeter Hurley struct uart_port *port = &device->port; 1330e3e143eSPeter Hurley unsigned int ier; 1340e3e143eSPeter Hurley 1350e3e143eSPeter Hurley /* assume the device was initialized, only mask interrupts */ 1360e3e143eSPeter Hurley ier = serial8250_early_in(port, UART_IER); 1370e3e143eSPeter Hurley serial8250_early_out(port, UART_IER, ier & UART_IER_UUE); 1380e3e143eSPeter Hurley } else 1399bef3d41SPaul Gortmaker init_port(device); 140d2fd6810SRob Herring 141d2fd6810SRob Herring device->con->write = early_serial8250_write; 1429bef3d41SPaul Gortmaker return 0; 1439bef3d41SPaul Gortmaker } 144d2fd6810SRob Herring EARLYCON_DECLARE(uart8250, early_serial8250_setup); 145d2fd6810SRob Herring EARLYCON_DECLARE(uart, early_serial8250_setup); 146d05f1570SScott Wood OF_EARLYCON_DECLARE(ns16550, "ns16550", early_serial8250_setup); 147d05f1570SScott Wood OF_EARLYCON_DECLARE(ns16550a, "ns16550a", early_serial8250_setup); 148