2005-04-17 02:20:36 +04:00
/*
* Early serial console for 8250 / 16550 devices
*
* ( c ) Copyright 2004 Hewlett - Packard Development Company , L . P .
* Bjorn Helgaas < bjorn . helgaas @ hp . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* Based on the 8250. c serial driver , Copyright ( C ) 2001 Russell King ,
* and on early_printk . c by Andi Kleen .
*
* This is for use before the serial driver has initialized , in
* particular , before the UARTs have been discovered and named .
* Instead of specifying the console device as , e . g . , " ttyS0 " ,
* we locate the device directly by its MMIO or I / O port address .
*
* The user can specify the device directly , e . g . ,
* console = uart , io , 0x3f8 , 9600 n8
* console = uart , mmio , 0xff5e0000 , 115200 n8
* or platform code can call early_uart_console_init ( ) to set
* the early UART device .
*
* After the normal serial driver starts , we try to locate the
* matching ttyS device and start a console there .
*/
# include <linux/tty.h>
# include <linux/init.h>
# include <linux/console.h>
# include <linux/serial_core.h>
# include <linux/serial_reg.h>
# include <linux/serial.h>
# include <asm/io.h>
# include <asm/serial.h>
struct early_uart_device {
struct uart_port port ;
char options [ 16 ] ; /* e.g., 115200n8 */
unsigned int baud ;
} ;
static struct early_uart_device early_device __initdata ;
static int early_uart_registered __initdata ;
static unsigned int __init serial_in ( struct uart_port * port , int offset )
{
if ( port - > iotype = = UPIO_MEM )
return readb ( port - > membase + offset ) ;
else
return inb ( port - > iobase + offset ) ;
}
static void __init serial_out ( struct uart_port * port , int offset , int value )
{
if ( port - > iotype = = UPIO_MEM )
writeb ( value , port - > membase + offset ) ;
else
outb ( value , port - > iobase + offset ) ;
}
# define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
static void __init wait_for_xmitr ( struct uart_port * port )
{
unsigned int status ;
for ( ; ; ) {
status = serial_in ( port , UART_LSR ) ;
if ( ( status & BOTH_EMPTY ) = = BOTH_EMPTY )
return ;
cpu_relax ( ) ;
}
}
static void __init putc ( struct uart_port * port , unsigned char c )
{
wait_for_xmitr ( port ) ;
serial_out ( port , UART_TX , c ) ;
}
static void __init early_uart_write ( struct console * console , const char * s , unsigned int count )
{
struct uart_port * port = & early_device . port ;
unsigned int ier ;
/* Save the IER and disable interrupts */
ier = serial_in ( port , UART_IER ) ;
serial_out ( port , UART_IER , 0 ) ;
while ( * s & & count - - > 0 ) {
putc ( port , * s ) ;
if ( * s = = ' \n ' )
putc ( port , ' \r ' ) ;
s + + ;
}
/* Wait for transmitter to become empty and restore the IER */
wait_for_xmitr ( port ) ;
serial_out ( port , UART_IER , ier ) ;
}
static unsigned int __init probe_baud ( struct uart_port * port )
{
unsigned char lcr , dll , dlm ;
unsigned int quot ;
lcr = serial_in ( port , UART_LCR ) ;
serial_out ( port , UART_LCR , lcr | UART_LCR_DLAB ) ;
dll = serial_in ( port , UART_DLL ) ;
dlm = serial_in ( port , UART_DLM ) ;
serial_out ( port , UART_LCR , lcr ) ;
quot = ( dlm < < 8 ) | dll ;
return ( port - > uartclk / 16 ) / quot ;
}
static void __init init_port ( struct early_uart_device * device )
{
struct uart_port * port = & device - > port ;
unsigned int divisor ;
unsigned char c ;
serial_out ( port , UART_LCR , 0x3 ) ; /* 8n1 */
serial_out ( port , UART_IER , 0 ) ; /* no interrupt */
serial_out ( port , UART_FCR , 0 ) ; /* no fifo */
serial_out ( port , UART_MCR , 0x3 ) ; /* DTR + RTS */
divisor = port - > uartclk / ( 16 * device - > baud ) ;
c = serial_in ( port , UART_LCR ) ;
serial_out ( port , UART_LCR , c | UART_LCR_DLAB ) ;
serial_out ( port , UART_DLL , divisor & 0xff ) ;
serial_out ( port , UART_DLM , ( divisor > > 8 ) & 0xff ) ;
serial_out ( port , UART_LCR , c & ~ UART_LCR_DLAB ) ;
}
static int __init parse_options ( struct early_uart_device * device , char * options )
{
struct uart_port * port = & device - > port ;
int mapsize = 64 ;
int mmio , length ;
if ( ! options )
return - ENODEV ;
port - > uartclk = BASE_BAUD * 16 ;
if ( ! strncmp ( options , " mmio, " , 5 ) ) {
port - > iotype = UPIO_MEM ;
port - > mapbase = simple_strtoul ( options + 5 , & options , 0 ) ;
port - > membase = ioremap ( port - > mapbase , mapsize ) ;
if ( ! port - > membase ) {
printk ( KERN_ERR " %s: Couldn't ioremap 0x%lx \n " ,
__FUNCTION__ , port - > mapbase ) ;
return - ENOMEM ;
}
mmio = 1 ;
} else if ( ! strncmp ( options , " io, " , 3 ) ) {
port - > iotype = UPIO_PORT ;
port - > iobase = simple_strtoul ( options + 3 , & options , 0 ) ;
mmio = 0 ;
} else
return - EINVAL ;
if ( ( options = strchr ( options , ' , ' ) ) ) {
options + + ;
2005-11-04 00:07:37 +03:00
device - > baud = simple_strtoul ( options , NULL , 0 ) ;
2005-04-17 02:20:36 +04:00
length = min ( strcspn ( options , " " ) , sizeof ( device - > options ) ) ;
strncpy ( device - > options , options , length ) ;
} else {
device - > baud = probe_baud ( port ) ;
snprintf ( device - > options , sizeof ( device - > options ) , " %u " ,
device - > baud ) ;
}
printk ( KERN_INFO " Early serial console at %s 0x%lx (options '%s') \n " ,
mmio ? " MMIO " : " I/O port " ,
mmio ? port - > mapbase : ( unsigned long ) port - > iobase ,
device - > options ) ;
return 0 ;
}
static int __init early_uart_setup ( struct console * console , char * options )
{
struct early_uart_device * device = & early_device ;
int err ;
if ( device - > port . membase | | device - > port . iobase )
return 0 ;
if ( ( err = parse_options ( device , options ) ) < 0 )
return err ;
init_port ( device ) ;
return 0 ;
}
static struct console early_uart_console __initdata = {
. name = " uart " ,
. write = early_uart_write ,
. setup = early_uart_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
} ;
static int __init early_uart_console_init ( void )
{
if ( ! early_uart_registered ) {
register_console ( & early_uart_console ) ;
early_uart_registered = 1 ;
}
return 0 ;
}
console_initcall ( early_uart_console_init ) ;
int __init early_serial_console_init ( char * cmdline )
{
char * options ;
int err ;
options = strstr ( cmdline , " console=uart, " ) ;
if ( ! options )
return - ENODEV ;
options = strchr ( cmdline , ' , ' ) + 1 ;
if ( ( err = early_uart_setup ( NULL , options ) ) < 0 )
return err ;
return early_uart_console_init ( ) ;
}
static int __init early_uart_console_switch ( void )
{
struct early_uart_device * device = & early_device ;
struct uart_port * port = & device - > port ;
int mmio , line ;
if ( ! ( early_uart_console . flags & CON_ENABLED ) )
return 0 ;
/* Try to start the normal driver on a matching line. */
mmio = ( port - > iotype = = UPIO_MEM ) ;
line = serial8250_start_console ( port , device - > options ) ;
if ( line < 0 )
printk ( " No ttyS device at %s 0x%lx for console \n " ,
mmio ? " MMIO " : " I/O port " ,
mmio ? port - > mapbase :
( unsigned long ) port - > iobase ) ;
unregister_console ( & early_uart_console ) ;
if ( mmio )
iounmap ( port - > membase ) ;
return 0 ;
}
late_initcall ( early_uart_console_switch ) ;