2005-04-17 02:20:36 +04:00
/*
* linux / drivers / serial / pxa . c
*
* Based on drivers / serial / 8250. c by Russell King .
*
* Author : Nicolas Pitre
* Created : Feb 20 , 2003
* Copyright : ( C ) 2003 Monta Vista Software , Inc .
*
* 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 .
*
* Note 1 : This driver is made separate from the already too overloaded
* 8250. c because it needs some kirks of its own and that ' ll make it
* easier to add DMA support .
*
* Note 2 : I ' m too sick of device allocation policies for serial ports .
* If someone else wants to request an " official " allocation of major / minor
* for this driver please be my guest . And don ' t forget that new hardware
* to come from Intel might have more than 3 or 4 of those UARTs . Let ' s
* hope for a better port registration and dynamic device allocation scheme
* with the serial core maintainer satisfaction to appear soon .
*/
# include <linux/config.h>
# if defined(CONFIG_SERIAL_PXA_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/serial_reg.h>
# include <linux/circ_buf.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2005-04-17 02:20:36 +04:00
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/serial_core.h>
# include <asm/io.h>
# include <asm/hardware.h>
# include <asm/irq.h>
# include <asm/arch/pxa-regs.h>
struct uart_pxa_port {
struct uart_port port ;
unsigned char ier ;
unsigned char lcr ;
unsigned char mcr ;
unsigned int lsr_break_flag ;
unsigned int cken ;
char * name ;
} ;
static inline unsigned int serial_in ( struct uart_pxa_port * up , int offset )
{
offset < < = 2 ;
return readl ( up - > port . membase + offset ) ;
}
static inline void serial_out ( struct uart_pxa_port * up , int offset , int value )
{
offset < < = 2 ;
writel ( value , up - > port . membase + offset ) ;
}
static void serial_pxa_enable_ms ( struct uart_port * port )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
up - > ier | = UART_IER_MSI ;
serial_out ( up , UART_IER , up - > ier ) ;
}
2005-08-31 13:12:14 +04:00
static void serial_pxa_stop_tx ( struct uart_port * port )
2005-04-17 02:20:36 +04:00
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
if ( up - > ier & UART_IER_THRI ) {
up - > ier & = ~ UART_IER_THRI ;
serial_out ( up , UART_IER , up - > ier ) ;
}
}
static void serial_pxa_stop_rx ( struct uart_port * port )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
up - > ier & = ~ UART_IER_RLSI ;
up - > port . read_status_mask & = ~ UART_LSR_DR ;
serial_out ( up , UART_IER , up - > ier ) ;
}
static inline void
receive_chars ( struct uart_pxa_port * up , int * status , struct pt_regs * regs )
{
struct tty_struct * tty = up - > port . info - > tty ;
unsigned int ch , flag ;
int max_count = 256 ;
do {
ch = serial_in ( up , UART_RX ) ;
flag = TTY_NORMAL ;
up - > port . icount . rx + + ;
if ( unlikely ( * status & ( UART_LSR_BI | UART_LSR_PE |
UART_LSR_FE | UART_LSR_OE ) ) ) {
/*
* For statistics only
*/
if ( * status & UART_LSR_BI ) {
* status & = ~ ( UART_LSR_FE | UART_LSR_PE ) ;
up - > port . icount . brk + + ;
/*
* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask .
*/
if ( uart_handle_break ( & up - > port ) )
goto ignore_char ;
} else if ( * status & UART_LSR_PE )
up - > port . icount . parity + + ;
else if ( * status & UART_LSR_FE )
up - > port . icount . frame + + ;
if ( * status & UART_LSR_OE )
up - > port . icount . overrun + + ;
/*
* Mask off conditions which should be ignored .
*/
* status & = up - > port . read_status_mask ;
# ifdef CONFIG_SERIAL_PXA_CONSOLE
if ( up - > port . line = = up - > port . cons - > index ) {
/* Recover the break flag from console xmit */
* status | = up - > lsr_break_flag ;
up - > lsr_break_flag = 0 ;
}
# endif
if ( * status & UART_LSR_BI ) {
flag = TTY_BREAK ;
} else if ( * status & UART_LSR_PE )
flag = TTY_PARITY ;
else if ( * status & UART_LSR_FE )
flag = TTY_FRAME ;
}
2005-05-10 02:21:59 +04:00
2005-04-17 02:20:36 +04:00
if ( uart_handle_sysrq_char ( & up - > port , ch , regs ) )
goto ignore_char ;
2005-05-10 02:21:59 +04:00
uart_insert_char ( & up - > port , * status , UART_LSR_OE , ch , flag ) ;
2005-04-17 02:20:36 +04:00
ignore_char :
* status = serial_in ( up , UART_LSR ) ;
} while ( ( * status & UART_LSR_DR ) & & ( max_count - - > 0 ) ) ;
tty_flip_buffer_push ( tty ) ;
}
static void transmit_chars ( struct uart_pxa_port * up )
{
struct circ_buf * xmit = & up - > port . info - > xmit ;
int count ;
if ( up - > port . x_char ) {
serial_out ( up , UART_TX , up - > port . x_char ) ;
up - > port . icount . tx + + ;
up - > port . x_char = 0 ;
return ;
}
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( & up - > port ) ) {
2005-08-31 13:12:14 +04:00
serial_pxa_stop_tx ( & up - > port ) ;
2005-04-17 02:20:36 +04:00
return ;
}
count = up - > port . fifosize / 2 ;
do {
serial_out ( up , UART_TX , xmit - > buf [ xmit - > tail ] ) ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
up - > port . icount . tx + + ;
if ( uart_circ_empty ( xmit ) )
break ;
} while ( - - count > 0 ) ;
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( & up - > port ) ;
if ( uart_circ_empty ( xmit ) )
2005-08-31 13:12:14 +04:00
serial_pxa_stop_tx ( & up - > port ) ;
2005-04-17 02:20:36 +04:00
}
2005-08-31 13:12:14 +04:00
static void serial_pxa_start_tx ( struct uart_port * port )
2005-04-17 02:20:36 +04:00
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
if ( ! ( up - > ier & UART_IER_THRI ) ) {
up - > ier | = UART_IER_THRI ;
serial_out ( up , UART_IER , up - > ier ) ;
}
}
static inline void check_modem_status ( struct uart_pxa_port * up )
{
int status ;
status = serial_in ( up , UART_MSR ) ;
if ( ( status & UART_MSR_ANY_DELTA ) = = 0 )
return ;
if ( status & UART_MSR_TERI )
up - > port . icount . rng + + ;
if ( status & UART_MSR_DDSR )
up - > port . icount . dsr + + ;
if ( status & UART_MSR_DDCD )
uart_handle_dcd_change ( & up - > port , status & UART_MSR_DCD ) ;
if ( status & UART_MSR_DCTS )
uart_handle_cts_change ( & up - > port , status & UART_MSR_CTS ) ;
wake_up_interruptible ( & up - > port . info - > delta_msr_wait ) ;
}
/*
* This handles the interrupt from one port .
*/
static inline irqreturn_t
serial_pxa_irq ( int irq , void * dev_id , struct pt_regs * regs )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) dev_id ;
unsigned int iir , lsr ;
iir = serial_in ( up , UART_IIR ) ;
if ( iir & UART_IIR_NO_INT )
return IRQ_NONE ;
lsr = serial_in ( up , UART_LSR ) ;
if ( lsr & UART_LSR_DR )
receive_chars ( up , & lsr , regs ) ;
check_modem_status ( up ) ;
if ( lsr & UART_LSR_THRE )
transmit_chars ( up ) ;
return IRQ_HANDLED ;
}
static unsigned int serial_pxa_tx_empty ( struct uart_port * port )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
unsigned long flags ;
unsigned int ret ;
spin_lock_irqsave ( & up - > port . lock , flags ) ;
ret = serial_in ( up , UART_LSR ) & UART_LSR_TEMT ? TIOCSER_TEMT : 0 ;
spin_unlock_irqrestore ( & up - > port . lock , flags ) ;
return ret ;
}
static unsigned int serial_pxa_get_mctrl ( struct uart_port * port )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
unsigned char status ;
unsigned int ret ;
status = serial_in ( up , UART_MSR ) ;
ret = 0 ;
if ( status & UART_MSR_DCD )
ret | = TIOCM_CAR ;
if ( status & UART_MSR_RI )
ret | = TIOCM_RNG ;
if ( status & UART_MSR_DSR )
ret | = TIOCM_DSR ;
if ( status & UART_MSR_CTS )
ret | = TIOCM_CTS ;
return ret ;
}
static void serial_pxa_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
unsigned char mcr = 0 ;
if ( mctrl & TIOCM_RTS )
mcr | = UART_MCR_RTS ;
if ( mctrl & TIOCM_DTR )
mcr | = UART_MCR_DTR ;
if ( mctrl & TIOCM_OUT1 )
mcr | = UART_MCR_OUT1 ;
if ( mctrl & TIOCM_OUT2 )
mcr | = UART_MCR_OUT2 ;
if ( mctrl & TIOCM_LOOP )
mcr | = UART_MCR_LOOP ;
mcr | = up - > mcr ;
serial_out ( up , UART_MCR , mcr ) ;
}
static void serial_pxa_break_ctl ( struct uart_port * port , int break_state )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
unsigned long flags ;
spin_lock_irqsave ( & up - > port . lock , flags ) ;
if ( break_state = = - 1 )
up - > lcr | = UART_LCR_SBC ;
else
up - > lcr & = ~ UART_LCR_SBC ;
serial_out ( up , UART_LCR , up - > lcr ) ;
spin_unlock_irqrestore ( & up - > port . lock , flags ) ;
}
#if 0
static void serial_pxa_dma_init ( struct pxa_uart * up )
{
up - > rxdma =
pxa_request_dma ( up - > name , DMA_PRIO_LOW , pxa_receive_dma , up ) ;
if ( up - > rxdma < 0 )
goto out ;
up - > txdma =
pxa_request_dma ( up - > name , DMA_PRIO_LOW , pxa_transmit_dma , up ) ;
if ( up - > txdma < 0 )
goto err_txdma ;
up - > dmadesc = kmalloc ( 4 * sizeof ( pxa_dma_desc ) , GFP_KERNEL ) ;
if ( ! up - > dmadesc )
goto err_alloc ;
/* ... */
err_alloc :
pxa_free_dma ( up - > txdma ) ;
err_rxdma :
pxa_free_dma ( up - > rxdma ) ;
out :
return ;
}
# endif
static int serial_pxa_startup ( struct uart_port * port )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
unsigned long flags ;
int retval ;
2005-10-28 19:25:02 +04:00
if ( port - > line = = 3 ) /* HWUART */
up - > mcr | = UART_MCR_AFE ;
else
2005-12-30 18:57:35 +03:00
up - > mcr = 0 ;
2005-04-17 02:20:36 +04:00
/*
* Allocate the IRQ
*/
retval = request_irq ( up - > port . irq , serial_pxa_irq , 0 , up - > name , up ) ;
if ( retval )
return retval ;
/*
* Clear the FIFO buffers and disable them .
* ( they will be reenabled in set_termios ( ) )
*/
serial_out ( up , UART_FCR , UART_FCR_ENABLE_FIFO ) ;
serial_out ( up , UART_FCR , UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT ) ;
serial_out ( up , UART_FCR , 0 ) ;
/*
* Clear the interrupt registers .
*/
( void ) serial_in ( up , UART_LSR ) ;
( void ) serial_in ( up , UART_RX ) ;
( void ) serial_in ( up , UART_IIR ) ;
( void ) serial_in ( up , UART_MSR ) ;
/*
* Now , initialize the UART
*/
serial_out ( up , UART_LCR , UART_LCR_WLEN8 ) ;
spin_lock_irqsave ( & up - > port . lock , flags ) ;
up - > port . mctrl | = TIOCM_OUT2 ;
serial_pxa_set_mctrl ( & up - > port , up - > port . mctrl ) ;
spin_unlock_irqrestore ( & up - > port . lock , flags ) ;
/*
* Finally , enable interrupts . Note : Modem status interrupts
* are set via set_termios ( ) , which will be occuring imminently
* anyway , so we don ' t enable them here .
*/
up - > ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE ;
serial_out ( up , UART_IER , up - > ier ) ;
/*
* And clear the interrupt registers again for luck .
*/
( void ) serial_in ( up , UART_LSR ) ;
( void ) serial_in ( up , UART_RX ) ;
( void ) serial_in ( up , UART_IIR ) ;
( void ) serial_in ( up , UART_MSR ) ;
return 0 ;
}
static void serial_pxa_shutdown ( struct uart_port * port )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
unsigned long flags ;
free_irq ( up - > port . irq , up ) ;
/*
* Disable interrupts from this port
*/
up - > ier = 0 ;
serial_out ( up , UART_IER , 0 ) ;
spin_lock_irqsave ( & up - > port . lock , flags ) ;
up - > port . mctrl & = ~ TIOCM_OUT2 ;
serial_pxa_set_mctrl ( & up - > port , up - > port . mctrl ) ;
spin_unlock_irqrestore ( & up - > port . lock , flags ) ;
/*
* Disable break condition and FIFOs
*/
serial_out ( up , UART_LCR , serial_in ( up , UART_LCR ) & ~ UART_LCR_SBC ) ;
serial_out ( up , UART_FCR , UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT ) ;
serial_out ( up , UART_FCR , 0 ) ;
}
static void
serial_pxa_set_termios ( struct uart_port * port , struct termios * termios ,
struct termios * old )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
unsigned char cval , fcr = 0 ;
unsigned long flags ;
unsigned int baud , quot ;
switch ( termios - > c_cflag & CSIZE ) {
case CS5 :
2005-06-24 22:48:22 +04:00
cval = UART_LCR_WLEN5 ;
2005-04-17 02:20:36 +04:00
break ;
case CS6 :
2005-06-24 22:48:22 +04:00
cval = UART_LCR_WLEN6 ;
2005-04-17 02:20:36 +04:00
break ;
case CS7 :
2005-06-24 22:48:22 +04:00
cval = UART_LCR_WLEN7 ;
2005-04-17 02:20:36 +04:00
break ;
default :
case CS8 :
2005-06-24 22:48:22 +04:00
cval = UART_LCR_WLEN8 ;
2005-04-17 02:20:36 +04:00
break ;
}
if ( termios - > c_cflag & CSTOPB )
2005-06-24 22:48:22 +04:00
cval | = UART_LCR_STOP ;
2005-04-17 02:20:36 +04:00
if ( termios - > c_cflag & PARENB )
cval | = UART_LCR_PARITY ;
if ( ! ( termios - > c_cflag & PARODD ) )
cval | = UART_LCR_EPAR ;
/*
* Ask the core to calculate the divisor for us .
*/
baud = uart_get_baud_rate ( port , termios , old , 0 , port - > uartclk / 16 ) ;
quot = uart_get_divisor ( port , baud ) ;
if ( ( up - > port . uartclk / quot ) < ( 2400 * 16 ) )
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1 ;
2005-10-28 19:25:02 +04:00
else if ( ( up - > port . uartclk / quot ) < ( 230400 * 16 ) )
2005-04-17 02:20:36 +04:00
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8 ;
2005-10-28 19:25:02 +04:00
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR32 ;
2005-04-17 02:20:36 +04:00
/*
* Ok , we ' re now changing the port state . Do it with
* interrupts disabled .
*/
spin_lock_irqsave ( & up - > port . lock , flags ) ;
/*
* Ensure the port will be enabled .
* This is required especially for serial console .
*/
up - > ier | = IER_UUE ;
/*
* Update the per - port timeout .
*/
2005-10-12 22:58:11 +04:00
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
2005-04-17 02:20:36 +04:00
up - > port . read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR ;
if ( termios - > c_iflag & INPCK )
up - > port . read_status_mask | = UART_LSR_FE | UART_LSR_PE ;
if ( termios - > c_iflag & ( BRKINT | PARMRK ) )
up - > port . read_status_mask | = UART_LSR_BI ;
/*
* Characters to ignore
*/
up - > port . ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNPAR )
up - > port . ignore_status_mask | = UART_LSR_PE | UART_LSR_FE ;
if ( termios - > c_iflag & IGNBRK ) {
up - > port . ignore_status_mask | = UART_LSR_BI ;
/*
* If we ' re ignoring parity and break indicators ,
* ignore overruns too ( for real raw support ) .
*/
if ( termios - > c_iflag & IGNPAR )
up - > port . ignore_status_mask | = UART_LSR_OE ;
}
/*
* ignore all characters if CREAD is not set
*/
if ( ( termios - > c_cflag & CREAD ) = = 0 )
up - > port . ignore_status_mask | = UART_LSR_DR ;
/*
* CTS flow control flag and modem status interrupts
*/
up - > ier & = ~ UART_IER_MSI ;
if ( UART_ENABLE_MS ( & up - > port , termios - > c_cflag ) )
up - > ier | = UART_IER_MSI ;
serial_out ( up , UART_IER , up - > ier ) ;
serial_out ( up , UART_LCR , cval | UART_LCR_DLAB ) ; /* set DLAB */
serial_out ( up , UART_DLL , quot & 0xff ) ; /* LS of divisor */
serial_out ( up , UART_DLM , quot > > 8 ) ; /* MS of divisor */
serial_out ( up , UART_LCR , cval ) ; /* reset DLAB */
up - > lcr = cval ; /* Save LCR */
serial_pxa_set_mctrl ( & up - > port , up - > port . mctrl ) ;
serial_out ( up , UART_FCR , fcr ) ;
spin_unlock_irqrestore ( & up - > port . lock , flags ) ;
}
static void
serial_pxa_pm ( struct uart_port * port , unsigned int state ,
unsigned int oldstate )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
pxa_set_cken ( up - > cken , ! state ) ;
if ( ! state )
udelay ( 1 ) ;
}
static void serial_pxa_release_port ( struct uart_port * port )
{
}
static int serial_pxa_request_port ( struct uart_port * port )
{
return 0 ;
}
static void serial_pxa_config_port ( struct uart_port * port , int flags )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
up - > port . type = PORT_PXA ;
}
static int
serial_pxa_verify_port ( struct uart_port * port , struct serial_struct * ser )
{
/* we don't want the core code to modify any port params */
return - EINVAL ;
}
static const char *
serial_pxa_type ( struct uart_port * port )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
return up - > name ;
}
# ifdef CONFIG_SERIAL_PXA_CONSOLE
2005-09-15 01:36:03 +04:00
static struct uart_pxa_port serial_pxa_ports [ ] ;
static struct uart_driver serial_pxa_reg ;
2005-04-17 02:20:36 +04:00
# define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
/*
* Wait for transmitter & holding register to empty
*/
static inline void wait_for_xmitr ( struct uart_pxa_port * up )
{
unsigned int status , tmout = 10000 ;
/* Wait up to 10ms for the character(s) to be sent. */
do {
status = serial_in ( up , UART_LSR ) ;
if ( status & UART_LSR_BI )
up - > lsr_break_flag = UART_LSR_BI ;
if ( - - tmout = = 0 )
break ;
udelay ( 1 ) ;
} while ( ( status & BOTH_EMPTY ) ! = BOTH_EMPTY ) ;
/* Wait up to 1s for flow control if necessary */
if ( up - > port . flags & UPF_CONS_FLOW ) {
tmout = 1000000 ;
while ( - - tmout & &
( ( serial_in ( up , UART_MSR ) & UART_MSR_CTS ) = = 0 ) )
udelay ( 1 ) ;
}
}
2006-03-20 23:00:09 +03:00
static void serial_pxa_console_putchar ( struct uart_port * port , int ch )
{
struct uart_pxa_port * up = ( struct uart_pxa_port * ) port ;
wait_for_xmitr ( up ) ;
serial_out ( up , UART_TX , ch ) ;
}
2005-04-17 02:20:36 +04:00
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port . . .
*
* The console_lock must be held when we get here .
*/
static void
serial_pxa_console_write ( struct console * co , const char * s , unsigned int count )
{
struct uart_pxa_port * up = & serial_pxa_ports [ co - > index ] ;
unsigned int ier ;
/*
2005-12-30 18:57:35 +03:00
* First save the IER then disable the interrupts
2005-04-17 02:20:36 +04:00
*/
ier = serial_in ( up , UART_IER ) ;
serial_out ( up , UART_IER , UART_IER_UUE ) ;
2006-03-20 23:00:09 +03:00
uart_console_write ( & up - > port , s , count , serial_pxa_console_putchar ) ;
2005-04-17 02:20:36 +04:00
/*
* Finally , wait for transmitter to become empty
* and restore the IER
*/
wait_for_xmitr ( up ) ;
serial_out ( up , UART_IER , ier ) ;
}
static int __init
serial_pxa_console_setup ( struct console * co , char * options )
{
struct uart_pxa_port * up ;
int baud = 9600 ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
if ( co - > index = = - 1 | | co - > index > = serial_pxa_reg . nr )
co - > index = 0 ;
up = & serial_pxa_ports [ co - > index ] ;
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
return uart_set_options ( & up - > port , co , baud , parity , bits , flow ) ;
}
static struct console serial_pxa_console = {
. name = " ttyS " ,
. write = serial_pxa_console_write ,
. device = uart_console_device ,
. setup = serial_pxa_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
. data = & serial_pxa_reg ,
} ;
static int __init
serial_pxa_console_init ( void )
{
register_console ( & serial_pxa_console ) ;
return 0 ;
}
console_initcall ( serial_pxa_console_init ) ;
# define PXA_CONSOLE &serial_pxa_console
# else
# define PXA_CONSOLE NULL
# endif
struct uart_ops serial_pxa_pops = {
. tx_empty = serial_pxa_tx_empty ,
. set_mctrl = serial_pxa_set_mctrl ,
. get_mctrl = serial_pxa_get_mctrl ,
. stop_tx = serial_pxa_stop_tx ,
. start_tx = serial_pxa_start_tx ,
. stop_rx = serial_pxa_stop_rx ,
. enable_ms = serial_pxa_enable_ms ,
. break_ctl = serial_pxa_break_ctl ,
. startup = serial_pxa_startup ,
. shutdown = serial_pxa_shutdown ,
. set_termios = serial_pxa_set_termios ,
. pm = serial_pxa_pm ,
. type = serial_pxa_type ,
. release_port = serial_pxa_release_port ,
. request_port = serial_pxa_request_port ,
. config_port = serial_pxa_config_port ,
. verify_port = serial_pxa_verify_port ,
} ;
static struct uart_pxa_port serial_pxa_ports [ ] = {
{ /* FFUART */
. name = " FFUART " ,
. cken = CKEN6_FFUART ,
. port = {
. type = PORT_PXA ,
. iotype = UPIO_MEM ,
. membase = ( void * ) & FFUART ,
. mapbase = __PREG ( FFUART ) ,
. irq = IRQ_FFUART ,
. uartclk = 921600 * 16 ,
. fifosize = 64 ,
. ops = & serial_pxa_pops ,
. line = 0 ,
} ,
} , { /* BTUART */
. name = " BTUART " ,
. cken = CKEN7_BTUART ,
. port = {
. type = PORT_PXA ,
. iotype = UPIO_MEM ,
. membase = ( void * ) & BTUART ,
. mapbase = __PREG ( BTUART ) ,
. irq = IRQ_BTUART ,
. uartclk = 921600 * 16 ,
. fifosize = 64 ,
. ops = & serial_pxa_pops ,
. line = 1 ,
} ,
} , { /* STUART */
. name = " STUART " ,
. cken = CKEN5_STUART ,
. port = {
. type = PORT_PXA ,
. iotype = UPIO_MEM ,
. membase = ( void * ) & STUART ,
. mapbase = __PREG ( STUART ) ,
. irq = IRQ_STUART ,
. uartclk = 921600 * 16 ,
. fifosize = 64 ,
. ops = & serial_pxa_pops ,
. line = 2 ,
} ,
2005-10-28 19:25:02 +04:00
} , { /* HWUART */
. name = " HWUART " ,
. cken = CKEN4_HWUART ,
. port = {
. type = PORT_PXA ,
. iotype = UPIO_MEM ,
. membase = ( void * ) & HWUART ,
. mapbase = __PREG ( HWUART ) ,
. irq = IRQ_HWUART ,
. uartclk = 921600 * 16 ,
. fifosize = 64 ,
. ops = & serial_pxa_pops ,
. line = 3 ,
} ,
2005-04-17 02:20:36 +04:00
}
} ;
static struct uart_driver serial_pxa_reg = {
. owner = THIS_MODULE ,
. driver_name = " PXA serial " ,
. devfs_name = " tts/ " ,
. dev_name = " ttyS " ,
. major = TTY_MAJOR ,
. minor = 64 ,
. nr = ARRAY_SIZE ( serial_pxa_ports ) ,
. cons = PXA_CONSOLE ,
} ;
2005-11-10 01:32:44 +03:00
static int serial_pxa_suspend ( struct platform_device * dev , pm_message_t state )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct uart_pxa_port * sport = platform_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2005-10-28 20:52:56 +04:00
if ( sport )
2005-04-17 02:20:36 +04:00
uart_suspend_port ( & serial_pxa_reg , & sport - > port ) ;
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int serial_pxa_resume ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct uart_pxa_port * sport = platform_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2005-10-28 20:52:56 +04:00
if ( sport )
2005-04-17 02:20:36 +04:00
uart_resume_port ( & serial_pxa_reg , & sport - > port ) ;
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int serial_pxa_probe ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
serial_pxa_ports [ dev - > id ] . port . dev = & dev - > dev ;
2005-04-17 02:20:36 +04:00
uart_add_one_port ( & serial_pxa_reg , & serial_pxa_ports [ dev - > id ] . port ) ;
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( dev , & serial_pxa_ports [ dev - > id ] ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-10 01:32:44 +03:00
static int serial_pxa_remove ( struct platform_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-10 01:32:44 +03:00
struct uart_pxa_port * sport = platform_get_drvdata ( dev ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 01:32:44 +03:00
platform_set_drvdata ( dev , NULL ) ;
2005-04-17 02:20:36 +04:00
if ( sport )
uart_remove_one_port ( & serial_pxa_reg , & sport - > port ) ;
return 0 ;
}
2005-11-10 01:32:44 +03:00
static struct platform_driver serial_pxa_driver = {
2005-04-17 02:20:36 +04:00
. probe = serial_pxa_probe ,
. remove = serial_pxa_remove ,
. suspend = serial_pxa_suspend ,
. resume = serial_pxa_resume ,
2005-11-10 01:32:44 +03:00
. driver = {
. name = " pxa2xx-uart " ,
} ,
2005-04-17 02:20:36 +04:00
} ;
int __init serial_pxa_init ( void )
{
int ret ;
ret = uart_register_driver ( & serial_pxa_reg ) ;
if ( ret ! = 0 )
return ret ;
2005-11-10 01:32:44 +03:00
ret = platform_driver_register ( & serial_pxa_driver ) ;
2005-04-17 02:20:36 +04:00
if ( ret ! = 0 )
uart_unregister_driver ( & serial_pxa_reg ) ;
return ret ;
}
void __exit serial_pxa_exit ( void )
{
2005-11-10 01:32:44 +03:00
platform_driver_unregister ( & serial_pxa_driver ) ;
2005-04-17 02:20:36 +04:00
uart_unregister_driver ( & serial_pxa_reg ) ;
}
module_init ( serial_pxa_init ) ;
module_exit ( serial_pxa_exit ) ;
MODULE_LICENSE ( " GPL " ) ;