2007-07-18 11:49:11 +04:00
/*
* zs . c : Serial port driver for IOASIC DECstations .
*
* Derived from drivers / sbus / char / sunserial . c by Paul Mackerras .
* Derived from drivers / macintosh / macserial . c by Harald Koerfgen .
*
* DECstation changes
* Copyright ( C ) 1998 - 2000 Harald Koerfgen
* Copyright ( C ) 2000 , 2001 , 2002 , 2003 , 2004 , 2005 , 2007 Maciej W . Rozycki
*
* For the rest of the code the original Copyright applies :
* Copyright ( C ) 1996 Paul Mackerras ( Paul . Mackerras @ cs . anu . edu . au )
* Copyright ( C ) 1995 David S . Miller ( davem @ caip . rutgers . edu )
*
*
* Note : for IOASIC systems the wiring is as follows :
*
* mouse / keyboard :
* DIN - 7 MJ - 4 signal SCC
* 2 1 TxD < - A . TxD
* 3 4 RxD - > A . RxD
*
* EIA - 232 / EIA - 423 :
* DB - 25 MMJ - 6 signal SCC
* 2 2 TxD < - B . TxD
* 3 5 RxD - > B . RxD
* 4 RTS < - ~ A . RTS
* 5 CTS - > ~ B . CTS
* 6 6 DSR - > ~ A . SYNC
* 8 CD - > ~ B . DCD
* 12 DSRS ( DCE ) - > ~ A . CTS ( * )
* 15 TxC - > B . TxC
* 17 RxC - > B . RxC
* 20 1 DTR < - ~ A . DTR
* 22 RI - > ~ A . DCD
* 23 DSRS ( DTE ) < - ~ B . RTS
*
* ( * ) EIA - 232 defines the signal at this pin to be SCD , while DSRS ( DCE )
* is shared with DSRS ( DTE ) at pin 23.
*
* As you can immediately notice the wiring of the RTS , DTR and DSR signals
* is a bit odd . This makes the handling of port B unnecessarily
* complicated and prevents the use of some automatic modes of operation .
*/
# if defined(CONFIG_SERIAL_ZS_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
# define SUPPORT_SYSRQ
# endif
# include <linux/bug.h>
# include <linux/console.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/ioport.h>
# include <linux/irqflags.h>
# include <linux/kernel.h>
# include <linux/major.h>
# include <linux/serial.h>
# include <linux/serial_core.h>
# include <linux/spinlock.h>
# include <linux/sysrq.h>
# include <linux/tty.h>
# include <linux/types.h>
# include <asm/atomic.h>
# include <asm/system.h>
# include <asm/dec/interrupts.h>
# include <asm/dec/ioasic_addrs.h>
# include <asm/dec/system.h>
# include "zs.h"
MODULE_AUTHOR ( " Maciej W. Rozycki <macro@linux-mips.org> " ) ;
MODULE_DESCRIPTION ( " DECstation Z85C30 serial driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
static char zs_name [ ] __initdata = " DECstation Z85C30 serial driver version " ;
static char zs_version [ ] __initdata = " 0.10 " ;
/*
* It would be nice to dynamically allocate everything that
* depends on ZS_NUM_SCCS , so we could support any number of
* Z85C30s , but for now . . .
*/
# define ZS_NUM_SCCS 2 /* Max # of ZS chips supported. */
# define ZS_NUM_CHAN 2 /* 2 channels per chip. */
# define ZS_CHAN_A 0 /* Index of the channel A. */
# define ZS_CHAN_B 1 /* Index of the channel B. */
# define ZS_CHAN_IO_SIZE 8 /* IOMEM space size. */
# define ZS_CHAN_IO_STRIDE 4 /* Register alignment. */
# define ZS_CHAN_IO_OFFSET 1 / * The SCC resides on the high byte
of the 16 - bit IOBUS . */
# define ZS_CLOCK 7372800 /* Z85C30 PCLK input clock rate. */
# define to_zport(uport) container_of(uport, struct zs_port, port)
struct zs_parms {
resource_size_t scc [ ZS_NUM_SCCS ] ;
int irq [ ZS_NUM_SCCS ] ;
} ;
static struct zs_scc zs_sccs [ ZS_NUM_SCCS ] ;
static u8 zs_init_regs [ ZS_NUM_REGS ] __initdata = {
0 , /* write 0 */
PAR_SPEC , /* write 1 */
0 , /* write 2 */
0 , /* write 3 */
X16CLK | SB1 , /* write 4 */
0 , /* write 5 */
0 , 0 , 0 , /* write 6, 7, 8 */
MIE | DLC | NV , /* write 9 */
NRZ , /* write 10 */
TCBR | RCBR , /* write 11 */
0 , 0 , /* BRG time constant, write 12 + 13 */
BRSRC | BRENABL , /* write 14 */
0 , /* write 15 */
} ;
/*
* Debugging .
*/
# undef ZS_DEBUG_REGS
/*
* Reading and writing Z85C30 registers .
*/
static void recovery_delay ( void )
{
udelay ( 2 ) ;
}
static u8 read_zsreg ( struct zs_port * zport , int reg )
{
void __iomem * control = zport - > port . membase + ZS_CHAN_IO_OFFSET ;
u8 retval ;
if ( reg ! = 0 ) {
writeb ( reg & 0xf , control ) ;
fast_iob ( ) ;
recovery_delay ( ) ;
}
retval = readb ( control ) ;
recovery_delay ( ) ;
return retval ;
}
static void write_zsreg ( struct zs_port * zport , int reg , u8 value )
{
void __iomem * control = zport - > port . membase + ZS_CHAN_IO_OFFSET ;
if ( reg ! = 0 ) {
writeb ( reg & 0xf , control ) ;
fast_iob ( ) ; recovery_delay ( ) ;
}
writeb ( value , control ) ;
fast_iob ( ) ;
recovery_delay ( ) ;
return ;
}
static u8 read_zsdata ( struct zs_port * zport )
{
void __iomem * data = zport - > port . membase +
ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET ;
u8 retval ;
retval = readb ( data ) ;
recovery_delay ( ) ;
return retval ;
}
static void write_zsdata ( struct zs_port * zport , u8 value )
{
void __iomem * data = zport - > port . membase +
ZS_CHAN_IO_STRIDE + ZS_CHAN_IO_OFFSET ;
writeb ( value , data ) ;
fast_iob ( ) ;
recovery_delay ( ) ;
return ;
}
# ifdef ZS_DEBUG_REGS
void zs_dump ( void )
{
struct zs_port * zport ;
int i , j ;
for ( i = 0 ; i < ZS_NUM_SCCS * ZS_NUM_CHAN ; i + + ) {
zport = & zs_sccs [ i / ZS_NUM_CHAN ] . zport [ i % ZS_NUM_CHAN ] ;
if ( ! zport - > scc )
continue ;
for ( j = 0 ; j < 16 ; j + + )
printk ( " W%-2d = 0x%02x \t " , j , zport - > regs [ j ] ) ;
printk ( " \n " ) ;
for ( j = 0 ; j < 16 ; j + + )
printk ( " R%-2d = 0x%02x \t " , j , read_zsreg ( zport , j ) ) ;
printk ( " \n \n " ) ;
}
}
# endif
static void zs_spin_lock_cond_irq ( spinlock_t * lock , int irq )
{
if ( irq )
spin_lock_irq ( lock ) ;
else
spin_lock ( lock ) ;
}
static void zs_spin_unlock_cond_irq ( spinlock_t * lock , int irq )
{
if ( irq )
spin_unlock_irq ( lock ) ;
else
spin_unlock ( lock ) ;
}
static int zs_receive_drain ( struct zs_port * zport )
{
int loops = 10000 ;
while ( ( read_zsreg ( zport , R0 ) & Rx_CH_AV ) & & loops - - )
read_zsdata ( zport ) ;
return loops ;
}
static int zs_transmit_drain ( struct zs_port * zport , int irq )
{
struct zs_scc * scc = zport - > scc ;
int loops = 10000 ;
while ( ! ( read_zsreg ( zport , R0 ) & Tx_BUF_EMP ) & & loops - - ) {
zs_spin_unlock_cond_irq ( & scc - > zlock , irq ) ;
udelay ( 2 ) ;
zs_spin_lock_cond_irq ( & scc - > zlock , irq ) ;
}
return loops ;
}
static int zs_line_drain ( struct zs_port * zport , int irq )
{
struct zs_scc * scc = zport - > scc ;
int loops = 10000 ;
while ( ! ( read_zsreg ( zport , R1 ) & ALL_SNT ) & & loops - - ) {
zs_spin_unlock_cond_irq ( & scc - > zlock , irq ) ;
udelay ( 2 ) ;
zs_spin_lock_cond_irq ( & scc - > zlock , irq ) ;
}
return loops ;
}
static void load_zsregs ( struct zs_port * zport , u8 * regs , int irq )
{
/* Let the current transmission finish. */
zs_line_drain ( zport , irq ) ;
/* Load 'em up. */
write_zsreg ( zport , R3 , regs [ 3 ] & ~ RxENABLE ) ;
write_zsreg ( zport , R5 , regs [ 5 ] & ~ TxENAB ) ;
write_zsreg ( zport , R4 , regs [ 4 ] ) ;
write_zsreg ( zport , R9 , regs [ 9 ] ) ;
write_zsreg ( zport , R1 , regs [ 1 ] ) ;
write_zsreg ( zport , R2 , regs [ 2 ] ) ;
write_zsreg ( zport , R10 , regs [ 10 ] ) ;
write_zsreg ( zport , R14 , regs [ 14 ] & ~ BRENABL ) ;
write_zsreg ( zport , R11 , regs [ 11 ] ) ;
write_zsreg ( zport , R12 , regs [ 12 ] ) ;
write_zsreg ( zport , R13 , regs [ 13 ] ) ;
write_zsreg ( zport , R14 , regs [ 14 ] ) ;
write_zsreg ( zport , R15 , regs [ 15 ] ) ;
if ( regs [ 3 ] & RxENABLE )
write_zsreg ( zport , R3 , regs [ 3 ] ) ;
if ( regs [ 5 ] & TxENAB )
write_zsreg ( zport , R5 , regs [ 5 ] ) ;
return ;
}
/*
* Status handling routines .
*/
/*
* zs_tx_empty ( ) - - get the transmitter empty status
*
* Purpose : Let user call ioctl ( ) to get info when the UART physically
* is emptied . On bus types like RS485 , the transmitter must
* release the bus after transmitting . This must be done when
* the transmit shift register is empty , not be done when the
* transmit holding register is empty . This functionality
* allows an RS485 driver to be written in user space .
*/
static unsigned int zs_tx_empty ( struct uart_port * uport )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
unsigned long flags ;
u8 status ;
spin_lock_irqsave ( & scc - > zlock , flags ) ;
status = read_zsreg ( zport , R1 ) ;
spin_unlock_irqrestore ( & scc - > zlock , flags ) ;
return status & ALL_SNT ? TIOCSER_TEMT : 0 ;
}
static unsigned int zs_raw_get_ab_mctrl ( struct zs_port * zport_a ,
struct zs_port * zport_b )
{
u8 status_a , status_b ;
unsigned int mctrl ;
status_a = read_zsreg ( zport_a , R0 ) ;
status_b = read_zsreg ( zport_b , R0 ) ;
mctrl = ( ( status_b & CTS ) ? TIOCM_CTS : 0 ) |
( ( status_b & DCD ) ? TIOCM_CAR : 0 ) |
( ( status_a & DCD ) ? TIOCM_RNG : 0 ) |
( ( status_a & SYNC_HUNT ) ? TIOCM_DSR : 0 ) ;
return mctrl ;
}
static unsigned int zs_raw_get_mctrl ( struct zs_port * zport )
{
struct zs_port * zport_a = & zport - > scc - > zport [ ZS_CHAN_A ] ;
return zport ! = zport_a ? zs_raw_get_ab_mctrl ( zport_a , zport ) : 0 ;
}
static unsigned int zs_raw_xor_mctrl ( struct zs_port * zport )
{
struct zs_port * zport_a = & zport - > scc - > zport [ ZS_CHAN_A ] ;
unsigned int mmask , mctrl , delta ;
u8 mask_a , mask_b ;
if ( zport = = zport_a )
return 0 ;
mask_a = zport_a - > regs [ 15 ] ;
mask_b = zport - > regs [ 15 ] ;
mmask = ( ( mask_b & CTSIE ) ? TIOCM_CTS : 0 ) |
( ( mask_b & DCDIE ) ? TIOCM_CAR : 0 ) |
( ( mask_a & DCDIE ) ? TIOCM_RNG : 0 ) |
( ( mask_a & SYNCIE ) ? TIOCM_DSR : 0 ) ;
mctrl = zport - > mctrl ;
if ( mmask ) {
mctrl & = ~ mmask ;
mctrl | = zs_raw_get_ab_mctrl ( zport_a , zport ) & mmask ;
}
delta = mctrl ^ zport - > mctrl ;
if ( delta )
zport - > mctrl = mctrl ;
return delta ;
}
static unsigned int zs_get_mctrl ( struct uart_port * uport )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
unsigned int mctrl ;
spin_lock ( & scc - > zlock ) ;
mctrl = zs_raw_get_mctrl ( zport ) ;
spin_unlock ( & scc - > zlock ) ;
return mctrl ;
}
static void zs_set_mctrl ( struct uart_port * uport , unsigned int mctrl )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
struct zs_port * zport_a = & scc - > zport [ ZS_CHAN_A ] ;
u8 oldloop , newloop ;
spin_lock ( & scc - > zlock ) ;
if ( zport ! = zport_a ) {
if ( mctrl & TIOCM_DTR )
zport_a - > regs [ 5 ] | = DTR ;
else
zport_a - > regs [ 5 ] & = ~ DTR ;
if ( mctrl & TIOCM_RTS )
zport_a - > regs [ 5 ] | = RTS ;
else
zport_a - > regs [ 5 ] & = ~ RTS ;
write_zsreg ( zport_a , R5 , zport_a - > regs [ 5 ] ) ;
}
/* Rarely modified, so don't poke at hardware unless necessary. */
oldloop = zport - > regs [ 14 ] ;
newloop = oldloop ;
if ( mctrl & TIOCM_LOOP )
newloop | = LOOPBAK ;
else
newloop & = ~ LOOPBAK ;
if ( newloop ! = oldloop ) {
zport - > regs [ 14 ] = newloop ;
write_zsreg ( zport , R14 , zport - > regs [ 14 ] ) ;
}
spin_unlock ( & scc - > zlock ) ;
}
static void zs_raw_stop_tx ( struct zs_port * zport )
{
write_zsreg ( zport , R0 , RES_Tx_P ) ;
zport - > tx_stopped = 1 ;
}
static void zs_stop_tx ( struct uart_port * uport )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
spin_lock ( & scc - > zlock ) ;
zs_raw_stop_tx ( zport ) ;
spin_unlock ( & scc - > zlock ) ;
}
static void zs_raw_transmit_chars ( struct zs_port * ) ;
static void zs_start_tx ( struct uart_port * uport )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
spin_lock ( & scc - > zlock ) ;
if ( zport - > tx_stopped ) {
zs_transmit_drain ( zport , 0 ) ;
zport - > tx_stopped = 0 ;
zs_raw_transmit_chars ( zport ) ;
}
spin_unlock ( & scc - > zlock ) ;
}
static void zs_stop_rx ( struct uart_port * uport )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
struct zs_port * zport_a = & scc - > zport [ ZS_CHAN_A ] ;
spin_lock ( & scc - > zlock ) ;
zport - > regs [ 15 ] & = ~ BRKIE ;
zport - > regs [ 1 ] & = ~ ( RxINT_MASK | TxINT_ENAB ) ;
zport - > regs [ 1 ] | = RxINT_DISAB ;
if ( zport ! = zport_a ) {
/* A-side DCD tracks RI and SYNC tracks DSR. */
zport_a - > regs [ 15 ] & = ~ ( DCDIE | SYNCIE ) ;
write_zsreg ( zport_a , R15 , zport_a - > regs [ 15 ] ) ;
if ( ! ( zport_a - > regs [ 15 ] & BRKIE ) ) {
zport_a - > regs [ 1 ] & = ~ EXT_INT_ENAB ;
write_zsreg ( zport_a , R1 , zport_a - > regs [ 1 ] ) ;
}
/* This-side DCD tracks DCD and CTS tracks CTS. */
zport - > regs [ 15 ] & = ~ ( DCDIE | CTSIE ) ;
zport - > regs [ 1 ] & = ~ EXT_INT_ENAB ;
} else {
/* DCD tracks RI and SYNC tracks DSR for the B side. */
if ( ! ( zport - > regs [ 15 ] & ( DCDIE | SYNCIE ) ) )
zport - > regs [ 1 ] & = ~ EXT_INT_ENAB ;
}
write_zsreg ( zport , R15 , zport - > regs [ 15 ] ) ;
write_zsreg ( zport , R1 , zport - > regs [ 1 ] ) ;
spin_unlock ( & scc - > zlock ) ;
}
static void zs_enable_ms ( struct uart_port * uport )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
struct zs_port * zport_a = & scc - > zport [ ZS_CHAN_A ] ;
if ( zport = = zport_a )
return ;
spin_lock ( & scc - > zlock ) ;
/* Clear Ext interrupts if not being handled already. */
if ( ! ( zport_a - > regs [ 1 ] & EXT_INT_ENAB ) )
write_zsreg ( zport_a , R0 , RES_EXT_INT ) ;
/* A-side DCD tracks RI and SYNC tracks DSR. */
zport_a - > regs [ 1 ] | = EXT_INT_ENAB ;
zport_a - > regs [ 15 ] | = DCDIE | SYNCIE ;
/* This-side DCD tracks DCD and CTS tracks CTS. */
zport - > regs [ 15 ] | = DCDIE | CTSIE ;
zs_raw_xor_mctrl ( zport ) ;
write_zsreg ( zport_a , R1 , zport_a - > regs [ 1 ] ) ;
write_zsreg ( zport_a , R15 , zport_a - > regs [ 15 ] ) ;
write_zsreg ( zport , R15 , zport - > regs [ 15 ] ) ;
spin_unlock ( & scc - > zlock ) ;
}
static void zs_break_ctl ( struct uart_port * uport , int break_state )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
unsigned long flags ;
spin_lock_irqsave ( & scc - > zlock , flags ) ;
if ( break_state = = - 1 )
zport - > regs [ 5 ] | = SND_BRK ;
else
zport - > regs [ 5 ] & = ~ SND_BRK ;
write_zsreg ( zport , R5 , zport - > regs [ 5 ] ) ;
spin_unlock_irqrestore ( & scc - > zlock , flags ) ;
}
/*
* Interrupt handling routines .
*/
# define Rx_BRK 0x0100 /* BREAK event software flag. */
# define Rx_SYS 0x0200 /* SysRq event software flag. */
static void zs_receive_chars ( struct zs_port * zport )
{
struct uart_port * uport = & zport - > port ;
struct zs_scc * scc = zport - > scc ;
struct uart_icount * icount ;
unsigned int avail , status , ch , flag ;
int count ;
for ( count = 16 ; count ; count - - ) {
spin_lock ( & scc - > zlock ) ;
avail = read_zsreg ( zport , R0 ) & Rx_CH_AV ;
spin_unlock ( & scc - > zlock ) ;
if ( ! avail )
break ;
spin_lock ( & scc - > zlock ) ;
status = read_zsreg ( zport , R1 ) & ( Rx_OVR | FRM_ERR | PAR_ERR ) ;
ch = read_zsdata ( zport ) ;
spin_unlock ( & scc - > zlock ) ;
flag = TTY_NORMAL ;
icount = & uport - > icount ;
icount - > rx + + ;
/* Handle the null char got when BREAK is removed. */
if ( ! ch )
status | = zport - > tty_break ;
if ( unlikely ( status &
( Rx_OVR | FRM_ERR | PAR_ERR | Rx_SYS | Rx_BRK ) ) ) {
zport - > tty_break = 0 ;
/* Reset the error indication. */
if ( status & ( Rx_OVR | FRM_ERR | PAR_ERR ) ) {
spin_lock ( & scc - > zlock ) ;
write_zsreg ( zport , R0 , ERR_RES ) ;
spin_unlock ( & scc - > zlock ) ;
}
if ( status & ( Rx_SYS | Rx_BRK ) ) {
icount - > brk + + ;
/* SysRq discards the null char. */
if ( status & Rx_SYS )
continue ;
} else if ( status & FRM_ERR )
icount - > frame + + ;
else if ( status & PAR_ERR )
icount - > parity + + ;
if ( status & Rx_OVR )
icount - > overrun + + ;
status & = uport - > read_status_mask ;
if ( status & Rx_BRK )
flag = TTY_BREAK ;
else if ( status & FRM_ERR )
flag = TTY_FRAME ;
else if ( status & PAR_ERR )
flag = TTY_PARITY ;
}
if ( uart_handle_sysrq_char ( uport , ch ) )
continue ;
uart_insert_char ( uport , status , Rx_OVR , ch , flag ) ;
}
2008-07-17 00:54:42 +04:00
tty_flip_buffer_push ( uport - > info - > port . tty ) ;
2007-07-18 11:49:11 +04:00
}
static void zs_raw_transmit_chars ( struct zs_port * zport )
{
struct circ_buf * xmit = & zport - > port . info - > xmit ;
/* XON/XOFF chars. */
if ( zport - > port . x_char ) {
write_zsdata ( zport , zport - > port . x_char ) ;
zport - > port . icount . tx + + ;
zport - > port . x_char = 0 ;
return ;
}
/* If nothing to do or stopped or hardware stopped. */
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( & zport - > port ) ) {
zs_raw_stop_tx ( zport ) ;
return ;
}
/* Send char. */
write_zsdata ( zport , xmit - > buf [ xmit - > tail ] ) ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
zport - > port . icount . tx + + ;
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( & zport - > port ) ;
/* Are we are done? */
if ( uart_circ_empty ( xmit ) )
zs_raw_stop_tx ( zport ) ;
}
static void zs_transmit_chars ( struct zs_port * zport )
{
struct zs_scc * scc = zport - > scc ;
spin_lock ( & scc - > zlock ) ;
zs_raw_transmit_chars ( zport ) ;
spin_unlock ( & scc - > zlock ) ;
}
static void zs_status_handle ( struct zs_port * zport , struct zs_port * zport_a )
{
struct uart_port * uport = & zport - > port ;
struct zs_scc * scc = zport - > scc ;
unsigned int delta ;
u8 status , brk ;
spin_lock ( & scc - > zlock ) ;
/* Get status from Read Register 0. */
status = read_zsreg ( zport , R0 ) ;
if ( zport - > regs [ 15 ] & BRKIE ) {
brk = status & BRK_ABRT ;
if ( brk & & ! zport - > brk ) {
spin_unlock ( & scc - > zlock ) ;
if ( uart_handle_break ( uport ) )
zport - > tty_break = Rx_SYS ;
else
zport - > tty_break = Rx_BRK ;
spin_lock ( & scc - > zlock ) ;
}
zport - > brk = brk ;
}
if ( zport ! = zport_a ) {
delta = zs_raw_xor_mctrl ( zport ) ;
spin_unlock ( & scc - > zlock ) ;
if ( delta & TIOCM_CTS )
uart_handle_cts_change ( uport ,
zport - > mctrl & TIOCM_CTS ) ;
if ( delta & TIOCM_CAR )
uart_handle_dcd_change ( uport ,
zport - > mctrl & TIOCM_CAR ) ;
if ( delta & TIOCM_RNG )
uport - > icount . dsr + + ;
if ( delta & TIOCM_DSR )
uport - > icount . rng + + ;
if ( delta )
wake_up_interruptible ( & uport - > info - > delta_msr_wait ) ;
spin_lock ( & scc - > zlock ) ;
}
/* Clear the status condition... */
write_zsreg ( zport , R0 , RES_EXT_INT ) ;
spin_unlock ( & scc - > zlock ) ;
}
/*
* This is the Z85C30 driver ' s generic interrupt routine .
*/
static irqreturn_t zs_interrupt ( int irq , void * dev_id )
{
struct zs_scc * scc = dev_id ;
struct zs_port * zport_a = & scc - > zport [ ZS_CHAN_A ] ;
struct zs_port * zport_b = & scc - > zport [ ZS_CHAN_B ] ;
irqreturn_t status = IRQ_NONE ;
u8 zs_intreg ;
int count ;
/*
* NOTE : The read register 3 , which holds the irq status ,
* does so for both channels on each chip . Although
* the status value itself must be read from the A
* channel and is only valid when read from channel A .
* Yes . . . broken hardware . . .
*/
for ( count = 16 ; count ; count - - ) {
spin_lock ( & scc - > zlock ) ;
zs_intreg = read_zsreg ( zport_a , R3 ) ;
spin_unlock ( & scc - > zlock ) ;
if ( ! zs_intreg )
break ;
/*
* We do not like losing characters , so we prioritise
* interrupt sources a little bit differently than
* the SCC would , was it allowed to .
*/
if ( zs_intreg & CHBRxIP )
zs_receive_chars ( zport_b ) ;
if ( zs_intreg & CHARxIP )
zs_receive_chars ( zport_a ) ;
if ( zs_intreg & CHBEXT )
zs_status_handle ( zport_b , zport_a ) ;
if ( zs_intreg & CHAEXT )
zs_status_handle ( zport_a , zport_a ) ;
if ( zs_intreg & CHBTxIP )
zs_transmit_chars ( zport_b ) ;
if ( zs_intreg & CHATxIP )
zs_transmit_chars ( zport_a ) ;
status = IRQ_HANDLED ;
}
return status ;
}
/*
* Finally , routines used to initialize the serial port .
*/
static int zs_startup ( struct uart_port * uport )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
unsigned long flags ;
int irq_guard ;
int ret ;
irq_guard = atomic_add_return ( 1 , & scc - > irq_guard ) ;
if ( irq_guard = = 1 ) {
ret = request_irq ( zport - > port . irq , zs_interrupt ,
IRQF_SHARED , " scc " , scc ) ;
if ( ret ) {
atomic_add ( - 1 , & scc - > irq_guard ) ;
printk ( KERN_ERR " zs: can't get irq %d \n " ,
zport - > port . irq ) ;
return ret ;
}
}
spin_lock_irqsave ( & scc - > zlock , flags ) ;
/* Clear the receive FIFO. */
zs_receive_drain ( zport ) ;
/* Clear the interrupt registers. */
write_zsreg ( zport , R0 , ERR_RES ) ;
write_zsreg ( zport , R0 , RES_Tx_P ) ;
/* But Ext only if not being handled already. */
if ( ! ( zport - > regs [ 1 ] & EXT_INT_ENAB ) )
write_zsreg ( zport , R0 , RES_EXT_INT ) ;
/* Finally, enable sequencing and interrupts. */
zport - > regs [ 1 ] & = ~ RxINT_MASK ;
zport - > regs [ 1 ] | = RxINT_ALL | TxINT_ENAB | EXT_INT_ENAB ;
zport - > regs [ 3 ] | = RxENABLE ;
zport - > regs [ 5 ] | = TxENAB ;
zport - > regs [ 15 ] | = BRKIE ;
write_zsreg ( zport , R1 , zport - > regs [ 1 ] ) ;
write_zsreg ( zport , R3 , zport - > regs [ 3 ] ) ;
write_zsreg ( zport , R5 , zport - > regs [ 5 ] ) ;
write_zsreg ( zport , R15 , zport - > regs [ 15 ] ) ;
/* Record the current state of RR0. */
zport - > mctrl = zs_raw_get_mctrl ( zport ) ;
zport - > brk = read_zsreg ( zport , R0 ) & BRK_ABRT ;
zport - > tx_stopped = 1 ;
spin_unlock_irqrestore ( & scc - > zlock , flags ) ;
return 0 ;
}
static void zs_shutdown ( struct uart_port * uport )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
unsigned long flags ;
int irq_guard ;
spin_lock_irqsave ( & scc - > zlock , flags ) ;
zport - > regs [ 5 ] & = ~ TxENAB ;
zport - > regs [ 3 ] & = ~ RxENABLE ;
write_zsreg ( zport , R5 , zport - > regs [ 5 ] ) ;
write_zsreg ( zport , R3 , zport - > regs [ 3 ] ) ;
spin_unlock_irqrestore ( & scc - > zlock , flags ) ;
irq_guard = atomic_add_return ( - 1 , & scc - > irq_guard ) ;
if ( ! irq_guard )
free_irq ( zport - > port . irq , scc ) ;
}
static void zs_reset ( struct zs_port * zport )
{
struct zs_scc * scc = zport - > scc ;
int irq ;
unsigned long flags ;
spin_lock_irqsave ( & scc - > zlock , flags ) ;
irq = ! irqs_disabled_flags ( flags ) ;
if ( ! scc - > initialised ) {
/* Reset the pointer first, just in case... */
read_zsreg ( zport , R0 ) ;
/* And let the current transmission finish. */
zs_line_drain ( zport , irq ) ;
write_zsreg ( zport , R9 , FHWRES ) ;
udelay ( 10 ) ;
write_zsreg ( zport , R9 , 0 ) ;
scc - > initialised = 1 ;
}
load_zsregs ( zport , zport - > regs , irq ) ;
spin_unlock_irqrestore ( & scc - > zlock , flags ) ;
}
static void zs_set_termios ( struct uart_port * uport , struct ktermios * termios ,
struct ktermios * old_termios )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
struct zs_port * zport_a = & scc - > zport [ ZS_CHAN_A ] ;
int irq ;
unsigned int baud , brg ;
unsigned long flags ;
spin_lock_irqsave ( & scc - > zlock , flags ) ;
irq = ! irqs_disabled_flags ( flags ) ;
/* Byte size. */
zport - > regs [ 3 ] & = ~ RxNBITS_MASK ;
zport - > regs [ 5 ] & = ~ TxNBITS_MASK ;
switch ( termios - > c_cflag & CSIZE ) {
case CS5 :
zport - > regs [ 3 ] | = Rx5 ;
zport - > regs [ 5 ] | = Tx5 ;
break ;
case CS6 :
zport - > regs [ 3 ] | = Rx6 ;
zport - > regs [ 5 ] | = Tx6 ;
break ;
case CS7 :
zport - > regs [ 3 ] | = Rx7 ;
zport - > regs [ 5 ] | = Tx7 ;
break ;
case CS8 :
default :
zport - > regs [ 3 ] | = Rx8 ;
zport - > regs [ 5 ] | = Tx8 ;
break ;
}
/* Parity and stop bits. */
zport - > regs [ 4 ] & = ~ ( XCLK_MASK | SB_MASK | PAR_ENA | PAR_EVEN ) ;
if ( termios - > c_cflag & CSTOPB )
zport - > regs [ 4 ] | = SB2 ;
else
zport - > regs [ 4 ] | = SB1 ;
if ( termios - > c_cflag & PARENB )
zport - > regs [ 4 ] | = PAR_ENA ;
if ( ! ( termios - > c_cflag & PARODD ) )
zport - > regs [ 4 ] | = PAR_EVEN ;
switch ( zport - > clk_mode ) {
case 64 :
zport - > regs [ 4 ] | = X64CLK ;
break ;
case 32 :
zport - > regs [ 4 ] | = X32CLK ;
break ;
case 16 :
zport - > regs [ 4 ] | = X16CLK ;
break ;
case 1 :
zport - > regs [ 4 ] | = X1CLK ;
break ;
default :
BUG ( ) ;
}
baud = uart_get_baud_rate ( uport , termios , old_termios , 0 ,
uport - > uartclk / zport - > clk_mode / 4 ) ;
brg = ZS_BPS_TO_BRG ( baud , uport - > uartclk / zport - > clk_mode ) ;
zport - > regs [ 12 ] = brg & 0xff ;
zport - > regs [ 13 ] = ( brg > > 8 ) & 0xff ;
uart_update_timeout ( uport , termios - > c_cflag , baud ) ;
uport - > read_status_mask = Rx_OVR ;
if ( termios - > c_iflag & INPCK )
uport - > read_status_mask | = FRM_ERR | PAR_ERR ;
if ( termios - > c_iflag & ( BRKINT | PARMRK ) )
uport - > read_status_mask | = Rx_BRK ;
uport - > ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNPAR )
uport - > ignore_status_mask | = FRM_ERR | PAR_ERR ;
if ( termios - > c_iflag & IGNBRK ) {
uport - > ignore_status_mask | = Rx_BRK ;
if ( termios - > c_iflag & IGNPAR )
uport - > ignore_status_mask | = Rx_OVR ;
}
if ( termios - > c_cflag & CREAD )
zport - > regs [ 3 ] | = RxENABLE ;
else
zport - > regs [ 3 ] & = ~ RxENABLE ;
if ( zport ! = zport_a ) {
if ( ! ( termios - > c_cflag & CLOCAL ) ) {
zport - > regs [ 15 ] | = DCDIE ;
} else
zport - > regs [ 15 ] & = ~ DCDIE ;
if ( termios - > c_cflag & CRTSCTS ) {
zport - > regs [ 15 ] | = CTSIE ;
} else
zport - > regs [ 15 ] & = ~ CTSIE ;
zs_raw_xor_mctrl ( zport ) ;
}
/* Load up the new values. */
load_zsregs ( zport , zport - > regs , irq ) ;
spin_unlock_irqrestore ( & scc - > zlock , flags ) ;
}
static const char * zs_type ( struct uart_port * uport )
{
return " Z85C30 SCC " ;
}
static void zs_release_port ( struct uart_port * uport )
{
iounmap ( uport - > membase ) ;
uport - > membase = 0 ;
release_mem_region ( uport - > mapbase , ZS_CHAN_IO_SIZE ) ;
}
static int zs_map_port ( struct uart_port * uport )
{
if ( ! uport - > membase )
uport - > membase = ioremap_nocache ( uport - > mapbase ,
ZS_CHAN_IO_SIZE ) ;
if ( ! uport - > membase ) {
printk ( KERN_ERR " zs: Cannot map MMIO \n " ) ;
return - ENOMEM ;
}
return 0 ;
}
static int zs_request_port ( struct uart_port * uport )
{
int ret ;
if ( ! request_mem_region ( uport - > mapbase , ZS_CHAN_IO_SIZE , " scc " ) ) {
printk ( KERN_ERR " zs: Unable to reserve MMIO resource \n " ) ;
return - EBUSY ;
}
ret = zs_map_port ( uport ) ;
if ( ret ) {
release_mem_region ( uport - > mapbase , ZS_CHAN_IO_SIZE ) ;
return ret ;
}
return 0 ;
}
static void zs_config_port ( struct uart_port * uport , int flags )
{
struct zs_port * zport = to_zport ( uport ) ;
if ( flags & UART_CONFIG_TYPE ) {
if ( zs_request_port ( uport ) )
return ;
uport - > type = PORT_ZS ;
zs_reset ( zport ) ;
}
}
static int zs_verify_port ( struct uart_port * uport , struct serial_struct * ser )
{
struct zs_port * zport = to_zport ( uport ) ;
int ret = 0 ;
if ( ser - > type ! = PORT_UNKNOWN & & ser - > type ! = PORT_ZS )
ret = - EINVAL ;
if ( ser - > irq ! = uport - > irq )
ret = - EINVAL ;
if ( ser - > baud_base ! = uport - > uartclk / zport - > clk_mode / 4 )
ret = - EINVAL ;
return ret ;
}
static struct uart_ops zs_ops = {
. tx_empty = zs_tx_empty ,
. set_mctrl = zs_set_mctrl ,
. get_mctrl = zs_get_mctrl ,
. stop_tx = zs_stop_tx ,
. start_tx = zs_start_tx ,
. stop_rx = zs_stop_rx ,
. enable_ms = zs_enable_ms ,
. break_ctl = zs_break_ctl ,
. startup = zs_startup ,
. shutdown = zs_shutdown ,
. set_termios = zs_set_termios ,
. type = zs_type ,
. release_port = zs_release_port ,
. request_port = zs_request_port ,
. config_port = zs_config_port ,
. verify_port = zs_verify_port ,
} ;
/*
* Initialize Z85C30 port structures .
*/
static int __init zs_probe_sccs ( void )
{
static int probed ;
struct zs_parms zs_parms ;
int chip , side , irq ;
int n_chips = 0 ;
int i ;
if ( probed )
return 0 ;
irq = dec_interrupt [ DEC_IRQ_SCC0 ] ;
if ( irq > = 0 ) {
zs_parms . scc [ n_chips ] = IOASIC_SCC0 ;
zs_parms . irq [ n_chips ] = dec_interrupt [ DEC_IRQ_SCC0 ] ;
n_chips + + ;
}
irq = dec_interrupt [ DEC_IRQ_SCC1 ] ;
if ( irq > = 0 ) {
zs_parms . scc [ n_chips ] = IOASIC_SCC1 ;
zs_parms . irq [ n_chips ] = dec_interrupt [ DEC_IRQ_SCC1 ] ;
n_chips + + ;
}
if ( ! n_chips )
return - ENXIO ;
probed = 1 ;
for ( chip = 0 ; chip < n_chips ; chip + + ) {
spin_lock_init ( & zs_sccs [ chip ] . zlock ) ;
for ( side = 0 ; side < ZS_NUM_CHAN ; side + + ) {
struct zs_port * zport = & zs_sccs [ chip ] . zport [ side ] ;
struct uart_port * uport = & zport - > port ;
zport - > scc = & zs_sccs [ chip ] ;
zport - > clk_mode = 16 ;
uport - > irq = zs_parms . irq [ chip ] ;
uport - > uartclk = ZS_CLOCK ;
uport - > fifosize = 1 ;
uport - > iotype = UPIO_MEM ;
uport - > flags = UPF_BOOT_AUTOCONF ;
uport - > ops = & zs_ops ;
uport - > line = chip * ZS_NUM_CHAN + side ;
uport - > mapbase = dec_kn_slot_base +
zs_parms . scc [ chip ] +
( side ^ ZS_CHAN_B ) * ZS_CHAN_IO_SIZE ;
for ( i = 0 ; i < ZS_NUM_REGS ; i + + )
zport - > regs [ i ] = zs_init_regs [ i ] ;
}
}
return 0 ;
}
# ifdef CONFIG_SERIAL_ZS_CONSOLE
static void zs_console_putchar ( struct uart_port * uport , int ch )
{
struct zs_port * zport = to_zport ( uport ) ;
struct zs_scc * scc = zport - > scc ;
int irq ;
unsigned long flags ;
spin_lock_irqsave ( & scc - > zlock , flags ) ;
irq = ! irqs_disabled_flags ( flags ) ;
if ( zs_transmit_drain ( zport , irq ) )
write_zsdata ( zport , ch ) ;
spin_unlock_irqrestore ( & scc - > zlock , flags ) ;
}
/*
* Print a string to the serial port trying not to disturb
* any possible real use of the port . . .
*/
static void zs_console_write ( struct console * co , const char * s ,
unsigned int count )
{
int chip = co - > index / ZS_NUM_CHAN , side = co - > index % ZS_NUM_CHAN ;
struct zs_port * zport = & zs_sccs [ chip ] . zport [ side ] ;
struct zs_scc * scc = zport - > scc ;
unsigned long flags ;
u8 txint , txenb ;
int irq ;
/* Disable transmit interrupts and enable the transmitter. */
spin_lock_irqsave ( & scc - > zlock , flags ) ;
txint = zport - > regs [ 1 ] ;
txenb = zport - > regs [ 5 ] ;
if ( txint & TxINT_ENAB ) {
zport - > regs [ 1 ] = txint & ~ TxINT_ENAB ;
write_zsreg ( zport , R1 , zport - > regs [ 1 ] ) ;
}
if ( ! ( txenb & TxENAB ) ) {
zport - > regs [ 5 ] = txenb | TxENAB ;
write_zsreg ( zport , R5 , zport - > regs [ 5 ] ) ;
}
spin_unlock_irqrestore ( & scc - > zlock , flags ) ;
uart_console_write ( & zport - > port , s , count , zs_console_putchar ) ;
/* Restore transmit interrupts and the transmitter enable. */
spin_lock_irqsave ( & scc - > zlock , flags ) ;
irq = ! irqs_disabled_flags ( flags ) ;
zs_line_drain ( zport , irq ) ;
if ( ! ( txenb & TxENAB ) ) {
zport - > regs [ 5 ] & = ~ TxENAB ;
write_zsreg ( zport , R5 , zport - > regs [ 5 ] ) ;
}
if ( txint & TxINT_ENAB ) {
zport - > regs [ 1 ] | = TxINT_ENAB ;
write_zsreg ( zport , R1 , zport - > regs [ 1 ] ) ;
}
spin_unlock_irqrestore ( & scc - > zlock , flags ) ;
}
/*
* Setup serial console baud / bits / parity . We do two things here :
* - construct a cflag setting for the first uart_open ( )
* - initialise the serial port
* Return non - zero if we didn ' t find a serial port .
*/
static int __init zs_console_setup ( struct console * co , char * options )
{
int chip = co - > index / ZS_NUM_CHAN , side = co - > index % ZS_NUM_CHAN ;
struct zs_port * zport = & zs_sccs [ chip ] . zport [ side ] ;
struct uart_port * uport = & zport - > port ;
int baud = 9600 ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
int ret ;
ret = zs_map_port ( uport ) ;
if ( ret )
return ret ;
zs_reset ( zport ) ;
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
return uart_set_options ( uport , co , baud , parity , bits , flow ) ;
}
static struct uart_driver zs_reg ;
static struct console zs_console = {
. name = " ttyS " ,
. write = zs_console_write ,
. device = uart_console_device ,
. setup = zs_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
. data = & zs_reg ,
} ;
/*
* Register console .
*/
static int __init zs_serial_console_init ( void )
{
int ret ;
ret = zs_probe_sccs ( ) ;
if ( ret )
return ret ;
register_console ( & zs_console ) ;
return 0 ;
}
console_initcall ( zs_serial_console_init ) ;
# define SERIAL_ZS_CONSOLE &zs_console
# else
# define SERIAL_ZS_CONSOLE NULL
# endif /* CONFIG_SERIAL_ZS_CONSOLE */
static struct uart_driver zs_reg = {
. owner = THIS_MODULE ,
. driver_name = " serial " ,
. dev_name = " ttyS " ,
. major = TTY_MAJOR ,
. minor = 64 ,
. nr = ZS_NUM_SCCS * ZS_NUM_CHAN ,
. cons = SERIAL_ZS_CONSOLE ,
} ;
/* zs_init inits the driver. */
static int __init zs_init ( void )
{
int i , ret ;
pr_info ( " %s%s \n " , zs_name , zs_version ) ;
/* Find out how many Z85C30 SCCs we have. */
ret = zs_probe_sccs ( ) ;
if ( ret )
return ret ;
ret = uart_register_driver ( & zs_reg ) ;
if ( ret )
return ret ;
for ( i = 0 ; i < ZS_NUM_SCCS * ZS_NUM_CHAN ; i + + ) {
struct zs_scc * scc = & zs_sccs [ i / ZS_NUM_CHAN ] ;
struct zs_port * zport = & scc - > zport [ i % ZS_NUM_CHAN ] ;
struct uart_port * uport = & zport - > port ;
if ( zport - > scc )
uart_add_one_port ( & zs_reg , uport ) ;
}
return 0 ;
}
static void __exit zs_exit ( void )
{
int i ;
for ( i = ZS_NUM_SCCS * ZS_NUM_CHAN - 1 ; i > = 0 ; i - - ) {
struct zs_scc * scc = & zs_sccs [ i / ZS_NUM_CHAN ] ;
struct zs_port * zport = & scc - > zport [ i % ZS_NUM_CHAN ] ;
struct uart_port * uport = & zport - > port ;
if ( zport - > scc )
uart_remove_one_port ( & zs_reg , uport ) ;
}
uart_unregister_driver ( & zs_reg ) ;
}
module_init ( zs_init ) ;
module_exit ( zs_exit ) ;