11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Early serial console for 8250/16550 devices 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * (c) Copyright 2004 Hewlett-Packard Development Company, L.P. 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Bjorn Helgaas <bjorn.helgaas@hp.com> 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This program is free software; you can redistribute it and/or modify 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it under the terms of the GNU General Public License version 2 as 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * published by the Free Software Foundation. 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Based on the 8250.c serial driver, Copyright (C) 2001 Russell King, 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and on early_printk.c by Andi Kleen. 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is for use before the serial driver has initialized, in 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * particular, before the UARTs have been discovered and named. 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Instead of specifying the console device as, e.g., "ttyS0", 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * we locate the device directly by its MMIO or I/O port address. 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The user can specify the device directly, e.g., 2018a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu * earlycon=uart8250,io,0x3f8,9600n8 2118a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu * earlycon=uart8250,mmio,0xff5e0000,115200n8 221917ac76e023339c73844bec775375b147f57ac7Samium Gromoff * earlycon=uart8250,mmio32,0xff5e0000,115200n8 2318a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu * or 2418a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu * console=uart8250,io,0x3f8,9600n8 2518a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu * console=uart8250,mmio,0xff5e0000,115200n8 261917ac76e023339c73844bec775375b147f57ac7Samium Gromoff * console=uart8250,mmio32,0xff5e0000,115200n8 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h> 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/console.h> 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serial_core.h> 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serial_reg.h> 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serial.h> 3518a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu#include <linux/serial_8250.h> 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/serial.h> 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 39d2fd6810a823bcde1ee232668f5689837aafa772Rob Herringstatic struct earlycon_device *early_device; 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 41ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camusunsigned int __weak __init serial8250_early_in(struct uart_port *port, int offset) 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 431917ac76e023339c73844bec775375b147f57ac7Samium Gromoff switch (port->iotype) { 441917ac76e023339c73844bec775375b147f57ac7Samium Gromoff case UPIO_MEM: 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return readb(port->membase + offset); 461917ac76e023339c73844bec775375b147f57ac7Samium Gromoff case UPIO_MEM32: 471917ac76e023339c73844bec775375b147f57ac7Samium Gromoff return readl(port->membase + (offset << 2)); 481917ac76e023339c73844bec775375b147f57ac7Samium Gromoff case UPIO_PORT: 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return inb(port->iobase + offset); 501917ac76e023339c73844bec775375b147f57ac7Samium Gromoff default: 511917ac76e023339c73844bec775375b147f57ac7Samium Gromoff return 0; 521917ac76e023339c73844bec775375b147f57ac7Samium Gromoff } 531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 55ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camusvoid __weak __init serial8250_early_out(struct uart_port *port, int offset, int value) 561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 571917ac76e023339c73844bec775375b147f57ac7Samium Gromoff switch (port->iotype) { 581917ac76e023339c73844bec775375b147f57ac7Samium Gromoff case UPIO_MEM: 591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds writeb(value, port->membase + offset); 601917ac76e023339c73844bec775375b147f57ac7Samium Gromoff break; 611917ac76e023339c73844bec775375b147f57ac7Samium Gromoff case UPIO_MEM32: 621917ac76e023339c73844bec775375b147f57ac7Samium Gromoff writel(value, port->membase + (offset << 2)); 631917ac76e023339c73844bec775375b147f57ac7Samium Gromoff break; 641917ac76e023339c73844bec775375b147f57ac7Samium Gromoff case UPIO_PORT: 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds outb(value, port->iobase + offset); 661917ac76e023339c73844bec775375b147f57ac7Samium Gromoff break; 671917ac76e023339c73844bec775375b147f57ac7Samium Gromoff } 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __init wait_for_xmitr(struct uart_port *port) 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int status; 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for (;;) { 77ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus status = serial8250_early_in(port, UART_LSR); 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((status & BOTH_EMPTY) == BOTH_EMPTY) 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return; 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cpu_relax(); 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 84bf2cdef30667d0d3d09c6934c95d1fb87c43345aYinghai Lustatic void __init serial_putc(struct uart_port *port, int c) 851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wait_for_xmitr(port); 87ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_TX, c); 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 90ce2e204f0c55567baa0323df8d327989d7113c66Alan Coxstatic void __init early_serial8250_write(struct console *console, 91ce2e204f0c55567baa0323df8d327989d7113c66Alan Cox const char *s, unsigned int count) 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 93d2fd6810a823bcde1ee232668f5689837aafa772Rob Herring struct uart_port *port = &early_device->port; 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int ier; 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Save the IER and disable interrupts */ 97ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus ier = serial8250_early_in(port, UART_IER); 98ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_IER, 0); 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 100bf2cdef30667d0d3d09c6934c95d1fb87c43345aYinghai Lu uart_console_write(port, s, count, serial_putc); 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Wait for transmitter to become empty and restore the IER */ 1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds wait_for_xmitr(port); 104ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_IER, ier); 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int __init probe_baud(struct uart_port *port) 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char lcr, dll, dlm; 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int quot; 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 112ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus lcr = serial8250_early_in(port, UART_LCR); 113ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_LCR, lcr | UART_LCR_DLAB); 114ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus dll = serial8250_early_in(port, UART_DLL); 115ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus dlm = serial8250_early_in(port, UART_DLM); 116ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_LCR, lcr); 1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds quot = (dlm << 8) | dll; 1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return (port->uartclk / 16) / quot; 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 122d2fd6810a823bcde1ee232668f5689837aafa772Rob Herringstatic void __init init_port(struct earlycon_device *device) 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct uart_port *port = &device->port; 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned int divisor; 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned char c; 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 128ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_LCR, 0x3); /* 8n1 */ 129ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_IER, 0); /* no interrupt */ 130ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_FCR, 0); /* no fifo */ 131ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_MCR, 0x3); /* DTR + RTS */ 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 133b15d5380e471f9ce27180b14d5080abc2e2f30ecAlexey Brodkin divisor = DIV_ROUND_CLOSEST(port->uartclk, 16 * device->baud); 134ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus c = serial8250_early_in(port, UART_LCR); 135ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_LCR, c | UART_LCR_DLAB); 136ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_DLL, divisor & 0xff); 137ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_DLM, (divisor >> 8) & 0xff); 138ed71871bed7198ca4aa6a79b7a93b73ad6408e98Noam Camus serial8250_early_out(port, UART_LCR, c & ~UART_LCR_DLAB); 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 141d2fd6810a823bcde1ee232668f5689837aafa772Rob Herringstatic int __init early_serial8250_setup(struct earlycon_device *device, 142d2fd6810a823bcde1ee232668f5689837aafa772Rob Herring const char *options) 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 144d2fd6810a823bcde1ee232668f5689837aafa772Rob Herring if (!(device->port.membase || device->port.iobase)) 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 14760efcf0414be5876d81276e3c1fd12680ba2ce71Rob Herring if (!device->baud) { 148d2fd6810a823bcde1ee232668f5689837aafa772Rob Herring device->baud = probe_baud(&device->port); 14960efcf0414be5876d81276e3c1fd12680ba2ce71Rob Herring snprintf(device->options, sizeof(device->options), "%u", 15060efcf0414be5876d81276e3c1fd12680ba2ce71Rob Herring device->baud); 15160efcf0414be5876d81276e3c1fd12680ba2ce71Rob Herring } 1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds init_port(device); 15418a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu 155d2fd6810a823bcde1ee232668f5689837aafa772Rob Herring early_device = device; 156d2fd6810a823bcde1ee232668f5689837aafa772Rob Herring device->con->write = early_serial8250_write; 15718a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu return 0; 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 159d2fd6810a823bcde1ee232668f5689837aafa772Rob HerringEARLYCON_DECLARE(uart8250, early_serial8250_setup); 160d2fd6810a823bcde1ee232668f5689837aafa772Rob HerringEARLYCON_DECLARE(uart, early_serial8250_setup); 1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 162fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herringint __init setup_early_serial8250_console(char *cmdline) 163fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herring{ 164fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herring char match[] = "uart8250"; 165fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herring 166fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herring if (cmdline && cmdline[4] == ',') 167fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herring match[4] = '\0'; 168fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herring 169fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herring return setup_earlycon(cmdline, match, early_serial8250_setup); 170fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herring} 171fe1cf8af918af3ff0dd58ce92e5a5da117cb1d92Rob Herring 172b6b1d87785712474d0ed80689c17107d616a1171Daniel Ritzint serial8250_find_port_for_earlycon(void) 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 174d2fd6810a823bcde1ee232668f5689837aafa772Rob Herring struct earlycon_device *device = early_device; 175d2fd6810a823bcde1ee232668f5689837aafa772Rob Herring struct uart_port *port = device ? &device->port : NULL; 17618a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu int line; 17718a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu int ret; 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 179d2fd6810a823bcde1ee232668f5689837aafa772Rob Herring if (!port || (!port->membase && !port->iobase)) 18018a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu return -ENODEV; 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 18218a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu line = serial8250_find_port(port); 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (line < 0) 18418a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu return -ENODEV; 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 18618a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu ret = update_console_cmdline("uart", 8250, 18718a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu "ttyS", line, device->options); 18818a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu if (ret < 0) 18918a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu ret = update_console_cmdline("uart", 0, 19018a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu "ttyS", line, device->options); 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 19218a8bd949d6adb311ea816125ff65050df1f3f6eYinghai Lu return ret; 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 194