2006-01-10 19:59:27 +03:00
/*
* linux / drivers / char / at91_serial . c
*
2006-10-04 18:02:05 +04:00
* Driver for Atmel AT91 / AT32 Serial ports
2006-01-10 19:59:27 +03:00
* Copyright ( C ) 2003 Rick Bronson
*
* Based on drivers / char / serial_sa1100 . c , by Deep Blue Solutions Ltd .
* Based on drivers / char / serial . c , by Linus Torvalds , Theodore Ts ' o .
*
* 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
*
*/
# include <linux/module.h>
# include <linux/tty.h>
# include <linux/ioport.h>
# include <linux/slab.h>
# include <linux/init.h>
# include <linux/serial.h>
2006-06-19 22:53:19 +04:00
# include <linux/clk.h>
2006-01-10 19:59:27 +03:00
# include <linux/console.h>
# include <linux/sysrq.h>
# include <linux/tty_flip.h>
2006-06-19 22:53:19 +04:00
# include <linux/platform_device.h>
2006-01-10 19:59:27 +03:00
# include <asm/io.h>
2006-06-19 22:53:19 +04:00
# include <asm/arch/at91rm9200_pdc.h>
# include <asm/mach/serial_at91.h>
2006-01-10 19:59:27 +03:00
# include <asm/arch/board.h>
2006-06-19 22:53:19 +04:00
# include <asm/arch/system.h>
2006-07-05 17:31:13 +04:00
# include <asm/arch/gpio.h>
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:00 +04:00
# include "atmel_serial.h"
2006-10-04 18:02:02 +04:00
# if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
2006-01-10 19:59:27 +03:00
# define SUPPORT_SYSRQ
# endif
# include <linux/serial_core.h>
2006-10-04 18:02:02 +04:00
# ifdef CONFIG_SERIAL_ATMEL_TTYAT
2006-01-10 19:59:27 +03:00
/* Use device name ttyAT, major 204 and minor 154-169. This is necessary if we
* should coexist with the 8250 driver , such as if we have an external 16 C550
* UART . */
2006-10-04 18:02:05 +04:00
# define SERIAL_ATMEL_MAJOR 204
2006-01-10 19:59:27 +03:00
# define MINOR_START 154
2006-10-04 18:02:05 +04:00
# define ATMEL_DEVICENAME "ttyAT"
2006-01-10 19:59:27 +03:00
# else
/* Use device name ttyS, major 4, minor 64-68. This is the usual serial port
* name , but it is legally reserved for the 8250 driver . */
2006-10-04 18:02:05 +04:00
# define SERIAL_ATMEL_MAJOR TTY_MAJOR
2006-01-10 19:59:27 +03:00
# define MINOR_START 64
2006-10-04 18:02:05 +04:00
# define ATMEL_DEVICENAME "ttyS"
2006-01-10 19:59:27 +03:00
# endif
2006-10-04 18:02:05 +04:00
# define ATMEL_ISR_PASS_LIMIT 256
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
# define UART_PUT_CR(port,v) writel(v, (port)->membase + ATMEL_US_CR)
# define UART_GET_MR(port) readl((port)->membase + ATMEL_US_MR)
# define UART_PUT_MR(port,v) writel(v, (port)->membase + ATMEL_US_MR)
# define UART_PUT_IER(port,v) writel(v, (port)->membase + ATMEL_US_IER)
# define UART_PUT_IDR(port,v) writel(v, (port)->membase + ATMEL_US_IDR)
# define UART_GET_IMR(port) readl((port)->membase + ATMEL_US_IMR)
# define UART_GET_CSR(port) readl((port)->membase + ATMEL_US_CSR)
# define UART_GET_CHAR(port) readl((port)->membase + ATMEL_US_RHR)
# define UART_PUT_CHAR(port,v) writel(v, (port)->membase + ATMEL_US_THR)
# define UART_GET_BRGR(port) readl((port)->membase + ATMEL_US_BRGR)
# define UART_PUT_BRGR(port,v) writel(v, (port)->membase + ATMEL_US_BRGR)
# define UART_PUT_RTOR(port,v) writel(v, (port)->membase + ATMEL_US_RTOR)
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
// #define UART_GET_CR(port) readl((port)->membase + ATMEL_US_CR) // is write-only
2006-01-10 19:59:27 +03:00
/* PDC registers */
2006-10-04 18:02:05 +04:00
# define UART_PUT_PTCR(port,v) writel(v, (port)->membase + ATMEL_PDC_PTCR)
# define UART_GET_PTSR(port) readl((port)->membase + ATMEL_PDC_PTSR)
2006-06-19 22:53:19 +04:00
2006-10-04 18:02:05 +04:00
# define UART_PUT_RPR(port,v) writel(v, (port)->membase + ATMEL_PDC_RPR)
# define UART_GET_RPR(port) readl((port)->membase + ATMEL_PDC_RPR)
# define UART_PUT_RCR(port,v) writel(v, (port)->membase + ATMEL_PDC_RCR)
# define UART_PUT_RNPR(port,v) writel(v, (port)->membase + ATMEL_PDC_RNPR)
# define UART_PUT_RNCR(port,v) writel(v, (port)->membase + ATMEL_PDC_RNCR)
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
# define UART_PUT_TPR(port,v) writel(v, (port)->membase + ATMEL_PDC_TPR)
# define UART_PUT_TCR(port,v) writel(v, (port)->membase + ATMEL_PDC_TCR)
//#define UART_PUT_TNPR(port,v) writel(v, (port)->membase + ATMEL_PDC_TNPR)
//#define UART_PUT_TNCR(port,v) writel(v, (port)->membase + ATMEL_PDC_TNCR)
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:07 +04:00
static int ( * atmel_open_hook ) ( struct uart_port * ) ;
static void ( * atmel_close_hook ) ( struct uart_port * ) ;
2006-01-10 19:59:27 +03:00
2006-06-19 22:53:19 +04:00
/*
* We wrap our port structure around the generic uart_port .
*/
2006-10-04 18:02:05 +04:00
struct atmel_uart_port {
2006-06-19 22:53:19 +04:00
struct uart_port uart ; /* uart */
struct clk * clk ; /* uart clock */
unsigned short suspended ; /* is port suspended? */
} ;
2006-10-04 18:02:05 +04:00
static struct atmel_uart_port atmel_ports [ ATMEL_MAX_UART ] ;
2006-06-19 22:53:19 +04:00
2006-01-10 19:59:27 +03:00
# ifdef SUPPORT_SYSRQ
2006-10-04 18:02:05 +04:00
static struct console atmel_console ;
2006-01-10 19:59:27 +03:00
# endif
/*
* Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty .
*/
2006-10-04 18:02:05 +04:00
static u_int atmel_tx_empty ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
return ( UART_GET_CSR ( port ) & ATMEL_US_TXEMPTY ) ? TIOCSER_TEMT : 0 ;
2006-01-10 19:59:27 +03:00
}
/*
* Set state of the modem control output lines
*/
2006-10-04 18:02:05 +04:00
static void atmel_set_mctrl ( struct uart_port * port , u_int mctrl )
2006-01-10 19:59:27 +03:00
{
unsigned int control = 0 ;
2006-06-19 22:53:19 +04:00
unsigned int mode ;
2006-01-10 19:59:27 +03:00
2006-06-19 22:53:19 +04:00
if ( arch_identify ( ) = = ARCH_ID_AT91RM9200 ) {
/*
* AT91RM9200 Errata # 39 : RTS0 is not internally connected to PA21 .
* We need to drive the pin manually .
*/
2006-09-27 12:44:11 +04:00
if ( port - > mapbase = = AT91RM9200_BASE_US0 ) {
2006-06-19 22:53:19 +04:00
if ( mctrl & TIOCM_RTS )
2006-07-05 17:31:13 +04:00
at91_set_gpio_value ( AT91_PIN_PA21 , 0 ) ;
2006-06-19 22:53:19 +04:00
else
2006-07-05 17:31:13 +04:00
at91_set_gpio_value ( AT91_PIN_PA21 , 1 ) ;
2006-06-19 22:53:19 +04:00
}
2006-01-10 19:59:27 +03:00
}
if ( mctrl & TIOCM_RTS )
2006-10-04 18:02:05 +04:00
control | = ATMEL_US_RTSEN ;
2006-01-10 19:59:27 +03:00
else
2006-10-04 18:02:05 +04:00
control | = ATMEL_US_RTSDIS ;
2006-01-10 19:59:27 +03:00
if ( mctrl & TIOCM_DTR )
2006-10-04 18:02:05 +04:00
control | = ATMEL_US_DTREN ;
2006-01-10 19:59:27 +03:00
else
2006-10-04 18:02:05 +04:00
control | = ATMEL_US_DTRDIS ;
2006-01-10 19:59:27 +03:00
2006-06-19 22:53:19 +04:00
UART_PUT_CR ( port , control ) ;
/* Local loopback mode? */
2006-10-04 18:02:05 +04:00
mode = UART_GET_MR ( port ) & ~ ATMEL_US_CHMODE ;
2006-06-19 22:53:19 +04:00
if ( mctrl & TIOCM_LOOP )
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_CHMODE_LOC_LOOP ;
2006-06-19 22:53:19 +04:00
else
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_CHMODE_NORMAL ;
2006-06-19 22:53:19 +04:00
UART_PUT_MR ( port , mode ) ;
2006-01-10 19:59:27 +03:00
}
/*
* Get state of the modem control input lines
*/
2006-10-04 18:02:05 +04:00
static u_int atmel_get_mctrl ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
unsigned int status , ret = 0 ;
status = UART_GET_CSR ( port ) ;
/*
* The control signals are active low .
*/
2006-10-04 18:02:05 +04:00
if ( ! ( status & ATMEL_US_DCD ) )
2006-01-10 19:59:27 +03:00
ret | = TIOCM_CD ;
2006-10-04 18:02:05 +04:00
if ( ! ( status & ATMEL_US_CTS ) )
2006-01-10 19:59:27 +03:00
ret | = TIOCM_CTS ;
2006-10-04 18:02:05 +04:00
if ( ! ( status & ATMEL_US_DSR ) )
2006-01-10 19:59:27 +03:00
ret | = TIOCM_DSR ;
2006-10-04 18:02:05 +04:00
if ( ! ( status & ATMEL_US_RI ) )
2006-01-10 19:59:27 +03:00
ret | = TIOCM_RI ;
return ret ;
}
/*
* Stop transmitting .
*/
2006-10-04 18:02:05 +04:00
static void atmel_stop_tx ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-06-19 22:53:19 +04:00
2006-10-04 18:02:05 +04:00
UART_PUT_IDR ( port , ATMEL_US_TXRDY ) ;
2006-01-10 19:59:27 +03:00
}
/*
* Start transmitting .
*/
2006-10-04 18:02:05 +04:00
static void atmel_start_tx ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-06-19 22:53:19 +04:00
2006-10-04 18:02:05 +04:00
UART_PUT_IER ( port , ATMEL_US_TXRDY ) ;
2006-01-10 19:59:27 +03:00
}
/*
* Stop receiving - port is in process of being closed .
*/
2006-10-04 18:02:05 +04:00
static void atmel_stop_rx ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-06-19 22:53:19 +04:00
2006-10-04 18:02:05 +04:00
UART_PUT_IDR ( port , ATMEL_US_RXRDY ) ;
2006-01-10 19:59:27 +03:00
}
/*
* Enable modem status interrupts
*/
2006-10-04 18:02:05 +04:00
static void atmel_enable_ms ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
UART_PUT_IER ( port , ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC ) ;
2006-01-10 19:59:27 +03:00
}
/*
* Control the transmission of a break signal
*/
2006-10-04 18:02:05 +04:00
static void atmel_break_ctl ( struct uart_port * port , int break_state )
2006-01-10 19:59:27 +03:00
{
if ( break_state ! = 0 )
2006-10-04 18:02:05 +04:00
UART_PUT_CR ( port , ATMEL_US_STTBRK ) ; /* start break */
2006-01-10 19:59:27 +03:00
else
2006-10-04 18:02:05 +04:00
UART_PUT_CR ( port , ATMEL_US_STPBRK ) ; /* stop break */
2006-01-10 19:59:27 +03:00
}
/*
* Characters received ( called from interrupt handler )
*/
2006-10-04 18:02:05 +04:00
static void atmel_rx_chars ( struct uart_port * port , struct pt_regs * regs )
2006-01-10 19:59:27 +03:00
{
struct tty_struct * tty = port - > info - > tty ;
unsigned int status , ch , flg ;
2006-06-19 22:53:19 +04:00
status = UART_GET_CSR ( port ) ;
2006-10-04 18:02:05 +04:00
while ( status & ATMEL_US_RXRDY ) {
2006-01-10 19:59:27 +03:00
ch = UART_GET_CHAR ( port ) ;
port - > icount . rx + + ;
flg = TTY_NORMAL ;
/*
* note that the error handling code is
* out of the main execution path
*/
2006-10-04 18:02:05 +04:00
if ( unlikely ( status & ( ATMEL_US_PARE | ATMEL_US_FRAME | ATMEL_US_OVRE | ATMEL_US_RXBRK ) ) ) {
UART_PUT_CR ( port , ATMEL_US_RSTSTA ) ; /* clear error */
if ( status & ATMEL_US_RXBRK ) {
status & = ~ ( ATMEL_US_PARE | ATMEL_US_FRAME ) ; /* ignore side-effect */
2006-06-19 22:53:19 +04:00
port - > icount . brk + + ;
if ( uart_handle_break ( port ) )
goto ignore_char ;
}
2006-10-04 18:02:05 +04:00
if ( status & ATMEL_US_PARE )
2006-01-10 19:59:27 +03:00
port - > icount . parity + + ;
2006-10-04 18:02:05 +04:00
if ( status & ATMEL_US_FRAME )
2006-01-10 19:59:27 +03:00
port - > icount . frame + + ;
2006-10-04 18:02:05 +04:00
if ( status & ATMEL_US_OVRE )
2006-01-10 19:59:27 +03:00
port - > icount . overrun + + ;
2006-06-19 22:53:19 +04:00
status & = port - > read_status_mask ;
2006-10-04 18:02:05 +04:00
if ( status & ATMEL_US_RXBRK )
2006-06-19 22:53:19 +04:00
flg = TTY_BREAK ;
2006-10-04 18:02:05 +04:00
else if ( status & ATMEL_US_PARE )
2006-01-10 19:59:27 +03:00
flg = TTY_PARITY ;
2006-10-04 18:02:05 +04:00
else if ( status & ATMEL_US_FRAME )
2006-01-10 19:59:27 +03:00
flg = TTY_FRAME ;
}
if ( uart_handle_sysrq_char ( port , ch , regs ) )
goto ignore_char ;
2006-10-04 18:02:05 +04:00
uart_insert_char ( port , status , ATMEL_US_OVRE , ch , flg ) ;
2006-01-10 19:59:27 +03:00
ignore_char :
2006-06-19 22:53:19 +04:00
status = UART_GET_CSR ( port ) ;
2006-01-10 19:59:27 +03:00
}
tty_flip_buffer_push ( tty ) ;
}
/*
* Transmit characters ( called from interrupt handler )
*/
2006-10-04 18:02:05 +04:00
static void atmel_tx_chars ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
struct circ_buf * xmit = & port - > info - > xmit ;
if ( port - > x_char ) {
UART_PUT_CHAR ( port , port - > x_char ) ;
port - > icount . tx + + ;
port - > x_char = 0 ;
return ;
}
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( port ) ) {
2006-10-04 18:02:05 +04:00
atmel_stop_tx ( port ) ;
2006-01-10 19:59:27 +03:00
return ;
}
2006-10-04 18:02:05 +04:00
while ( UART_GET_CSR ( port ) & ATMEL_US_TXRDY ) {
2006-01-10 19:59:27 +03:00
UART_PUT_CHAR ( port , xmit - > buf [ xmit - > tail ] ) ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
port - > icount . tx + + ;
if ( uart_circ_empty ( xmit ) )
break ;
}
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( port ) ;
if ( uart_circ_empty ( xmit ) )
2006-10-04 18:02:05 +04:00
atmel_stop_tx ( port ) ;
2006-01-10 19:59:27 +03:00
}
/*
* Interrupt handler
*/
2006-10-04 18:02:05 +04:00
static irqreturn_t atmel_interrupt ( int irq , void * dev_id , struct pt_regs * regs )
2006-01-10 19:59:27 +03:00
{
struct uart_port * port = dev_id ;
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-01-10 19:59:27 +03:00
unsigned int status , pending , pass_counter = 0 ;
status = UART_GET_CSR ( port ) ;
2006-06-19 22:53:19 +04:00
pending = status & UART_GET_IMR ( port ) ;
while ( pending ) {
/* Interrupt receive */
2006-10-04 18:02:05 +04:00
if ( pending & ATMEL_US_RXRDY )
atmel_rx_chars ( port , regs ) ;
2006-06-19 22:53:19 +04:00
// TODO: All reads to CSR will clear these interrupts!
2006-10-04 18:02:05 +04:00
if ( pending & ATMEL_US_RIIC ) port - > icount . rng + + ;
if ( pending & ATMEL_US_DSRIC ) port - > icount . dsr + + ;
if ( pending & ATMEL_US_DCDIC )
uart_handle_dcd_change ( port , ! ( status & ATMEL_US_DCD ) ) ;
if ( pending & ATMEL_US_CTSIC )
uart_handle_cts_change ( port , ! ( status & ATMEL_US_CTS ) ) ;
if ( pending & ( ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC ) )
2006-06-19 22:53:19 +04:00
wake_up_interruptible ( & port - > info - > delta_msr_wait ) ;
/* Interrupt transmit */
2006-10-04 18:02:05 +04:00
if ( pending & ATMEL_US_TXRDY )
atmel_tx_chars ( port ) ;
2006-06-19 22:53:19 +04:00
2006-10-04 18:02:05 +04:00
if ( pass_counter + + > ATMEL_ISR_PASS_LIMIT )
2006-06-19 22:53:19 +04:00
break ;
2006-01-10 19:59:27 +03:00
2006-06-19 22:53:19 +04:00
status = UART_GET_CSR ( port ) ;
pending = status & UART_GET_IMR ( port ) ;
2006-01-10 19:59:27 +03:00
}
return IRQ_HANDLED ;
}
/*
* Perform initialization and enable port for reception
*/
2006-10-04 18:02:05 +04:00
static int atmel_startup ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-01-10 19:59:27 +03:00
int retval ;
/*
* Ensure that no interrupts are enabled otherwise when
* request_irq ( ) is called we could get stuck trying to
* handle an unexpected interrupt
*/
UART_PUT_IDR ( port , - 1 ) ;
/*
* Allocate the IRQ
*/
2006-10-04 18:02:05 +04:00
retval = request_irq ( port - > irq , atmel_interrupt , IRQF_SHARED , " atmel_serial " , port ) ;
2006-01-10 19:59:27 +03:00
if ( retval ) {
2006-10-04 18:02:05 +04:00
printk ( " atmel_serial: atmel_startup - Can't get irq \n " ) ;
2006-01-10 19:59:27 +03:00
return retval ;
}
/*
* If there is a specific " open " function ( to register
* control line interrupts )
*/
2006-10-04 18:02:07 +04:00
if ( atmel_open_hook ) {
retval = atmel_open_hook ( port ) ;
2006-01-10 19:59:27 +03:00
if ( retval ) {
free_irq ( port - > irq , port ) ;
return retval ;
}
}
/*
* Finally , enable the serial port
*/
2006-10-04 18:02:05 +04:00
UART_PUT_CR ( port , ATMEL_US_RSTSTA | ATMEL_US_RSTRX ) ;
UART_PUT_CR ( port , ATMEL_US_TXEN | ATMEL_US_RXEN ) ; /* enable xmit & rcvr */
2006-06-19 22:53:19 +04:00
2006-10-04 18:02:05 +04:00
UART_PUT_IER ( port , ATMEL_US_RXRDY ) ; /* enable receive only */
2006-06-19 22:53:19 +04:00
2006-01-10 19:59:27 +03:00
return 0 ;
}
/*
* Disable the port
*/
2006-10-04 18:02:05 +04:00
static void atmel_shutdown ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-06-19 22:53:19 +04:00
2006-01-10 19:59:27 +03:00
/*
* Disable all interrupts , port and break condition .
*/
2006-10-04 18:02:05 +04:00
UART_PUT_CR ( port , ATMEL_US_RSTSTA ) ;
2006-01-10 19:59:27 +03:00
UART_PUT_IDR ( port , - 1 ) ;
/*
* Free the interrupt
*/
free_irq ( port - > irq , port ) ;
/*
* If there is a specific " close " function ( to unregister
* control line interrupts )
*/
2006-10-04 18:02:07 +04:00
if ( atmel_close_hook )
atmel_close_hook ( port ) ;
2006-01-10 19:59:27 +03:00
}
/*
* Power / Clock management .
*/
2006-10-04 18:02:05 +04:00
static void atmel_serial_pm ( struct uart_port * port , unsigned int state , unsigned int oldstate )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-06-19 22:53:19 +04:00
2006-01-10 19:59:27 +03:00
switch ( state ) {
case 0 :
/*
* Enable the peripheral clock for this serial port .
* This is called on uart_open ( ) or a resume event .
*/
2006-10-04 18:02:05 +04:00
clk_enable ( atmel_port - > clk ) ;
2006-01-10 19:59:27 +03:00
break ;
case 3 :
/*
* Disable the peripheral clock for this serial port .
* This is called on uart_close ( ) or a suspend event .
*/
2006-10-04 18:02:05 +04:00
clk_disable ( atmel_port - > clk ) ;
2006-01-10 19:59:27 +03:00
break ;
default :
2006-10-04 18:02:05 +04:00
printk ( KERN_ERR " atmel_serial: unknown pm %d \n " , state ) ;
2006-01-10 19:59:27 +03:00
}
}
/*
* Change the port parameters
*/
2006-10-04 18:02:05 +04:00
static void atmel_set_termios ( struct uart_port * port , struct termios * termios , struct termios * old )
2006-01-10 19:59:27 +03:00
{
unsigned long flags ;
unsigned int mode , imr , quot , baud ;
baud = uart_get_baud_rate ( port , termios , old , 0 , port - > uartclk / 16 ) ;
quot = uart_get_divisor ( port , baud ) ;
/* Get current mode register */
2006-10-04 18:02:05 +04:00
mode = UART_GET_MR ( port ) & ~ ( ATMEL_US_CHRL | ATMEL_US_NBSTOP | ATMEL_US_PAR ) ;
2006-01-10 19:59:27 +03:00
/* byte size */
switch ( termios - > c_cflag & CSIZE ) {
case CS5 :
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_CHRL_5 ;
2006-01-10 19:59:27 +03:00
break ;
case CS6 :
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_CHRL_6 ;
2006-01-10 19:59:27 +03:00
break ;
case CS7 :
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_CHRL_7 ;
2006-01-10 19:59:27 +03:00
break ;
default :
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_CHRL_8 ;
2006-01-10 19:59:27 +03:00
break ;
}
/* stop bits */
if ( termios - > c_cflag & CSTOPB )
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_NBSTOP_2 ;
2006-01-10 19:59:27 +03:00
/* parity */
if ( termios - > c_cflag & PARENB ) {
if ( termios - > c_cflag & CMSPAR ) { /* Mark or Space parity */
if ( termios - > c_cflag & PARODD )
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_PAR_MARK ;
2006-01-10 19:59:27 +03:00
else
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_PAR_SPACE ;
2006-01-10 19:59:27 +03:00
}
else if ( termios - > c_cflag & PARODD )
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_PAR_ODD ;
2006-01-10 19:59:27 +03:00
else
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_PAR_EVEN ;
2006-01-10 19:59:27 +03:00
}
else
2006-10-04 18:02:05 +04:00
mode | = ATMEL_US_PAR_NONE ;
2006-01-10 19:59:27 +03:00
spin_lock_irqsave ( & port - > lock , flags ) ;
2006-10-04 18:02:05 +04:00
port - > read_status_mask = ATMEL_US_OVRE ;
2006-01-10 19:59:27 +03:00
if ( termios - > c_iflag & INPCK )
2006-10-04 18:02:05 +04:00
port - > read_status_mask | = ( ATMEL_US_FRAME | ATMEL_US_PARE ) ;
2006-01-10 19:59:27 +03:00
if ( termios - > c_iflag & ( BRKINT | PARMRK ) )
2006-10-04 18:02:05 +04:00
port - > read_status_mask | = ATMEL_US_RXBRK ;
2006-01-10 19:59:27 +03:00
/*
* Characters to ignore
*/
port - > ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNPAR )
2006-10-04 18:02:05 +04:00
port - > ignore_status_mask | = ( ATMEL_US_FRAME | ATMEL_US_PARE ) ;
2006-01-10 19:59:27 +03:00
if ( termios - > c_iflag & IGNBRK ) {
2006-10-04 18:02:05 +04:00
port - > ignore_status_mask | = ATMEL_US_RXBRK ;
2006-01-10 19:59:27 +03:00
/*
* If we ' re ignoring parity and break indicators ,
* ignore overruns too ( for real raw support ) .
*/
if ( termios - > c_iflag & IGNPAR )
2006-10-04 18:02:05 +04:00
port - > ignore_status_mask | = ATMEL_US_OVRE ;
2006-01-10 19:59:27 +03:00
}
// TODO: Ignore all characters if CREAD is set.
/* update the per-port timeout */
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
/* disable interrupts and drain transmitter */
imr = UART_GET_IMR ( port ) ; /* get interrupt mask */
UART_PUT_IDR ( port , - 1 ) ; /* disable all interrupts */
2006-10-04 18:02:05 +04:00
while ( ! ( UART_GET_CSR ( port ) & ATMEL_US_TXEMPTY ) ) { barrier ( ) ; }
2006-01-10 19:59:27 +03:00
/* disable receiver and transmitter */
2006-10-04 18:02:05 +04:00
UART_PUT_CR ( port , ATMEL_US_TXDIS | ATMEL_US_RXDIS ) ;
2006-01-10 19:59:27 +03:00
/* set the parity, stop bits and data size */
UART_PUT_MR ( port , mode ) ;
/* set the baud rate */
UART_PUT_BRGR ( port , quot ) ;
2006-10-04 18:02:05 +04:00
UART_PUT_CR ( port , ATMEL_US_RSTSTA | ATMEL_US_RSTRX ) ;
UART_PUT_CR ( port , ATMEL_US_TXEN | ATMEL_US_RXEN ) ;
2006-01-10 19:59:27 +03:00
/* restore interrupts */
UART_PUT_IER ( port , imr ) ;
/* CTS flow-control and modem-status interrupts */
if ( UART_ENABLE_MS ( port , termios - > c_cflag ) )
port - > ops - > enable_ms ( port ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
/*
* Return string describing the specified port
*/
2006-10-04 18:02:05 +04:00
static const char * atmel_type ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:06 +04:00
return ( port - > type = = PORT_ATMEL ) ? " ATMEL_SERIAL " : NULL ;
2006-01-10 19:59:27 +03:00
}
/*
* Release the memory region ( s ) being used by ' port ' .
*/
2006-10-04 18:02:05 +04:00
static void atmel_release_port ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-06-19 22:53:19 +04:00
struct platform_device * pdev = to_platform_device ( port - > dev ) ;
int size = pdev - > resource [ 0 ] . end - pdev - > resource [ 0 ] . start + 1 ;
release_mem_region ( port - > mapbase , size ) ;
if ( port - > flags & UPF_IOREMAP ) {
iounmap ( port - > membase ) ;
port - > membase = NULL ;
}
2006-01-10 19:59:27 +03:00
}
/*
* Request the memory region ( s ) being used by ' port ' .
*/
2006-10-04 18:02:05 +04:00
static int atmel_request_port ( struct uart_port * port )
2006-01-10 19:59:27 +03:00
{
2006-06-19 22:53:19 +04:00
struct platform_device * pdev = to_platform_device ( port - > dev ) ;
int size = pdev - > resource [ 0 ] . end - pdev - > resource [ 0 ] . start + 1 ;
2006-10-04 18:02:05 +04:00
if ( ! request_mem_region ( port - > mapbase , size , " atmel_serial " ) )
2006-06-19 22:53:19 +04:00
return - EBUSY ;
if ( port - > flags & UPF_IOREMAP ) {
port - > membase = ioremap ( port - > mapbase , size ) ;
if ( port - > membase = = NULL ) {
release_mem_region ( port - > mapbase , size ) ;
return - ENOMEM ;
}
}
2006-01-10 19:59:27 +03:00
2006-06-19 22:53:19 +04:00
return 0 ;
2006-01-10 19:59:27 +03:00
}
/*
* Configure / autoconfigure the port .
*/
2006-10-04 18:02:05 +04:00
static void atmel_config_port ( struct uart_port * port , int flags )
2006-01-10 19:59:27 +03:00
{
if ( flags & UART_CONFIG_TYPE ) {
2006-10-04 18:02:06 +04:00
port - > type = PORT_ATMEL ;
2006-10-04 18:02:05 +04:00
atmel_request_port ( port ) ;
2006-01-10 19:59:27 +03:00
}
}
/*
* Verify the new serial_struct ( for TIOCSSERIAL ) .
*/
2006-10-04 18:02:05 +04:00
static int atmel_verify_port ( struct uart_port * port , struct serial_struct * ser )
2006-01-10 19:59:27 +03:00
{
int ret = 0 ;
2006-10-04 18:02:06 +04:00
if ( ser - > type ! = PORT_UNKNOWN & & ser - > type ! = PORT_ATMEL )
2006-01-10 19:59:27 +03:00
ret = - EINVAL ;
if ( port - > irq ! = ser - > irq )
ret = - EINVAL ;
if ( ser - > io_type ! = SERIAL_IO_MEM )
ret = - EINVAL ;
if ( port - > uartclk / 16 ! = ser - > baud_base )
ret = - EINVAL ;
if ( ( void * ) port - > mapbase ! = ser - > iomem_base )
ret = - EINVAL ;
if ( port - > iobase ! = ser - > port )
ret = - EINVAL ;
if ( ser - > hub6 ! = 0 )
ret = - EINVAL ;
return ret ;
}
2006-10-04 18:02:05 +04:00
static struct uart_ops atmel_pops = {
. tx_empty = atmel_tx_empty ,
. set_mctrl = atmel_set_mctrl ,
. get_mctrl = atmel_get_mctrl ,
. stop_tx = atmel_stop_tx ,
. start_tx = atmel_start_tx ,
. stop_rx = atmel_stop_rx ,
. enable_ms = atmel_enable_ms ,
. break_ctl = atmel_break_ctl ,
. startup = atmel_startup ,
. shutdown = atmel_shutdown ,
. set_termios = atmel_set_termios ,
. type = atmel_type ,
. release_port = atmel_release_port ,
. request_port = atmel_request_port ,
. config_port = atmel_config_port ,
. verify_port = atmel_verify_port ,
. pm = atmel_serial_pm ,
2006-01-10 19:59:27 +03:00
} ;
2006-06-19 22:53:19 +04:00
/*
* Configure the port from the platform device resource info .
*/
2006-10-04 18:02:05 +04:00
static void __devinit atmel_init_port ( struct atmel_uart_port * atmel_port , struct platform_device * pdev )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct uart_port * port = & atmel_port - > uart ;
2006-10-04 18:02:04 +04:00
struct atmel_uart_data * data = pdev - > dev . platform_data ;
2006-06-19 22:53:19 +04:00
port - > iotype = UPIO_MEM ;
port - > flags = UPF_BOOT_AUTOCONF ;
2006-10-04 18:02:05 +04:00
port - > ops = & atmel_pops ;
2006-06-19 22:53:19 +04:00
port - > fifosize = 1 ;
port - > line = pdev - > id ;
port - > dev = & pdev - > dev ;
port - > mapbase = pdev - > resource [ 0 ] . start ;
port - > irq = pdev - > resource [ 1 ] . start ;
2006-10-04 18:02:08 +04:00
if ( data - > regs )
/* Already mapped by setup code */
port - > membase = data - > regs ;
2006-06-19 22:53:19 +04:00
else {
port - > flags | = UPF_IOREMAP ;
port - > membase = NULL ;
}
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
if ( ! atmel_port - > clk ) { /* for console, the clock could already be configured */
atmel_port - > clk = clk_get ( & pdev - > dev , " usart " ) ;
clk_enable ( atmel_port - > clk ) ;
port - > uartclk = clk_get_rate ( atmel_port - > clk ) ;
2006-06-19 22:53:19 +04:00
}
2006-01-10 19:59:27 +03:00
}
2006-06-19 22:53:19 +04:00
/*
* Register board - specific modem - control line handlers .
*/
2006-10-04 18:02:07 +04:00
void __init atmel_register_uart_fns ( struct atmel_port_fns * fns )
2006-01-10 19:59:27 +03:00
{
if ( fns - > enable_ms )
2006-10-04 18:02:05 +04:00
atmel_pops . enable_ms = fns - > enable_ms ;
2006-01-10 19:59:27 +03:00
if ( fns - > get_mctrl )
2006-10-04 18:02:05 +04:00
atmel_pops . get_mctrl = fns - > get_mctrl ;
2006-01-10 19:59:27 +03:00
if ( fns - > set_mctrl )
2006-10-04 18:02:05 +04:00
atmel_pops . set_mctrl = fns - > set_mctrl ;
2006-10-04 18:02:07 +04:00
atmel_open_hook = fns - > open ;
atmel_close_hook = fns - > close ;
2006-10-04 18:02:05 +04:00
atmel_pops . pm = fns - > pm ;
atmel_pops . set_wake = fns - > set_wake ;
2006-01-10 19:59:27 +03:00
}
2006-10-04 18:02:02 +04:00
# ifdef CONFIG_SERIAL_ATMEL_CONSOLE
2006-10-04 18:02:05 +04:00
static void atmel_console_putchar ( struct uart_port * port , int ch )
2006-03-20 23:00:09 +03:00
{
2006-10-04 18:02:05 +04:00
while ( ! ( UART_GET_CSR ( port ) & ATMEL_US_TXRDY ) )
2006-03-20 23:00:09 +03:00
barrier ( ) ;
UART_PUT_CHAR ( port , ch ) ;
}
2006-01-10 19:59:27 +03:00
/*
* Interrupts are disabled on entering
*/
2006-10-04 18:02:05 +04:00
static void atmel_console_write ( struct console * co , const char * s , u_int count )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct uart_port * port = & atmel_ports [ co - > index ] . uart ;
2006-03-20 23:00:09 +03:00
unsigned int status , imr ;
2006-01-10 19:59:27 +03:00
/*
* First , save IMR and then disable interrupts
*/
imr = UART_GET_IMR ( port ) ; /* get interrupt mask */
2006-10-04 18:02:05 +04:00
UART_PUT_IDR ( port , ATMEL_US_RXRDY | ATMEL_US_TXRDY ) ;
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
uart_console_write ( port , s , count , atmel_console_putchar ) ;
2006-01-10 19:59:27 +03:00
/*
* Finally , wait for transmitter to become empty
* and restore IMR
*/
do {
status = UART_GET_CSR ( port ) ;
2006-10-04 18:02:05 +04:00
} while ( ! ( status & ATMEL_US_TXRDY ) ) ;
2006-01-10 19:59:27 +03:00
UART_PUT_IER ( port , imr ) ; /* set interrupts back the way they were */
}
/*
* If the port was already initialised ( eg , by a boot loader ) , try to determine
* the current setup .
*/
2006-10-04 18:02:05 +04:00
static void __init atmel_console_get_options ( struct uart_port * port , int * baud , int * parity , int * bits )
2006-01-10 19:59:27 +03:00
{
unsigned int mr , quot ;
// TODO: CR is a write-only register
// unsigned int cr;
//
2006-10-04 18:02:05 +04:00
// cr = UART_GET_CR(port) & (ATMEL_US_RXEN | ATMEL_US_TXEN);
// if (cr == (ATMEL_US_RXEN | ATMEL_US_TXEN)) {
2006-01-10 19:59:27 +03:00
// /* ok, the port was enabled */
// }
2006-10-04 18:02:05 +04:00
mr = UART_GET_MR ( port ) & ATMEL_US_CHRL ;
if ( mr = = ATMEL_US_CHRL_8 )
2006-01-10 19:59:27 +03:00
* bits = 8 ;
else
* bits = 7 ;
2006-10-04 18:02:05 +04:00
mr = UART_GET_MR ( port ) & ATMEL_US_PAR ;
if ( mr = = ATMEL_US_PAR_EVEN )
2006-01-10 19:59:27 +03:00
* parity = ' e ' ;
2006-10-04 18:02:05 +04:00
else if ( mr = = ATMEL_US_PAR_ODD )
2006-01-10 19:59:27 +03:00
* parity = ' o ' ;
quot = UART_GET_BRGR ( port ) ;
* baud = port - > uartclk / ( 16 * ( quot ) ) ;
}
2006-10-04 18:02:05 +04:00
static int __init atmel_console_setup ( struct console * co , char * options )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct uart_port * port = & atmel_ports [ co - > index ] . uart ;
2006-01-10 19:59:27 +03:00
int baud = 115200 ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
2006-06-19 22:53:19 +04:00
if ( port - > membase = = 0 ) /* Port not initialized yet - delay setup */
return - ENODEV ;
2006-01-10 19:59:27 +03:00
UART_PUT_IDR ( port , - 1 ) ; /* disable interrupts */
2006-10-04 18:02:05 +04:00
UART_PUT_CR ( port , ATMEL_US_RSTSTA | ATMEL_US_RSTRX ) ;
UART_PUT_CR ( port , ATMEL_US_TXEN | ATMEL_US_RXEN ) ;
2006-01-10 19:59:27 +03:00
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
else
2006-10-04 18:02:05 +04:00
atmel_console_get_options ( port , & baud , & parity , & bits ) ;
2006-01-10 19:59:27 +03:00
return uart_set_options ( port , co , baud , parity , bits , flow ) ;
}
2006-10-04 18:02:05 +04:00
static struct uart_driver atmel_uart ;
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
static struct console atmel_console = {
. name = ATMEL_DEVICENAME ,
. write = atmel_console_write ,
2006-01-10 19:59:27 +03:00
. device = uart_console_device ,
2006-10-04 18:02:05 +04:00
. setup = atmel_console_setup ,
2006-01-10 19:59:27 +03:00
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
2006-10-04 18:02:05 +04:00
. data = & atmel_uart ,
2006-01-10 19:59:27 +03:00
} ;
2006-10-04 18:02:05 +04:00
# define ATMEL_CONSOLE_DEVICE &atmel_console
2006-01-10 19:59:27 +03:00
2006-06-19 22:53:19 +04:00
/*
* Early console initialization ( before VM subsystem initialized ) .
*/
2006-10-04 18:02:05 +04:00
static int __init atmel_console_init ( void )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:04 +04:00
if ( atmel_default_console_device ) {
2006-10-04 18:02:05 +04:00
add_preferred_console ( ATMEL_DEVICENAME , atmel_default_console_device - > id , NULL ) ;
atmel_init_port ( & ( atmel_ports [ atmel_default_console_device - > id ] ) , atmel_default_console_device ) ;
register_console ( & atmel_console ) ;
2006-06-19 22:53:19 +04:00
}
2006-01-10 19:59:27 +03:00
return 0 ;
}
2006-10-04 18:02:05 +04:00
console_initcall ( atmel_console_init ) ;
2006-01-10 19:59:27 +03:00
2006-06-19 22:53:19 +04:00
/*
* Late console initialization .
*/
2006-10-04 18:02:05 +04:00
static int __init atmel_late_console_init ( void )
2006-06-19 22:53:19 +04:00
{
2006-10-04 18:02:05 +04:00
if ( atmel_default_console_device & & ! ( atmel_console . flags & CON_ENABLED ) )
register_console ( & atmel_console ) ;
2006-06-19 22:53:19 +04:00
return 0 ;
}
2006-10-04 18:02:05 +04:00
core_initcall ( atmel_late_console_init ) ;
2006-06-19 22:53:19 +04:00
2006-01-10 19:59:27 +03:00
# else
2006-10-04 18:02:05 +04:00
# define ATMEL_CONSOLE_DEVICE NULL
2006-01-10 19:59:27 +03:00
# endif
2006-10-04 18:02:05 +04:00
static struct uart_driver atmel_uart = {
2006-01-10 19:59:27 +03:00
. owner = THIS_MODULE ,
2006-10-04 18:02:05 +04:00
. driver_name = " atmel_serial " ,
. dev_name = ATMEL_DEVICENAME ,
. major = SERIAL_ATMEL_MAJOR ,
2006-01-10 19:59:27 +03:00
. minor = MINOR_START ,
2006-10-04 18:02:04 +04:00
. nr = ATMEL_MAX_UART ,
2006-10-04 18:02:05 +04:00
. cons = ATMEL_CONSOLE_DEVICE ,
2006-01-10 19:59:27 +03:00
} ;
2006-06-19 22:53:19 +04:00
# ifdef CONFIG_PM
2006-10-04 18:02:05 +04:00
static int atmel_serial_suspend ( struct platform_device * pdev , pm_message_t state )
2006-01-10 19:59:27 +03:00
{
2006-06-19 22:53:19 +04:00
struct uart_port * port = platform_get_drvdata ( pdev ) ;
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-06-19 22:53:19 +04:00
if ( device_may_wakeup ( & pdev - > dev ) & & ! at91_suspend_entering_slow_clock ( ) )
enable_irq_wake ( port - > irq ) ;
else {
disable_irq_wake ( port - > irq ) ;
2006-10-04 18:02:05 +04:00
uart_suspend_port ( & atmel_uart , port ) ;
atmel_port - > suspended = 1 ;
2006-06-19 22:53:19 +04:00
}
2006-01-10 19:59:27 +03:00
2006-06-19 22:53:19 +04:00
return 0 ;
}
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
static int atmel_serial_resume ( struct platform_device * pdev )
2006-06-19 22:53:19 +04:00
{
struct uart_port * port = platform_get_drvdata ( pdev ) ;
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
if ( atmel_port - > suspended ) {
uart_resume_port ( & atmel_uart , port ) ;
atmel_port - > suspended = 0 ;
2006-01-10 19:59:27 +03:00
}
return 0 ;
}
2006-06-19 22:53:19 +04:00
# else
2006-10-04 18:02:05 +04:00
# define atmel_serial_suspend NULL
# define atmel_serial_resume NULL
2006-06-19 22:53:19 +04:00
# endif
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
static int __devinit atmel_serial_probe ( struct platform_device * pdev )
2006-01-10 19:59:27 +03:00
{
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * port ;
2006-06-19 22:53:19 +04:00
int ret ;
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
port = & atmel_ports [ pdev - > id ] ;
atmel_init_port ( port , pdev ) ;
2006-01-10 19:59:27 +03:00
2006-10-04 18:02:05 +04:00
ret = uart_add_one_port ( & atmel_uart , & port - > uart ) ;
2006-06-19 22:53:19 +04:00
if ( ! ret ) {
device_init_wakeup ( & pdev - > dev , 1 ) ;
platform_set_drvdata ( pdev , port ) ;
}
return ret ;
}
2006-10-04 18:02:05 +04:00
static int __devexit atmel_serial_remove ( struct platform_device * pdev )
2006-06-19 22:53:19 +04:00
{
struct uart_port * port = platform_get_drvdata ( pdev ) ;
2006-10-04 18:02:05 +04:00
struct atmel_uart_port * atmel_port = ( struct atmel_uart_port * ) port ;
2006-06-19 22:53:19 +04:00
int ret = 0 ;
2006-10-04 18:02:05 +04:00
clk_disable ( atmel_port - > clk ) ;
clk_put ( atmel_port - > clk ) ;
2006-06-19 22:53:19 +04:00
device_init_wakeup ( & pdev - > dev , 0 ) ;
platform_set_drvdata ( pdev , NULL ) ;
if ( port ) {
2006-10-04 18:02:05 +04:00
ret = uart_remove_one_port ( & atmel_uart , port ) ;
2006-06-19 22:53:19 +04:00
kfree ( port ) ;
}
return ret ;
}
2006-10-04 18:02:05 +04:00
static struct platform_driver atmel_serial_driver = {
. probe = atmel_serial_probe ,
. remove = __devexit_p ( atmel_serial_remove ) ,
. suspend = atmel_serial_suspend ,
. resume = atmel_serial_resume ,
2006-06-19 22:53:19 +04:00
. driver = {
2006-10-04 18:02:03 +04:00
. name = " atmel_usart " ,
2006-06-19 22:53:19 +04:00
. owner = THIS_MODULE ,
} ,
} ;
2006-10-04 18:02:05 +04:00
static int __init atmel_serial_init ( void )
2006-06-19 22:53:19 +04:00
{
int ret ;
2006-10-04 18:02:05 +04:00
ret = uart_register_driver ( & atmel_uart ) ;
2006-06-19 22:53:19 +04:00
if ( ret )
return ret ;
2006-10-04 18:02:05 +04:00
ret = platform_driver_register ( & atmel_serial_driver ) ;
2006-06-19 22:53:19 +04:00
if ( ret )
2006-10-04 18:02:05 +04:00
uart_unregister_driver ( & atmel_uart ) ;
2006-06-19 22:53:19 +04:00
return ret ;
}
2006-10-04 18:02:05 +04:00
static void __exit atmel_serial_exit ( void )
2006-06-19 22:53:19 +04:00
{
2006-10-04 18:02:05 +04:00
platform_driver_unregister ( & atmel_serial_driver ) ;
uart_unregister_driver ( & atmel_uart ) ;
2006-01-10 19:59:27 +03:00
}
2006-10-04 18:02:05 +04:00
module_init ( atmel_serial_init ) ;
module_exit ( atmel_serial_exit ) ;
2006-01-10 19:59:27 +03:00
MODULE_AUTHOR ( " Rick Bronson " ) ;
2006-10-04 18:02:05 +04:00
MODULE_DESCRIPTION ( " Atmel AT91 / AT32 serial port driver " ) ;
2006-01-10 19:59:27 +03:00
MODULE_LICENSE ( " GPL " ) ;