2014-04-18 17:19:55 -05:00
/*
* Copyright ( C ) 2014 Linaro Ltd .
* Author : Rob Herring < robh @ kernel . org >
*
* Based on 8250 earlycon :
* ( 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 .
*/
# include <linux/console.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/serial_core.h>
2014-03-27 08:06:16 -05:00
# include <linux/sizes.h>
# include <linux/mod_devicetable.h>
2014-04-18 17:19:55 -05:00
# ifdef CONFIG_FIX_EARLYCON_MEM
# include <asm/fixmap.h>
# endif
# include <asm/serial.h>
static struct console early_con = {
2014-06-12 12:52:44 -05:00
. name = " uart " , /* 8250 console switch requires this name */
2014-04-18 17:19:55 -05:00
. flags = CON_PRINTBUFFER | CON_BOOT ,
. index = - 1 ,
} ;
static struct earlycon_device early_console_dev = {
. con = & early_con ,
} ;
2014-03-27 08:06:16 -05:00
static const struct of_device_id __earlycon_of_table_sentinel
__used __section ( __earlycon_of_table_end ) ;
2014-04-18 17:19:55 -05:00
static void __iomem * __init earlycon_map ( unsigned long paddr , size_t size )
{
void __iomem * base ;
# ifdef CONFIG_FIX_EARLYCON_MEM
set_fixmap_io ( FIX_EARLYCON_MEM_BASE , paddr & PAGE_MASK ) ;
base = ( void __iomem * ) __fix_to_virt ( FIX_EARLYCON_MEM_BASE ) ;
base + = paddr & ~ PAGE_MASK ;
# else
base = ioremap ( paddr , size ) ;
# endif
if ( ! base )
pr_err ( " %s: Couldn't map 0x%llx \n " , __func__ ,
( unsigned long long ) paddr ) ;
return base ;
}
static int __init parse_options ( struct earlycon_device * device ,
char * options )
{
struct uart_port * port = & device - > port ;
2014-04-30 19:48:29 -05:00
int mmio , mmio32 , length ;
2014-04-18 17:19:55 -05:00
unsigned long addr ;
if ( ! options )
return - ENODEV ;
mmio = ! strncmp ( options , " mmio, " , 5 ) ;
mmio32 = ! strncmp ( options , " mmio32, " , 7 ) ;
if ( mmio | | mmio32 ) {
port - > iotype = ( mmio ? UPIO_MEM : UPIO_MEM32 ) ;
options + = mmio ? 5 : 7 ;
2014-04-30 19:48:29 -05:00
addr = simple_strtoul ( options , NULL , 0 ) ;
2014-04-18 17:19:55 -05:00
port - > mapbase = addr ;
if ( mmio32 )
port - > regshift = 2 ;
} else if ( ! strncmp ( options , " io, " , 3 ) ) {
port - > iotype = UPIO_PORT ;
options + = 3 ;
2014-04-30 19:48:29 -05:00
addr = simple_strtoul ( options , NULL , 0 ) ;
2014-04-18 17:19:55 -05:00
port - > iobase = addr ;
mmio = 0 ;
} else if ( ! strncmp ( options , " 0x " , 2 ) ) {
port - > iotype = UPIO_MEM ;
2014-04-30 19:48:29 -05:00
addr = simple_strtoul ( options , NULL , 0 ) ;
2014-04-18 17:19:55 -05:00
port - > mapbase = addr ;
} else {
return - EINVAL ;
}
port - > uartclk = BASE_BAUD * 16 ;
options = strchr ( options , ' , ' ) ;
if ( options ) {
options + + ;
2014-04-30 19:48:29 -05:00
device - > baud = simple_strtoul ( options , NULL , 0 ) ;
2014-04-18 17:19:55 -05:00
length = min ( strcspn ( options , " " ) + 1 ,
( size_t ) ( sizeof ( device - > options ) ) ) ;
strlcpy ( device - > options , options , length ) ;
}
2014-10-17 04:01:11 -05:00
if ( port - > iotype = = UPIO_MEM | | port - > iotype = = UPIO_MEM32 )
2014-04-18 17:19:55 -05:00
pr_info ( " Early serial console at MMIO%s 0x%llx (options '%s') \n " ,
mmio32 ? " 32 " : " " ,
( unsigned long long ) port - > mapbase ,
device - > options ) ;
else
pr_info ( " Early serial console at I/O port 0x%lx (options '%s') \n " ,
port - > iobase ,
device - > options ) ;
return 0 ;
}
int __init setup_earlycon ( char * buf , const char * match ,
int ( * setup ) ( struct earlycon_device * , const char * ) )
{
int err ;
size_t len ;
struct uart_port * port = & early_console_dev . port ;
if ( ! buf | | ! match | | ! setup )
return 0 ;
len = strlen ( match ) ;
if ( strncmp ( buf , match , len ) )
return 0 ;
if ( buf [ len ] & & ( buf [ len ] ! = ' , ' ) )
return 0 ;
buf + = len + 1 ;
err = parse_options ( & early_console_dev , buf ) ;
/* On parsing error, pass the options buf to the setup function */
if ( ! err )
buf = NULL ;
if ( port - > mapbase )
port - > membase = earlycon_map ( port - > mapbase , 64 ) ;
early_console_dev . con - > data = & early_console_dev ;
err = setup ( & early_console_dev , buf ) ;
if ( err < 0 )
return err ;
if ( ! early_console_dev . con - > write )
return - ENODEV ;
register_console ( early_console_dev . con ) ;
return 0 ;
}
2014-03-27 08:06:16 -05:00
int __init of_setup_earlycon ( unsigned long addr ,
int ( * setup ) ( struct earlycon_device * , const char * ) )
{
int err ;
struct uart_port * port = & early_console_dev . port ;
port - > iotype = UPIO_MEM ;
port - > mapbase = addr ;
port - > uartclk = BASE_BAUD * 16 ;
port - > membase = earlycon_map ( addr , SZ_4K ) ;
early_console_dev . con - > data = & early_console_dev ;
err = setup ( & early_console_dev , NULL ) ;
if ( err < 0 )
return err ;
if ( ! early_console_dev . con - > write )
return - ENODEV ;
register_console ( early_console_dev . con ) ;
return 0 ;
}