2005-04-17 02:20:36 +04:00
/*
2005-11-13 00:59:59 +03:00
* dz . c : Serial port driver for DECStations equiped
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
*
* [ 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
# include <linux/config.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/init.h>
# include <linux/console.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/serial_core.h>
# include <linux/serial.h>
# include <asm/bootinfo.h>
# 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>
# include <asm/irq.h>
# include <asm/system.h>
# include <asm/uaccess.h>
# define CONSOLE_LINE (3) /* for definition of struct console */
# include "dz.h"
# define DZ_INTR_DEBUG 1
static char * dz_name = " DECstation DZ serial driver version " ;
static char * dz_version = " 1.02 " ;
struct dz_port {
struct uart_port port ;
unsigned int cflag ;
} ;
static struct dz_port dz_ports [ DZ_NB_PORT ] ;
# ifdef DEBUG_DZ
/*
2005-11-13 00:59:59 +03:00
* debugging code to send out chars via prom
2005-04-17 02:20:36 +04:00
*/
static void debug_console ( const char * s , int count )
{
unsigned i ;
for ( i = 0 ; i < count ; i + + ) {
if ( * s = = 10 )
prom_printf ( " %c " , 13 ) ;
prom_printf ( " %c " , * s + + ) ;
}
}
# endif
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* 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 .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static inline unsigned short dz_in ( struct dz_port * dport , unsigned offset )
{
volatile unsigned short * addr =
( volatile unsigned short * ) ( dport - > port . membase + offset ) ;
return * addr ;
}
static inline void dz_out ( struct dz_port * dport , unsigned offset ,
unsigned short value )
{
volatile unsigned short * addr =
( volatile unsigned short * ) ( dport - > port . membase + offset ) ;
* addr = value ;
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* 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
{
struct dz_port * dport = ( struct dz_port * ) uport ;
unsigned short tmp , mask = 1 < < dport - > port . line ;
unsigned long flags ;
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
tmp = dz_in ( dport , DZ_TCR ) ; /* read the TX flag */
tmp & = ~ mask ; /* clear the TX flag */
dz_out ( dport , DZ_TCR , tmp ) ;
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
}
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
{
struct dz_port * dport = ( struct dz_port * ) uport ;
unsigned short tmp , mask = 1 < < dport - > port . line ;
unsigned long flags ;
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
tmp = dz_in ( dport , DZ_TCR ) ; /* read the TX flag */
tmp | = mask ; /* set the TX flag */
dz_out ( dport , DZ_TCR , tmp ) ;
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
}
static void dz_stop_rx ( struct uart_port * uport )
{
struct dz_port * dport = ( struct dz_port * ) uport ;
unsigned long flags ;
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
dport - > cflag & = ~ DZ_CREAD ;
dz_out ( dport , DZ_LPR , dport - > cflag ) ;
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
}
static void dz_enable_ms ( struct uart_port * port )
{
/* nothing to do */
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2005-11-13 00:59:59 +03:00
* Here starts 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 .
2005-04-17 02:20:36 +04:00
*
* Note : rs_interrupt ( ) is a " fast " interrupt , which means that it
* runs with interrupts turned off . People who may want to modify
* rs_interrupt ( ) should try to keep the interrupt handler as fast as
* 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 .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static inline void dz_receive_chars ( struct dz_port * dport )
{
struct tty_struct * tty = NULL ;
struct uart_icount * icount ;
int ignore = 0 ;
unsigned short status , tmp ;
unsigned char ch , flag ;
/* this code is going to be a problem...
the call to tty_flip_buffer is going to need
to be rethought . . .
*/
do {
status = dz_in ( dport , DZ_RBUF ) ;
/* punt so we don't get duplicate characters */
if ( ! ( status & DZ_DVAL ) )
goto ignore_char ;
ch = UCHAR ( status ) ; /* grab the char */
flag = TTY_NORMAL ;
#if 0
if ( info - > is_console ) {
if ( ch = = 0 )
return ; /* it's a break ... */
}
# endif
tty = dport - > port . info - > tty ; /* now tty points to the proper dev */
icount = & dport - > port . icount ;
if ( ! tty )
break ;
icount - > rx + + ;
/* keep track of the statistics */
if ( status & ( DZ_OERR | DZ_FERR | DZ_PERR ) ) {
if ( status & DZ_PERR ) /* parity error */
icount - > parity + + ;
else if ( status & DZ_FERR ) /* frame error */
icount - > frame + + ;
if ( status & DZ_OERR ) /* overrun error */
icount - > overrun + + ;
/* check to see if we should ignore the character
and mask off conditions that should be ignored
*/
if ( status & dport - > port . ignore_status_mask ) {
if ( + + ignore > 100 )
break ;
goto ignore_char ;
}
/* mask off the error conditions we want to ignore */
tmp = status & dport - > port . read_status_mask ;
if ( tmp & DZ_PERR ) {
flag = TTY_PARITY ;
# ifdef DEBUG_DZ
debug_console ( " PERR \n " , 5 ) ;
# endif
} else if ( tmp & DZ_FERR ) {
flag = TTY_FRAME ;
# ifdef DEBUG_DZ
debug_console ( " FERR \n " , 5 ) ;
# endif
}
if ( tmp & DZ_OERR ) {
# ifdef DEBUG_DZ
debug_console ( " OERR \n " , 5 ) ;
# endif
tty_insert_flip_char ( tty , ch , flag ) ;
ch = 0 ;
flag = TTY_OVERRUN ;
}
}
tty_insert_flip_char ( tty , ch , flag ) ;
ignore_char :
2006-02-09 00:43:03 +03:00
;
2005-04-17 02:20:36 +04:00
} while ( status & DZ_DVAL ) ;
if ( tty )
tty_flip_buffer_push ( tty ) ;
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* transmit_char ( )
*
* This routine deals with outputs to any lines .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static inline void dz_transmit_chars ( struct dz_port * dport )
{
struct circ_buf * xmit = & dport - > port . info - > xmit ;
unsigned char tmp ;
if ( dport - > port . x_char ) { /* XON/XOFF chars */
dz_out ( dport , DZ_TDR , dport - > port . x_char ) ;
dport - > port . icount . tx + + ;
dport - > port . x_char = 0 ;
return ;
}
/* if nothing to do or stopped or hardware stopped */
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( & dport - > port ) ) {
2005-08-31 13:12:14 +04:00
dz_stop_tx ( & dport - > port ) ;
2005-04-17 02:20:36 +04:00
return ;
}
/*
* if something to do . . . ( rember the dz has no output fifo so we go
* one char at a time : - <
*/
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 ) ;
/* Are we done */
if ( uart_circ_empty ( xmit ) )
2005-08-31 13:12:14 +04:00
dz_stop_tx ( & dport - > port ) ;
2005-04-17 02:20:36 +04:00
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* check_modem_status ( )
*
* Only valid for the MODEM line duh !
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static inline void check_modem_status ( struct dz_port * dport )
{
unsigned short status ;
/* if not ne modem line just return */
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 .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static irqreturn_t dz_interrupt ( int irq , void * dev , struct pt_regs * regs )
{
struct dz_port * dport ;
unsigned short status ;
/* get the reason why we just got an irq */
status = dz_in ( ( struct dz_port * ) dev , DZ_CSR ) ;
dport = & dz_ports [ LINE ( status ) ] ;
if ( status & DZ_RDONE )
dz_receive_chars ( dport ) ;
if ( status & DZ_TRDY )
dz_transmit_chars ( dport ) ;
/* FIXME: what about check modem status??? --rmk */
return IRQ_HANDLED ;
}
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Here ends the DZ interrupt routines .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static unsigned int dz_get_mctrl ( struct uart_port * uport )
{
struct dz_port * dport = ( struct dz_port * ) uport ;
unsigned int mctrl = TIOCM_CAR | TIOCM_DSR | TIOCM_CTS ;
if ( dport - > port . line = = DZ_MODEM ) {
/*
* CHECKME : This is a guess from the other code . . . - - rmk
*/
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 )
{
struct dz_port * dport = ( struct dz_port * ) uport ;
unsigned short tmp ;
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 )
{
struct dz_port * dport = ( struct dz_port * ) uport ;
unsigned long flags ;
unsigned short tmp ;
/* The dz lines for the mouse/keyboard must be
* opened using their respective drivers .
*/
if ( ( dport - > port . line = = DZ_KEYBOARD ) | |
( dport - > port . line = = DZ_MOUSE ) )
return - ENODEV ;
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
/* enable the interrupt and the scanning */
tmp = dz_in ( dport , DZ_CSR ) ;
tmp | = DZ_RIE | DZ_TIE | DZ_MSE ;
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 )
{
2005-08-31 13:12:14 +04:00
dz_stop_tx ( uport ) ;
2005-04-17 02:20:36 +04:00
}
/*
* get_lsr_info - get line status register info
*
* 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 .
2005-04-17 02:20:36 +04:00
*/
static unsigned int dz_tx_empty ( struct uart_port * uport )
{
struct dz_port * dport = ( struct dz_port * ) uport ;
unsigned short status = dz_in ( dport , DZ_LPR ) ;
/* FIXME: this appears to be obviously broken --rmk. */
return status ? TIOCSER_TEMT : 0 ;
}
static void dz_break_ctl ( struct uart_port * uport , int break_state )
{
struct dz_port * dport = ( struct dz_port * ) uport ;
unsigned long flags ;
unsigned short tmp , mask = 1 < < uport - > line ;
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 ) ;
}
static void dz_set_termios ( struct uart_port * uport , struct termios * termios ,
struct termios * old_termios )
{
struct dz_port * dport = ( struct dz_port * ) uport ;
unsigned long flags ;
unsigned int cflag , baud ;
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 ) ;
switch ( baud ) {
case 50 :
cflag | = DZ_B50 ;
break ;
case 75 :
cflag | = DZ_B75 ;
break ;
case 110 :
cflag | = DZ_B110 ;
break ;
case 134 :
cflag | = DZ_B134 ;
break ;
case 150 :
cflag | = DZ_B150 ;
break ;
case 300 :
cflag | = DZ_B300 ;
break ;
case 600 :
cflag | = DZ_B600 ;
break ;
case 1200 :
cflag | = DZ_B1200 ;
break ;
case 1800 :
cflag | = DZ_B1800 ;
break ;
case 2000 :
cflag | = DZ_B2000 ;
break ;
case 2400 :
cflag | = DZ_B2400 ;
break ;
case 3600 :
cflag | = DZ_B3600 ;
break ;
case 4800 :
cflag | = DZ_B4800 ;
break ;
case 7200 :
cflag | = DZ_B7200 ;
break ;
case 9600 :
default :
cflag | = DZ_B9600 ;
}
if ( termios - > c_cflag & CREAD )
cflag | = DZ_RXENAB ;
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
dz_out ( dport , DZ_LPR , cflag ) ;
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 ;
/* characters to ignore */
uport - > ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNPAR )
dport - > port . ignore_status_mask | = DZ_FERR | DZ_PERR ;
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
}
static const char * dz_type ( struct uart_port * port )
{
return " DZ " ;
}
static void dz_release_port ( struct uart_port * port )
{
/* nothing to do */
}
static int dz_request_port ( struct uart_port * port )
{
return 0 ;
}
static void dz_config_port ( struct uart_port * port , int flags )
{
if ( flags & UART_CONFIG_TYPE )
port - > type = PORT_DZ ;
}
/*
* verify the new serial_struct ( for TIOCSSERIAL ) .
*/
static int dz_verify_port ( struct uart_port * port , struct serial_struct * ser )
{
int ret = 0 ;
if ( ser - > type ! = PORT_UNKNOWN & & ser - > type ! = PORT_DZ )
ret = - EINVAL ;
if ( ser - > irq ! = port - > irq )
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 ,
. enable_ms = dz_enable_ms ,
. break_ctl = dz_break_ctl ,
. startup = dz_startup ,
. shutdown = dz_shutdown ,
. set_termios = dz_set_termios ,
. 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 ;
struct dz_port * dport ;
unsigned long base ;
int i ;
if ( ! first )
return ;
first = 0 ;
if ( mips_machtype = = MACH_DS23100 | |
mips_machtype = = MACH_DS5100 )
2005-11-13 01:00:27 +03:00
base = CKSEG1ADDR ( KN01_SLOT_BASE + KN01_DZ11 ) ;
2005-04-17 02:20:36 +04:00
else
2005-11-13 01:00:27 +03:00
base = CKSEG1ADDR ( KN02_SLOT_BASE + KN02_DZ11 ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 , dport = dz_ports ; i < DZ_NB_PORT ; i + + , dport + + ) {
spin_lock_init ( & dport - > port . lock ) ;
dport - > port . membase = ( char * ) base ;
2006-02-05 13:48:10 +03:00
dport - > port . iotype = UPIO_PORT ;
2005-04-17 02:20:36 +04:00
dport - > port . irq = dec_interrupt [ DEC_IRQ_DZ11 ] ;
dport - > port . line = i ;
dport - > port . fifosize = 1 ;
dport - > port . ops = & dz_ops ;
dport - > port . flags = UPF_BOOT_AUTOCONF ;
}
}
static void dz_reset ( struct dz_port * dport )
{
dz_out ( dport , DZ_CSR , DZ_CLR ) ;
while ( dz_in ( dport , DZ_CSR ) & DZ_CLR ) ;
/* FIXME: cpu_relax? */
iob ( ) ;
/* enable scanning */
dz_out ( dport , DZ_CSR , DZ_MSE ) ;
}
# ifdef CONFIG_SERIAL_DZ_CONSOLE
2006-03-20 23:00:09 +03:00
static void dz_console_putchar ( struct uart_port * port , int ch )
2005-04-17 02:20:36 +04:00
{
2006-03-20 23:00:09 +03:00
struct dz_port * dport = ( struct dz_port * ) uport ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
int loops = 2500 ;
2006-03-20 23:00:09 +03:00
unsigned short tmp = ( unsigned char ) ch ;
2005-04-17 02:20:36 +04:00
/* this code sends stuff out to serial device - spinning its
wheels and waiting . */
spin_lock_irqsave ( & dport - > port . lock , flags ) ;
/* spin our wheels */
while ( ( ( dz_in ( dport , DZ_CSR ) & DZ_TRDY ) ! = DZ_TRDY ) & & loops - - )
/* FIXME: cpu_relax, udelay? --rmk */
;
/* Actually transmit the character. */
dz_out ( dport , DZ_TDR , tmp ) ;
spin_unlock_irqrestore ( & dport - > port . lock , flags ) ;
}
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
*/
static void dz_console_print ( struct console * cons ,
const char * str ,
unsigned int count )
{
struct dz_port * dport = & dz_ports [ CONSOLE_LINE ] ;
# 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 )
{
struct dz_port * dport = & dz_ports [ CONSOLE_LINE ] ;
int baud = 9600 ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
int ret ;
unsigned short mask , tmp ;
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
dz_reset ( dport ) ;
ret = uart_set_options ( & dport - > port , co , baud , parity , bits , flow ) ;
if ( ret = = 0 ) {
mask = 1 < < dport - > port . line ;
tmp = dz_in ( dport , DZ_TCR ) ; /* read the TX flag */
if ( ! ( tmp & mask ) ) {
tmp | = mask ; /* set the TX flag */
dz_out ( dport , DZ_TCR , tmp ) ;
}
}
return ret ;
}
static struct console dz_sercons =
{
. name = " ttyS " ,
. write = dz_console_print ,
. device = uart_console_device ,
. setup = dz_console_setup ,
. flags = CON_CONSDEV | CON_PRINTBUFFER ,
. index = CONSOLE_LINE ,
} ;
void __init dz_serial_console_init ( void )
{
dz_init_ports ( ) ;
register_console ( & dz_sercons ) ;
}
# define SERIAL_DZ_CONSOLE &dz_sercons
# else
# define SERIAL_DZ_CONSOLE NULL
# endif /* CONFIG_SERIAL_DZ_CONSOLE */
static struct uart_driver dz_reg = {
. owner = THIS_MODULE ,
. driver_name = " serial " ,
. dev_name = " ttyS%d " ,
. major = TTY_MAJOR ,
. minor = 64 ,
. nr = DZ_NB_PORT ,
. cons = SERIAL_DZ_CONSOLE ,
} ;
int __init dz_init ( void )
{
unsigned long flags ;
int ret , i ;
printk ( " %s%s \n " , dz_name , dz_version ) ;
dz_init_ports ( ) ;
save_flags ( flags ) ;
cli ( ) ;
# ifndef CONFIG_SERIAL_DZ_CONSOLE
/* reset the chip */
dz_reset ( & dz_ports [ 0 ] ) ;
# endif
/* order matters here... the trick is that flags
is updated . . . in request_irq - to immediatedly obliterate
it is unwise . */
restore_flags ( flags ) ;
if ( request_irq ( dz_ports [ 0 ] . port . irq , dz_interrupt ,
SA_INTERRUPT , " DZ " , & dz_ports [ 0 ] ) )
panic ( " Unable to register DZ interrupt " ) ;
ret = uart_register_driver ( & dz_reg ) ;
if ( ret ! = 0 )
return ret ;
for ( i = 0 ; i < DZ_NB_PORT ; i + + )
uart_add_one_port ( & dz_reg , & dz_ports [ i ] . port ) ;
return ret ;
}
MODULE_DESCRIPTION ( " DECstation DZ serial driver " ) ;
MODULE_LICENSE ( " GPL " ) ;