2010-05-05 12:35:23 +04:00
/*
* altera_uart . c - - Altera UART driver
*
* Based on mcf . c - - Freescale ColdFire UART driver
*
* ( C ) Copyright 2003 - 2007 , Greg Ungerer < gerg @ snapgear . com >
* ( C ) Copyright 2008 , Thomas Chou < thomas @ wytron . com . tw >
* ( C ) Copyright 2010 , Tobias Klauser < tklauser @ distanz . ch >
*
* 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/kernel.h>
# include <linux/init.h>
2010-10-01 17:21:33 +04:00
# include <linux/timer.h>
2010-05-05 12:35:23 +04:00
# include <linux/interrupt.h>
# include <linux/module.h>
# include <linux/console.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/serial.h>
# include <linux/serial_core.h>
# include <linux/platform_device.h>
# include <linux/io.h>
# include <linux/altera_uart.h>
# define DRV_NAME "altera_uart"
2010-10-01 18:23:24 +04:00
# define SERIAL_ALTERA_MAJOR 204
# define SERIAL_ALTERA_MINOR 213
2010-05-05 12:35:23 +04:00
/*
* Altera UART register definitions according to the Nios UART datasheet :
* http : //www.altera.com/literature/ds/ds_nios_uart.pdf
*/
# define ALTERA_UART_SIZE 32
# define ALTERA_UART_RXDATA_REG 0
# define ALTERA_UART_TXDATA_REG 4
# define ALTERA_UART_STATUS_REG 8
# define ALTERA_UART_CONTROL_REG 12
# define ALTERA_UART_DIVISOR_REG 16
# define ALTERA_UART_EOP_REG 20
# define ALTERA_UART_STATUS_PE_MSK 0x0001 /* parity error */
# define ALTERA_UART_STATUS_FE_MSK 0x0002 /* framing error */
# define ALTERA_UART_STATUS_BRK_MSK 0x0004 /* break */
# define ALTERA_UART_STATUS_ROE_MSK 0x0008 /* RX overrun error */
# define ALTERA_UART_STATUS_TOE_MSK 0x0010 /* TX overrun error */
# define ALTERA_UART_STATUS_TMT_MSK 0x0020 /* TX shift register state */
# define ALTERA_UART_STATUS_TRDY_MSK 0x0040 /* TX ready */
# define ALTERA_UART_STATUS_RRDY_MSK 0x0080 /* RX ready */
# define ALTERA_UART_STATUS_E_MSK 0x0100 /* exception condition */
# define ALTERA_UART_STATUS_DCTS_MSK 0x0400 /* CTS logic-level change */
# define ALTERA_UART_STATUS_CTS_MSK 0x0800 /* CTS logic state */
# define ALTERA_UART_STATUS_EOP_MSK 0x1000 /* EOP written/read */
/* Enable interrupt on... */
# define ALTERA_UART_CONTROL_PE_MSK 0x0001 /* ...parity error */
# define ALTERA_UART_CONTROL_FE_MSK 0x0002 /* ...framing error */
# define ALTERA_UART_CONTROL_BRK_MSK 0x0004 /* ...break */
# define ALTERA_UART_CONTROL_ROE_MSK 0x0008 /* ...RX overrun */
# define ALTERA_UART_CONTROL_TOE_MSK 0x0010 /* ...TX overrun */
# define ALTERA_UART_CONTROL_TMT_MSK 0x0020 /* ...TX shift register empty */
# define ALTERA_UART_CONTROL_TRDY_MSK 0x0040 /* ...TX ready */
# define ALTERA_UART_CONTROL_RRDY_MSK 0x0080 /* ...RX ready */
# define ALTERA_UART_CONTROL_E_MSK 0x0100 /* ...exception*/
# define ALTERA_UART_CONTROL_TRBK_MSK 0x0200 /* TX break */
# define ALTERA_UART_CONTROL_DCTS_MSK 0x0400 /* Interrupt on CTS change */
# define ALTERA_UART_CONTROL_RTS_MSK 0x0800 /* RTS signal */
# define ALTERA_UART_CONTROL_EOP_MSK 0x1000 /* Interrupt on EOP */
/*
* Local per - uart structure .
*/
struct altera_uart {
struct uart_port port ;
2010-10-01 17:21:33 +04:00
struct timer_list tmr ;
2010-05-05 12:35:23 +04:00
unsigned int sigs ; /* Local copy of line sigs */
unsigned short imr ; /* Local IMR mirror */
} ;
2010-10-01 17:21:54 +04:00
static u32 altera_uart_readl ( struct uart_port * port , int reg )
{
struct altera_uart_platform_uart * platp = port - > private_data ;
return readl ( port - > membase + ( reg < < platp - > bus_shift ) ) ;
}
static void altera_uart_writel ( struct uart_port * port , u32 dat , int reg )
{
struct altera_uart_platform_uart * platp = port - > private_data ;
writel ( dat , port - > membase + ( reg < < platp - > bus_shift ) ) ;
}
2010-05-05 12:35:23 +04:00
static unsigned int altera_uart_tx_empty ( struct uart_port * port )
{
2010-10-01 17:21:54 +04:00
return ( altera_uart_readl ( port , ALTERA_UART_STATUS_REG ) &
2010-05-05 12:35:23 +04:00
ALTERA_UART_STATUS_TMT_MSK ) ? TIOCSER_TEMT : 0 ;
}
static unsigned int altera_uart_get_mctrl ( struct uart_port * port )
{
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
unsigned int sigs ;
2010-10-01 17:21:54 +04:00
sigs = ( altera_uart_readl ( port , ALTERA_UART_STATUS_REG ) &
2010-05-05 12:35:23 +04:00
ALTERA_UART_STATUS_CTS_MSK ) ? TIOCM_CTS : 0 ;
sigs | = ( pp - > sigs & TIOCM_RTS ) ;
return sigs ;
}
static void altera_uart_set_mctrl ( struct uart_port * port , unsigned int sigs )
{
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
pp - > sigs = sigs ;
if ( sigs & TIOCM_RTS )
pp - > imr | = ALTERA_UART_CONTROL_RTS_MSK ;
else
pp - > imr & = ~ ALTERA_UART_CONTROL_RTS_MSK ;
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , pp - > imr , ALTERA_UART_CONTROL_REG ) ;
2010-05-05 12:35:23 +04:00
}
static void altera_uart_start_tx ( struct uart_port * port )
{
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
pp - > imr | = ALTERA_UART_CONTROL_TRDY_MSK ;
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , pp - > imr , ALTERA_UART_CONTROL_REG ) ;
2010-05-05 12:35:23 +04:00
}
static void altera_uart_stop_tx ( struct uart_port * port )
{
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
pp - > imr & = ~ ALTERA_UART_CONTROL_TRDY_MSK ;
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , pp - > imr , ALTERA_UART_CONTROL_REG ) ;
2010-05-05 12:35:23 +04:00
}
static void altera_uart_stop_rx ( struct uart_port * port )
{
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
pp - > imr & = ~ ALTERA_UART_CONTROL_RRDY_MSK ;
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , pp - > imr , ALTERA_UART_CONTROL_REG ) ;
2010-05-05 12:35:23 +04:00
}
static void altera_uart_break_ctl ( struct uart_port * port , int break_state )
{
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
if ( break_state = = - 1 )
pp - > imr | = ALTERA_UART_CONTROL_TRBK_MSK ;
else
pp - > imr & = ~ ALTERA_UART_CONTROL_TRBK_MSK ;
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , pp - > imr , ALTERA_UART_CONTROL_REG ) ;
2010-05-05 12:35:23 +04:00
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
static void altera_uart_enable_ms ( struct uart_port * port )
{
}
static void altera_uart_set_termios ( struct uart_port * port ,
struct ktermios * termios ,
struct ktermios * old )
{
unsigned long flags ;
unsigned int baud , baudclk ;
baud = uart_get_baud_rate ( port , termios , old , 0 , 4000000 ) ;
baudclk = port - > uartclk / baud ;
if ( old )
tty_termios_copy_hw ( termios , old ) ;
tty_termios_encode_baud_rate ( termios , baud , baud ) ;
spin_lock_irqsave ( & port - > lock , flags ) ;
2010-10-01 17:21:33 +04:00
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , baudclk , ALTERA_UART_DIVISOR_REG ) ;
2010-05-05 12:35:23 +04:00
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
static void altera_uart_rx_chars ( struct altera_uart * pp )
{
struct uart_port * port = & pp - > port ;
unsigned char ch , flag ;
unsigned short status ;
2010-10-01 17:21:54 +04:00
while ( ( status = altera_uart_readl ( port , ALTERA_UART_STATUS_REG ) ) &
2010-05-05 12:35:23 +04:00
ALTERA_UART_STATUS_RRDY_MSK ) {
2010-10-01 17:21:54 +04:00
ch = altera_uart_readl ( port , ALTERA_UART_RXDATA_REG ) ;
2010-05-05 12:35:23 +04:00
flag = TTY_NORMAL ;
port - > icount . rx + + ;
if ( status & ALTERA_UART_STATUS_E_MSK ) {
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , status ,
ALTERA_UART_STATUS_REG ) ;
2010-05-05 12:35:23 +04:00
if ( status & ALTERA_UART_STATUS_BRK_MSK ) {
port - > icount . brk + + ;
if ( uart_handle_break ( port ) )
continue ;
} else if ( status & ALTERA_UART_STATUS_PE_MSK ) {
port - > icount . parity + + ;
} else if ( status & ALTERA_UART_STATUS_ROE_MSK ) {
port - > icount . overrun + + ;
} else if ( status & ALTERA_UART_STATUS_FE_MSK ) {
port - > icount . frame + + ;
}
status & = port - > read_status_mask ;
if ( status & ALTERA_UART_STATUS_BRK_MSK )
flag = TTY_BREAK ;
else if ( status & ALTERA_UART_STATUS_PE_MSK )
flag = TTY_PARITY ;
else if ( status & ALTERA_UART_STATUS_FE_MSK )
flag = TTY_FRAME ;
}
if ( uart_handle_sysrq_char ( port , ch ) )
continue ;
uart_insert_char ( port , status , ALTERA_UART_STATUS_ROE_MSK , ch ,
flag ) ;
}
tty_flip_buffer_push ( port - > state - > port . tty ) ;
}
static void altera_uart_tx_chars ( struct altera_uart * pp )
{
struct uart_port * port = & pp - > port ;
struct circ_buf * xmit = & port - > state - > xmit ;
if ( port - > x_char ) {
/* Send special char - probably flow control */
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , port - > x_char , ALTERA_UART_TXDATA_REG ) ;
2010-05-05 12:35:23 +04:00
port - > x_char = 0 ;
port - > icount . tx + + ;
return ;
}
2010-10-01 17:21:54 +04:00
while ( altera_uart_readl ( port , ALTERA_UART_STATUS_REG ) &
2010-05-05 12:35:23 +04:00
ALTERA_UART_STATUS_TRDY_MSK ) {
if ( xmit - > head = = xmit - > tail )
break ;
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , xmit - > buf [ xmit - > tail ] ,
ALTERA_UART_TXDATA_REG ) ;
2010-05-05 12:35:23 +04:00
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
port - > icount . tx + + ;
}
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( port ) ;
if ( xmit - > head = = xmit - > tail ) {
pp - > imr & = ~ ALTERA_UART_CONTROL_TRDY_MSK ;
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , pp - > imr , ALTERA_UART_CONTROL_REG ) ;
2010-05-05 12:35:23 +04:00
}
}
static irqreturn_t altera_uart_interrupt ( int irq , void * data )
{
struct uart_port * port = data ;
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
unsigned int isr ;
2010-10-01 17:21:54 +04:00
isr = altera_uart_readl ( port , ALTERA_UART_STATUS_REG ) & pp - > imr ;
2010-05-25 18:59:55 +04:00
spin_lock ( & port - > lock ) ;
2010-05-05 12:35:23 +04:00
if ( isr & ALTERA_UART_STATUS_RRDY_MSK )
altera_uart_rx_chars ( pp ) ;
if ( isr & ALTERA_UART_STATUS_TRDY_MSK )
altera_uart_tx_chars ( pp ) ;
2010-05-25 18:59:55 +04:00
spin_unlock ( & port - > lock ) ;
2010-05-05 12:35:23 +04:00
return IRQ_RETVAL ( isr ) ;
}
2010-10-01 17:21:33 +04:00
static void altera_uart_timer ( unsigned long data )
{
struct uart_port * port = ( void * ) data ;
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
altera_uart_interrupt ( 0 , port ) ;
mod_timer ( & pp - > tmr , jiffies + uart_poll_timeout ( port ) ) ;
}
2010-05-05 12:35:23 +04:00
static void altera_uart_config_port ( struct uart_port * port , int flags )
{
port - > type = PORT_ALTERA_UART ;
/* Clear mask, so no surprise interrupts. */
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , 0 , ALTERA_UART_CONTROL_REG ) ;
2010-05-05 12:35:23 +04:00
/* Clear status register */
2010-10-01 17:21:54 +04:00
altera_uart_writel ( port , 0 , ALTERA_UART_STATUS_REG ) ;
2010-05-05 12:35:23 +04:00
}
static int altera_uart_startup ( struct uart_port * port )
{
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
unsigned long flags ;
int ret ;
2010-10-01 17:21:33 +04:00
if ( ! port - > irq ) {
setup_timer ( & pp - > tmr , altera_uart_timer , ( unsigned long ) port ) ;
mod_timer ( & pp - > tmr , jiffies + uart_poll_timeout ( port ) ) ;
return 0 ;
}
2010-05-05 12:35:23 +04:00
ret = request_irq ( port - > irq , altera_uart_interrupt , IRQF_DISABLED ,
DRV_NAME , port ) ;
if ( ret ) {
pr_err ( DRV_NAME " : unable to attach Altera UART %d "
" interrupt vector=%d \n " , port - > line , port - > irq ) ;
return ret ;
}
spin_lock_irqsave ( & port - > lock , flags ) ;
/* Enable RX interrupts now */
pp - > imr = ALTERA_UART_CONTROL_RRDY_MSK ;
writel ( pp - > imr , port - > membase + ALTERA_UART_CONTROL_REG ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
return 0 ;
}
static void altera_uart_shutdown ( struct uart_port * port )
{
struct altera_uart * pp = container_of ( port , struct altera_uart , port ) ;
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
/* Disable all interrupts now */
pp - > imr = 0 ;
writel ( pp - > imr , port - > membase + ALTERA_UART_CONTROL_REG ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2010-10-01 17:21:33 +04:00
if ( port - > irq )
free_irq ( port - > irq , port ) ;
else
del_timer_sync ( & pp - > tmr ) ;
2010-05-05 12:35:23 +04:00
}
static const char * altera_uart_type ( struct uart_port * port )
{
return ( port - > type = = PORT_ALTERA_UART ) ? " Altera UART " : NULL ;
}
static int altera_uart_request_port ( struct uart_port * port )
{
/* UARTs always present */
return 0 ;
}
static void altera_uart_release_port ( struct uart_port * port )
{
/* Nothing to release... */
}
static int altera_uart_verify_port ( struct uart_port * port ,
struct serial_struct * ser )
{
if ( ( ser - > type ! = PORT_UNKNOWN ) & & ( ser - > type ! = PORT_ALTERA_UART ) )
return - EINVAL ;
return 0 ;
}
/*
* Define the basic serial functions we support .
*/
static struct uart_ops altera_uart_ops = {
. tx_empty = altera_uart_tx_empty ,
. get_mctrl = altera_uart_get_mctrl ,
. set_mctrl = altera_uart_set_mctrl ,
. start_tx = altera_uart_start_tx ,
. stop_tx = altera_uart_stop_tx ,
. stop_rx = altera_uart_stop_rx ,
. enable_ms = altera_uart_enable_ms ,
. break_ctl = altera_uart_break_ctl ,
. startup = altera_uart_startup ,
. shutdown = altera_uart_shutdown ,
. set_termios = altera_uart_set_termios ,
. type = altera_uart_type ,
. request_port = altera_uart_request_port ,
. release_port = altera_uart_release_port ,
. config_port = altera_uart_config_port ,
. verify_port = altera_uart_verify_port ,
} ;
static struct altera_uart altera_uart_ports [ CONFIG_SERIAL_ALTERA_UART_MAXPORTS ] ;
# if defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE)
int __init early_altera_uart_setup ( struct altera_uart_platform_uart * platp )
{
struct uart_port * port ;
int i ;
for ( i = 0 ; i < CONFIG_SERIAL_ALTERA_UART_MAXPORTS & & platp [ i ] . mapbase ; i + + ) {
port = & altera_uart_ports [ i ] . port ;
port - > line = i ;
port - > type = PORT_ALTERA_UART ;
port - > mapbase = platp [ i ] . mapbase ;
port - > membase = ioremap ( port - > mapbase , ALTERA_UART_SIZE ) ;
port - > iotype = SERIAL_IO_MEM ;
port - > irq = platp [ i ] . irq ;
port - > uartclk = platp [ i ] . uartclk ;
2010-10-01 17:22:37 +04:00
port - > flags = UPF_BOOT_AUTOCONF ;
2010-05-05 12:35:23 +04:00
port - > ops = & altera_uart_ops ;
2010-10-01 17:21:54 +04:00
port - > private_data = platp ;
2010-05-05 12:35:23 +04:00
}
return 0 ;
}
2010-05-25 19:00:08 +04:00
static void altera_uart_console_putc ( struct uart_port * port , const char c )
2010-05-05 12:35:23 +04:00
{
2010-10-01 17:21:54 +04:00
while ( ! ( altera_uart_readl ( port , ALTERA_UART_STATUS_REG ) &
2010-07-21 02:26:10 +04:00
ALTERA_UART_STATUS_TRDY_MSK ) )
2010-05-25 19:00:08 +04:00
cpu_relax ( ) ;
2010-05-05 12:35:23 +04:00
writel ( c , port - > membase + ALTERA_UART_TXDATA_REG ) ;
}
static void altera_uart_console_write ( struct console * co , const char * s ,
unsigned int count )
{
2010-05-25 19:00:08 +04:00
struct uart_port * port = & ( altera_uart_ports + co - > index ) - > port ;
2010-05-05 12:35:23 +04:00
for ( ; count ; count - - , s + + ) {
2010-05-25 19:00:08 +04:00
altera_uart_console_putc ( port , * s ) ;
2010-05-05 12:35:23 +04:00
if ( * s = = ' \n ' )
2010-05-25 19:00:08 +04:00
altera_uart_console_putc ( port , ' \r ' ) ;
2010-05-05 12:35:23 +04:00
}
}
static int __init altera_uart_console_setup ( struct console * co , char * options )
{
struct uart_port * port ;
int baud = CONFIG_SERIAL_ALTERA_UART_BAUDRATE ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
if ( co - > index < 0 | | co - > index > = CONFIG_SERIAL_ALTERA_UART_MAXPORTS )
return - EINVAL ;
port = & altera_uart_ports [ co - > index ] . port ;
2010-10-01 17:23:13 +04:00
if ( ! port - > membase )
2010-05-05 12:35:23 +04:00
return - ENODEV ;
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
return uart_set_options ( port , co , baud , parity , bits , flow ) ;
}
static struct uart_driver altera_uart_driver ;
static struct console altera_uart_console = {
2010-10-01 18:23:24 +04:00
. name = " ttyAL " ,
2010-05-05 12:35:23 +04:00
. write = altera_uart_console_write ,
. device = uart_console_device ,
. setup = altera_uart_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
. data = & altera_uart_driver ,
} ;
static int __init altera_uart_console_init ( void )
{
register_console ( & altera_uart_console ) ;
return 0 ;
}
console_initcall ( altera_uart_console_init ) ;
# define ALTERA_UART_CONSOLE (&altera_uart_console)
# else
# define ALTERA_UART_CONSOLE NULL
# endif /* CONFIG_ALTERA_UART_CONSOLE */
/*
* Define the altera_uart UART driver structure .
*/
static struct uart_driver altera_uart_driver = {
. owner = THIS_MODULE ,
. driver_name = DRV_NAME ,
2010-10-01 18:23:24 +04:00
. dev_name = " ttyAL " ,
. major = SERIAL_ALTERA_MAJOR ,
. minor = SERIAL_ALTERA_MINOR ,
2010-05-05 12:35:23 +04:00
. nr = CONFIG_SERIAL_ALTERA_UART_MAXPORTS ,
. cons = ALTERA_UART_CONSOLE ,
} ;
static int __devinit altera_uart_probe ( struct platform_device * pdev )
{
struct altera_uart_platform_uart * platp = pdev - > dev . platform_data ;
struct uart_port * port ;
2010-10-01 17:21:42 +04:00
struct resource * res_mem ;
struct resource * res_irq ;
int i = pdev - > id ;
2010-05-05 12:35:23 +04:00
2010-10-01 17:21:42 +04:00
/* -1 emphasizes that the platform must have one port, no .N suffix */
if ( i = = - 1 )
i = 0 ;
2010-05-05 12:35:23 +04:00
2010-10-01 17:21:42 +04:00
if ( i > = CONFIG_SERIAL_ALTERA_UART_MAXPORTS )
return - EINVAL ;
2010-05-05 12:35:23 +04:00
2010-10-01 17:21:42 +04:00
port = & altera_uart_ports [ i ] . port ;
res_mem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res_mem )
port - > mapbase = res_mem - > start ;
else if ( platp - > mapbase )
port - > mapbase = platp - > mapbase ;
else
return - EINVAL ;
res_irq = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( res_irq )
port - > irq = res_irq - > start ;
else if ( platp - > irq )
port - > irq = platp - > irq ;
port - > membase = ioremap ( port - > mapbase , ALTERA_UART_SIZE ) ;
if ( ! port - > membase )
return - ENOMEM ;
port - > line = i ;
port - > type = PORT_ALTERA_UART ;
port - > iotype = SERIAL_IO_MEM ;
port - > uartclk = platp - > uartclk ;
port - > ops = & altera_uart_ops ;
2010-10-01 17:22:37 +04:00
port - > flags = UPF_BOOT_AUTOCONF ;
2010-10-01 17:21:54 +04:00
port - > private_data = platp ;
2010-10-01 17:21:42 +04:00
uart_add_one_port ( & altera_uart_driver , port ) ;
2010-05-05 12:35:23 +04:00
return 0 ;
}
2010-05-31 17:01:08 +04:00
static int __devexit altera_uart_remove ( struct platform_device * pdev )
2010-05-05 12:35:23 +04:00
{
2010-10-01 17:21:42 +04:00
struct uart_port * port = & altera_uart_ports [ pdev - > id ] . port ;
2010-05-05 12:35:23 +04:00
2010-10-01 17:21:42 +04:00
uart_remove_one_port ( & altera_uart_driver , port ) ;
2010-05-05 12:35:23 +04:00
return 0 ;
}
static struct platform_driver altera_uart_platform_driver = {
. probe = altera_uart_probe ,
. remove = __devexit_p ( altera_uart_remove ) ,
. driver = {
. name = DRV_NAME ,
. owner = THIS_MODULE ,
. pm = NULL ,
} ,
} ;
static int __init altera_uart_init ( void )
{
int rc ;
rc = uart_register_driver ( & altera_uart_driver ) ;
if ( rc )
return rc ;
rc = platform_driver_register ( & altera_uart_platform_driver ) ;
if ( rc ) {
uart_unregister_driver ( & altera_uart_driver ) ;
return rc ;
}
return 0 ;
}
static void __exit altera_uart_exit ( void )
{
platform_driver_unregister ( & altera_uart_platform_driver ) ;
uart_unregister_driver ( & altera_uart_driver ) ;
}
module_init ( altera_uart_init ) ;
module_exit ( altera_uart_exit ) ;
MODULE_DESCRIPTION ( " Altera UART driver " ) ;
MODULE_AUTHOR ( " Thomas Chou <thomas@wytron.com.tw> " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;
2010-10-01 18:23:24 +04:00
MODULE_ALIAS_CHARDEV_MAJOR ( SERIAL_ALTERA_MAJOR ) ;