2005-04-17 02:20:36 +04:00
/*
2006-12-07 07:38:59 +03:00
* dz . c : Serial port driver for DECstations equipped
2005-04-17 02:20:36 +04:00
* with the DZ chipset .
*
2005-11-13 00:59:59 +03:00
* Copyright ( C ) 1998 Olivier A . D . Lebaillif
*
2005-04-17 02:20:36 +04:00
* Email : olivier . lebaillif @ ifrsys . com
*
2008-02-07 11:15:09 +03:00
* Copyright ( C ) 2004 , 2006 , 2007 Maciej W . Rozycki
2006-12-07 07:38:59 +03:00
*
2005-04-17 02:20:36 +04:00
* [ 31 - AUG - 98 ] triemer
* Changed IRQ to use Harald ' s dec internals interrupts . h
* removed base_addr code - moving address assignment to setup . c
* Changed name of dz_init to rs_init to be consistent with tc code
* [ 13 - NOV - 98 ] triemer fixed code to receive characters
2005-11-13 00:59:59 +03:00
* after patches by harald to irq code .
2005-04-17 02:20:36 +04:00
* [ 09 - JAN - 99 ] triemer minor fix for schedule - due to removal of timeout
* field from " current " - somewhere between 2.1 .121 and 2.1 .131
Qua Jun 27 15 : 02 : 26 BRT 2001
* [ 27 - JUN - 2001 ] Arnaldo Carvalho de Melo < acme @ conectiva . com . br > - cleanups
2005-11-13 00:59:59 +03:00
*
* Parts ( C ) 1999 David Airlie , airlied @ linux . ie
* [ 07 - SEP - 99 ] Bugfixes
2005-04-17 02:20:36 +04:00
*
* [ 06 - Jan - 2002 ] Russell King < rmk @ arm . linux . org . uk >
* Converted to new serial core
*/
# undef DEBUG_DZ
2006-12-07 07:38:59 +03:00
# if defined(CONFIG_SERIAL_DZ_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
# define SUPPORT_SYSRQ
# endif
2008-02-07 11:15:09 +03:00
# include <linux/bitops.h>
# include <linux/compiler.h>
# include <linux/console.h>
2006-12-07 07:38:59 +03:00
# include <linux/delay.h>
2008-02-07 11:15:09 +03:00
# include <linux/errno.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
2008-02-07 11:15:09 +03:00
# include <linux/interrupt.h>
2008-02-07 11:15:15 +03:00
# include <linux/ioport.h>
2008-02-07 11:15:09 +03:00
# include <linux/kernel.h>
# include <linux/major.h>
# include <linux/module.h>
# include <linux/serial.h>
# include <linux/serial_core.h>
2006-12-07 07:38:59 +03:00
# include <linux/sysrq.h>
2005-04-17 02:20:36 +04:00
# include <linux/tty.h>
2011-09-01 18:20:57 +04:00
# include <linux/tty_flip.h>
2005-04-17 02:20:36 +04:00
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2005-04-17 02:20:36 +04:00
# include <asm/bootinfo.h>
2008-02-07 11:15:15 +03:00
# include <asm/io.h>
2008-02-07 11:15:09 +03:00
2005-04-17 02:20:36 +04:00
# include <asm/dec/interrupts.h>
# include <asm/dec/kn01.h>
# include <asm/dec/kn02.h>
# include <asm/dec/machtype.h>
# include <asm/dec/prom.h>
2008-02-07 11:15:15 +03:00
# include <asm/dec/system.h>
2005-04-17 02:20:36 +04:00
# include "dz.h"
2008-02-07 11:15:15 +03:00
MODULE_DESCRIPTION ( " DECstation DZ serial driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
static char dz_name [ ] __initdata = " DECstation DZ serial driver version " ;
static char dz_version [ ] __initdata = " 1.04 " ;
2005-04-17 02:20:36 +04:00
struct dz_port {
2008-02-07 11:15:15 +03:00
struct dz_mux * mux ;
2005-04-17 02:20:36 +04:00
struct uart_port port ;
unsigned int cflag ;
} ;
2008-02-07 11:15:15 +03:00
struct dz_mux {
struct dz_port dport [ DZ_NB_PORT ] ;
atomic_t map_guard ;
atomic_t irq_guard ;
int initialised ;
} ;
static struct dz_mux dz_mux ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:15 +03:00
static inline struct dz_port * to_dport ( struct uart_port * uport )
{
return container_of ( uport , struct dz_port , port ) ;
}
2005-04-17 02:20:36 +04:00
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* dz_in ( ) and dz_out ( )
*
2005-11-13 00:59:59 +03:00
* These routines are used to access the registers of the DZ
2005-04-17 02:20:36 +04:00
* chip , hiding relocation differences between implementation .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2008-02-07 11:15:15 +03:00
static u16 dz_in ( struct dz_port * dport , unsigned offset )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
void __iomem * addr = dport - > port . membase + offset ;
2006-12-07 07:38:59 +03:00
2008-02-07 11:15:15 +03:00
return readw ( addr ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:15:15 +03:00
static void dz_out ( struct dz_port * dport , unsigned offset , u16 value )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
void __iomem * addr = dport - > port . membase + offset ;
2006-12-07 07:38:59 +03:00
2008-02-07 11:15:15 +03:00
writew ( value , addr ) ;
2005-04-17 02:20:36 +04:00
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* rs_stop ( ) and rs_start ( )
*
2005-11-13 00:59:59 +03:00
* These routines are called before setting or resetting
* tty - > stopped . They enable or disable transmitter interrupts ,
2005-04-17 02:20:36 +04:00
* as necessary .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2005-08-31 13:12:14 +04:00
static void dz_stop_tx ( struct uart_port * uport )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2008-02-07 11:15:15 +03:00
u16 tmp , mask = 1 < < dport - > port . line ;
2005-04-17 02:20:36 +04:00
tmp = dz_in ( dport , DZ_TCR ) ; /* read the TX flag */
tmp & = ~ mask ; /* clear the TX flag */
dz_out ( dport , DZ_TCR , tmp ) ;
}
2005-08-31 13:12:14 +04:00
static void dz_start_tx ( struct uart_port * uport )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2008-02-07 11:15:15 +03:00
u16 tmp , mask = 1 < < dport - > port . line ;
2005-04-17 02:20:36 +04:00
tmp = dz_in ( dport , DZ_TCR ) ; /* read the TX flag */
tmp | = mask ; /* set the TX flag */
dz_out ( dport , DZ_TCR , tmp ) ;
}
static void dz_stop_rx ( struct uart_port * uport )
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:14 +03:00
dport - > cflag & = ~ DZ_RXENAB ;
dz_out ( dport , DZ_LPR , dport - > cflag ) ;
2005-04-17 02:20:36 +04:00
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
2006-12-07 07:38:59 +03:00
* Here start the interrupt handling routines . All of the following
* subroutines are declared as inline and are folded into
* dz_interrupt . They were separated out for readability ' s sake .
*
* Note : dz_interrupt ( ) is a " fast " interrupt , which means that it
2005-04-17 02:20:36 +04:00
* runs with interrupts turned off . People who may want to modify
2006-12-07 07:38:59 +03:00
* dz_interrupt ( ) should try to keep the interrupt handler as fast as
2005-04-17 02:20:36 +04:00
* possible . After you are done making modifications , it is not a bad
* idea to do :
2005-11-13 00:59:59 +03:00
*
2005-04-17 02:20:36 +04:00
* make drivers / serial / dz . s
*
* and look at the resulting assemble code in dz . s .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* receive_char ( )
*
* This routine deals with inputs from any lines .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2008-02-07 11:15:15 +03:00
static inline void dz_receive_chars ( struct dz_mux * mux )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:12 +03:00
struct uart_port * uport ;
2008-02-07 11:15:15 +03:00
struct dz_port * dport = & mux - > dport [ 0 ] ;
2005-04-17 02:20:36 +04:00
struct uart_icount * icount ;
2006-12-07 07:38:59 +03:00
int lines_rx [ DZ_NB_PORT ] = { [ 0 . . . DZ_NB_PORT - 1 ] = 0 } ;
2005-04-17 02:20:36 +04:00
unsigned char ch , flag ;
2008-02-07 11:15:15 +03:00
u16 status ;
2006-12-07 07:38:59 +03:00
int i ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:15 +03:00
while ( ( status = dz_in ( dport , DZ_RBUF ) ) & DZ_DVAL ) {
dport = & mux - > dport [ LINE ( status ) ] ;
2008-02-07 11:15:12 +03:00
uport = & dport - > port ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:38:59 +03:00
ch = UCHAR ( status ) ; /* grab the char */
2008-02-07 11:15:12 +03:00
flag = TTY_NORMAL ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:12 +03:00
icount = & uport - > icount ;
2005-04-17 02:20:36 +04:00
icount - > rx + + ;
2008-02-07 11:15:12 +03:00
if ( unlikely ( status & ( DZ_OERR | DZ_FERR | DZ_PERR ) ) ) {
2006-12-07 07:38:59 +03:00
/*
2008-02-07 11:15:12 +03:00
* There is no separate BREAK status bit , so treat
* null characters with framing errors as BREAKs ;
* normally , otherwise . For this move the Framing
* Error bit to a simulated BREAK bit .
2005-04-17 02:20:36 +04:00
*/
2008-02-07 11:15:12 +03:00
if ( ! ch ) {
status | = ( status & DZ_FERR ) > >
( ffs ( DZ_FERR ) - ffs ( DZ_BREAK ) ) ;
status & = ~ DZ_FERR ;
}
/* Handle SysRq/SAK & keep track of the statistics. */
if ( status & DZ_BREAK ) {
icount - > brk + + ;
if ( uart_handle_break ( uport ) )
continue ;
} else if ( status & DZ_FERR )
icount - > frame + + ;
else if ( status & DZ_PERR )
icount - > parity + + ;
if ( status & DZ_OERR )
icount - > overrun + + ;
status & = uport - > read_status_mask ;
if ( status & DZ_BREAK )
2006-12-07 07:38:59 +03:00
flag = TTY_BREAK ;
2008-02-07 11:15:12 +03:00
else if ( status & DZ_FERR )
2005-04-17 02:20:36 +04:00
flag = TTY_FRAME ;
2008-02-07 11:15:12 +03:00
else if ( status & DZ_PERR )
flag = TTY_PARITY ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:15:12 +03:00
if ( uart_handle_sysrq_char ( uport , ch ) )
2006-12-07 07:38:59 +03:00
continue ;
2008-02-07 11:15:12 +03:00
uart_insert_char ( uport , status , DZ_OERR , ch , flag ) ;
lines_rx [ LINE ( status ) ] = 1 ;
2006-12-07 07:38:59 +03:00
}
for ( i = 0 ; i < DZ_NB_PORT ; i + + )
if ( lines_rx [ i ] )
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( & mux - > dport [ i ] . port . state - > port ) ;
2005-04-17 02:20:36 +04:00
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* transmit_char ( )
*
* This routine deals with outputs to any lines .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2008-02-07 11:15:15 +03:00
static inline void dz_transmit_chars ( struct dz_mux * mux )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = & mux - > dport [ 0 ] ;
2006-12-07 07:38:59 +03:00
struct circ_buf * xmit ;
2005-04-17 02:20:36 +04:00
unsigned char tmp ;
2008-02-07 11:15:15 +03:00
u16 status ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:15 +03:00
status = dz_in ( dport , DZ_CSR ) ;
dport = & mux - > dport [ LINE ( status ) ] ;
2009-09-20 00:13:28 +04:00
xmit = & dport - > port . state - > xmit ;
2006-12-07 07:38:59 +03:00
if ( dport - > port . x_char ) { /* XON/XOFF chars */
2005-04-17 02:20:36 +04:00
dz_out ( dport , DZ_TDR , dport - > port . x_char ) ;
dport - > port . icount . tx + + ;
dport - > port . x_char = 0 ;
return ;
}
2006-12-07 07:38:59 +03:00
/* If nothing to do or stopped or hardware stopped. */
2005-04-17 02:20:36 +04:00
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( & dport - > port ) ) {
2008-02-07 11:15:11 +03:00
spin_lock ( & dport - > port . lock ) ;
2005-08-31 13:12:14 +04:00
dz_stop_tx ( & dport - > port ) ;
2008-02-07 11:15:11 +03:00
spin_unlock ( & dport - > port . lock ) ;
2005-04-17 02:20:36 +04:00
return ;
}
/*
2006-12-07 07:38:59 +03:00
* If something to do . . . ( remember the dz has no output fifo ,
* so we go one char at a time ) : - <
2005-04-17 02:20:36 +04:00
*/
tmp = xmit - > buf [ xmit - > tail ] ;
xmit - > tail = ( xmit - > tail + 1 ) & ( DZ_XMIT_SIZE - 1 ) ;
dz_out ( dport , DZ_TDR , tmp ) ;
dport - > port . icount . tx + + ;
if ( uart_circ_chars_pending ( xmit ) < DZ_WAKEUP_CHARS )
uart_write_wakeup ( & dport - > port ) ;
2006-12-07 07:38:59 +03:00
/* Are we are done. */
2008-02-07 11:15:11 +03:00
if ( uart_circ_empty ( xmit ) ) {
spin_lock ( & dport - > port . lock ) ;
2005-08-31 13:12:14 +04:00
dz_stop_tx ( & dport - > port ) ;
2008-02-07 11:15:11 +03:00
spin_unlock ( & dport - > port . lock ) ;
}
2005-04-17 02:20:36 +04:00
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2006-12-07 07:38:59 +03:00
* check_modem_status ( )
2005-04-17 02:20:36 +04:00
*
2006-12-07 07:38:59 +03:00
* DS 3100 & 5100 : Only valid for the MODEM line , duh !
* DS 5000 / 200 : Valid for the MODEM and PRINTER line .
2005-04-17 02:20:36 +04:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static inline void check_modem_status ( struct dz_port * dport )
{
2006-12-07 07:38:59 +03:00
/*
* FIXME :
* 1. No status change interrupt ; use a timer .
* 2. Handle the 3100 / 5000 as appropriate . - - macro
*/
2008-02-07 11:15:15 +03:00
u16 status ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:38:59 +03:00
/* If not the modem line just return. */
2005-04-17 02:20:36 +04:00
if ( dport - > port . line ! = DZ_MODEM )
return ;
status = dz_in ( dport , DZ_MSR ) ;
/* it's easy, since DSR2 is the only bit in the register */
if ( status )
dport - > port . icount . dsr + + ;
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* dz_interrupt ( )
*
* this is the main interrupt routine for the DZ chip .
* It deals with the multiple ports .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2008-02-07 11:15:15 +03:00
static irqreturn_t dz_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
struct dz_mux * mux = dev_id ;
struct dz_port * dport = & mux - > dport [ 0 ] ;
u16 status ;
2005-04-17 02:20:36 +04:00
/* get the reason why we just got an irq */
2006-12-07 07:38:59 +03:00
status = dz_in ( dport , DZ_CSR ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:38:59 +03:00
if ( ( status & ( DZ_RDONE | DZ_RIE ) ) = = ( DZ_RDONE | DZ_RIE ) )
2008-02-07 11:15:15 +03:00
dz_receive_chars ( mux ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:38:59 +03:00
if ( ( status & ( DZ_TRDY | DZ_TIE ) ) = = ( DZ_TRDY | DZ_TIE ) )
2008-02-07 11:15:15 +03:00
dz_transmit_chars ( mux ) ;
2005-04-17 02:20:36 +04:00
return IRQ_HANDLED ;
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Here ends the DZ interrupt routines .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static unsigned int dz_get_mctrl ( struct uart_port * uport )
{
2006-12-07 07:38:59 +03:00
/*
* FIXME : Handle the 3100 / 5000 as appropriate . - - macro
*/
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2005-04-17 02:20:36 +04:00
unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS ;
if ( dport - > port . line = = DZ_MODEM ) {
if ( dz_in ( dport , DZ_MSR ) & DZ_MODEM_DSR )
mctrl & = ~ TIOCM_DSR ;
}
return mctrl ;
}
static void dz_set_mctrl ( struct uart_port * uport , unsigned int mctrl )
{
2006-12-07 07:38:59 +03:00
/*
* FIXME : Handle the 3100 / 5000 as appropriate . - - macro
*/
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2008-02-07 11:15:15 +03:00
u16 tmp ;
2005-04-17 02:20:36 +04:00
if ( dport - > port . line = = DZ_MODEM ) {
tmp = dz_in ( dport , DZ_TCR ) ;
if ( mctrl & TIOCM_DTR )
tmp & = ~ DZ_MODEM_DTR ;
else
tmp | = DZ_MODEM_DTR ;
dz_out ( dport , DZ_TCR , tmp ) ;
}
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* startup ( )
*
* various initialization tasks
2005-11-13 00:59:59 +03:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2005-04-17 02:20:36 +04:00
*/
static int dz_startup ( struct uart_port * uport )
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2008-02-07 11:15:15 +03:00
struct dz_mux * mux = dport - > mux ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2008-02-07 11:15:15 +03:00
int irq_guard ;
int ret ;
u16 tmp ;
irq_guard = atomic_add_return ( 1 , & mux - > irq_guard ) ;
if ( irq_guard ! = 1 )
return 0 ;
ret = request_irq ( dport - > port . irq , dz_interrupt ,
IRQF_SHARED , " dz " , mux ) ;
if ( ret ) {
atomic_add ( - 1 , & mux - > irq_guard ) ;
printk ( KERN_ERR " dz: Cannot get IRQ %d! \n " , dport - > port . irq ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
2008-02-07 11:15:15 +03:00
/* Enable interrupts. */
2005-04-17 02:20:36 +04:00
tmp = dz_in ( dport , DZ_CSR ) ;
2008-02-07 11:15:15 +03:00
tmp | = DZ_RIE | DZ_TIE ;
2005-04-17 02:20:36 +04:00
dz_out ( dport , DZ_CSR , tmp ) ;
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
return 0 ;
}
2005-11-13 00:59:59 +03:00
/*
2005-04-17 02:20:36 +04:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* shutdown ( )
*
* This routine will shutdown a serial port ; interrupts are disabled , and
* DTR is dropped if the hangup on close termio flag is on .
2005-11-13 00:59:59 +03:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2005-04-17 02:20:36 +04:00
*/
static void dz_shutdown ( struct uart_port * uport )
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2008-02-07 11:15:15 +03:00
struct dz_mux * mux = dport - > mux ;
2008-02-07 11:15:11 +03:00
unsigned long flags ;
2008-02-07 11:15:15 +03:00
int irq_guard ;
u16 tmp ;
2008-02-07 11:15:11 +03:00
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
dz_stop_tx ( & dport - > port ) ;
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
2008-02-07 11:15:15 +03:00
irq_guard = atomic_add_return ( - 1 , & mux - > irq_guard ) ;
if ( ! irq_guard ) {
/* Disable interrupts. */
tmp = dz_in ( dport , DZ_CSR ) ;
tmp & = ~ ( DZ_RIE | DZ_TIE ) ;
dz_out ( dport , DZ_CSR , tmp ) ;
free_irq ( dport - > port . irq , mux ) ;
}
2005-04-17 02:20:36 +04:00
}
/*
2006-12-07 07:38:59 +03:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* dz_tx_empty ( ) - - get the transmitter empty status
2005-04-17 02:20:36 +04:00
*
* 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
2005-11-13 00:59:59 +03:00
* allows an RS485 driver to be written in user space .
2006-12-07 07:38:59 +03:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2005-04-17 02:20:36 +04:00
*/
static unsigned int dz_tx_empty ( struct uart_port * uport )
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2006-12-07 07:38:59 +03:00
unsigned short tmp , mask = 1 < < dport - > port . line ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:38:59 +03:00
tmp = dz_in ( dport , DZ_TCR ) ;
tmp & = mask ;
return tmp ? 0 : TIOCSER_TEMT ;
2005-04-17 02:20:36 +04:00
}
static void dz_break_ctl ( struct uart_port * uport , int break_state )
{
2006-12-07 07:38:59 +03:00
/*
* FIXME : Can ' t access BREAK bits in TDR easily ;
* reuse the code for polled TX . - - macro
*/
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2006-12-07 07:38:59 +03:00
unsigned short tmp , mask = 1 < < dport - > port . line ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & uport - > lock , flags ) ;
tmp = dz_in ( dport , DZ_TCR ) ;
if ( break_state )
tmp | = mask ;
else
tmp & = ~ mask ;
dz_out ( dport , DZ_TCR , tmp ) ;
spin_unlock_irqrestore ( & uport - > lock , flags ) ;
}
2008-02-07 11:15:14 +03:00
static int dz_encode_baud_rate ( unsigned int baud )
{
switch ( baud ) {
case 50 :
return DZ_B50 ;
case 75 :
return DZ_B75 ;
case 110 :
return DZ_B110 ;
case 134 :
return DZ_B134 ;
case 150 :
return DZ_B150 ;
case 300 :
return DZ_B300 ;
case 600 :
return DZ_B600 ;
case 1200 :
return DZ_B1200 ;
case 1800 :
return DZ_B1800 ;
case 2000 :
return DZ_B2000 ;
case 2400 :
return DZ_B2400 ;
case 3600 :
return DZ_B3600 ;
case 4800 :
return DZ_B4800 ;
case 7200 :
return DZ_B7200 ;
case 9600 :
return DZ_B9600 ;
default :
return - 1 ;
}
}
2008-02-07 11:15:15 +03:00
static void dz_reset ( struct dz_port * dport )
{
struct dz_mux * mux = dport - > mux ;
if ( mux - > initialised )
return ;
dz_out ( dport , DZ_CSR , DZ_CLR ) ;
while ( dz_in ( dport , DZ_CSR ) & DZ_CLR ) ;
iob ( ) ;
/* Enable scanning. */
dz_out ( dport , DZ_CSR , DZ_MSE ) ;
mux - > initialised = 1 ;
}
2006-12-08 13:38:45 +03:00
static void dz_set_termios ( struct uart_port * uport , struct ktermios * termios ,
struct ktermios * old_termios )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned int cflag , baud ;
2008-02-07 11:15:14 +03:00
int bflag ;
2005-04-17 02:20:36 +04:00
cflag = dport - > port . line ;
switch ( termios - > c_cflag & CSIZE ) {
case CS5 :
cflag | = DZ_CS5 ;
break ;
case CS6 :
cflag | = DZ_CS6 ;
break ;
case CS7 :
cflag | = DZ_CS7 ;
break ;
case CS8 :
default :
cflag | = DZ_CS8 ;
}
if ( termios - > c_cflag & CSTOPB )
cflag | = DZ_CSTOPB ;
if ( termios - > c_cflag & PARENB )
cflag | = DZ_PARENB ;
if ( termios - > c_cflag & PARODD )
cflag | = DZ_PARODD ;
baud = uart_get_baud_rate ( uport , termios , old_termios , 50 , 9600 ) ;
2008-02-07 11:15:14 +03:00
bflag = dz_encode_baud_rate ( baud ) ;
if ( bflag < 0 ) { /* Try to keep unchanged. */
baud = uart_get_baud_rate ( uport , old_termios , NULL , 50 , 9600 ) ;
bflag = dz_encode_baud_rate ( baud ) ;
if ( bflag < 0 ) { /* Resort to 9600. */
baud = 9600 ;
bflag = DZ_B9600 ;
}
tty_termios_encode_baud_rate ( termios , baud , baud ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:15:14 +03:00
cflag | = bflag ;
2005-04-17 02:20:36 +04:00
if ( termios - > c_cflag & CREAD )
cflag | = DZ_RXENAB ;
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
2008-02-07 11:15:14 +03:00
uart_update_timeout ( uport , termios - > c_cflag , baud ) ;
dz_out ( dport , DZ_LPR , cflag ) ;
2005-04-17 02:20:36 +04:00
dport - > cflag = cflag ;
/* setup accept flag */
dport - > port . read_status_mask = DZ_OERR ;
if ( termios - > c_iflag & INPCK )
dport - > port . read_status_mask | = DZ_FERR | DZ_PERR ;
2014-06-16 16:10:41 +04:00
if ( termios - > c_iflag & ( IGNBRK | BRKINT | PARMRK ) )
2008-02-07 11:15:12 +03:00
dport - > port . read_status_mask | = DZ_BREAK ;
2005-04-17 02:20:36 +04:00
/* characters to ignore */
uport - > ignore_status_mask = 0 ;
2008-02-07 11:15:12 +03:00
if ( ( termios - > c_iflag & ( IGNPAR | IGNBRK ) ) = = ( IGNPAR | IGNBRK ) )
dport - > port . ignore_status_mask | = DZ_OERR ;
2005-04-17 02:20:36 +04:00
if ( termios - > c_iflag & IGNPAR )
dport - > port . ignore_status_mask | = DZ_FERR | DZ_PERR ;
2008-02-07 11:15:12 +03:00
if ( termios - > c_iflag & IGNBRK )
dport - > port . ignore_status_mask | = DZ_BREAK ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
}
2008-07-24 08:29:49 +04:00
/*
* Hack alert !
* Required solely so that the initial PROM - based console
* works undisturbed in parallel with this one .
*/
static void dz_pm ( struct uart_port * uport , unsigned int state ,
unsigned int oldstate )
{
struct dz_port * dport = to_dport ( uport ) ;
unsigned long flags ;
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
if ( state < 3 )
dz_start_tx ( & dport - > port ) ;
else
dz_stop_tx ( & dport - > port ) ;
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
}
2008-02-07 11:15:15 +03:00
static const char * dz_type ( struct uart_port * uport )
2005-04-17 02:20:36 +04:00
{
return " DZ " ;
}
2008-02-07 11:15:15 +03:00
static void dz_release_port ( struct uart_port * uport )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
struct dz_mux * mux = to_dport ( uport ) - > mux ;
int map_guard ;
iounmap ( uport - > membase ) ;
uport - > membase = NULL ;
map_guard = atomic_add_return ( - 1 , & mux - > map_guard ) ;
if ( ! map_guard )
release_mem_region ( uport - > mapbase , dec_kn_slot_size ) ;
2005-04-17 02:20:36 +04:00
}
2008-02-07 11:15:15 +03:00
static int dz_map_port ( struct uart_port * uport )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
if ( ! uport - > membase )
uport - > membase = ioremap_nocache ( uport - > mapbase ,
dec_kn_slot_size ) ;
if ( ! uport - > membase ) {
printk ( KERN_ERR " dz: Cannot map MMIO \n " ) ;
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-02-07 11:15:15 +03:00
static int dz_request_port ( struct uart_port * uport )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
struct dz_mux * mux = to_dport ( uport ) - > mux ;
int map_guard ;
int ret ;
map_guard = atomic_add_return ( 1 , & mux - > map_guard ) ;
if ( map_guard = = 1 ) {
if ( ! request_mem_region ( uport - > mapbase , dec_kn_slot_size ,
" dz " ) ) {
atomic_add ( - 1 , & mux - > map_guard ) ;
printk ( KERN_ERR
" dz: Unable to reserve MMIO resource \n " ) ;
return - EBUSY ;
}
}
ret = dz_map_port ( uport ) ;
if ( ret ) {
map_guard = atomic_add_return ( - 1 , & mux - > map_guard ) ;
if ( ! map_guard )
release_mem_region ( uport - > mapbase , dec_kn_slot_size ) ;
return ret ;
}
return 0 ;
}
static void dz_config_port ( struct uart_port * uport , int flags )
{
struct dz_port * dport = to_dport ( uport ) ;
if ( flags & UART_CONFIG_TYPE ) {
if ( dz_request_port ( uport ) )
return ;
uport - > type = PORT_DZ ;
dz_reset ( dport ) ;
}
2005-04-17 02:20:36 +04:00
}
/*
2008-02-07 11:15:15 +03:00
* Verify the new serial_struct ( for TIOCSSERIAL ) .
2005-04-17 02:20:36 +04:00
*/
2008-02-07 11:15:15 +03:00
static int dz_verify_port ( struct uart_port * uport , struct serial_struct * ser )
2005-04-17 02:20:36 +04:00
{
int ret = 0 ;
2008-02-07 11:15:15 +03:00
2005-04-17 02:20:36 +04:00
if ( ser - > type ! = PORT_UNKNOWN & & ser - > type ! = PORT_DZ )
ret = - EINVAL ;
2008-02-07 11:15:15 +03:00
if ( ser - > irq ! = uport - > irq )
2005-04-17 02:20:36 +04:00
ret = - EINVAL ;
return ret ;
}
static struct uart_ops dz_ops = {
. tx_empty = dz_tx_empty ,
. get_mctrl = dz_get_mctrl ,
. set_mctrl = dz_set_mctrl ,
. stop_tx = dz_stop_tx ,
. start_tx = dz_start_tx ,
. stop_rx = dz_stop_rx ,
. break_ctl = dz_break_ctl ,
. startup = dz_startup ,
. shutdown = dz_shutdown ,
. set_termios = dz_set_termios ,
2008-07-24 08:29:49 +04:00
. pm = dz_pm ,
2005-04-17 02:20:36 +04:00
. type = dz_type ,
. release_port = dz_release_port ,
. request_port = dz_request_port ,
. config_port = dz_config_port ,
. verify_port = dz_verify_port ,
} ;
static void __init dz_init_ports ( void )
{
static int first = 1 ;
unsigned long base ;
2008-02-07 11:15:15 +03:00
int line ;
2005-04-17 02:20:36 +04:00
if ( ! first )
return ;
first = 0 ;
2008-02-07 11:15:15 +03:00
if ( mips_machtype = = MACH_DS23100 | | mips_machtype = = MACH_DS5100 )
base = dec_kn_slot_base + KN01_DZ11 ;
2005-04-17 02:20:36 +04:00
else
2008-02-07 11:15:15 +03:00
base = dec_kn_slot_base + KN02_DZ11 ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:15 +03:00
for ( line = 0 ; line < DZ_NB_PORT ; line + + ) {
struct dz_port * dport = & dz_mux . dport [ line ] ;
struct uart_port * uport = & dport - > port ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:15 +03:00
dport - > mux = & dz_mux ;
uport - > irq = dec_interrupt [ DEC_IRQ_DZ11 ] ;
uport - > fifosize = 1 ;
uport - > iotype = UPIO_MEM ;
uport - > flags = UPF_BOOT_AUTOCONF ;
uport - > ops = & dz_ops ;
uport - > line = line ;
uport - > mapbase = base ;
}
2005-04-17 02:20:36 +04:00
}
# ifdef CONFIG_SERIAL_DZ_CONSOLE
2006-12-07 07:38:59 +03:00
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* dz_console_putchar ( ) - - transmit a character
*
* Polled transmission . This is tricky . We need to mask transmit
* interrupts so that they do not interfere , enable the transmitter
* for the line requested and then wait till the transmit scanner
* requests data for this line . But it may request data for another
* line first , in which case we have to disable its transmitter and
* repeat waiting till our line pops up . Only then the character may
* be transmitted . Finally , the state of the transmitter mask is
* restored . Welcome to the world of PDP - 11 !
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
2006-06-26 20:18:11 +04:00
static void dz_console_putchar ( struct uart_port * uport , int ch )
2005-04-17 02:20:36 +04:00
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = to_dport ( uport ) ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
2006-12-07 07:38:59 +03:00
unsigned short csr , tcr , trdy , mask ;
int loops = 10000 ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
2006-12-07 07:38:59 +03:00
csr = dz_in ( dport , DZ_CSR ) ;
dz_out ( dport , DZ_CSR , csr & ~ DZ_TIE ) ;
tcr = dz_in ( dport , DZ_TCR ) ;
tcr | = 1 < < dport - > port . line ;
mask = tcr ;
dz_out ( dport , DZ_TCR , mask ) ;
iob ( ) ;
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:07 +03:00
do {
2006-12-07 07:38:59 +03:00
trdy = dz_in ( dport , DZ_CSR ) ;
if ( ! ( trdy & DZ_TRDY ) )
continue ;
trdy = ( trdy & DZ_TLINE ) > > 8 ;
if ( trdy = = dport - > port . line )
break ;
mask & = ~ ( 1 < < trdy ) ;
dz_out ( dport , DZ_TCR , mask ) ;
iob ( ) ;
udelay ( 2 ) ;
2008-04-28 13:11:50 +04:00
} while ( - - loops ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:38:59 +03:00
if ( loops ) /* Cannot send otherwise. */
dz_out ( dport , DZ_TDR , ch ) ;
2005-04-17 02:20:36 +04:00
2006-12-07 07:38:59 +03:00
dz_out ( dport , DZ_TCR , tcr ) ;
dz_out ( dport , DZ_CSR , csr ) ;
2005-04-17 02:20:36 +04:00
}
2006-03-20 23:00:09 +03:00
2005-11-13 00:59:59 +03:00
/*
2005-04-17 02:20:36 +04:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* dz_console_print ( )
*
* dz_console_print is registered for printk .
* The console must be locked when we get here .
2005-11-13 00:59:59 +03:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2005-04-17 02:20:36 +04:00
*/
2006-12-07 07:38:59 +03:00
static void dz_console_print ( struct console * co ,
2005-04-17 02:20:36 +04:00
const char * str ,
unsigned int count )
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = & dz_mux . dport [ co - > index ] ;
2005-04-17 02:20:36 +04:00
# ifdef DEBUG_DZ
prom_printf ( ( char * ) str ) ;
# endif
2006-03-20 23:00:09 +03:00
uart_console_write ( & dport - > port , str , count , dz_console_putchar ) ;
2005-04-17 02:20:36 +04:00
}
static int __init dz_console_setup ( struct console * co , char * options )
{
2008-02-07 11:15:15 +03:00
struct dz_port * dport = & dz_mux . dport [ co - > index ] ;
struct uart_port * uport = & dport - > port ;
2005-04-17 02:20:36 +04:00
int baud = 9600 ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
2008-02-07 11:15:15 +03:00
int ret ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:15 +03:00
ret = dz_map_port ( uport ) ;
if ( ret )
return ret ;
2005-04-17 02:20:36 +04:00
2008-07-24 08:29:49 +04:00
spin_lock_init ( & dport - > port . lock ) ; /* For dz_pm(). */
2005-04-17 02:20:36 +04:00
dz_reset ( dport ) ;
2008-07-24 08:29:49 +04:00
dz_pm ( uport , 0 , - 1 ) ;
2005-04-17 02:20:36 +04:00
2008-02-07 11:15:15 +03:00
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
2006-12-07 07:38:59 +03:00
return uart_set_options ( & dport - > port , co , baud , parity , bits , flow ) ;
2005-04-17 02:20:36 +04:00
}
2006-12-07 07:38:59 +03:00
static struct uart_driver dz_reg ;
2008-02-07 11:15:10 +03:00
static struct console dz_console = {
2005-04-17 02:20:36 +04:00
. name = " ttyS " ,
. write = dz_console_print ,
. device = uart_console_device ,
. setup = dz_console_setup ,
2006-12-07 07:38:59 +03:00
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
. data = & dz_reg ,
2005-04-17 02:20:36 +04:00
} ;
2006-12-07 07:38:59 +03:00
static int __init dz_serial_console_init ( void )
2005-04-17 02:20:36 +04:00
{
2006-12-07 07:38:59 +03:00
if ( ! IOASIC ) {
dz_init_ports ( ) ;
2008-02-07 11:15:10 +03:00
register_console ( & dz_console ) ;
2006-12-07 07:38:59 +03:00
return 0 ;
} else
return - ENXIO ;
2005-04-17 02:20:36 +04:00
}
2006-12-07 07:38:59 +03:00
console_initcall ( dz_serial_console_init ) ;
2008-02-07 11:15:10 +03:00
# define SERIAL_DZ_CONSOLE &dz_console
2005-04-17 02:20:36 +04:00
# else
# define SERIAL_DZ_CONSOLE NULL
# endif /* CONFIG_SERIAL_DZ_CONSOLE */
static struct uart_driver dz_reg = {
. owner = THIS_MODULE ,
. driver_name = " serial " ,
2006-12-07 07:38:59 +03:00
. dev_name = " ttyS " ,
2005-04-17 02:20:36 +04:00
. major = TTY_MAJOR ,
. minor = 64 ,
. nr = DZ_NB_PORT ,
. cons = SERIAL_DZ_CONSOLE ,
} ;
2006-12-07 07:38:59 +03:00
static int __init dz_init ( void )
2005-04-17 02:20:36 +04:00
{
int ret , i ;
2006-12-07 07:38:59 +03:00
if ( IOASIC )
return - ENXIO ;
2005-04-17 02:20:36 +04:00
printk ( " %s%s \n " , dz_name , dz_version ) ;
dz_init_ports ( ) ;
ret = uart_register_driver ( & dz_reg ) ;
2008-02-07 11:15:15 +03:00
if ( ret )
return ret ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < DZ_NB_PORT ; i + + )
2008-02-07 11:15:15 +03:00
uart_add_one_port ( & dz_reg , & dz_mux . dport [ i ] . port ) ;
2008-02-07 11:15:08 +03:00
2008-02-07 11:15:15 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2006-12-07 07:38:59 +03:00
module_init ( dz_init ) ;