2007-07-11 12:18:45 -07:00
/* -*- linux-c -*- ------------------------------------------------------- *
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
* Copyright 2007 rPath , Inc . - All Rights Reserved
2009-04-01 18:13:46 -07:00
* Copyright 2009 Intel Corporation ; author H . Peter Anvin
2007-07-11 12:18:45 -07:00
*
* This file is part of the Linux kernel , and is made available under
* the terms of the GNU General Public License version 2.
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*
2010-07-11 11:06:57 +03:00
* Very simple screen and serial I / O
2007-07-11 12:18:45 -07:00
*/
# include "boot.h"
2010-07-11 11:06:57 +03:00
# define DEFAULT_SERIAL_PORT 0x3f8 /* ttyS0 */
static int early_serial_base ;
# define XMTRDY 0x20
# define DLAB 0x80
# define TXR 0 /* Transmit register (WRITE) */
# define RXR 0 /* Receive register (READ) */
# define IER 1 /* Interrupt Enable */
# define IIR 2 /* Interrupt ID */
# define FCR 2 /* FIFO control */
# define LCR 3 /* Line control */
# define MCR 4 /* Modem control */
# define LSR 5 /* Line Status */
# define MSR 6 /* Modem Status */
# define DLL 0 /* Divisor Latch Low */
# define DLH 1 /* Divisor latch High */
# define DEFAULT_BAUD 9600
2007-07-11 12:18:45 -07:00
/*
* These functions are in . inittext so they can be used to signal
* error during initialization .
*/
2010-07-11 11:06:57 +03:00
static void __attribute__ ( ( section ( " .inittext " ) ) ) serial_putchar ( int ch )
2007-07-11 12:18:45 -07:00
{
2010-07-11 11:06:57 +03:00
unsigned timeout = 0xffff ;
2007-07-11 12:18:45 -07:00
2010-07-11 11:06:57 +03:00
while ( ( inb ( early_serial_base + LSR ) & XMTRDY ) = = 0 & & - - timeout )
cpu_relax ( ) ;
outb ( ch , early_serial_base + TXR ) ;
}
static void __attribute__ ( ( section ( " .inittext " ) ) ) bios_putchar ( int ch )
{
struct biosregs ireg ;
2007-07-11 12:18:45 -07:00
2009-04-01 18:13:46 -07:00
initregs ( & ireg ) ;
ireg . bx = 0x0007 ;
ireg . cx = 0x0001 ;
ireg . ah = 0x0e ;
ireg . al = ch ;
intcall ( 0x10 , & ireg , NULL ) ;
2007-07-11 12:18:45 -07:00
}
2010-07-11 11:06:57 +03:00
void __attribute__ ( ( section ( " .inittext " ) ) ) putchar ( int ch )
{
if ( ch = = ' \n ' )
putchar ( ' \r ' ) ; /* \n -> \r\n */
bios_putchar ( ch ) ;
if ( early_serial_base ! = 0 )
serial_putchar ( ch ) ;
}
2007-07-11 12:18:45 -07:00
void __attribute__ ( ( section ( " .inittext " ) ) ) puts ( const char * str )
{
2009-04-01 18:13:46 -07:00
while ( * str )
2007-07-11 12:18:45 -07:00
putchar ( * str + + ) ;
}
/*
* Read the CMOS clock through the BIOS , and return the
* seconds in BCD .
*/
static u8 gettime ( void )
{
2009-04-01 18:13:46 -07:00
struct biosregs ireg , oreg ;
2007-07-11 12:18:45 -07:00
2009-04-01 18:13:46 -07:00
initregs ( & ireg ) ;
ireg . ah = 0x02 ;
intcall ( 0x1a , & ireg , & oreg ) ;
2007-07-11 12:18:45 -07:00
2009-04-01 18:13:46 -07:00
return oreg . dh ;
2007-07-11 12:18:45 -07:00
}
/*
* Read from the keyboard
*/
int getchar ( void )
{
2009-04-01 18:13:46 -07:00
struct biosregs ireg , oreg ;
initregs ( & ireg ) ;
/* ireg.ah = 0x00; */
intcall ( 0x16 , & ireg , & oreg ) ;
2007-07-11 12:18:45 -07:00
2009-04-01 18:13:46 -07:00
return oreg . al ;
2007-07-11 12:18:45 -07:00
}
static int kbd_pending ( void )
{
2009-04-01 18:13:46 -07:00
struct biosregs ireg , oreg ;
initregs ( & ireg ) ;
ireg . ah = 0x01 ;
intcall ( 0x16 , & ireg , & oreg ) ;
return ! ( oreg . eflags & X86_EFLAGS_ZF ) ;
2007-07-11 12:18:45 -07:00
}
void kbd_flush ( void )
{
for ( ; ; ) {
if ( ! kbd_pending ( ) )
break ;
getchar ( ) ;
}
}
int getchar_timeout ( void )
{
int cnt = 30 ;
int t0 , t1 ;
t0 = gettime ( ) ;
while ( cnt ) {
if ( kbd_pending ( ) )
return getchar ( ) ;
t1 = gettime ( ) ;
if ( t0 ! = t1 ) {
cnt - - ;
t0 = t1 ;
}
}
return 0 ; /* Timeout! */
}
2010-07-11 11:06:57 +03:00
2010-07-14 11:26:57 -07:00
static void early_serial_init ( int port , int baud )
2010-07-11 11:06:57 +03:00
{
unsigned char c ;
unsigned divisor ;
2010-07-14 11:26:57 -07:00
outb ( 0x3 , port + LCR ) ; /* 8n1 */
outb ( 0 , port + IER ) ; /* no interrupt */
outb ( 0 , port + FCR ) ; /* no fifo */
outb ( 0x3 , port + MCR ) ; /* DTR + RTS */
2010-07-11 11:06:57 +03:00
divisor = 115200 / baud ;
2010-07-14 11:26:57 -07:00
c = inb ( port + LCR ) ;
outb ( c | DLAB , port + LCR ) ;
outb ( divisor & 0xff , port + DLL ) ;
outb ( ( divisor > > 8 ) & 0xff , port + DLH ) ;
outb ( c & ~ DLAB , port + LCR ) ;
early_serial_base = port ;
printf ( " Early serial console at I/O port 0x%x baud: %d \n " , port , baud ) ;
2010-07-11 11:06:57 +03:00
}
2010-07-14 11:26:57 -07:00
static void parse_earlyprintk ( void )
2010-07-11 11:06:57 +03:00
{
int baud = DEFAULT_BAUD ;
char arg [ 32 ] ;
int pos = 0 ;
2010-07-14 11:26:57 -07:00
int port = 0 ;
2010-07-11 11:06:57 +03:00
if ( cmdline_find_option ( " earlyprintk " , arg , sizeof arg ) > 0 ) {
char * e ;
if ( ! strncmp ( arg , " serial " , 6 ) ) {
2010-07-14 11:26:57 -07:00
port = DEFAULT_SERIAL_PORT ;
2010-07-11 11:06:57 +03:00
pos + = 6 ;
}
if ( arg [ pos ] = = ' , ' )
pos + + ;
if ( ! strncmp ( arg , " ttyS " , 4 ) ) {
static const int bases [ ] = { 0x3f8 , 0x2f8 } ;
2010-07-14 11:26:57 -07:00
int idx = 0 ;
2010-07-11 11:06:57 +03:00
if ( ! strncmp ( arg + pos , " ttyS " , 4 ) )
pos + = 4 ;
if ( arg [ pos + + ] = = ' 1 ' )
2010-07-14 11:26:57 -07:00
idx = 1 ;
2010-07-11 11:06:57 +03:00
2010-07-14 11:26:57 -07:00
port = bases [ idx ] ;
2010-07-11 11:06:57 +03:00
}
if ( arg [ pos ] = = ' , ' )
pos + + ;
baud = simple_strtoull ( arg + pos , & e , 0 ) ;
if ( baud = = 0 | | arg + pos = = e )
baud = DEFAULT_BAUD ;
}
2010-07-14 11:26:57 -07:00
if ( port )
early_serial_init ( port , baud ) ;
2010-07-13 13:35:17 -07:00
}
# define BASE_BAUD (1843200 / 16)
static unsigned int probe_baud ( int port )
{
unsigned char lcr , dll , dlh ;
unsigned int quot ;
lcr = inb ( port + LCR ) ;
outb ( lcr | DLAB , port + LCR ) ;
dll = inb ( port + DLL ) ;
dlh = inb ( port + DLH ) ;
outb ( lcr , port + LCR ) ;
quot = ( dlh < < 8 ) | dll ;
return BASE_BAUD / quot ;
}
2010-07-14 11:26:57 -07:00
static void parse_console_uart8250 ( void )
2010-07-13 13:35:17 -07:00
{
char optstr [ 64 ] , * options ;
int baud = DEFAULT_BAUD ;
2010-07-14 11:26:57 -07:00
int port = 0 ;
2010-07-13 13:35:17 -07:00
/*
* console = uart8250 , io , 0x3f8 , 115200 n8
* need to make sure it is last one console !
*/
if ( cmdline_find_option ( " console " , optstr , sizeof optstr ) < = 0 )
2010-07-14 11:26:57 -07:00
return ;
2010-07-13 13:35:17 -07:00
options = optstr ;
if ( ! strncmp ( options , " uart8250,io, " , 12 ) )
2010-07-14 11:26:57 -07:00
port = simple_strtoull ( options + 12 , & options , 0 ) ;
2010-07-13 13:35:17 -07:00
else if ( ! strncmp ( options , " uart,io, " , 8 ) )
2010-07-14 11:26:57 -07:00
port = simple_strtoull ( options + 8 , & options , 0 ) ;
2010-07-13 13:35:17 -07:00
else
2010-07-14 11:26:57 -07:00
return ;
2010-07-13 13:35:17 -07:00
if ( options & & ( options [ 0 ] = = ' , ' ) )
baud = simple_strtoull ( options + 1 , & options , 0 ) ;
else
2010-07-14 11:26:57 -07:00
baud = probe_baud ( port ) ;
2010-07-13 13:35:17 -07:00
2010-07-14 11:26:57 -07:00
if ( port )
early_serial_init ( port , baud ) ;
2010-07-13 13:35:17 -07:00
}
void console_init ( void )
{
2010-07-14 11:26:57 -07:00
parse_earlyprintk ( ) ;
2010-07-13 13:35:17 -07:00
if ( ! early_serial_base )
2010-07-14 11:26:57 -07:00
parse_console_uart8250 ( ) ;
2010-07-11 11:06:57 +03:00
}