2005-04-17 02:20:36 +04:00
/*
* linux / drivers / char / amba . c
*
* Driver for AMBA serial ports
*
* Based on drivers / char / serial . c , by Linus Torvalds , Theodore Ts ' o .
*
* Copyright 1999 ARM Limited
* Copyright ( C ) 2000 Deep Blue Solutions Ltd .
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* $ Id : amba . c , v 1.41 2002 / 07 / 28 10 : 03 : 27 rmk Exp $
*
* This is a generic driver for ARM AMBA - type serial ports . They
* have a lot of 16550 - like features , but are not register compatible .
* Note that although they do have CTS , DCD and DSR inputs , they do
* not have an RI input , nor do they have DTR or RTS outputs . If
* required , these have to be supplied via some other means ( eg , GPIO )
* and hooked into this driver .
*/
# include <linux/config.h>
# if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
# define SUPPORT_SYSRQ
# endif
# include <linux/module.h>
# include <linux/ioport.h>
# include <linux/init.h>
# include <linux/console.h>
# include <linux/sysrq.h>
# include <linux/device.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/serial_core.h>
# include <linux/serial.h>
2006-01-07 16:52:45 +03:00
# include <linux/amba/bus.h>
# include <linux/amba/serial.h>
2006-01-07 19:15:52 +03:00
# include <linux/clk.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
2005-10-28 17:05:16 +04:00
# include <asm/sizes.h>
2005-04-17 02:20:36 +04:00
# define UART_NR 14
# define SERIAL_AMBA_MAJOR 204
# define SERIAL_AMBA_MINOR 64
# define SERIAL_AMBA_NR UART_NR
# define AMBA_ISR_PASS_LIMIT 256
2005-11-19 14:10:35 +03:00
# define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)
# define UART_DUMMY_DR_RX (1 << 16)
2005-04-17 02:20:36 +04:00
/*
* We wrap our port structure around the generic uart_port .
*/
struct uart_amba_port {
struct uart_port port ;
struct clk * clk ;
unsigned int im ; /* interrupt mask */
unsigned int old_status ;
} ;
2005-08-31 13:12:14 +04:00
static void pl011_stop_tx ( struct uart_port * port )
2005-04-17 02:20:36 +04:00
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
uap - > im & = ~ UART011_TXIM ;
writew ( uap - > im , uap - > port . membase + UART011_IMSC ) ;
}
2005-08-31 13:12:14 +04:00
static void pl011_start_tx ( struct uart_port * port )
2005-04-17 02:20:36 +04:00
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
uap - > im | = UART011_TXIM ;
writew ( uap - > im , uap - > port . membase + UART011_IMSC ) ;
}
static void pl011_stop_rx ( struct uart_port * port )
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
uap - > im & = ~ ( UART011_RXIM | UART011_RTIM | UART011_FEIM |
UART011_PEIM | UART011_BEIM | UART011_OEIM ) ;
writew ( uap - > im , uap - > port . membase + UART011_IMSC ) ;
}
static void pl011_enable_ms ( struct uart_port * port )
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
uap - > im | = UART011_RIMIM | UART011_CTSMIM | UART011_DCDMIM | UART011_DSRMIM ;
writew ( uap - > im , uap - > port . membase + UART011_IMSC ) ;
}
static void
# ifdef SUPPORT_SYSRQ
pl011_rx_chars ( struct uart_amba_port * uap , struct pt_regs * regs )
# else
pl011_rx_chars ( struct uart_amba_port * uap )
# endif
{
struct tty_struct * tty = uap - > port . info - > tty ;
2005-11-19 14:10:35 +03:00
unsigned int status , ch , flag , max_count = 256 ;
2005-04-17 02:20:36 +04:00
status = readw ( uap - > port . membase + UART01x_FR ) ;
while ( ( status & UART01x_FR_RXFE ) = = 0 & & max_count - - ) {
2005-11-19 14:10:35 +03:00
ch = readw ( uap - > port . membase + UART01x_DR ) | UART_DUMMY_DR_RX ;
2005-04-17 02:20:36 +04:00
flag = TTY_NORMAL ;
uap - > port . icount . rx + + ;
/*
* Note that the error handling code is
* out of the main execution path
*/
2005-11-19 14:10:35 +03:00
if ( unlikely ( ch & UART_DR_ERROR ) ) {
if ( ch & UART011_DR_BE ) {
ch & = ~ ( UART011_DR_FE | UART011_DR_PE ) ;
2005-04-17 02:20:36 +04:00
uap - > port . icount . brk + + ;
if ( uart_handle_break ( & uap - > port ) )
goto ignore_char ;
2005-11-19 14:10:35 +03:00
} else if ( ch & UART011_DR_PE )
2005-04-17 02:20:36 +04:00
uap - > port . icount . parity + + ;
2005-11-19 14:10:35 +03:00
else if ( ch & UART011_DR_FE )
2005-04-17 02:20:36 +04:00
uap - > port . icount . frame + + ;
2005-11-19 14:10:35 +03:00
if ( ch & UART011_DR_OE )
2005-04-17 02:20:36 +04:00
uap - > port . icount . overrun + + ;
2005-11-19 14:10:35 +03:00
ch & = uap - > port . read_status_mask ;
2005-04-17 02:20:36 +04:00
2005-11-19 14:10:35 +03:00
if ( ch & UART011_DR_BE )
2005-04-17 02:20:36 +04:00
flag = TTY_BREAK ;
2005-11-19 14:10:35 +03:00
else if ( ch & UART011_DR_PE )
2005-04-17 02:20:36 +04:00
flag = TTY_PARITY ;
2005-11-19 14:10:35 +03:00
else if ( ch & UART011_DR_FE )
2005-04-17 02:20:36 +04:00
flag = TTY_FRAME ;
}
2005-12-27 14:10:34 +03:00
if ( uart_handle_sysrq_char ( & uap - > port , ch & 255 , regs ) )
2005-04-17 02:20:36 +04:00
goto ignore_char ;
2005-11-19 14:10:35 +03:00
uart_insert_char ( & uap - > port , ch , UART011_DR_OE , ch , flag ) ;
2005-05-10 02:21:59 +04:00
2005-04-17 02:20:36 +04:00
ignore_char :
status = readw ( uap - > port . membase + UART01x_FR ) ;
}
tty_flip_buffer_push ( tty ) ;
return ;
}
static void pl011_tx_chars ( struct uart_amba_port * uap )
{
struct circ_buf * xmit = & uap - > port . info - > xmit ;
int count ;
if ( uap - > port . x_char ) {
writew ( uap - > port . x_char , uap - > port . membase + UART01x_DR ) ;
uap - > port . icount . tx + + ;
uap - > port . x_char = 0 ;
return ;
}
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( & uap - > port ) ) {
2005-08-31 13:12:14 +04:00
pl011_stop_tx ( & uap - > port ) ;
2005-04-17 02:20:36 +04:00
return ;
}
count = uap - > port . fifosize > > 1 ;
do {
writew ( xmit - > buf [ xmit - > tail ] , uap - > port . membase + UART01x_DR ) ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
uap - > port . icount . tx + + ;
if ( uart_circ_empty ( xmit ) )
break ;
} while ( - - count > 0 ) ;
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( & uap - > port ) ;
if ( uart_circ_empty ( xmit ) )
2005-08-31 13:12:14 +04:00
pl011_stop_tx ( & uap - > port ) ;
2005-04-17 02:20:36 +04:00
}
static void pl011_modem_status ( struct uart_amba_port * uap )
{
unsigned int status , delta ;
status = readw ( uap - > port . membase + UART01x_FR ) & UART01x_FR_MODEM_ANY ;
delta = status ^ uap - > old_status ;
uap - > old_status = status ;
if ( ! delta )
return ;
if ( delta & UART01x_FR_DCD )
uart_handle_dcd_change ( & uap - > port , status & UART01x_FR_DCD ) ;
if ( delta & UART01x_FR_DSR )
uap - > port . icount . dsr + + ;
if ( delta & UART01x_FR_CTS )
uart_handle_cts_change ( & uap - > port , status & UART01x_FR_CTS ) ;
wake_up_interruptible ( & uap - > port . info - > delta_msr_wait ) ;
}
static irqreturn_t pl011_int ( int irq , void * dev_id , struct pt_regs * regs )
{
struct uart_amba_port * uap = dev_id ;
unsigned int status , pass_counter = AMBA_ISR_PASS_LIMIT ;
int handled = 0 ;
spin_lock ( & uap - > port . lock ) ;
status = readw ( uap - > port . membase + UART011_MIS ) ;
if ( status ) {
do {
writew ( status & ~ ( UART011_TXIS | UART011_RTIS |
UART011_RXIS ) ,
uap - > port . membase + UART011_ICR ) ;
if ( status & ( UART011_RTIS | UART011_RXIS ) )
# ifdef SUPPORT_SYSRQ
pl011_rx_chars ( uap , regs ) ;
# else
pl011_rx_chars ( uap ) ;
# endif
if ( status & ( UART011_DSRMIS | UART011_DCDMIS |
UART011_CTSMIS | UART011_RIMIS ) )
pl011_modem_status ( uap ) ;
if ( status & UART011_TXIS )
pl011_tx_chars ( uap ) ;
if ( pass_counter - - = = 0 )
break ;
status = readw ( uap - > port . membase + UART011_MIS ) ;
} while ( status ! = 0 ) ;
handled = 1 ;
}
spin_unlock ( & uap - > port . lock ) ;
return IRQ_RETVAL ( handled ) ;
}
static unsigned int pl01x_tx_empty ( struct uart_port * port )
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
unsigned int status = readw ( uap - > port . membase + UART01x_FR ) ;
return status & ( UART01x_FR_BUSY | UART01x_FR_TXFF ) ? 0 : TIOCSER_TEMT ;
}
static unsigned int pl01x_get_mctrl ( struct uart_port * port )
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
unsigned int result = 0 ;
unsigned int status = readw ( uap - > port . membase + UART01x_FR ) ;
# define BIT(uartbit, tiocmbit) \
if ( status & uartbit ) \
result | = tiocmbit
BIT ( UART01x_FR_DCD , TIOCM_CAR ) ;
BIT ( UART01x_FR_DSR , TIOCM_DSR ) ;
BIT ( UART01x_FR_CTS , TIOCM_CTS ) ;
BIT ( UART011_FR_RI , TIOCM_RNG ) ;
# undef BIT
return result ;
}
static void pl011_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
unsigned int cr ;
cr = readw ( uap - > port . membase + UART011_CR ) ;
# define BIT(tiocmbit, uartbit) \
if ( mctrl & tiocmbit ) \
cr | = uartbit ; \
else \
cr & = ~ uartbit
BIT ( TIOCM_RTS , UART011_CR_RTS ) ;
BIT ( TIOCM_DTR , UART011_CR_DTR ) ;
BIT ( TIOCM_OUT1 , UART011_CR_OUT1 ) ;
BIT ( TIOCM_OUT2 , UART011_CR_OUT2 ) ;
BIT ( TIOCM_LOOP , UART011_CR_LBE ) ;
# undef BIT
writew ( cr , uap - > port . membase + UART011_CR ) ;
}
static void pl011_break_ctl ( struct uart_port * port , int break_state )
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
unsigned long flags ;
unsigned int lcr_h ;
spin_lock_irqsave ( & uap - > port . lock , flags ) ;
lcr_h = readw ( uap - > port . membase + UART011_LCRH ) ;
if ( break_state = = - 1 )
lcr_h | = UART01x_LCRH_BRK ;
else
lcr_h & = ~ UART01x_LCRH_BRK ;
writew ( lcr_h , uap - > port . membase + UART011_LCRH ) ;
spin_unlock_irqrestore ( & uap - > port . lock , flags ) ;
}
static int pl011_startup ( struct uart_port * port )
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
unsigned int cr ;
int retval ;
/*
* Try to enable the clock producer .
*/
retval = clk_enable ( uap - > clk ) ;
if ( retval )
goto out ;
uap - > port . uartclk = clk_get_rate ( uap - > clk ) ;
/*
* Allocate the IRQ
*/
retval = request_irq ( uap - > port . irq , pl011_int , 0 , " uart-pl011 " , uap ) ;
if ( retval )
goto clk_dis ;
writew ( UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8 ,
uap - > port . membase + UART011_IFLS ) ;
/*
* Provoke TX FIFO interrupt into asserting .
*/
cr = UART01x_CR_UARTEN | UART011_CR_TXE | UART011_CR_LBE ;
writew ( cr , uap - > port . membase + UART011_CR ) ;
writew ( 0 , uap - > port . membase + UART011_FBRD ) ;
writew ( 1 , uap - > port . membase + UART011_IBRD ) ;
writew ( 0 , uap - > port . membase + UART011_LCRH ) ;
writew ( 0 , uap - > port . membase + UART01x_DR ) ;
while ( readw ( uap - > port . membase + UART01x_FR ) & UART01x_FR_BUSY )
barrier ( ) ;
cr = UART01x_CR_UARTEN | UART011_CR_RXE | UART011_CR_TXE ;
writew ( cr , uap - > port . membase + UART011_CR ) ;
/*
* initialise the old status of the modem signals
*/
uap - > old_status = readw ( uap - > port . membase + UART01x_FR ) & UART01x_FR_MODEM_ANY ;
/*
* Finally , enable interrupts
*/
spin_lock_irq ( & uap - > port . lock ) ;
uap - > im = UART011_RXIM | UART011_RTIM ;
writew ( uap - > im , uap - > port . membase + UART011_IMSC ) ;
spin_unlock_irq ( & uap - > port . lock ) ;
return 0 ;
clk_dis :
clk_disable ( uap - > clk ) ;
out :
return retval ;
}
static void pl011_shutdown ( struct uart_port * port )
{
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
unsigned long val ;
/*
* disable all interrupts
*/
spin_lock_irq ( & uap - > port . lock ) ;
uap - > im = 0 ;
writew ( uap - > im , uap - > port . membase + UART011_IMSC ) ;
writew ( 0xffff , uap - > port . membase + UART011_ICR ) ;
spin_unlock_irq ( & uap - > port . lock ) ;
/*
* Free the interrupt
*/
free_irq ( uap - > port . irq , uap ) ;
/*
* disable the port
*/
writew ( UART01x_CR_UARTEN | UART011_CR_TXE , uap - > port . membase + UART011_CR ) ;
/*
* disable break condition and fifos
*/
val = readw ( uap - > port . membase + UART011_LCRH ) ;
val & = ~ ( UART01x_LCRH_BRK | UART01x_LCRH_FEN ) ;
writew ( val , uap - > port . membase + UART011_LCRH ) ;
/*
* Shut down the clock producer
*/
clk_disable ( uap - > clk ) ;
}
static void
pl011_set_termios ( struct uart_port * port , struct termios * termios ,
struct termios * old )
{
unsigned int lcr_h , old_cr ;
unsigned long flags ;
unsigned int baud , quot ;
/*
* Ask the core to calculate the divisor for us .
*/
baud = uart_get_baud_rate ( port , termios , old , 0 , port - > uartclk / 16 ) ;
quot = port - > uartclk * 4 / baud ;
switch ( termios - > c_cflag & CSIZE ) {
case CS5 :
lcr_h = UART01x_LCRH_WLEN_5 ;
break ;
case CS6 :
lcr_h = UART01x_LCRH_WLEN_6 ;
break ;
case CS7 :
lcr_h = UART01x_LCRH_WLEN_7 ;
break ;
default : // CS8
lcr_h = UART01x_LCRH_WLEN_8 ;
break ;
}
if ( termios - > c_cflag & CSTOPB )
lcr_h | = UART01x_LCRH_STP2 ;
if ( termios - > c_cflag & PARENB ) {
lcr_h | = UART01x_LCRH_PEN ;
if ( ! ( termios - > c_cflag & PARODD ) )
lcr_h | = UART01x_LCRH_EPS ;
}
if ( port - > fifosize > 1 )
lcr_h | = UART01x_LCRH_FEN ;
spin_lock_irqsave ( & port - > lock , flags ) ;
/*
* Update the per - port timeout .
*/
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
2005-11-19 14:10:35 +03:00
port - > read_status_mask = UART011_DR_OE | 255 ;
2005-04-17 02:20:36 +04:00
if ( termios - > c_iflag & INPCK )
2005-11-19 14:10:35 +03:00
port - > read_status_mask | = UART011_DR_FE | UART011_DR_PE ;
2005-04-17 02:20:36 +04:00
if ( termios - > c_iflag & ( BRKINT | PARMRK ) )
2005-11-19 14:10:35 +03:00
port - > read_status_mask | = UART011_DR_BE ;
2005-04-17 02:20:36 +04:00
/*
* Characters to ignore
*/
port - > ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNPAR )
2005-11-19 14:10:35 +03:00
port - > ignore_status_mask | = UART011_DR_FE | UART011_DR_PE ;
2005-04-17 02:20:36 +04:00
if ( termios - > c_iflag & IGNBRK ) {
2005-11-19 14:10:35 +03:00
port - > ignore_status_mask | = UART011_DR_BE ;
2005-04-17 02:20:36 +04:00
/*
* If we ' re ignoring parity and break indicators ,
* ignore overruns too ( for real raw support ) .
*/
if ( termios - > c_iflag & IGNPAR )
2005-11-19 14:10:35 +03:00
port - > ignore_status_mask | = UART011_DR_OE ;
2005-04-17 02:20:36 +04:00
}
/*
* Ignore all characters if CREAD is not set .
*/
if ( ( termios - > c_cflag & CREAD ) = = 0 )
2005-11-19 14:10:35 +03:00
port - > ignore_status_mask | = UART_DUMMY_DR_RX ;
2005-04-17 02:20:36 +04:00
if ( UART_ENABLE_MS ( port , termios - > c_cflag ) )
pl011_enable_ms ( port ) ;
/* first, disable everything */
old_cr = readw ( port - > membase + UART011_CR ) ;
writew ( 0 , port - > membase + UART011_CR ) ;
/* Set baud rate */
writew ( quot & 0x3f , port - > membase + UART011_FBRD ) ;
writew ( quot > > 6 , port - > membase + UART011_IBRD ) ;
/*
* - - - - - - - - - - v - - - - - - - - - - v - - - - - - - - - - v - - - - - - - - - - v - - - - -
* NOTE : MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
* - - - - - - - - - - ^ - - - - - - - - - - ^ - - - - - - - - - - ^ - - - - - - - - - - ^ - - - - -
*/
writew ( lcr_h , port - > membase + UART011_LCRH ) ;
writew ( old_cr , port - > membase + UART011_CR ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
static const char * pl011_type ( struct uart_port * port )
{
return port - > type = = PORT_AMBA ? " AMBA/PL011 " : NULL ;
}
/*
* Release the memory region ( s ) being used by ' port '
*/
static void pl010_release_port ( struct uart_port * port )
{
release_mem_region ( port - > mapbase , SZ_4K ) ;
}
/*
* Request the memory region ( s ) being used by ' port '
*/
static int pl010_request_port ( struct uart_port * port )
{
return request_mem_region ( port - > mapbase , SZ_4K , " uart-pl011 " )
! = NULL ? 0 : - EBUSY ;
}
/*
* Configure / autoconfigure the port .
*/
static void pl010_config_port ( struct uart_port * port , int flags )
{
if ( flags & UART_CONFIG_TYPE ) {
port - > type = PORT_AMBA ;
pl010_request_port ( port ) ;
}
}
/*
* verify the new serial_struct ( for TIOCSSERIAL ) .
*/
static int pl010_verify_port ( struct uart_port * port , struct serial_struct * ser )
{
int ret = 0 ;
if ( ser - > type ! = PORT_UNKNOWN & & ser - > type ! = PORT_AMBA )
ret = - EINVAL ;
if ( ser - > irq < 0 | | ser - > irq > = NR_IRQS )
ret = - EINVAL ;
if ( ser - > baud_base < 9600 )
ret = - EINVAL ;
return ret ;
}
static struct uart_ops amba_pl011_pops = {
. tx_empty = pl01x_tx_empty ,
. set_mctrl = pl011_set_mctrl ,
. get_mctrl = pl01x_get_mctrl ,
. stop_tx = pl011_stop_tx ,
. start_tx = pl011_start_tx ,
. stop_rx = pl011_stop_rx ,
. enable_ms = pl011_enable_ms ,
. break_ctl = pl011_break_ctl ,
. startup = pl011_startup ,
. shutdown = pl011_shutdown ,
. set_termios = pl011_set_termios ,
. type = pl011_type ,
. release_port = pl010_release_port ,
. request_port = pl010_request_port ,
. config_port = pl010_config_port ,
. verify_port = pl010_verify_port ,
} ;
static struct uart_amba_port * amba_ports [ UART_NR ] ;
# ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE
2006-03-20 23:00:09 +03:00
static void pl011_console_putchar ( struct uart_port * port , int ch )
2005-04-17 02:20:36 +04:00
{
2006-03-20 23:00:09 +03:00
struct uart_amba_port * uap = ( struct uart_amba_port * ) port ;
2005-04-17 02:20:36 +04:00
2006-03-20 23:00:09 +03:00
while ( readw ( uap - > port . membase + UART01x_FR ) & UART01x_FR_TXFF )
barrier ( ) ;
2005-04-17 02:20:36 +04:00
writew ( ch , uap - > port . membase + UART01x_DR ) ;
}
static void
pl011_console_write ( struct console * co , const char * s , unsigned int count )
{
struct uart_amba_port * uap = amba_ports [ co - > index ] ;
unsigned int status , old_cr , new_cr ;
clk_enable ( uap - > clk ) ;
/*
* First save the CR then disable the interrupts
*/
old_cr = readw ( uap - > port . membase + UART011_CR ) ;
new_cr = old_cr & ~ UART011_CR_CTSEN ;
new_cr | = UART01x_CR_UARTEN | UART011_CR_TXE ;
writew ( new_cr , uap - > port . membase + UART011_CR ) ;
2006-03-20 23:00:09 +03:00
uart_console_write ( & uap - > port , s , count , pl011_console_putchar ) ;
2005-04-17 02:20:36 +04:00
/*
* Finally , wait for transmitter to become empty
* and restore the TCR
*/
do {
status = readw ( uap - > port . membase + UART01x_FR ) ;
} while ( status & UART01x_FR_BUSY ) ;
writew ( old_cr , uap - > port . membase + UART011_CR ) ;
clk_disable ( uap - > clk ) ;
}
static void __init
pl011_console_get_options ( struct uart_amba_port * uap , int * baud ,
int * parity , int * bits )
{
if ( readw ( uap - > port . membase + UART011_CR ) & UART01x_CR_UARTEN ) {
unsigned int lcr_h , ibrd , fbrd ;
lcr_h = readw ( uap - > port . membase + UART011_LCRH ) ;
* parity = ' n ' ;
if ( lcr_h & UART01x_LCRH_PEN ) {
if ( lcr_h & UART01x_LCRH_EPS )
* parity = ' e ' ;
else
* parity = ' o ' ;
}
if ( ( lcr_h & 0x60 ) = = UART01x_LCRH_WLEN_7 )
* bits = 7 ;
else
* bits = 8 ;
ibrd = readw ( uap - > port . membase + UART011_IBRD ) ;
fbrd = readw ( uap - > port . membase + UART011_FBRD ) ;
* baud = uap - > port . uartclk * 4 / ( 64 * ibrd + fbrd ) ;
}
}
static int __init pl011_console_setup ( struct console * co , char * options )
{
struct uart_amba_port * uap ;
int baud = 38400 ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
/*
* Check whether an invalid uart number has been specified , and
* if so , search for the first available port that does have
* console support .
*/
if ( co - > index > = UART_NR )
co - > index = 0 ;
uap = amba_ports [ co - > index ] ;
uap - > port . uartclk = clk_get_rate ( uap - > clk ) ;
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
else
pl011_console_get_options ( uap , & baud , & parity , & bits ) ;
return uart_set_options ( & uap - > port , co , baud , parity , bits , flow ) ;
}
2005-09-15 01:36:03 +04:00
static struct uart_driver amba_reg ;
2005-04-17 02:20:36 +04:00
static struct console amba_console = {
. name = " ttyAMA " ,
. write = pl011_console_write ,
. device = uart_console_device ,
. setup = pl011_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
. data = & amba_reg ,
} ;
# define AMBA_CONSOLE (&amba_console)
# else
# define AMBA_CONSOLE NULL
# endif
static struct uart_driver amba_reg = {
. owner = THIS_MODULE ,
. driver_name = " ttyAMA " ,
. dev_name = " ttyAMA " ,
. major = SERIAL_AMBA_MAJOR ,
. minor = SERIAL_AMBA_MINOR ,
. nr = UART_NR ,
. cons = AMBA_CONSOLE ,
} ;
static int pl011_probe ( struct amba_device * dev , void * id )
{
struct uart_amba_port * uap ;
void __iomem * base ;
int i , ret ;
for ( i = 0 ; i < ARRAY_SIZE ( amba_ports ) ; i + + )
if ( amba_ports [ i ] = = NULL )
break ;
if ( i = = ARRAY_SIZE ( amba_ports ) ) {
ret = - EBUSY ;
goto out ;
}
uap = kmalloc ( sizeof ( struct uart_amba_port ) , GFP_KERNEL ) ;
if ( uap = = NULL ) {
ret = - ENOMEM ;
goto out ;
}
base = ioremap ( dev - > res . start , PAGE_SIZE ) ;
if ( ! base ) {
ret = - ENOMEM ;
goto free ;
}
memset ( uap , 0 , sizeof ( struct uart_amba_port ) ) ;
uap - > clk = clk_get ( & dev - > dev , " UARTCLK " ) ;
if ( IS_ERR ( uap - > clk ) ) {
ret = PTR_ERR ( uap - > clk ) ;
goto unmap ;
}
uap - > port . dev = & dev - > dev ;
uap - > port . mapbase = dev - > res . start ;
uap - > port . membase = base ;
uap - > port . iotype = UPIO_MEM ;
uap - > port . irq = dev - > irq [ 0 ] ;
uap - > port . fifosize = 16 ;
uap - > port . ops = & amba_pl011_pops ;
uap - > port . flags = UPF_BOOT_AUTOCONF ;
uap - > port . line = i ;
amba_ports [ i ] = uap ;
amba_set_drvdata ( dev , uap ) ;
ret = uart_add_one_port ( & amba_reg , & uap - > port ) ;
if ( ret ) {
amba_set_drvdata ( dev , NULL ) ;
amba_ports [ i ] = NULL ;
clk_put ( uap - > clk ) ;
unmap :
iounmap ( base ) ;
free :
kfree ( uap ) ;
}
out :
return ret ;
}
static int pl011_remove ( struct amba_device * dev )
{
struct uart_amba_port * uap = amba_get_drvdata ( dev ) ;
int i ;
amba_set_drvdata ( dev , NULL ) ;
uart_remove_one_port ( & amba_reg , & uap - > port ) ;
for ( i = 0 ; i < ARRAY_SIZE ( amba_ports ) ; i + + )
if ( amba_ports [ i ] = = uap )
amba_ports [ i ] = NULL ;
iounmap ( uap - > port . membase ) ;
clk_put ( uap - > clk ) ;
kfree ( uap ) ;
return 0 ;
}
static struct amba_id pl011_ids [ ] __initdata = {
{
. id = 0x00041011 ,
. mask = 0x000fffff ,
} ,
{ 0 , 0 } ,
} ;
static struct amba_driver pl011_driver = {
. drv = {
. name = " uart-pl011 " ,
} ,
. id_table = pl011_ids ,
. probe = pl011_probe ,
. remove = pl011_remove ,
} ;
static int __init pl011_init ( void )
{
int ret ;
printk ( KERN_INFO " Serial: AMBA PL011 UART driver \n " ) ;
ret = uart_register_driver ( & amba_reg ) ;
if ( ret = = 0 ) {
ret = amba_driver_register ( & pl011_driver ) ;
if ( ret )
uart_unregister_driver ( & amba_reg ) ;
}
return ret ;
}
static void __exit pl011_exit ( void )
{
amba_driver_unregister ( & pl011_driver ) ;
uart_unregister_driver ( & amba_reg ) ;
}
module_init ( pl011_init ) ;
module_exit ( pl011_exit ) ;
MODULE_AUTHOR ( " ARM Ltd/Deep Blue Solutions Ltd " ) ;
MODULE_DESCRIPTION ( " ARM AMBA serial port driver " ) ;
MODULE_LICENSE ( " GPL " ) ;