2005-09-06 05:56:02 +04:00
/*
2011-03-31 05:57:33 +04:00
* udbg for NS16550 compatible serial ports
2005-09-06 05:56:02 +04:00
*
* Copyright ( C ) 2001 - 2005 PPC 64 Team , IBM Corp
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include <linux/types.h>
2005-09-06 05:57:00 +04:00
# include <asm/udbg.h>
2005-09-06 05:56:02 +04:00
# include <asm/io.h>
2011-04-15 02:32:08 +04:00
# include <asm/reg_a2.h>
2005-09-06 05:56:02 +04:00
extern u8 real_readb ( volatile u8 __iomem * addr ) ;
extern void real_writeb ( u8 data , volatile u8 __iomem * addr ) ;
2007-02-05 01:36:49 +03:00
extern u8 real_205_readb ( volatile u8 __iomem * addr ) ;
extern void real_205_writeb ( u8 data , volatile u8 __iomem * addr ) ;
2005-09-06 05:56:02 +04:00
struct NS16550 {
/* this struct must be packed */
unsigned char rbr ; /* 0 */
unsigned char ier ; /* 1 */
unsigned char fcr ; /* 2 */
unsigned char lcr ; /* 3 */
unsigned char mcr ; /* 4 */
unsigned char lsr ; /* 5 */
unsigned char msr ; /* 6 */
unsigned char scr ; /* 7 */
} ;
# define thr rbr
# define iir fcr
# define dll rbr
# define dlm ier
# define dlab lcr
# define LSR_DR 0x01 /* Data ready */
# define LSR_OE 0x02 /* Overrun */
# define LSR_PE 0x04 /* Parity error */
# define LSR_FE 0x08 /* Framing error */
# define LSR_BI 0x10 /* Break */
# define LSR_THRE 0x20 /* Xmit holding register empty */
# define LSR_TEMT 0x40 /* Xmitter empty */
# define LSR_ERR 0x80 /* Error */
2005-11-23 09:56:06 +03:00
# define LCR_DLAB 0x80
2007-12-11 06:48:23 +03:00
static struct NS16550 __iomem * udbg_comport ;
2005-09-06 05:56:02 +04:00
2009-03-09 10:52:41 +03:00
static void udbg_550_flush ( void )
2005-09-06 05:56:02 +04:00
{
if ( udbg_comport ) {
while ( ( in_8 ( & udbg_comport - > lsr ) & LSR_THRE ) = = 0 )
/* wait for idle */ ;
2009-03-09 10:52:41 +03:00
}
}
static void udbg_550_putc ( char c )
{
if ( udbg_comport ) {
2005-09-06 05:56:02 +04:00
if ( c = = ' \n ' )
udbg_550_putc ( ' \r ' ) ;
2009-03-09 10:52:41 +03:00
udbg_550_flush ( ) ;
out_8 ( & udbg_comport - > thr , c ) ;
2005-09-06 05:56:02 +04:00
}
}
static int udbg_550_getc_poll ( void )
{
if ( udbg_comport ) {
if ( ( in_8 ( & udbg_comport - > lsr ) & LSR_DR ) ! = 0 )
return in_8 ( & udbg_comport - > rbr ) ;
else
return - 1 ;
}
return - 1 ;
}
2005-11-30 08:54:12 +03:00
static int udbg_550_getc ( void )
2005-09-06 05:56:02 +04:00
{
if ( udbg_comport ) {
while ( ( in_8 ( & udbg_comport - > lsr ) & LSR_DR ) = = 0 )
/* wait for char */ ;
return in_8 ( & udbg_comport - > rbr ) ;
}
2005-11-30 08:54:12 +03:00
return - 1 ;
2005-09-06 05:56:02 +04:00
}
2005-11-23 09:56:06 +03:00
void udbg_init_uart ( void __iomem * comport , unsigned int speed ,
unsigned int clock )
2005-09-06 05:56:02 +04:00
{
2006-07-04 08:11:23 +04:00
unsigned int dll , base_bauds ;
2005-11-23 09:56:06 +03:00
2006-07-04 08:11:23 +04:00
if ( clock = = 0 )
clock = 1843200 ;
2005-11-23 09:56:06 +03:00
if ( speed = = 0 )
speed = 9600 ;
2006-07-04 08:11:23 +04:00
base_bauds = clock / 16 ;
2005-11-23 09:56:06 +03:00
dll = base_bauds / speed ;
2005-09-06 05:56:02 +04:00
if ( comport ) {
udbg_comport = ( struct NS16550 __iomem * ) comport ;
out_8 ( & udbg_comport - > lcr , 0x00 ) ;
out_8 ( & udbg_comport - > ier , 0xff ) ;
out_8 ( & udbg_comport - > ier , 0x00 ) ;
2005-11-23 09:56:06 +03:00
out_8 ( & udbg_comport - > lcr , LCR_DLAB ) ;
out_8 ( & udbg_comport - > dll , dll & 0xff ) ;
out_8 ( & udbg_comport - > dlm , dll > > 8 ) ;
/* 8 data, 1 stop, no parity */
out_8 ( & udbg_comport - > lcr , 0x03 ) ;
/* RTS/DTR */
out_8 ( & udbg_comport - > mcr , 0x03 ) ;
/* Clear & enable FIFOs */
out_8 ( & udbg_comport - > fcr , 0x07 ) ;
2005-09-06 05:56:42 +04:00
udbg_putc = udbg_550_putc ;
2009-03-09 10:52:41 +03:00
udbg_flush = udbg_550_flush ;
2005-09-06 05:56:42 +04:00
udbg_getc = udbg_550_getc ;
udbg_getc_poll = udbg_550_getc_poll ;
2005-09-06 05:56:02 +04:00
}
}
2005-11-23 09:56:06 +03:00
unsigned int udbg_probe_uart_speed ( void __iomem * comport , unsigned int clock )
{
unsigned int dll , dlm , divisor , prescaler , speed ;
u8 old_lcr ;
2007-12-11 06:48:23 +03:00
struct NS16550 __iomem * port = comport ;
2005-11-23 09:56:06 +03:00
old_lcr = in_8 ( & port - > lcr ) ;
/* select divisor latch registers. */
out_8 ( & port - > lcr , LCR_DLAB ) ;
/* now, read the divisor */
dll = in_8 ( & port - > dll ) ;
dlm = in_8 ( & port - > dlm ) ;
divisor = dlm < < 8 | dll ;
/* check prescaling */
if ( in_8 ( & port - > mcr ) & 0x80 )
prescaler = 4 ;
else
prescaler = 1 ;
/* restore the LCR */
out_8 ( & port - > lcr , old_lcr ) ;
/* calculate speed */
speed = ( clock / prescaler ) / ( divisor * 16 ) ;
/* sanity check */
2008-10-14 18:36:31 +04:00
if ( speed > ( clock / 16 ) )
2005-11-23 09:56:06 +03:00
speed = 9600 ;
return speed ;
}
2005-09-06 05:56:02 +04:00
# ifdef CONFIG_PPC_MAPLE
2009-03-09 10:52:41 +03:00
void udbg_maple_real_flush ( void )
2005-09-06 05:56:02 +04:00
{
if ( udbg_comport ) {
while ( ( real_readb ( & udbg_comport - > lsr ) & LSR_THRE ) = = 0 )
/* wait for idle */ ;
2009-03-09 10:52:41 +03:00
}
}
void udbg_maple_real_putc ( char c )
{
if ( udbg_comport ) {
2005-09-06 05:56:02 +04:00
if ( c = = ' \n ' )
udbg_maple_real_putc ( ' \r ' ) ;
2009-03-09 10:52:41 +03:00
udbg_maple_real_flush ( ) ;
real_writeb ( c , & udbg_comport - > thr ) ; eieio ( ) ;
2005-09-06 05:56:02 +04:00
}
}
2006-01-11 03:54:09 +03:00
void __init udbg_init_maple_realmode ( void )
2005-09-06 05:56:02 +04:00
{
2007-12-11 06:48:23 +03:00
udbg_comport = ( struct NS16550 __iomem * ) 0xf40003f8 ;
2005-09-06 05:56:02 +04:00
2005-09-06 05:56:42 +04:00
udbg_putc = udbg_maple_real_putc ;
2009-03-09 10:52:41 +03:00
udbg_flush = udbg_maple_real_flush ;
2005-09-06 05:56:42 +04:00
udbg_getc = NULL ;
udbg_getc_poll = NULL ;
2005-09-06 05:56:02 +04:00
}
# endif /* CONFIG_PPC_MAPLE */
2007-02-05 01:36:49 +03:00
# ifdef CONFIG_PPC_PASEMI
2009-03-09 10:52:41 +03:00
void udbg_pas_real_flush ( void )
2007-02-05 01:36:49 +03:00
{
if ( udbg_comport ) {
while ( ( real_205_readb ( & udbg_comport - > lsr ) & LSR_THRE ) = = 0 )
/* wait for idle */ ;
2009-03-09 10:52:41 +03:00
}
}
void udbg_pas_real_putc ( char c )
{
if ( udbg_comport ) {
2007-02-05 01:36:49 +03:00
if ( c = = ' \n ' )
udbg_pas_real_putc ( ' \r ' ) ;
2009-03-09 10:52:41 +03:00
udbg_pas_real_flush ( ) ;
real_205_writeb ( c , & udbg_comport - > thr ) ; eieio ( ) ;
2007-02-05 01:36:49 +03:00
}
}
void udbg_init_pas_realmode ( void )
{
2007-12-11 06:48:23 +03:00
udbg_comport = ( struct NS16550 __iomem * ) 0xfcff03f8UL ;
2007-02-05 01:36:49 +03:00
udbg_putc = udbg_pas_real_putc ;
2009-03-09 10:52:41 +03:00
udbg_flush = udbg_pas_real_flush ;
2007-02-05 01:36:49 +03:00
udbg_getc = NULL ;
udbg_getc_poll = NULL ;
}
# endif /* CONFIG_PPC_MAPLE */
2007-05-08 06:59:31 +04:00
# ifdef CONFIG_PPC_EARLY_DEBUG_44x
# include <platforms/44x/44x.h>
2009-06-16 19:55:19 +04:00
static void udbg_44x_as1_flush ( void )
2007-05-08 06:59:31 +04:00
{
if ( udbg_comport ) {
while ( ( as1_readb ( & udbg_comport - > lsr ) & LSR_THRE ) = = 0 )
/* wait for idle */ ;
2009-03-09 10:52:41 +03:00
}
}
static void udbg_44x_as1_putc ( char c )
{
if ( udbg_comport ) {
2007-05-08 06:59:31 +04:00
if ( c = = ' \n ' )
udbg_44x_as1_putc ( ' \r ' ) ;
2009-03-09 10:52:41 +03:00
udbg_44x_as1_flush ( ) ;
as1_writeb ( c , & udbg_comport - > thr ) ; eieio ( ) ;
2007-05-08 06:59:31 +04:00
}
}
2007-09-17 14:56:47 +04:00
static int udbg_44x_as1_getc ( void )
{
if ( udbg_comport ) {
while ( ( as1_readb ( & udbg_comport - > lsr ) & LSR_DR ) = = 0 )
; /* wait for char */
return as1_readb ( & udbg_comport - > rbr ) ;
}
return - 1 ;
}
2007-05-08 06:59:31 +04:00
void __init udbg_init_44x_as1 ( void )
{
udbg_comport =
2007-12-11 06:48:23 +03:00
( struct NS16550 __iomem * ) PPC44x_EARLY_DEBUG_VIRTADDR ;
2007-05-08 06:59:31 +04:00
udbg_putc = udbg_44x_as1_putc ;
2009-03-09 10:52:41 +03:00
udbg_flush = udbg_44x_as1_flush ;
2007-09-17 14:56:47 +04:00
udbg_getc = udbg_44x_as1_getc ;
2007-05-08 06:59:31 +04:00
}
# endif /* CONFIG_PPC_EARLY_DEBUG_44x */
2007-12-21 07:39:26 +03:00
# ifdef CONFIG_PPC_EARLY_DEBUG_40x
2009-03-09 10:52:41 +03:00
static void udbg_40x_real_flush ( void )
2007-12-21 07:39:26 +03:00
{
if ( udbg_comport ) {
while ( ( real_readb ( & udbg_comport - > lsr ) & LSR_THRE ) = = 0 )
/* wait for idle */ ;
2009-03-09 10:52:41 +03:00
}
}
static void udbg_40x_real_putc ( char c )
{
if ( udbg_comport ) {
2007-12-21 07:39:26 +03:00
if ( c = = ' \n ' )
udbg_40x_real_putc ( ' \r ' ) ;
2009-03-09 10:52:41 +03:00
udbg_40x_real_flush ( ) ;
real_writeb ( c , & udbg_comport - > thr ) ; eieio ( ) ;
2007-12-21 07:39:26 +03:00
}
}
static int udbg_40x_real_getc ( void )
{
if ( udbg_comport ) {
while ( ( real_readb ( & udbg_comport - > lsr ) & LSR_DR ) = = 0 )
; /* wait for char */
return real_readb ( & udbg_comport - > rbr ) ;
}
return - 1 ;
}
void __init udbg_init_40x_realmode ( void )
{
udbg_comport = ( struct NS16550 __iomem * )
CONFIG_PPC_EARLY_DEBUG_40x_PHYSADDR ;
udbg_putc = udbg_40x_real_putc ;
2009-03-09 10:52:41 +03:00
udbg_flush = udbg_40x_real_flush ;
2007-12-21 07:39:26 +03:00
udbg_getc = udbg_40x_real_getc ;
udbg_getc_poll = NULL ;
}
# endif /* CONFIG_PPC_EARLY_DEBUG_40x */
2011-04-15 02:32:08 +04:00
# ifdef CONFIG_PPC_EARLY_DEBUG_WSP
static void udbg_wsp_flush ( void )
{
if ( udbg_comport ) {
while ( ( readb ( & udbg_comport - > lsr ) & LSR_THRE ) = = 0 )
/* wait for idle */ ;
}
}
static void udbg_wsp_putc ( char c )
{
if ( udbg_comport ) {
if ( c = = ' \n ' )
udbg_wsp_putc ( ' \r ' ) ;
udbg_wsp_flush ( ) ;
writeb ( c , & udbg_comport - > thr ) ; eieio ( ) ;
}
}
static int udbg_wsp_getc ( void )
{
if ( udbg_comport ) {
while ( ( readb ( & udbg_comport - > lsr ) & LSR_DR ) = = 0 )
; /* wait for char */
return readb ( & udbg_comport - > rbr ) ;
}
return - 1 ;
}
static int udbg_wsp_getc_poll ( void )
{
if ( udbg_comport )
if ( readb ( & udbg_comport - > lsr ) & LSR_DR )
return readb ( & udbg_comport - > rbr ) ;
return - 1 ;
}
void __init udbg_init_wsp ( void )
{
udbg_comport = ( struct NS16550 __iomem * ) WSP_UART_VIRT ;
udbg_init_uart ( udbg_comport , 57600 , 50000000 ) ;
udbg_putc = udbg_wsp_putc ;
udbg_flush = udbg_wsp_flush ;
udbg_getc = udbg_wsp_getc ;
udbg_getc_poll = udbg_wsp_getc_poll ;
}
# endif /* CONFIG_PPC_EARLY_DEBUG_WSP */