2007-06-30 10:04:21 +04:00
/*
* linux / drivers / mmc / card / sdio_uart . c - SDIO UART / GPS driver
*
* Based on drivers / serial / 8250. c and drivers / serial / serial_core . c
* by Russell King .
*
* Author : Nicolas Pitre
* Created : June 15 , 2007
* Copyright : MontaVista 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 : Although this driver assumes a 16550 A - like UART implementation ,
* it is not possible to leverage the common 8250 / 16550 driver , nor the
* core UART infrastructure , as they assumes direct access to the hardware
* registers , often under a spinlock . This is not possible in the SDIO
* context as SDIO access functions must be able to sleep .
*
* Because we need to lock the SDIO host to ensure an exclusive access to
* the card , we simply rely on that lock to also prevent and serialize
* concurrent access to the same port .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/kernel.h>
2009-11-30 16:16:36 +03:00
# include <linux/sched.h>
2007-06-30 10:04:21 +04:00
# include <linux/mutex.h>
2009-04-01 02:19:20 +04:00
# include <linux/seq_file.h>
2007-06-30 10:04:21 +04:00
# include <linux/serial_reg.h>
# include <linux/circ_buf.h>
# include <linux/gfp.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
2010-02-08 13:08:39 +03:00
# include <linux/kfifo.h>
2007-06-30 10:04:21 +04:00
# include <linux/mmc/core.h>
# include <linux/mmc/card.h>
# include <linux/mmc/sdio_func.h>
# include <linux/mmc/sdio_ids.h>
# define UART_NR 8 /* Number of UARTs this driver can handle */
2010-02-08 13:08:39 +03:00
# define FIFO_SIZE PAGE_SIZE
2007-06-30 10:04:21 +04:00
# define WAKEUP_CHARS 256
struct uart_icount {
__u32 cts ;
__u32 dsr ;
__u32 rng ;
__u32 dcd ;
__u32 rx ;
__u32 tx ;
__u32 frame ;
__u32 overrun ;
__u32 parity ;
__u32 brk ;
} ;
struct sdio_uart_port {
2009-11-05 16:28:06 +03:00
struct tty_port port ;
2007-06-30 10:04:21 +04:00
struct kref kref ;
struct tty_struct * tty ;
unsigned int index ;
struct sdio_func * func ;
struct mutex func_lock ;
2007-08-21 01:17:37 +04:00
struct task_struct * in_sdio_uart_irq ;
2007-06-30 10:04:21 +04:00
unsigned int regs_offset ;
2010-02-08 13:08:39 +03:00
struct kfifo xmit_fifo ;
2007-06-30 10:04:21 +04:00
spinlock_t write_lock ;
struct uart_icount icount ;
unsigned int uartclk ;
unsigned int mctrl ;
2009-11-30 16:16:36 +03:00
unsigned int rx_mctrl ;
2007-06-30 10:04:21 +04:00
unsigned int read_status_mask ;
unsigned int ignore_status_mask ;
unsigned char x_char ;
unsigned char ier ;
unsigned char lcr ;
} ;
static struct sdio_uart_port * sdio_uart_table [ UART_NR ] ;
static DEFINE_SPINLOCK ( sdio_uart_table_lock ) ;
static int sdio_uart_add_port ( struct sdio_uart_port * port )
{
int index , ret = - EBUSY ;
kref_init ( & port - > kref ) ;
mutex_init ( & port - > func_lock ) ;
spin_lock_init ( & port - > write_lock ) ;
2010-02-08 13:08:39 +03:00
if ( kfifo_alloc ( & port - > xmit_fifo , FIFO_SIZE , GFP_KERNEL ) )
return - ENOMEM ;
2007-06-30 10:04:21 +04:00
spin_lock ( & sdio_uart_table_lock ) ;
for ( index = 0 ; index < UART_NR ; index + + ) {
if ( ! sdio_uart_table [ index ] ) {
port - > index = index ;
sdio_uart_table [ index ] = port ;
ret = 0 ;
break ;
}
}
spin_unlock ( & sdio_uart_table_lock ) ;
return ret ;
}
static struct sdio_uart_port * sdio_uart_port_get ( unsigned index )
{
struct sdio_uart_port * port ;
if ( index > = UART_NR )
return NULL ;
spin_lock ( & sdio_uart_table_lock ) ;
port = sdio_uart_table [ index ] ;
if ( port )
kref_get ( & port - > kref ) ;
spin_unlock ( & sdio_uart_table_lock ) ;
return port ;
}
static void sdio_uart_port_destroy ( struct kref * kref )
{
struct sdio_uart_port * port =
container_of ( kref , struct sdio_uart_port , kref ) ;
2010-02-08 13:08:39 +03:00
kfifo_free ( & port - > xmit_fifo ) ;
2007-06-30 10:04:21 +04:00
kfree ( port ) ;
}
static void sdio_uart_port_put ( struct sdio_uart_port * port )
{
kref_put ( & port - > kref , sdio_uart_port_destroy ) ;
}
static void sdio_uart_port_remove ( struct sdio_uart_port * port )
{
struct sdio_func * func ;
2009-11-30 16:16:09 +03:00
struct tty_struct * tty ;
2007-06-30 10:04:21 +04:00
BUG_ON ( sdio_uart_table [ port - > index ] ! = port ) ;
spin_lock ( & sdio_uart_table_lock ) ;
sdio_uart_table [ port - > index ] = NULL ;
spin_unlock ( & sdio_uart_table_lock ) ;
/*
* We ' re killing a port that potentially still is in use by
* the tty layer . Be careful to prevent any further access
* to the SDIO function and arrange for the tty layer to
* give up on that port ASAP .
* Beware : the lock ordering is critical .
*/
2009-11-05 16:28:38 +03:00
mutex_lock ( & port - > port . mutex ) ;
2007-06-30 10:04:21 +04:00
mutex_lock ( & port - > func_lock ) ;
func = port - > func ;
sdio_claim_host ( func ) ;
port - > func = NULL ;
mutex_unlock ( & port - > func_lock ) ;
2009-11-30 16:16:09 +03:00
tty = tty_port_tty_get ( & port - > port ) ;
/* tty_hangup is async so is this safe as is ?? */
if ( tty ) {
tty_hangup ( tty ) ;
2009-11-05 16:28:29 +03:00
tty_kref_put ( tty ) ;
}
2009-11-05 16:28:38 +03:00
mutex_unlock ( & port - > port . mutex ) ;
2007-06-30 10:04:21 +04:00
sdio_release_irq ( func ) ;
sdio_disable_func ( func ) ;
sdio_release_host ( func ) ;
sdio_uart_port_put ( port ) ;
}
static int sdio_uart_claim_func ( struct sdio_uart_port * port )
{
mutex_lock ( & port - > func_lock ) ;
if ( unlikely ( ! port - > func ) ) {
mutex_unlock ( & port - > func_lock ) ;
return - ENODEV ;
}
2007-08-21 01:17:37 +04:00
if ( likely ( port - > in_sdio_uart_irq ! = current ) )
sdio_claim_host ( port - > func ) ;
2007-06-30 10:04:21 +04:00
mutex_unlock ( & port - > func_lock ) ;
return 0 ;
}
static inline void sdio_uart_release_func ( struct sdio_uart_port * port )
{
2007-08-21 01:17:37 +04:00
if ( likely ( port - > in_sdio_uart_irq ! = current ) )
sdio_release_host ( port - > func ) ;
2007-06-30 10:04:21 +04:00
}
static inline unsigned int sdio_in ( struct sdio_uart_port * port , int offset )
{
unsigned char c ;
c = sdio_readb ( port - > func , port - > regs_offset + offset , NULL ) ;
return c ;
}
static inline void sdio_out ( struct sdio_uart_port * port , int offset , int value )
{
sdio_writeb ( port - > func , value , port - > regs_offset + offset , NULL ) ;
}
static unsigned int sdio_uart_get_mctrl ( struct sdio_uart_port * port )
{
unsigned char status ;
unsigned int ret ;
2009-11-30 16:16:36 +03:00
/* FIXME: What stops this losing the delta bits and breaking
sdio_uart_check_modem_status ? */
2007-06-30 10:04:21 +04:00
status = sdio_in ( port , 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 ;
}
2009-10-15 23:44:00 +04:00
static void sdio_uart_write_mctrl ( struct sdio_uart_port * port ,
unsigned int mctrl )
2007-06-30 10:04:21 +04:00
{
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 ;
sdio_out ( port , UART_MCR , mcr ) ;
}
static inline void sdio_uart_update_mctrl ( struct sdio_uart_port * port ,
unsigned int set , unsigned int clear )
{
unsigned int old ;
old = port - > mctrl ;
port - > mctrl = ( old & ~ clear ) | set ;
if ( old ! = port - > mctrl )
sdio_uart_write_mctrl ( port , port - > mctrl ) ;
}
# define sdio_uart_set_mctrl(port, x) sdio_uart_update_mctrl(port, x, 0)
# define sdio_uart_clear_mctrl(port, x) sdio_uart_update_mctrl(port, 0, x)
static void sdio_uart_change_speed ( struct sdio_uart_port * port ,
struct ktermios * termios ,
struct ktermios * old )
{
unsigned char cval , fcr = 0 ;
unsigned int baud , quot ;
switch ( termios - > c_cflag & CSIZE ) {
case CS5 :
cval = UART_LCR_WLEN5 ;
break ;
case CS6 :
cval = UART_LCR_WLEN6 ;
break ;
case CS7 :
cval = UART_LCR_WLEN7 ;
break ;
default :
case CS8 :
cval = UART_LCR_WLEN8 ;
break ;
}
if ( termios - > c_cflag & CSTOPB )
cval | = UART_LCR_STOP ;
if ( termios - > c_cflag & PARENB )
cval | = UART_LCR_PARITY ;
if ( ! ( termios - > c_cflag & PARODD ) )
cval | = UART_LCR_EPAR ;
for ( ; ; ) {
baud = tty_termios_baud_rate ( termios ) ;
if ( baud = = 0 )
baud = 9600 ; /* Special case: B0 rate. */
if ( baud < = port - > uartclk )
break ;
/*
* Oops , the quotient was zero . Try again with the old
* baud rate if possible , otherwise default to 9600.
*/
termios - > c_cflag & = ~ CBAUD ;
if ( old ) {
termios - > c_cflag | = old - > c_cflag & CBAUD ;
old = NULL ;
} else
termios - > c_cflag | = B9600 ;
}
quot = ( 2 * port - > uartclk + baud ) / ( 2 * baud ) ;
if ( baud < 2400 )
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1 ;
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 ;
port - > read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR ;
if ( termios - > c_iflag & INPCK )
port - > read_status_mask | = UART_LSR_FE | UART_LSR_PE ;
if ( termios - > c_iflag & ( BRKINT | PARMRK ) )
port - > read_status_mask | = UART_LSR_BI ;
/*
* Characters to ignore
*/
port - > ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNPAR )
port - > ignore_status_mask | = UART_LSR_PE | UART_LSR_FE ;
if ( termios - > c_iflag & IGNBRK ) {
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 )
port - > ignore_status_mask | = UART_LSR_OE ;
}
/*
* ignore all characters if CREAD is not set
*/
if ( ( termios - > c_cflag & CREAD ) = = 0 )
port - > ignore_status_mask | = UART_LSR_DR ;
/*
* CTS flow control flag and modem status interrupts
*/
port - > ier & = ~ UART_IER_MSI ;
if ( ( termios - > c_cflag & CRTSCTS ) | | ! ( termios - > c_cflag & CLOCAL ) )
port - > ier | = UART_IER_MSI ;
port - > lcr = cval ;
sdio_out ( port , UART_IER , port - > ier ) ;
sdio_out ( port , UART_LCR , cval | UART_LCR_DLAB ) ;
sdio_out ( port , UART_DLL , quot & 0xff ) ;
sdio_out ( port , UART_DLM , quot > > 8 ) ;
sdio_out ( port , UART_LCR , cval ) ;
sdio_out ( port , UART_FCR , fcr ) ;
sdio_uart_write_mctrl ( port , port - > mctrl ) ;
}
static void sdio_uart_start_tx ( struct sdio_uart_port * port )
{
if ( ! ( port - > ier & UART_IER_THRI ) ) {
port - > ier | = UART_IER_THRI ;
sdio_out ( port , UART_IER , port - > ier ) ;
}
}
static void sdio_uart_stop_tx ( struct sdio_uart_port * port )
{
if ( port - > ier & UART_IER_THRI ) {
port - > ier & = ~ UART_IER_THRI ;
sdio_out ( port , UART_IER , port - > ier ) ;
}
}
static void sdio_uart_stop_rx ( struct sdio_uart_port * port )
{
port - > ier & = ~ UART_IER_RLSI ;
port - > read_status_mask & = ~ UART_LSR_DR ;
sdio_out ( port , UART_IER , port - > ier ) ;
}
2009-10-15 23:44:00 +04:00
static void sdio_uart_receive_chars ( struct sdio_uart_port * port ,
unsigned int * status )
2007-06-30 10:04:21 +04:00
{
2009-11-05 16:28:29 +03:00
struct tty_struct * tty = tty_port_tty_get ( & port - > port ) ;
2007-06-30 10:04:21 +04:00
unsigned int ch , flag ;
int max_count = 256 ;
do {
ch = sdio_in ( port , UART_RX ) ;
flag = TTY_NORMAL ;
port - > icount . rx + + ;
if ( unlikely ( * status & ( UART_LSR_BI | UART_LSR_PE |
2009-10-15 23:44:00 +04:00
UART_LSR_FE | UART_LSR_OE ) ) ) {
2007-06-30 10:04:21 +04:00
/*
* For statistics only
*/
if ( * status & UART_LSR_BI ) {
* status & = ~ ( UART_LSR_FE | UART_LSR_PE ) ;
port - > icount . brk + + ;
} else if ( * status & UART_LSR_PE )
port - > icount . parity + + ;
else if ( * status & UART_LSR_FE )
port - > icount . frame + + ;
if ( * status & UART_LSR_OE )
port - > icount . overrun + + ;
/*
* Mask off conditions which should be ignored .
*/
* status & = port - > read_status_mask ;
2009-10-15 23:44:00 +04:00
if ( * status & UART_LSR_BI )
2007-06-30 10:04:21 +04:00
flag = TTY_BREAK ;
2009-10-15 23:44:00 +04:00
else if ( * status & UART_LSR_PE )
2007-06-30 10:04:21 +04:00
flag = TTY_PARITY ;
else if ( * status & UART_LSR_FE )
flag = TTY_FRAME ;
}
if ( ( * status & port - > ignore_status_mask & ~ UART_LSR_OE ) = = 0 )
2009-11-05 16:28:29 +03:00
if ( tty )
tty_insert_flip_char ( tty , ch , flag ) ;
2007-06-30 10:04:21 +04:00
/*
* Overrun is special . Since it ' s reported immediately ,
* it doesn ' t affect the current character .
*/
if ( * status & ~ port - > ignore_status_mask & UART_LSR_OE )
2009-11-05 16:28:29 +03:00
if ( tty )
tty_insert_flip_char ( tty , 0 , TTY_OVERRUN ) ;
2007-06-30 10:04:21 +04:00
* status = sdio_in ( port , UART_LSR ) ;
} while ( ( * status & UART_LSR_DR ) & & ( max_count - - > 0 ) ) ;
2009-11-05 16:28:29 +03:00
if ( tty ) {
tty_flip_buffer_push ( tty ) ;
tty_kref_put ( tty ) ;
}
2007-06-30 10:04:21 +04:00
}
static void sdio_uart_transmit_chars ( struct sdio_uart_port * port )
{
2010-02-08 13:08:39 +03:00
struct kfifo * xmit = & port - > xmit_fifo ;
2007-06-30 10:04:21 +04:00
int count ;
2009-11-05 16:28:29 +03:00
struct tty_struct * tty ;
2010-02-08 13:08:39 +03:00
u8 iobuf [ 16 ] ;
int len ;
2007-06-30 10:04:21 +04:00
if ( port - > x_char ) {
sdio_out ( port , UART_TX , port - > x_char ) ;
port - > icount . tx + + ;
port - > x_char = 0 ;
return ;
}
2009-11-05 16:28:29 +03:00
tty = tty_port_tty_get ( & port - > port ) ;
2010-02-08 13:08:39 +03:00
if ( tty = = NULL | | ! kfifo_len ( xmit ) | |
2009-11-30 16:16:25 +03:00
tty - > stopped | | tty - > hw_stopped ) {
2007-06-30 10:04:21 +04:00
sdio_uart_stop_tx ( port ) ;
2009-11-05 16:28:29 +03:00
tty_kref_put ( tty ) ;
2007-06-30 10:04:21 +04:00
return ;
}
2010-02-08 13:08:39 +03:00
len = kfifo_out_locked ( xmit , iobuf , 16 , & port - > write_lock ) ;
for ( count = 0 ; count < len ; count + + ) {
sdio_out ( port , UART_TX , iobuf [ count ] ) ;
2007-06-30 10:04:21 +04:00
port - > icount . tx + + ;
2010-02-08 13:08:39 +03:00
}
2007-06-30 10:04:21 +04:00
2010-02-08 13:08:39 +03:00
len = kfifo_len ( xmit ) ;
if ( len < WAKEUP_CHARS ) {
2009-11-05 16:28:06 +03:00
tty_wakeup ( tty ) ;
2010-02-08 13:08:39 +03:00
if ( len = = 0 )
sdio_uart_stop_tx ( port ) ;
}
2009-11-05 16:28:29 +03:00
tty_kref_put ( tty ) ;
2007-06-30 10:04:21 +04:00
}
static void sdio_uart_check_modem_status ( struct sdio_uart_port * port )
{
int status ;
2009-11-05 16:28:29 +03:00
struct tty_struct * tty ;
2007-06-30 10:04:21 +04:00
status = sdio_in ( port , UART_MSR ) ;
if ( ( status & UART_MSR_ANY_DELTA ) = = 0 )
return ;
if ( status & UART_MSR_TERI )
port - > icount . rng + + ;
if ( status & UART_MSR_DDSR )
port - > icount . dsr + + ;
2009-11-30 16:16:36 +03:00
if ( status & UART_MSR_DDCD ) {
2007-06-30 10:04:21 +04:00
port - > icount . dcd + + ;
2009-11-30 16:16:36 +03:00
/* DCD raise - wake for open */
if ( status & UART_MSR_DCD )
wake_up_interruptible ( & port - > port . open_wait ) ;
else {
/* DCD drop - hang up if tty attached */
tty = tty_port_tty_get ( & port - > port ) ;
if ( tty ) {
tty_hangup ( tty ) ;
tty_kref_put ( tty ) ;
}
}
}
2007-06-30 10:04:21 +04:00
if ( status & UART_MSR_DCTS ) {
port - > icount . cts + + ;
2009-11-05 16:28:29 +03:00
tty = tty_port_tty_get ( & port - > port ) ;
if ( tty & & ( tty - > termios - > c_cflag & CRTSCTS ) ) {
2007-06-30 10:04:21 +04:00
int cts = ( status & UART_MSR_CTS ) ;
2009-11-05 16:28:06 +03:00
if ( tty - > hw_stopped ) {
2007-06-30 10:04:21 +04:00
if ( cts ) {
2009-11-05 16:28:06 +03:00
tty - > hw_stopped = 0 ;
2007-06-30 10:04:21 +04:00
sdio_uart_start_tx ( port ) ;
2009-11-05 16:28:06 +03:00
tty_wakeup ( tty ) ;
2007-06-30 10:04:21 +04:00
}
} else {
if ( ! cts ) {
2009-11-05 16:28:06 +03:00
tty - > hw_stopped = 1 ;
2007-06-30 10:04:21 +04:00
sdio_uart_stop_tx ( port ) ;
}
}
}
2009-11-05 16:28:29 +03:00
tty_kref_put ( tty ) ;
2007-06-30 10:04:21 +04:00
}
}
/*
* This handles the interrupt from one port .
*/
static void sdio_uart_irq ( struct sdio_func * func )
{
struct sdio_uart_port * port = sdio_get_drvdata ( func ) ;
unsigned int iir , lsr ;
2007-08-21 01:17:37 +04:00
/*
* In a few places sdio_uart_irq ( ) is called directly instead of
* waiting for the actual interrupt to be raised and the SDIO IRQ
* thread scheduled in order to reduce latency . However , some
* interaction with the tty core may end up calling us back
* ( serial echo , flow control , etc . ) through those same places
* causing undesirable effects . Let ' s stop the recursion here .
*/
if ( unlikely ( port - > in_sdio_uart_irq = = current ) )
return ;
2007-06-30 10:04:21 +04:00
iir = sdio_in ( port , UART_IIR ) ;
if ( iir & UART_IIR_NO_INT )
return ;
2007-08-21 01:17:37 +04:00
port - > in_sdio_uart_irq = current ;
2007-06-30 10:04:21 +04:00
lsr = sdio_in ( port , UART_LSR ) ;
if ( lsr & UART_LSR_DR )
sdio_uart_receive_chars ( port , & lsr ) ;
sdio_uart_check_modem_status ( port ) ;
if ( lsr & UART_LSR_THRE )
sdio_uart_transmit_chars ( port ) ;
2007-08-21 01:17:37 +04:00
port - > in_sdio_uart_irq = NULL ;
2007-06-30 10:04:21 +04:00
}
2009-11-30 16:16:36 +03:00
static int uart_carrier_raised ( struct tty_port * tport )
{
struct sdio_uart_port * port =
container_of ( tport , struct sdio_uart_port , port ) ;
unsigned int ret = sdio_uart_claim_func ( port ) ;
2009-12-18 23:40:42 +03:00
if ( ret ) /* Missing hardware shouldn't block for carrier */
2009-11-30 16:16:36 +03:00
return 1 ;
ret = sdio_uart_get_mctrl ( port ) ;
sdio_uart_release_func ( port ) ;
if ( ret & TIOCM_CAR )
return 1 ;
return 0 ;
}
2009-11-30 16:16:09 +03:00
/**
* uart_dtr_rts - port helper to set uart signals
* @ tport : tty port to be updated
* @ onoff : set to turn on DTR / RTS
*
* Called by the tty port helpers when the modem signals need to be
* adjusted during an open , close and hangup .
*/
static void uart_dtr_rts ( struct tty_port * tport , int onoff )
2007-06-30 10:04:21 +04:00
{
2009-11-30 16:16:09 +03:00
struct sdio_uart_port * port =
container_of ( tport , struct sdio_uart_port , port ) ;
2009-11-30 16:16:30 +03:00
int ret = sdio_uart_claim_func ( port ) ;
if ( ret )
return ;
2009-11-30 16:16:09 +03:00
if ( onoff = = 0 )
sdio_uart_clear_mctrl ( port , TIOCM_DTR | TIOCM_RTS ) ;
else
sdio_uart_set_mctrl ( port , TIOCM_DTR | TIOCM_RTS ) ;
2009-11-30 16:16:30 +03:00
sdio_uart_release_func ( port ) ;
2009-11-30 16:16:09 +03:00
}
2009-11-05 16:28:29 +03:00
2009-11-30 16:16:09 +03:00
/**
* sdio_uart_activate - start up hardware
* @ tport : tty port to activate
* @ tty : tty bound to this port
*
* Activate a tty port . The port locking guarantees us this will be
* run exactly once per set of opens , and if successful will see the
* shutdown method run exactly once to match . Start up and shutdown are
* protected from each other by the internal locking and will not run
* at the same time even during a hangup event .
*
* If we successfully start up the port we take an extra kref as we
* will keep it around until shutdown when the kref is dropped .
*/
static int sdio_uart_activate ( struct tty_port * tport , struct tty_struct * tty )
{
struct sdio_uart_port * port =
container_of ( tport , struct sdio_uart_port , port ) ;
int ret ;
2007-06-30 10:04:21 +04:00
/*
* Set the TTY IO error marker - we will only clear this
* once we have successfully opened the port .
*/
2009-11-05 16:28:06 +03:00
set_bit ( TTY_IO_ERROR , & tty - > flags ) ;
2007-06-30 10:04:21 +04:00
2010-02-08 13:08:39 +03:00
kfifo_reset ( & port - > xmit_fifo ) ;
2007-06-30 10:04:21 +04:00
ret = sdio_uart_claim_func ( port ) ;
if ( ret )
2010-02-08 13:08:39 +03:00
return ret ;
2007-06-30 10:04:21 +04:00
ret = sdio_enable_func ( port - > func ) ;
if ( ret )
2010-02-08 13:08:39 +03:00
goto err1 ;
2007-06-30 10:04:21 +04:00
ret = sdio_claim_irq ( port - > func , sdio_uart_irq ) ;
if ( ret )
2010-02-08 13:08:39 +03:00
goto err2 ;
2007-06-30 10:04:21 +04:00
/*
* Clear the FIFO buffers and disable them .
* ( they will be reenabled in sdio_change_speed ( ) )
*/
sdio_out ( port , UART_FCR , UART_FCR_ENABLE_FIFO ) ;
sdio_out ( port , UART_FCR , UART_FCR_ENABLE_FIFO |
2009-10-15 23:44:00 +04:00
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT ) ;
2007-06-30 10:04:21 +04:00
sdio_out ( port , UART_FCR , 0 ) ;
/*
* Clear the interrupt registers .
*/
( void ) sdio_in ( port , UART_LSR ) ;
( void ) sdio_in ( port , UART_RX ) ;
( void ) sdio_in ( port , UART_IIR ) ;
( void ) sdio_in ( port , UART_MSR ) ;
/*
* Now , initialize the UART
*/
sdio_out ( port , UART_LCR , UART_LCR_WLEN8 ) ;
2009-11-30 16:16:25 +03:00
port - > ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE ;
2007-06-30 10:04:21 +04:00
port - > mctrl = TIOCM_OUT2 ;
2009-11-05 16:28:06 +03:00
sdio_uart_change_speed ( port , tty - > termios , NULL ) ;
2007-06-30 10:04:21 +04:00
2009-11-05 16:28:06 +03:00
if ( tty - > termios - > c_cflag & CBAUD )
2007-06-30 10:04:21 +04:00
sdio_uart_set_mctrl ( port , TIOCM_RTS | TIOCM_DTR ) ;
2009-11-05 16:28:06 +03:00
if ( tty - > termios - > c_cflag & CRTSCTS )
2007-06-30 10:04:21 +04:00
if ( ! ( sdio_uart_get_mctrl ( port ) & TIOCM_CTS ) )
2009-11-05 16:28:06 +03:00
tty - > hw_stopped = 1 ;
2007-06-30 10:04:21 +04:00
2009-11-05 16:28:17 +03:00
clear_bit ( TTY_IO_ERROR , & tty - > flags ) ;
2007-06-30 10:04:21 +04:00
/* Kick the IRQ handler once while we're still holding the host lock */
sdio_uart_irq ( port - > func ) ;
sdio_uart_release_func ( port ) ;
return 0 ;
err2 :
2010-02-08 13:08:39 +03:00
sdio_disable_func ( port - > func ) ;
2007-06-30 10:04:21 +04:00
err1 :
2010-02-08 13:08:39 +03:00
sdio_uart_release_func ( port ) ;
2007-06-30 10:04:21 +04:00
return ret ;
}
2009-11-30 16:16:09 +03:00
/**
* sdio_uart_shutdown - stop hardware
* @ tport : tty port to shut down
*
* Deactivate a tty port . The port locking guarantees us this will be
* run only if a successful matching activate already ran . The two are
* protected from each other by the internal locking and will not run
* at the same time even during a hangup event .
*/
static void sdio_uart_shutdown ( struct tty_port * tport )
2007-06-30 10:04:21 +04:00
{
2009-11-30 16:16:09 +03:00
struct sdio_uart_port * port =
container_of ( tport , struct sdio_uart_port , port ) ;
2007-06-30 10:04:21 +04:00
int ret ;
ret = sdio_uart_claim_func ( port ) ;
if ( ret )
2010-02-08 13:08:39 +03:00
return ;
2007-06-30 10:04:21 +04:00
sdio_uart_stop_rx ( port ) ;
2009-10-15 23:44:00 +04:00
/* Disable interrupts from this port */
2007-06-30 10:04:21 +04:00
sdio_release_irq ( port - > func ) ;
port - > ier = 0 ;
sdio_out ( port , UART_IER , 0 ) ;
sdio_uart_clear_mctrl ( port , TIOCM_OUT2 ) ;
/* Disable break condition and FIFOs. */
port - > lcr & = ~ UART_LCR_SBC ;
sdio_out ( port , UART_LCR , port - > lcr ) ;
sdio_out ( port , UART_FCR , UART_FCR_ENABLE_FIFO |
UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT ) ;
sdio_out ( port , UART_FCR , 0 ) ;
sdio_disable_func ( port - > func ) ;
sdio_uart_release_func ( port ) ;
}
2009-11-30 16:16:09 +03:00
/**
* sdio_uart_install - install method
* @ driver : the driver in use ( sdio_uart in our case )
* @ tty : the tty being bound
*
* Look up and bind the tty and the driver together . Initialize
* any needed private data ( in our case the termios )
*/
2007-06-30 10:04:21 +04:00
2009-11-30 16:16:09 +03:00
static int sdio_uart_install ( struct tty_driver * driver , struct tty_struct * tty )
{
int idx = tty - > index ;
struct sdio_uart_port * port = sdio_uart_port_get ( idx ) ;
int ret = tty_init_termios ( tty ) ;
if ( ret = = 0 ) {
tty_driver_kref_get ( driver ) ;
tty - > count + + ;
/* This is the ref sdio_uart_port get provided */
tty - > driver_data = port ;
driver - > ttys [ idx ] = tty ;
} else
2007-06-30 10:04:21 +04:00
sdio_uart_port_put ( port ) ;
2009-11-30 16:16:09 +03:00
return ret ;
2007-06-30 10:04:21 +04:00
}
2009-11-30 16:16:09 +03:00
/**
* sdio_uart_cleanup - called on the last tty kref drop
* @ tty : the tty being destroyed
*
* Called asynchronously when the last reference to the tty is dropped .
* We cannot destroy the tty - > driver_data port kref until this point
*/
static void sdio_uart_cleanup ( struct tty_struct * tty )
2007-06-30 10:04:21 +04:00
{
struct sdio_uart_port * port = tty - > driver_data ;
2009-11-30 16:16:09 +03:00
tty - > driver_data = NULL ; /* Bug trap */
sdio_uart_port_put ( port ) ;
}
2007-06-30 10:04:21 +04:00
2009-11-30 16:16:09 +03:00
/*
* Open / close / hangup is now entirely boilerplate
*/
2007-06-30 10:04:21 +04:00
2009-11-30 16:16:09 +03:00
static int sdio_uart_open ( struct tty_struct * tty , struct file * filp )
{
struct sdio_uart_port * port = tty - > driver_data ;
return tty_port_open ( & port - > port , tty , filp ) ;
}
2007-06-30 10:04:21 +04:00
2009-11-30 16:16:09 +03:00
static void sdio_uart_close ( struct tty_struct * tty , struct file * filp )
{
struct sdio_uart_port * port = tty - > driver_data ;
tty_port_close ( & port - > port , tty , filp ) ;
}
2007-06-30 10:04:21 +04:00
2009-11-30 16:16:09 +03:00
static void sdio_uart_hangup ( struct tty_struct * tty )
{
struct sdio_uart_port * port = tty - > driver_data ;
tty_port_hangup ( & port - > port ) ;
2007-06-30 10:04:21 +04:00
}
2009-11-30 16:16:25 +03:00
static int sdio_uart_write ( struct tty_struct * tty , const unsigned char * buf ,
2007-06-30 10:04:21 +04:00
int count )
{
struct sdio_uart_port * port = tty - > driver_data ;
2010-02-08 13:08:39 +03:00
int ret ;
2007-06-30 10:04:21 +04:00
if ( ! port - > func )
return - ENODEV ;
2010-02-08 13:08:39 +03:00
ret = kfifo_in_locked ( & port - > xmit_fifo , buf , count , & port - > write_lock ) ;
2009-11-30 16:16:25 +03:00
if ( ! ( port - > ier & UART_IER_THRI ) ) {
2007-06-30 10:04:21 +04:00
int err = sdio_uart_claim_func ( port ) ;
if ( ! err ) {
sdio_uart_start_tx ( port ) ;
sdio_uart_irq ( port - > func ) ;
sdio_uart_release_func ( port ) ;
} else
ret = err ;
}
return ret ;
}
static int sdio_uart_write_room ( struct tty_struct * tty )
{
struct sdio_uart_port * port = tty - > driver_data ;
2010-02-08 13:08:39 +03:00
return FIFO_SIZE - kfifo_len ( & port - > xmit_fifo ) ;
2007-06-30 10:04:21 +04:00
}
static int sdio_uart_chars_in_buffer ( struct tty_struct * tty )
{
struct sdio_uart_port * port = tty - > driver_data ;
2010-02-08 13:08:39 +03:00
return kfifo_len ( & port - > xmit_fifo ) ;
2007-06-30 10:04:21 +04:00
}
static void sdio_uart_send_xchar ( struct tty_struct * tty , char ch )
{
struct sdio_uart_port * port = tty - > driver_data ;
port - > x_char = ch ;
if ( ch & & ! ( port - > ier & UART_IER_THRI ) ) {
if ( sdio_uart_claim_func ( port ) ! = 0 )
return ;
sdio_uart_start_tx ( port ) ;
sdio_uart_irq ( port - > func ) ;
sdio_uart_release_func ( port ) ;
}
}
static void sdio_uart_throttle ( struct tty_struct * tty )
{
struct sdio_uart_port * port = tty - > driver_data ;
if ( ! I_IXOFF ( tty ) & & ! ( tty - > termios - > c_cflag & CRTSCTS ) )
return ;
if ( sdio_uart_claim_func ( port ) ! = 0 )
return ;
if ( I_IXOFF ( tty ) ) {
port - > x_char = STOP_CHAR ( tty ) ;
sdio_uart_start_tx ( port ) ;
}
if ( tty - > termios - > c_cflag & CRTSCTS )
sdio_uart_clear_mctrl ( port , TIOCM_RTS ) ;
sdio_uart_irq ( port - > func ) ;
sdio_uart_release_func ( port ) ;
}
static void sdio_uart_unthrottle ( struct tty_struct * tty )
{
struct sdio_uart_port * port = tty - > driver_data ;
if ( ! I_IXOFF ( tty ) & & ! ( tty - > termios - > c_cflag & CRTSCTS ) )
return ;
if ( sdio_uart_claim_func ( port ) ! = 0 )
return ;
if ( I_IXOFF ( tty ) ) {
if ( port - > x_char ) {
port - > x_char = 0 ;
} else {
port - > x_char = START_CHAR ( tty ) ;
sdio_uart_start_tx ( port ) ;
}
}
if ( tty - > termios - > c_cflag & CRTSCTS )
sdio_uart_set_mctrl ( port , TIOCM_RTS ) ;
sdio_uart_irq ( port - > func ) ;
sdio_uart_release_func ( port ) ;
}
2009-11-30 16:16:25 +03:00
static void sdio_uart_set_termios ( struct tty_struct * tty ,
struct ktermios * old_termios )
2007-06-30 10:04:21 +04:00
{
struct sdio_uart_port * port = tty - > driver_data ;
unsigned int cflag = tty - > termios - > c_cflag ;
if ( sdio_uart_claim_func ( port ) ! = 0 )
return ;
sdio_uart_change_speed ( port , tty - > termios , old_termios ) ;
/* Handle transition to B0 status */
if ( ( old_termios - > c_cflag & CBAUD ) & & ! ( cflag & CBAUD ) )
sdio_uart_clear_mctrl ( port , TIOCM_RTS | TIOCM_DTR ) ;
/* Handle transition away from B0 status */
if ( ! ( old_termios - > c_cflag & CBAUD ) & & ( cflag & CBAUD ) ) {
unsigned int mask = TIOCM_DTR ;
if ( ! ( cflag & CRTSCTS ) | | ! test_bit ( TTY_THROTTLED , & tty - > flags ) )
mask | = TIOCM_RTS ;
sdio_uart_set_mctrl ( port , mask ) ;
}
/* Handle turning off CRTSCTS */
if ( ( old_termios - > c_cflag & CRTSCTS ) & & ! ( cflag & CRTSCTS ) ) {
tty - > hw_stopped = 0 ;
sdio_uart_start_tx ( port ) ;
}
/* Handle turning on CRTSCTS */
if ( ! ( old_termios - > c_cflag & CRTSCTS ) & & ( cflag & CRTSCTS ) ) {
if ( ! ( sdio_uart_get_mctrl ( port ) & TIOCM_CTS ) ) {
tty - > hw_stopped = 1 ;
sdio_uart_stop_tx ( port ) ;
}
}
sdio_uart_release_func ( port ) ;
}
2008-07-10 15:28:48 +04:00
static int sdio_uart_break_ctl ( struct tty_struct * tty , int break_state )
2007-06-30 10:04:21 +04:00
{
struct sdio_uart_port * port = tty - > driver_data ;
2008-07-10 15:28:48 +04:00
int result ;
2007-06-30 10:04:21 +04:00
2008-07-10 15:28:48 +04:00
result = sdio_uart_claim_func ( port ) ;
if ( result ! = 0 )
return result ;
2007-06-30 10:04:21 +04:00
if ( break_state = = - 1 )
port - > lcr | = UART_LCR_SBC ;
else
port - > lcr & = ~ UART_LCR_SBC ;
sdio_out ( port , UART_LCR , port - > lcr ) ;
sdio_uart_release_func ( port ) ;
2008-07-10 15:28:48 +04:00
return 0 ;
2007-06-30 10:04:21 +04:00
}
static int sdio_uart_tiocmget ( struct tty_struct * tty , struct file * file )
{
struct sdio_uart_port * port = tty - > driver_data ;
int result ;
result = sdio_uart_claim_func ( port ) ;
if ( ! result ) {
result = port - > mctrl | sdio_uart_get_mctrl ( port ) ;
sdio_uart_release_func ( port ) ;
}
return result ;
}
static int sdio_uart_tiocmset ( struct tty_struct * tty , struct file * file ,
unsigned int set , unsigned int clear )
{
struct sdio_uart_port * port = tty - > driver_data ;
int result ;
2009-10-15 23:44:00 +04:00
result = sdio_uart_claim_func ( port ) ;
2009-11-30 16:16:25 +03:00
if ( ! result ) {
2007-06-30 10:04:21 +04:00
sdio_uart_update_mctrl ( port , set , clear ) ;
sdio_uart_release_func ( port ) ;
}
return result ;
}
2009-04-01 02:19:20 +04:00
static int sdio_uart_proc_show ( struct seq_file * m , void * v )
2007-07-05 07:40:34 +04:00
{
2009-04-01 02:19:20 +04:00
int i ;
2007-07-05 07:40:34 +04:00
2009-04-01 02:19:20 +04:00
seq_printf ( m , " serinfo:1.0 driver%s%s revision:%s \n " ,
2007-07-05 07:40:34 +04:00
" " , " " , " " ) ;
2009-04-01 02:19:20 +04:00
for ( i = 0 ; i < UART_NR ; i + + ) {
2007-07-05 07:40:34 +04:00
struct sdio_uart_port * port = sdio_uart_port_get ( i ) ;
if ( port ) {
2009-04-01 02:19:20 +04:00
seq_printf ( m , " %d: uart:SDIO " , i ) ;
2009-11-30 16:16:25 +03:00
if ( capable ( CAP_SYS_ADMIN ) ) {
2009-04-01 02:19:20 +04:00
seq_printf ( m , " tx:%d rx:%d " ,
2009-10-15 23:44:00 +04:00
port - > icount . tx , port - > icount . rx ) ;
2007-07-05 07:40:34 +04:00
if ( port - > icount . frame )
2009-04-01 02:19:20 +04:00
seq_printf ( m , " fe:%d " ,
2009-10-15 23:44:00 +04:00
port - > icount . frame ) ;
2007-07-05 07:40:34 +04:00
if ( port - > icount . parity )
2009-04-01 02:19:20 +04:00
seq_printf ( m , " pe:%d " ,
2009-10-15 23:44:00 +04:00
port - > icount . parity ) ;
2007-07-05 07:40:34 +04:00
if ( port - > icount . brk )
2009-04-01 02:19:20 +04:00
seq_printf ( m , " brk:%d " ,
2009-10-15 23:44:00 +04:00
port - > icount . brk ) ;
2007-07-05 07:40:34 +04:00
if ( port - > icount . overrun )
2009-04-01 02:19:20 +04:00
seq_printf ( m , " oe:%d " ,
2009-10-15 23:44:00 +04:00
port - > icount . overrun ) ;
2007-07-05 07:40:34 +04:00
if ( port - > icount . cts )
2009-04-01 02:19:20 +04:00
seq_printf ( m , " cts:%d " ,
2009-10-15 23:44:00 +04:00
port - > icount . cts ) ;
2007-07-05 07:40:34 +04:00
if ( port - > icount . dsr )
2009-04-01 02:19:20 +04:00
seq_printf ( m , " dsr:%d " ,
2009-10-15 23:44:00 +04:00
port - > icount . dsr ) ;
2007-07-05 07:40:34 +04:00
if ( port - > icount . rng )
2009-04-01 02:19:20 +04:00
seq_printf ( m , " rng:%d " ,
2009-10-15 23:44:00 +04:00
port - > icount . rng ) ;
2007-07-05 07:40:34 +04:00
if ( port - > icount . dcd )
2009-04-01 02:19:20 +04:00
seq_printf ( m , " dcd:%d " ,
2009-10-15 23:44:00 +04:00
port - > icount . dcd ) ;
2007-07-05 07:40:34 +04:00
}
sdio_uart_port_put ( port ) ;
2009-04-01 02:19:20 +04:00
seq_putc ( m , ' \n ' ) ;
2007-07-05 07:40:34 +04:00
}
}
2009-04-01 02:19:20 +04:00
return 0 ;
}
2007-07-05 07:40:34 +04:00
2009-04-01 02:19:20 +04:00
static int sdio_uart_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , sdio_uart_proc_show , NULL ) ;
2007-07-05 07:40:34 +04:00
}
2009-04-01 02:19:20 +04:00
static const struct file_operations sdio_uart_proc_fops = {
. owner = THIS_MODULE ,
. open = sdio_uart_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2009-11-30 16:16:09 +03:00
static const struct tty_port_operations sdio_uart_port_ops = {
. dtr_rts = uart_dtr_rts ,
2009-11-30 16:16:36 +03:00
. carrier_raised = uart_carrier_raised ,
2009-11-30 16:16:09 +03:00
. shutdown = sdio_uart_shutdown ,
. activate = sdio_uart_activate ,
} ;
2007-06-30 10:04:21 +04:00
static const struct tty_operations sdio_uart_ops = {
. open = sdio_uart_open ,
. close = sdio_uart_close ,
. write = sdio_uart_write ,
. write_room = sdio_uart_write_room ,
. chars_in_buffer = sdio_uart_chars_in_buffer ,
. send_xchar = sdio_uart_send_xchar ,
. throttle = sdio_uart_throttle ,
. unthrottle = sdio_uart_unthrottle ,
. set_termios = sdio_uart_set_termios ,
2009-11-30 16:16:09 +03:00
. hangup = sdio_uart_hangup ,
2007-06-30 10:04:21 +04:00
. break_ctl = sdio_uart_break_ctl ,
. tiocmget = sdio_uart_tiocmget ,
. tiocmset = sdio_uart_tiocmset ,
2009-11-30 16:16:09 +03:00
. install = sdio_uart_install ,
. cleanup = sdio_uart_cleanup ,
2009-04-01 02:19:20 +04:00
. proc_fops = & sdio_uart_proc_fops ,
2007-06-30 10:04:21 +04:00
} ;
static struct tty_driver * sdio_uart_tty_driver ;
static int sdio_uart_probe ( struct sdio_func * func ,
const struct sdio_device_id * id )
{
struct sdio_uart_port * port ;
int ret ;
port = kzalloc ( sizeof ( struct sdio_uart_port ) , GFP_KERNEL ) ;
if ( ! port )
return - ENOMEM ;
if ( func - > class = = SDIO_CLASS_UART ) {
printk ( KERN_WARNING " %s: need info on UART class basic setup \n " ,
sdio_func_id ( func ) ) ;
kfree ( port ) ;
return - ENOSYS ;
} else if ( func - > class = = SDIO_CLASS_GPS ) {
/*
* We need tuple 0x91 . It contains SUBTPL_SIOREG
* and SUBTPL_RCVCAPS .
*/
struct sdio_func_tuple * tpl ;
for ( tpl = func - > tuples ; tpl ; tpl = tpl - > next ) {
if ( tpl - > code ! = 0x91 )
continue ;
if ( tpl - > size < 10 )
continue ;
if ( tpl - > data [ 1 ] = = 0 ) /* SUBTPL_SIOREG */
break ;
}
if ( ! tpl ) {
printk ( KERN_WARNING
2009-11-30 16:16:25 +03:00
" %s: can't find tuple 0x91 subtuple 0 (SUBTPL_SIOREG) for GPS class \n " ,
2007-06-30 10:04:21 +04:00
sdio_func_id ( func ) ) ;
kfree ( port ) ;
return - EINVAL ;
}
printk ( KERN_DEBUG " %s: Register ID = 0x%02x, Exp ID = 0x%02x \n " ,
sdio_func_id ( func ) , tpl - > data [ 2 ] , tpl - > data [ 3 ] ) ;
port - > regs_offset = ( tpl - > data [ 4 ] < < 0 ) |
( tpl - > data [ 5 ] < < 8 ) |
( tpl - > data [ 6 ] < < 16 ) ;
printk ( KERN_DEBUG " %s: regs offset = 0x%x \n " ,
sdio_func_id ( func ) , port - > regs_offset ) ;
port - > uartclk = tpl - > data [ 7 ] * 115200 ;
if ( port - > uartclk = = 0 )
port - > uartclk = 115200 ;
printk ( KERN_DEBUG " %s: clk %d baudcode %u 4800-div %u \n " ,
sdio_func_id ( func ) , port - > uartclk ,
tpl - > data [ 7 ] , tpl - > data [ 8 ] | ( tpl - > data [ 9 ] < < 8 ) ) ;
} else {
kfree ( port ) ;
return - EINVAL ;
}
port - > func = func ;
sdio_set_drvdata ( func , port ) ;
2009-11-05 16:28:06 +03:00
tty_port_init ( & port - > port ) ;
2009-11-30 16:16:09 +03:00
port - > port . ops = & sdio_uart_port_ops ;
2007-06-30 10:04:21 +04:00
ret = sdio_uart_add_port ( port ) ;
if ( ret ) {
kfree ( port ) ;
} else {
struct device * dev ;
2009-11-30 16:16:25 +03:00
dev = tty_register_device ( sdio_uart_tty_driver ,
port - > index , & func - > dev ) ;
2007-06-30 10:04:21 +04:00
if ( IS_ERR ( dev ) ) {
sdio_uart_port_remove ( port ) ;
ret = PTR_ERR ( dev ) ;
}
}
return ret ;
}
static void sdio_uart_remove ( struct sdio_func * func )
{
struct sdio_uart_port * port = sdio_get_drvdata ( func ) ;
tty_unregister_device ( sdio_uart_tty_driver , port - > index ) ;
sdio_uart_port_remove ( port ) ;
}
static const struct sdio_device_id sdio_uart_ids [ ] = {
{ SDIO_DEVICE_CLASS ( SDIO_CLASS_UART ) } ,
{ SDIO_DEVICE_CLASS ( SDIO_CLASS_GPS ) } ,
{ /* end: all zeroes */ } ,
} ;
MODULE_DEVICE_TABLE ( sdio , sdio_uart_ids ) ;
static struct sdio_driver sdio_uart_driver = {
. probe = sdio_uart_probe ,
. remove = sdio_uart_remove ,
. name = " sdio_uart " ,
. id_table = sdio_uart_ids ,
} ;
static int __init sdio_uart_init ( void )
{
int ret ;
struct tty_driver * tty_drv ;
sdio_uart_tty_driver = tty_drv = alloc_tty_driver ( UART_NR ) ;
if ( ! tty_drv )
return - ENOMEM ;
tty_drv - > owner = THIS_MODULE ;
tty_drv - > driver_name = " sdio_uart " ;
tty_drv - > name = " ttySDIO " ;
tty_drv - > major = 0 ; /* dynamically allocated */
tty_drv - > minor_start = 0 ;
tty_drv - > type = TTY_DRIVER_TYPE_SERIAL ;
tty_drv - > subtype = SERIAL_TYPE_NORMAL ;
tty_drv - > flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV ;
tty_drv - > init_termios = tty_std_termios ;
tty_drv - > init_termios . c_cflag = B4800 | CS8 | CREAD | HUPCL | CLOCAL ;
2007-08-15 21:27:29 +04:00
tty_drv - > init_termios . c_ispeed = 4800 ;
tty_drv - > init_termios . c_ospeed = 4800 ;
2007-06-30 10:04:21 +04:00
tty_set_operations ( tty_drv , & sdio_uart_ops ) ;
ret = tty_register_driver ( tty_drv ) ;
if ( ret )
goto err1 ;
ret = sdio_register_driver ( & sdio_uart_driver ) ;
if ( ret )
goto err2 ;
return 0 ;
err2 :
tty_unregister_driver ( tty_drv ) ;
err1 :
put_tty_driver ( tty_drv ) ;
return ret ;
}
static void __exit sdio_uart_exit ( void )
{
sdio_unregister_driver ( & sdio_uart_driver ) ;
tty_unregister_driver ( sdio_uart_tty_driver ) ;
put_tty_driver ( sdio_uart_tty_driver ) ;
}
module_init ( sdio_uart_init ) ;
module_exit ( sdio_uart_exit ) ;
MODULE_AUTHOR ( " Nicolas Pitre " ) ;
MODULE_LICENSE ( " GPL " ) ;