2005-04-17 02:20:36 +04:00
/*
* linux / drivers / char / serial167 . c
*
* Driver for MVME166 / 7 board serial ports , which are via a CD2401 .
* Based very much on cyclades . c .
*
* MVME166 / 7 work by Richard Hirst [ richard @ sleepie . demon . co . uk ]
*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
* static char rcsid [ ] =
* " $Revision: 1.36.1.4 $$Date: 1995/03/29 06:14:14 $ " ;
*
* linux / kernel / cyclades . c
*
* Maintained by Marcio Saito ( cyclades @ netcom . com ) and
* Randolph Bentson ( bentson @ grieg . seaslug . org )
*
* Much of the design and some of the code came from serial . c
* which was copyright ( C ) 1991 , 1992 Linus Torvalds . It was
* extensively rewritten by Theodore Ts ' o , 8 / 16 / 92 - - 9 / 14 / 92 ,
* and then fixed as suggested by Michael K . Johnson 12 / 12 / 92.
*
* This version does not support shared irq ' s .
*
* $ Log : cyclades . c , v $
* Revision 1.36 .1 .4 1995 / 03 / 29 06 : 14 : 14 bentson
* disambiguate between Cyclom - 16 Y and Cyclom - 32 Ye ;
*
* Changes :
*
* 200 lines of changes record removed - RGH 11 - 10 - 95 , starting work on
* converting this to drive serial ports on mvme166 ( cd2401 ) .
*
* Arnaldo Carvalho de Melo < acme @ conectiva . com . br > - 2000 / 08 / 25
* - get rid of verify_area
* - use get_user to access memory from userspace in set_threshold ,
* set_default_threshold and set_timeout
* - don ' t use the panic function in serial167_init
* - do resource release on failure on serial167_init
* - include missing restore_flags in mvme167_serial_console_setup
*
* Kars de Jong < jongk @ linux - m68k . org > - 2004 / 09 / 06
* - replace bottom half handler with task queue handler
*/
# include <linux/errno.h>
# include <linux/signal.h>
# include <linux/sched.h>
# include <linux/timer.h>
# include <linux/tty.h>
# include <linux/interrupt.h>
# include <linux/serial.h>
# include <linux/serialP.h>
# include <linux/string.h>
# include <linux/fcntl.h>
# include <linux/ptrace.h>
# include <linux/serial167.h>
# include <linux/delay.h>
# include <linux/major.h>
# include <linux/mm.h>
# include <linux/console.h>
# include <linux/module.h>
# include <linux/bitops.h>
2006-10-10 00:27:42 +04:00
# include <linux/tty_flip.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/io.h>
# include <asm/mvme16xhw.h>
# include <asm/bootinfo.h>
# include <asm/setup.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <asm/uaccess.h>
# include <linux/init.h>
# define SERIAL_PARANOIA_CHECK
# undef SERIAL_DEBUG_OPEN
# undef SERIAL_DEBUG_THROTTLE
# undef SERIAL_DEBUG_OTHER
# undef SERIAL_DEBUG_IO
# undef SERIAL_DEBUG_COUNT
# undef SERIAL_DEBUG_DTR
# undef CYCLOM_16Y_HACK
# define CYCLOM_ENABLE_MONITORING
# define WAKEUP_CHARS 256
# define STD_COM_FLAGS (0)
# define SERIAL_TYPE_NORMAL 1
static struct tty_driver * cy_serial_driver ;
extern int serial_console ;
static struct cyclades_port * serial_console_info = NULL ;
static unsigned int serial_console_cflag = 0 ;
u_char initial_console_speed ;
/* Base address of cd2401 chip on mvme166/7 */
# define BASE_ADDR (0xfff45000)
# define pcc2chip ((volatile u_char *)0xfff42000)
# define PccSCCMICR 0x1d
# define PccSCCTICR 0x1e
# define PccSCCRICR 0x1f
# define PccTPIACKR 0x25
# define PccRPIACKR 0x27
# define PccIMLR 0x3f
/* This is the per-port data structure */
struct cyclades_port cy_port [ ] = {
2007-02-10 12:45:08 +03:00
/* CARD# */
{ - 1 } , /* ttyS0 */
{ - 1 } , /* ttyS1 */
{ - 1 } , /* ttyS2 */
{ - 1 } , /* ttyS3 */
2005-04-17 02:20:36 +04:00
} ;
2007-02-10 12:45:08 +03:00
2006-01-10 07:54:02 +03:00
# define NR_PORTS ARRAY_SIZE(cy_port)
2005-04-17 02:20:36 +04:00
/*
* This is used to look up the divisor speeds and the timeouts
* We ' re normally limited to 15 distinct baud rates . The extra
* are accessed via settings in info - > flags .
* 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ,
* 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 ,
* HI VHI
*/
static int baud_table [ ] = {
2007-02-10 12:45:08 +03:00
0 , 50 , 75 , 110 , 134 , 150 , 200 , 300 , 600 , 1200 ,
1800 , 2400 , 4800 , 9600 , 19200 , 38400 , 57600 , 76800 , 115200 , 150000 ,
0
} ;
2005-04-17 02:20:36 +04:00
#if 0
2007-02-10 12:45:08 +03:00
static char baud_co [ ] = { /* 25 MHz clock option table */
/* value => 00 01 02 03 04 */
/* divide by 8 32 128 512 2048 */
0x00 , 0x04 , 0x04 , 0x04 , 0x04 , 0x04 , 0x03 , 0x03 , 0x03 , 0x02 ,
0x02 , 0x02 , 0x01 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static char baud_bpr [ ] = { /* 25 MHz baud rate period table */
0x00 , 0xf5 , 0xa3 , 0x6f , 0x5c , 0x51 , 0xf5 , 0xa3 , 0x51 , 0xa3 ,
0x6d , 0x51 , 0xa3 , 0x51 , 0xa3 , 0x51 , 0x36 , 0x29 , 0x1b , 0x15
} ;
2005-04-17 02:20:36 +04:00
# endif
/* I think 166 brd clocks 2401 at 20MHz.... */
/* These values are written directly to tcor, and >> 5 for writing to rcor */
2007-02-10 12:45:08 +03:00
static u_char baud_co [ ] = { /* 20 MHz clock option table */
0x00 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x80 , 0x60 , 0x60 , 0x40 ,
0x40 , 0x40 , 0x20 , 0x20 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
2005-04-17 02:20:36 +04:00
/* These values written directly to tbpr/rbpr */
2007-02-10 12:45:08 +03:00
static u_char baud_bpr [ ] = { /* 20 MHz baud rate period table */
0x00 , 0xc0 , 0x80 , 0x58 , 0x6c , 0x40 , 0xc0 , 0x81 , 0x40 , 0x81 ,
0x57 , 0x40 , 0x81 , 0x40 , 0x81 , 0x40 , 0x2b , 0x20 , 0x15 , 0x10
} ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static u_char baud_cor4 [ ] = { /* receive threshold */
0x0a , 0x0a , 0x0a , 0x0a , 0x0a , 0x0a , 0x0a , 0x0a , 0x0a , 0x0a ,
0x0a , 0x0a , 0x0a , 0x09 , 0x09 , 0x08 , 0x08 , 0x08 , 0x08 , 0x07
} ;
2005-04-17 02:20:36 +04:00
static void shutdown ( struct cyclades_port * ) ;
2007-02-10 12:45:08 +03:00
static int startup ( struct cyclades_port * ) ;
2005-04-17 02:20:36 +04:00
static void cy_throttle ( struct tty_struct * ) ;
static void cy_unthrottle ( struct tty_struct * ) ;
static void config_setup ( struct cyclades_port * ) ;
extern void console_print ( const char * ) ;
# ifdef CYCLOM_SHOW_STATUS
static void show_status ( int ) ;
# endif
# ifdef CONFIG_REMOTE_DEBUG
static void debug_setup ( void ) ;
2007-02-10 12:45:08 +03:00
void queueDebugChar ( int c ) ;
2005-04-17 02:20:36 +04:00
int getDebugChar ( void ) ;
# define DEBUG_PORT 1
# define DEBUG_LEN 256
typedef struct {
2007-02-10 12:45:08 +03:00
int in ;
int out ;
unsigned char buf [ DEBUG_LEN ] ;
2005-04-17 02:20:36 +04:00
} debugq ;
debugq debugiq ;
# endif
/*
* I have my own version of udelay ( ) , as it is needed when initialising
* the chip , before the delay loop has been calibrated . Should probably
* reference one of the vmechip2 or pccchip2 counter for an accurate
* delay , but this wild guess will do for now .
*/
2007-02-10 12:45:08 +03:00
void my_udelay ( long us )
2005-04-17 02:20:36 +04:00
{
u_char x ;
volatile u_char * p = & x ;
int i ;
while ( us - - )
for ( i = 100 ; i ; i - - )
x | = * p ;
}
2007-02-10 12:45:08 +03:00
static inline int serial_paranoia_check ( struct cyclades_port * info , char * name ,
const char * routine )
2005-04-17 02:20:36 +04:00
{
# ifdef SERIAL_PARANOIA_CHECK
2007-02-10 12:45:08 +03:00
if ( ! info ) {
printk ( " Warning: null cyclades_port for (%s) in %s \n " , name ,
routine ) ;
return 1 ;
}
if ( ( long ) info < ( long ) ( & cy_port [ 0 ] )
| | ( long ) ( & cy_port [ NR_PORTS ] ) < ( long ) info ) {
printk ( " Warning: cyclades_port out of range for (%s) in %s \n " ,
name , routine ) ;
return 1 ;
}
if ( info - > magic ! = CYCLADES_MAGIC ) {
printk ( " Warning: bad magic number for serial struct (%s) in "
" %s \n " , name , routine ) ;
return 1 ;
}
2005-04-17 02:20:36 +04:00
# endif
return 0 ;
2007-02-10 12:45:08 +03:00
} /* serial_paranoia_check */
2005-04-17 02:20:36 +04:00
#if 0
/* The following diagnostic routines allow the driver to spew
information on the screen , even ( especially ! ) during interrupts .
*/
2007-02-10 12:45:08 +03:00
void SP ( char * data )
{
unsigned long flags ;
local_irq_save ( flags ) ;
console_print ( data ) ;
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
2005-04-17 02:20:36 +04:00
char scrn [ 2 ] ;
2007-02-10 12:45:08 +03:00
void CP ( char data )
{
unsigned long flags ;
local_irq_save ( flags ) ;
scrn [ 0 ] = data ;
console_print ( scrn ) ;
local_irq_restore ( flags ) ;
} /* CP */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
void CP1 ( int data )
{
( data < 10 ) ? CP ( data + ' 0 ' ) : CP ( data + ' A ' - 10 ) ;
} /* CP1 */
void CP2 ( int data )
{
CP1 ( ( data > > 4 ) & 0x0f ) ;
CP1 ( data & 0x0f ) ;
} /* CP2 */
void CP4 ( int data )
{
CP2 ( ( data > > 8 ) & 0xff ) ;
CP2 ( data & 0xff ) ;
} /* CP4 */
void CP8 ( long data )
{
CP4 ( ( data > > 16 ) & 0xffff ) ;
CP4 ( data & 0xffff ) ;
} /* CP8 */
2005-04-17 02:20:36 +04:00
# endif
/* This routine waits up to 1000 micro-seconds for the previous
command to the Cirrus chip to complete and then issues the
new command . An error is returned if the previous command
didn ' t finish within the time limit .
*/
2007-02-10 12:45:08 +03:00
u_short write_cy_cmd ( volatile u_char * base_addr , u_char cmd )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
unsigned long flags ;
volatile int i ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
/* Check to see that the previous command has completed */
2007-02-10 12:45:08 +03:00
for ( i = 0 ; i < 100 ; i + + ) {
if ( base_addr [ CyCCR ] = = 0 ) {
break ;
}
my_udelay ( 10L ) ;
2005-04-17 02:20:36 +04:00
}
/* if the CCR never cleared, the previous command
2007-02-10 12:45:08 +03:00
didn ' t finish within the " reasonable time " */
if ( i = = 10 ) {
local_irq_restore ( flags ) ;
return ( - 1 ) ;
2005-04-17 02:20:36 +04:00
}
/* Issue the new command */
base_addr [ CyCCR ] = cmd ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
return ( 0 ) ;
} /* write_cy_cmd */
2005-04-17 02:20:36 +04:00
/* cy_start and cy_stop provide software output flow control as a
function of XON / XOFF , software CTS , and other such stuff . */
2007-02-10 12:45:08 +03:00
static void cy_stop ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
volatile unsigned char * base_addr = ( unsigned char * ) BASE_ADDR ;
int channel ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OTHER
2007-02-10 12:45:08 +03:00
printk ( " cy_stop %s \n " , tty - > name ) ; /* */
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_stop " ) )
return ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) ( channel ) ; /* index channel */
base_addr [ CyIER ] & = ~ ( CyTxMpty | CyTxRdy ) ;
local_irq_restore ( flags ) ;
} /* cy_stop */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static void cy_start ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
volatile unsigned char * base_addr = ( unsigned char * ) BASE_ADDR ;
int channel ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OTHER
2007-02-10 12:45:08 +03:00
printk ( " cy_start %s \n " , tty - > name ) ; /* */
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_start " ) )
return ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) ( channel ) ;
base_addr [ CyIER ] | = CyTxMpty ;
local_irq_restore ( flags ) ;
} /* cy_start */
2005-04-17 02:20:36 +04:00
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver
* ( also known as the " bottom half " ) . This can be called any
* number of times for any channel without harm .
*/
2007-02-10 12:45:08 +03:00
static inline void cy_sched_event ( struct cyclades_port * info , int event )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
info - > event | = 1 < < event ; /* remember what kind of event and who */
schedule_work ( & info - > tqueue ) ;
} /* cy_sched_event */
2005-04-17 02:20:36 +04:00
/* The real interrupt service routines are called
whenever the card wants its hand held - - chars
received , out buffer empty , modem change , etc .
*/
2007-02-10 12:45:08 +03:00
static irqreturn_t cd2401_rxerr_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct tty_struct * tty ;
struct cyclades_port * info ;
volatile unsigned char * base_addr = ( unsigned char * ) BASE_ADDR ;
unsigned char err , rfoc ;
int channel ;
char data ;
/* determine the channel and change to that context */
channel = ( u_short ) ( base_addr [ CyLICR ] > > 2 ) ;
info = & cy_port [ channel ] ;
info - > last_active = jiffies ;
if ( ( err = base_addr [ CyRISR ] ) & CyTIMEOUT ) {
/* This is a receive timeout interrupt, ignore it */
base_addr [ CyREOIR ] = CyNOTRANS ;
return IRQ_HANDLED ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/* Read a byte of data if there is any - assume the error
* is associated with this character */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( ( rfoc = base_addr [ CyRFOC ] ) ! = 0 )
data = base_addr [ CyRDR ] ;
else
data = 0 ;
/* if there is nowhere to put the data, discard it */
if ( info - > tty = = 0 ) {
base_addr [ CyREOIR ] = rfoc ? 0 : CyNOTRANS ;
return IRQ_HANDLED ;
} else { /* there is an open port for this data */
tty = info - > tty ;
if ( err & info - > ignore_status_mask ) {
base_addr [ CyREOIR ] = rfoc ? 0 : CyNOTRANS ;
return IRQ_HANDLED ;
}
if ( tty_buffer_request_room ( tty , 1 ) ! = 0 ) {
if ( err & info - > read_status_mask ) {
if ( err & CyBREAK ) {
tty_insert_flip_char ( tty , data ,
TTY_BREAK ) ;
if ( info - > flags & ASYNC_SAK ) {
do_SAK ( tty ) ;
}
} else if ( err & CyFRAME ) {
tty_insert_flip_char ( tty , data ,
TTY_FRAME ) ;
} else if ( err & CyPARITY ) {
tty_insert_flip_char ( tty , data ,
TTY_PARITY ) ;
} else if ( err & CyOVERRUN ) {
tty_insert_flip_char ( tty , 0 ,
TTY_OVERRUN ) ;
/*
If the flip buffer itself is
overflowing , we still loose
the next incoming character .
*/
if ( tty_buffer_request_room ( tty , 1 ) ! =
0 ) {
tty_insert_flip_char ( tty , data ,
TTY_FRAME ) ;
}
/* These two conditions may imply */
/* a normal read should be done. */
/* else if(data & CyTIMEOUT) */
/* else if(data & CySPECHAR) */
} else {
tty_insert_flip_char ( tty , 0 ,
TTY_NORMAL ) ;
}
} else {
tty_insert_flip_char ( tty , data , TTY_NORMAL ) ;
}
} else {
/* there was a software buffer overrun
and nothing could be done about it ! ! ! */
}
}
tty_schedule_flip ( tty ) ;
/* end of service */
2005-04-17 02:20:36 +04:00
base_addr [ CyREOIR ] = rfoc ? 0 : CyNOTRANS ;
return IRQ_HANDLED ;
2007-02-10 12:45:08 +03:00
} /* cy_rxerr_interrupt */
static irqreturn_t cd2401_modem_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info ;
volatile unsigned char * base_addr = ( unsigned char * ) BASE_ADDR ;
int channel ;
int mdm_change ;
int mdm_status ;
/* determine the channel and change to that context */
channel = ( u_short ) ( base_addr [ CyLICR ] > > 2 ) ;
info = & cy_port [ channel ] ;
info - > last_active = jiffies ;
mdm_change = base_addr [ CyMISR ] ;
mdm_status = base_addr [ CyMSVR1 ] ;
if ( info - > tty = = 0 ) { /* nowhere to put the data, ignore it */
;
} else {
if ( ( mdm_change & CyDCD )
& & ( info - > flags & ASYNC_CHECK_CD ) ) {
if ( mdm_status & CyDCD ) {
2005-04-17 02:20:36 +04:00
/* CP('!'); */
2007-02-10 12:45:08 +03:00
cy_sched_event ( info , Cy_EVENT_OPEN_WAKEUP ) ;
} else {
2005-04-17 02:20:36 +04:00
/* CP('@'); */
2007-02-10 12:45:08 +03:00
cy_sched_event ( info , Cy_EVENT_HANGUP ) ;
}
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
if ( ( mdm_change & CyCTS )
& & ( info - > flags & ASYNC_CTS_FLOW ) ) {
if ( info - > tty - > stopped ) {
if ( mdm_status & CyCTS ) {
/* !!! cy_start isn't used because... */
info - > tty - > stopped = 0 ;
base_addr [ CyIER ] | = CyTxMpty ;
cy_sched_event ( info ,
Cy_EVENT_WRITE_WAKEUP ) ;
}
} else {
if ( ! ( mdm_status & CyCTS ) ) {
/* !!! cy_stop isn't used because... */
info - > tty - > stopped = 1 ;
base_addr [ CyIER ] & =
~ ( CyTxMpty | CyTxRdy ) ;
}
}
}
if ( mdm_status & CyDSR ) {
2005-04-17 02:20:36 +04:00
}
}
2007-02-10 12:45:08 +03:00
base_addr [ CyMEOIR ] = 0 ;
return IRQ_HANDLED ;
} /* cy_modem_interrupt */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static irqreturn_t cd2401_tx_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info ;
volatile unsigned char * base_addr = ( unsigned char * ) BASE_ADDR ;
int channel ;
int char_count , saved_cnt ;
int outch ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/* determine the channel and change to that context */
channel = ( u_short ) ( base_addr [ CyLICR ] > > 2 ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_REMOTE_DEBUG
2007-02-10 12:45:08 +03:00
if ( channel = = DEBUG_PORT ) {
panic ( " TxInt on debug port!!! " ) ;
}
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
info = & cy_port [ channel ] ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/* validate the port number (as configured and open) */
if ( ( channel < 0 ) | | ( NR_PORTS < = channel ) ) {
base_addr [ CyIER ] & = ~ ( CyTxMpty | CyTxRdy ) ;
base_addr [ CyTEOIR ] = CyNOTRANS ;
return IRQ_HANDLED ;
}
info - > last_active = jiffies ;
if ( info - > tty = = 0 ) {
base_addr [ CyIER ] & = ~ ( CyTxMpty | CyTxRdy ) ;
if ( info - > xmit_cnt < WAKEUP_CHARS ) {
cy_sched_event ( info , Cy_EVENT_WRITE_WAKEUP ) ;
}
base_addr [ CyTEOIR ] = CyNOTRANS ;
return IRQ_HANDLED ;
}
/* load the on-chip space available for outbound data */
saved_cnt = char_count = base_addr [ CyTFTC ] ;
if ( info - > x_char ) { /* send special char */
outch = info - > x_char ;
2005-04-17 02:20:36 +04:00
base_addr [ CyTDR ] = outch ;
char_count - - ;
2007-02-10 12:45:08 +03:00
info - > x_char = 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
if ( info - > x_break ) {
/* The Cirrus chip requires the "Embedded Transmit
Commands " of start break, delay, and end break
sequences to be sent . The duration of the
break is given in TICs , which runs at HZ
( typically 100 ) and the PPR runs at 200 Hz ,
so the delay is duration * 200 / HZ , and thus a
break can run from 1 / 100 sec to about 5 / 4 sec .
Need to check these values - RGH 141095.
*/
base_addr [ CyTDR ] = 0 ; /* start break */
base_addr [ CyTDR ] = 0x81 ;
base_addr [ CyTDR ] = 0 ; /* delay a bit */
base_addr [ CyTDR ] = 0x82 ;
base_addr [ CyTDR ] = info - > x_break * 200 / HZ ;
base_addr [ CyTDR ] = 0 ; /* terminate break */
base_addr [ CyTDR ] = 0x83 ;
char_count - = 7 ;
info - > x_break = 0 ;
}
while ( char_count > 0 ) {
if ( ! info - > xmit_cnt ) {
base_addr [ CyIER ] & = ~ ( CyTxMpty | CyTxRdy ) ;
break ;
}
if ( info - > xmit_buf = = 0 ) {
base_addr [ CyIER ] & = ~ ( CyTxMpty | CyTxRdy ) ;
break ;
}
if ( info - > tty - > stopped | | info - > tty - > hw_stopped ) {
base_addr [ CyIER ] & = ~ ( CyTxMpty | CyTxRdy ) ;
break ;
}
/* Because the Embedded Transmit Commands have been
enabled , we must check to see if the escape
character , NULL , is being sent . If it is , we
must ensure that there is room for it to be
doubled in the output stream . Therefore we
no longer advance the pointer when the character
is fetched , but rather wait until after the check
for a NULL output character . ( This is necessary
because there may not be room for the two chars
needed to send a NULL .
*/
outch = info - > xmit_buf [ info - > xmit_tail ] ;
if ( outch ) {
info - > xmit_cnt - - ;
info - > xmit_tail = ( info - > xmit_tail + 1 )
& ( PAGE_SIZE - 1 ) ;
base_addr [ CyTDR ] = outch ;
char_count - - ;
} else {
if ( char_count > 1 ) {
info - > xmit_cnt - - ;
info - > xmit_tail = ( info - > xmit_tail + 1 )
& ( PAGE_SIZE - 1 ) ;
base_addr [ CyTDR ] = outch ;
base_addr [ CyTDR ] = 0 ;
char_count - - ;
char_count - - ;
} else {
break ;
}
}
}
if ( info - > xmit_cnt < WAKEUP_CHARS ) {
cy_sched_event ( info , Cy_EVENT_WRITE_WAKEUP ) ;
}
base_addr [ CyTEOIR ] = ( char_count ! = saved_cnt ) ? 0 : CyNOTRANS ;
return IRQ_HANDLED ;
} /* cy_tx_interrupt */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static irqreturn_t cd2401_rx_interrupt ( int irq , void * dev_id )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct tty_struct * tty ;
struct cyclades_port * info ;
volatile unsigned char * base_addr = ( unsigned char * ) BASE_ADDR ;
int channel ;
char data ;
int char_count ;
int save_cnt ;
int len ;
/* determine the channel and change to that context */
channel = ( u_short ) ( base_addr [ CyLICR ] > > 2 ) ;
info = & cy_port [ channel ] ;
info - > last_active = jiffies ;
save_cnt = char_count = base_addr [ CyRFOC ] ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_REMOTE_DEBUG
2007-02-10 12:45:08 +03:00
if ( channel = = DEBUG_PORT ) {
while ( char_count - - ) {
data = base_addr [ CyRDR ] ;
queueDebugChar ( data ) ;
}
} else
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
/* if there is nowhere to put the data, discard it */
if ( info - > tty = = 0 ) {
while ( char_count - - ) {
data = base_addr [ CyRDR ] ;
}
} else { /* there is an open port for this data */
tty = info - > tty ;
/* load # characters available from the chip */
2005-04-17 02:20:36 +04:00
# ifdef CYCLOM_ENABLE_MONITORING
2007-02-10 12:45:08 +03:00
+ + info - > mon . int_count ;
info - > mon . char_count + = char_count ;
if ( char_count > info - > mon . char_max )
info - > mon . char_max = char_count ;
info - > mon . char_last = char_count ;
# endif
len = tty_buffer_request_room ( tty , char_count ) ;
while ( len - - ) {
data = base_addr [ CyRDR ] ;
tty_insert_flip_char ( tty , data , TTY_NORMAL ) ;
2005-04-17 02:20:36 +04:00
# ifdef CYCLOM_16Y_HACK
2007-02-10 12:45:08 +03:00
udelay ( 10L ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
}
tty_schedule_flip ( tty ) ;
}
/* end of service */
base_addr [ CyREOIR ] = save_cnt ? 0 : CyNOTRANS ;
return IRQ_HANDLED ;
} /* cy_rx_interrupt */
2005-04-17 02:20:36 +04:00
/*
* This routine is used to handle the " bottom half " processing for the
* serial driver , known also the " software interrupt " processing .
* This processing is done at the kernel interrupt level , after the
* cy # / _interrupt ( ) has returned , BUT WITH INTERRUPTS TURNED ON . This
* is where time - consuming activities which can not be done in the
* interrupt driver proper are done ; the interrupt driver schedules
* them using cy_sched_event ( ) , and they get done here .
*
* This is done through one level of indirection - - the task queue .
* When a hardware interrupt service routine wants service by the
* driver ' s bottom half , it enqueues the appropriate tq_struct ( one
* per port ) to the keventd work queue and sets a request flag
* that the work queue be processed .
*
* Although this may seem unwieldy , it gives the system a way to
* pass an argument ( in this case the pointer to the cyclades_port
* structure ) to the bottom half of the driver . Previous kernels
* had to poll every port to see if that port needed servicing .
*/
2007-02-10 12:45:08 +03:00
static void do_softint ( struct work_struct * ugly_api )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info =
container_of ( ugly_api , struct cyclades_port , tqueue ) ;
struct tty_struct * tty ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
tty = info - > tty ;
if ( ! tty )
return ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( test_and_clear_bit ( Cy_EVENT_HANGUP , & info - > event ) ) {
tty_hangup ( info - > tty ) ;
wake_up_interruptible ( & info - > open_wait ) ;
info - > flags & = ~ ASYNC_NORMAL_ACTIVE ;
}
if ( test_and_clear_bit ( Cy_EVENT_OPEN_WAKEUP , & info - > event ) ) {
wake_up_interruptible ( & info - > open_wait ) ;
}
if ( test_and_clear_bit ( Cy_EVENT_WRITE_WAKEUP , & info - > event ) ) {
tty_wakeup ( tty ) ;
}
} /* do_softint */
2005-04-17 02:20:36 +04:00
/* This is called whenever a port becomes active;
interrupts are enabled and DTR & RTS are turned on .
*/
2007-02-10 12:45:08 +03:00
static int startup ( struct cyclades_port * info )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
unsigned long flags ;
volatile unsigned char * base_addr = ( unsigned char * ) BASE_ADDR ;
int channel ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( info - > flags & ASYNC_INITIALIZED ) {
return 0 ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( ! info - > type ) {
if ( info - > tty ) {
set_bit ( TTY_IO_ERROR , & info - > tty - > flags ) ;
}
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
if ( ! info - > xmit_buf ) {
info - > xmit_buf = ( unsigned char * ) get_zeroed_page ( GFP_KERNEL ) ;
if ( ! info - > xmit_buf ) {
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
config_setup ( info ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " startup channel %d \n " , channel ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
write_cy_cmd ( base_addr , CyENB_RCVR | CyENB_XMTR ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyCAR ] = ( u_char ) channel ; /* !!! Is this needed? */
2005-04-17 02:20:36 +04:00
base_addr [ CyMSVR1 ] = CyRTS ;
/* CP('S');CP('1'); */
base_addr [ CyMSVR2 ] = CyDTR ;
# ifdef SERIAL_DEBUG_DTR
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: raising DTR \n " , __LINE__ ) ;
printk ( " status: 0x%x, 0x%x \n " , base_addr [ CyMSVR1 ] ,
base_addr [ CyMSVR2 ] ) ;
2005-04-17 02:20:36 +04:00
# endif
base_addr [ CyIER ] | = CyRxData ;
info - > flags | = ASYNC_INITIALIZED ;
2007-02-10 12:45:08 +03:00
if ( info - > tty ) {
clear_bit ( TTY_IO_ERROR , & info - > tty - > flags ) ;
2005-04-17 02:20:36 +04:00
}
info - > xmit_cnt = info - > xmit_head = info - > xmit_tail = 0 ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " done \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
return 0 ;
} /* startup */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
void start_xmit ( struct cyclades_port * info )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
unsigned long flags ;
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
base_addr [ CyCAR ] = channel ;
base_addr [ CyIER ] | = CyTxMpty ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
} /* start_xmit */
2005-04-17 02:20:36 +04:00
/*
* This routine shuts down a serial port ; interrupts are disabled ,
* and DTR is dropped if the hangup on close termio flag is on .
*/
2007-02-10 12:45:08 +03:00
static void shutdown ( struct cyclades_port * info )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
unsigned long flags ;
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( ! ( info - > flags & ASYNC_INITIALIZED ) ) {
2005-04-17 02:20:36 +04:00
/* CP('$'); */
2007-02-10 12:45:08 +03:00
return ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " shutdown channel %d \n " , channel ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
/* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE
SENT BEFORE DROPPING THE LINE ! ! ! ( Perhaps
set some flag that is read when XMTY happens . )
Other choices are to delay some fixed interval
or schedule some later processing .
*/
local_irq_save ( flags ) ;
if ( info - > xmit_buf ) {
free_page ( ( unsigned long ) info - > xmit_buf ) ;
info - > xmit_buf = NULL ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
if ( ! info - > tty | | ( info - > tty - > termios - > c_cflag & HUPCL ) ) {
2007-02-10 12:45:08 +03:00
base_addr [ CyMSVR1 ] = 0 ;
2005-04-17 02:20:36 +04:00
/* CP('C');CP('1'); */
2007-02-10 12:45:08 +03:00
base_addr [ CyMSVR2 ] = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_DTR
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: dropping DTR \n " , __LINE__ ) ;
printk ( " status: 0x%x, 0x%x \n " , base_addr [ CyMSVR1 ] ,
base_addr [ CyMSVR2 ] ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
}
write_cy_cmd ( base_addr , CyDIS_RCVR ) ;
/* it may be appropriate to clear _XMIT at
some later date ( after testing ) ! ! ! */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( info - > tty ) {
set_bit ( TTY_IO_ERROR , & info - > tty - > flags ) ;
2005-04-17 02:20:36 +04:00
}
info - > flags & = ~ ASYNC_INITIALIZED ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " done \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
} /* shutdown */
2005-04-17 02:20:36 +04:00
/*
* This routine finds or computes the various line characteristics .
*/
2007-02-10 12:45:08 +03:00
static void config_setup ( struct cyclades_port * info )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
unsigned long flags ;
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
unsigned cflag ;
int i ;
unsigned char ti , need_init_chan = 0 ;
if ( ! info - > tty | | ! info - > tty - > termios ) {
return ;
}
if ( info - > line = = - 1 ) {
return ;
}
cflag = info - > tty - > termios - > c_cflag ;
/* baud rate */
i = cflag & CBAUD ;
2005-04-17 02:20:36 +04:00
# ifdef CBAUDEX
/* Starting with kernel 1.1.65, there is direct support for
higher baud rates . The following code supports those
changes . The conditional aspect allows this driver to be
used for earlier as well as later kernel versions . ( The
mapping is slightly different from serial . c because there
is still the possibility of supporting 75 kbit / sec with
the Cyclades board . )
*/
2007-02-10 12:45:08 +03:00
if ( i & CBAUDEX ) {
if ( i = = B57600 )
i = 16 ;
else if ( i = = B115200 )
i = 18 ;
2005-04-17 02:20:36 +04:00
# ifdef B78600
2007-02-10 12:45:08 +03:00
else if ( i = = B78600 )
i = 17 ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
else
info - > tty - > termios - > c_cflag & = ~ CBAUDEX ;
}
# endif
if ( i = = 15 ) {
if ( ( info - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_HI )
i + = 1 ;
if ( ( info - > flags & ASYNC_SPD_MASK ) = = ASYNC_SPD_VHI )
i + = 3 ;
}
/* Don't ever change the speed of the console port. It will
* run at the speed specified in bootinfo , or at 19.2 K */
/* Actually, it should run at whatever speed 166Bug was using */
/* Note info->timeout isn't used at present */
if ( info ! = serial_console_info ) {
info - > tbpr = baud_bpr [ i ] ; /* Tx BPR */
info - > tco = baud_co [ i ] ; /* Tx CO */
info - > rbpr = baud_bpr [ i ] ; /* Rx BPR */
info - > rco = baud_co [ i ] > > 5 ; /* Rx CO */
if ( baud_table [ i ] = = 134 ) {
info - > timeout =
( info - > xmit_fifo_size * HZ * 30 / 269 ) + 2 ;
/* get it right for 134.5 baud */
} else if ( baud_table [ i ] ) {
info - > timeout =
( info - > xmit_fifo_size * HZ * 15 / baud_table [ i ] ) +
2 ;
/* this needs to be propagated into the card info */
} else {
info - > timeout = 0 ;
}
}
/* By tradition (is it a standard?) a baud rate of zero
implies the line should be / has been closed . A bit
later in this routine such a test is performed . */
/* byte size and parity */
info - > cor7 = 0 ;
info - > cor6 = 0 ;
info - > cor5 = 0 ;
info - > cor4 = ( info - > default_threshold ? info - > default_threshold : baud_cor4 [ i ] ) ; /* receive threshold */
/* Following two lines added 101295, RGH. */
/* It is obviously wrong to access CyCORx, and not info->corx here,
* try and remember to fix it later ! */
channel = info - > line ;
base_addr [ CyCAR ] = ( u_char ) channel ;
if ( C_CLOCAL ( info - > tty ) ) {
if ( base_addr [ CyIER ] & CyMdmCh )
base_addr [ CyIER ] & = ~ CyMdmCh ; /* without modem intr */
/* ignore 1->0 modem transitions */
if ( base_addr [ CyCOR4 ] & ( CyDSR | CyCTS | CyDCD ) )
base_addr [ CyCOR4 ] & = ~ ( CyDSR | CyCTS | CyDCD ) ;
/* ignore 0->1 modem transitions */
if ( base_addr [ CyCOR5 ] & ( CyDSR | CyCTS | CyDCD ) )
base_addr [ CyCOR5 ] & = ~ ( CyDSR | CyCTS | CyDCD ) ;
2005-04-17 02:20:36 +04:00
} else {
2007-02-10 12:45:08 +03:00
if ( ( base_addr [ CyIER ] & CyMdmCh ) ! = CyMdmCh )
base_addr [ CyIER ] | = CyMdmCh ; /* with modem intr */
/* act on 1->0 modem transitions */
if ( ( base_addr [ CyCOR4 ] & ( CyDSR | CyCTS | CyDCD ) ) ! =
( CyDSR | CyCTS | CyDCD ) )
base_addr [ CyCOR4 ] | = CyDSR | CyCTS | CyDCD ;
/* act on 0->1 modem transitions */
if ( ( base_addr [ CyCOR5 ] & ( CyDSR | CyCTS | CyDCD ) ) ! =
( CyDSR | CyCTS | CyDCD ) )
base_addr [ CyCOR5 ] | = CyDSR | CyCTS | CyDCD ;
}
info - > cor3 = ( cflag & CSTOPB ) ? Cy_2_STOP : Cy_1_STOP ;
info - > cor2 = CyETC ;
switch ( cflag & CSIZE ) {
case CS5 :
info - > cor1 = Cy_5_BITS ;
break ;
case CS6 :
info - > cor1 = Cy_6_BITS ;
break ;
case CS7 :
info - > cor1 = Cy_7_BITS ;
break ;
case CS8 :
info - > cor1 = Cy_8_BITS ;
break ;
}
if ( cflag & PARENB ) {
if ( cflag & PARODD ) {
info - > cor1 | = CyPARITY_O ;
} else {
info - > cor1 | = CyPARITY_E ;
}
} else {
info - > cor1 | = CyPARITY_NONE ;
}
/* CTS flow control flag */
2005-04-17 02:20:36 +04:00
#if 0
2007-02-10 12:45:08 +03:00
/* Don't complcate matters for now! RGH 141095 */
if ( cflag & CRTSCTS ) {
info - > flags | = ASYNC_CTS_FLOW ;
info - > cor2 | = CyCtsAE ;
} else {
info - > flags & = ~ ASYNC_CTS_FLOW ;
info - > cor2 & = ~ CyCtsAE ;
}
# endif
if ( cflag & CLOCAL )
info - > flags & = ~ ASYNC_CHECK_CD ;
else
info - > flags | = ASYNC_CHECK_CD ;
2005-04-17 02:20:36 +04:00
/***********************************************
The hardware option , CyRtsAO , presents RTS when
the chip has characters to send . Since most modems
use RTS as reverse ( inbound ) flow control , this
option is not used . If inbound flow control is
necessary , DTR can be programmed to provide the
appropriate signals for use with a non - standard
cable . Contact Marcio Saito for details .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
/* CyCMR set once only in mvme167_init_serial() */
if ( base_addr [ CyLICR ] ! = channel < < 2 )
2007-02-10 12:45:08 +03:00
base_addr [ CyLICR ] = channel < < 2 ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyLIVR ] ! = 0x5c )
2007-02-10 12:45:08 +03:00
base_addr [ CyLIVR ] = 0x5c ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/* tx and rx baud rate */
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyCOR1 ] ! = info - > cor1 )
2007-02-10 12:45:08 +03:00
need_init_chan = 1 ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyTCOR ] ! = info - > tco )
2007-02-10 12:45:08 +03:00
base_addr [ CyTCOR ] = info - > tco ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyTBPR ] ! = info - > tbpr )
2007-02-10 12:45:08 +03:00
base_addr [ CyTBPR ] = info - > tbpr ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyRCOR ] ! = info - > rco )
2007-02-10 12:45:08 +03:00
base_addr [ CyRCOR ] = info - > rco ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyRBPR ] ! = info - > rbpr )
2007-02-10 12:45:08 +03:00
base_addr [ CyRBPR ] = info - > rbpr ;
2005-04-17 02:20:36 +04:00
/* set line characteristics according configuration */
if ( base_addr [ CySCHR1 ] ! = START_CHAR ( info - > tty ) )
2007-02-10 12:45:08 +03:00
base_addr [ CySCHR1 ] = START_CHAR ( info - > tty ) ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CySCHR2 ] ! = STOP_CHAR ( info - > tty ) )
2007-02-10 12:45:08 +03:00
base_addr [ CySCHR2 ] = STOP_CHAR ( info - > tty ) ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CySCRL ] ! = START_CHAR ( info - > tty ) )
2007-02-10 12:45:08 +03:00
base_addr [ CySCRL ] = START_CHAR ( info - > tty ) ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CySCRH ] ! = START_CHAR ( info - > tty ) )
2007-02-10 12:45:08 +03:00
base_addr [ CySCRH ] = START_CHAR ( info - > tty ) ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyCOR1 ] ! = info - > cor1 )
2007-02-10 12:45:08 +03:00
base_addr [ CyCOR1 ] = info - > cor1 ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyCOR2 ] ! = info - > cor2 )
2007-02-10 12:45:08 +03:00
base_addr [ CyCOR2 ] = info - > cor2 ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyCOR3 ] ! = info - > cor3 )
2007-02-10 12:45:08 +03:00
base_addr [ CyCOR3 ] = info - > cor3 ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyCOR4 ] ! = info - > cor4 )
2007-02-10 12:45:08 +03:00
base_addr [ CyCOR4 ] = info - > cor4 ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyCOR5 ] ! = info - > cor5 )
2007-02-10 12:45:08 +03:00
base_addr [ CyCOR5 ] = info - > cor5 ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyCOR6 ] ! = info - > cor6 )
2007-02-10 12:45:08 +03:00
base_addr [ CyCOR6 ] = info - > cor6 ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyCOR7 ] ! = info - > cor7 )
2007-02-10 12:45:08 +03:00
base_addr [ CyCOR7 ] = info - > cor7 ;
2005-04-17 02:20:36 +04:00
if ( need_init_chan )
2007-02-10 12:45:08 +03:00
write_cy_cmd ( base_addr , CyINIT_CHAN ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyCAR ] = ( u_char ) channel ; /* !!! Is this needed? */
2005-04-17 02:20:36 +04:00
/* 2ms default rx timeout */
ti = info - > default_timeout ? info - > default_timeout : 0x02 ;
if ( base_addr [ CyRTPRL ] ! = ti )
2007-02-10 12:45:08 +03:00
base_addr [ CyRTPRL ] = ti ;
2005-04-17 02:20:36 +04:00
if ( base_addr [ CyRTPRH ] ! = 0 )
2007-02-10 12:45:08 +03:00
base_addr [ CyRTPRH ] = 0 ;
2005-04-17 02:20:36 +04:00
/* Set up RTS here also ????? RGH 141095 */
2007-02-10 12:45:08 +03:00
if ( i = = 0 ) { /* baud rate is zero, turn off line */
if ( ( base_addr [ CyMSVR2 ] & CyDTR ) = = CyDTR )
base_addr [ CyMSVR2 ] = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_DTR
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: dropping DTR \n " , __LINE__ ) ;
printk ( " status: 0x%x, 0x%x \n " , base_addr [ CyMSVR1 ] ,
base_addr [ CyMSVR2 ] ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
} else {
if ( ( base_addr [ CyMSVR2 ] & CyDTR ) ! = CyDTR )
base_addr [ CyMSVR2 ] = CyDTR ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_DTR
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: raising DTR \n " , __LINE__ ) ;
printk ( " status: 0x%x, 0x%x \n " , base_addr [ CyMSVR1 ] ,
base_addr [ CyMSVR2 ] ) ;
2005-04-17 02:20:36 +04:00
# endif
}
2007-02-10 12:45:08 +03:00
if ( info - > tty ) {
clear_bit ( TTY_IO_ERROR , & info - > tty - > flags ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
} /* config_setup */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static void cy_put_char ( struct tty_struct * tty , unsigned char ch )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_IO
2007-02-10 12:45:08 +03:00
printk ( " cy_put_char %s(0x%02x) \n " , tty - > name , ch ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_put_char " ) )
return ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( ! info - > xmit_buf )
return ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
if ( info - > xmit_cnt > = PAGE_SIZE - 1 ) {
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
return ;
2005-04-17 02:20:36 +04:00
}
info - > xmit_buf [ info - > xmit_head + + ] = ch ;
info - > xmit_head & = PAGE_SIZE - 1 ;
info - > xmit_cnt + + ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
} /* cy_put_char */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static void cy_flush_chars ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
unsigned long flags ;
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_IO
2007-02-10 12:45:08 +03:00
printk ( " cy_flush_chars %s \n " , tty - > name ) ; /* */
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_flush_chars " ) )
return ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( info - > xmit_cnt < = 0 | | tty - > stopped
| | tty - > hw_stopped | | ! info - > xmit_buf )
return ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
base_addr [ CyCAR ] = channel ;
base_addr [ CyIER ] | = CyTxMpty ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
} /* cy_flush_chars */
2005-04-17 02:20:36 +04:00
/* This routine gets called when tty_write has put something into
the write_queue . If the port is not already transmitting stuff ,
start it off by enabling interrupts . The interrupt service
routine will then ensure that the characters are sent . If the
port is already active , there is no need to kick it .
*/
2007-02-10 12:45:08 +03:00
static int cy_write ( struct tty_struct * tty , const unsigned char * buf , int count )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
unsigned long flags ;
int c , total = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_IO
2007-02-10 12:45:08 +03:00
printk ( " cy_write %s \n " , tty - > name ) ; /* */
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_write " ) ) {
return 0 ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( ! info - > xmit_buf ) {
return 0 ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
while ( 1 ) {
local_irq_save ( flags ) ;
c = min_t ( int , count , min ( SERIAL_XMIT_SIZE - info - > xmit_cnt - 1 ,
SERIAL_XMIT_SIZE - info - > xmit_head ) ) ;
if ( c < = 0 ) {
local_irq_restore ( flags ) ;
break ;
}
memcpy ( info - > xmit_buf + info - > xmit_head , buf , c ) ;
info - > xmit_head =
( info - > xmit_head + c ) & ( SERIAL_XMIT_SIZE - 1 ) ;
info - > xmit_cnt + = c ;
local_irq_restore ( flags ) ;
buf + = c ;
count - = c ;
total + = c ;
}
if ( info - > xmit_cnt & & ! tty - > stopped & & ! tty - > hw_stopped ) {
start_xmit ( info ) ;
}
return total ;
} /* cy_write */
static int cy_write_room ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
int ret ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_IO
2007-02-10 12:45:08 +03:00
printk ( " cy_write_room %s \n " , tty - > name ) ; /* */
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_write_room " ) )
return 0 ;
ret = PAGE_SIZE - info - > xmit_cnt - 1 ;
if ( ret < 0 )
ret = 0 ;
return ret ;
} /* cy_write_room */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static int cy_chars_in_buffer ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_IO
2007-02-10 12:45:08 +03:00
printk ( " cy_chars_in_buffer %s %d \n " , tty - > name , info - > xmit_cnt ) ; /* */
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_chars_in_buffer " ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
return info - > xmit_cnt ;
} /* cy_chars_in_buffer */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static void cy_flush_buffer ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
unsigned long flags ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_IO
2007-02-10 12:45:08 +03:00
printk ( " cy_flush_buffer %s \n " , tty - > name ) ; /* */
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_flush_buffer " ) )
return ;
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
info - > xmit_cnt = info - > xmit_head = info - > xmit_tail = 0 ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
tty_wakeup ( tty ) ;
} /* cy_flush_buffer */
2005-04-17 02:20:36 +04:00
/* This routine is called by the upper-layer tty layer to signal
that incoming characters should be throttled or that the
throttle should be released .
*/
2007-02-10 12:45:08 +03:00
static void cy_throttle ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
unsigned long flags ;
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_THROTTLE
2007-02-10 12:45:08 +03:00
char buf [ 64 ] ;
printk ( " throttle %s: %d.... \n " , tty_name ( tty , buf ) ,
tty - > ldisc . chars_in_buffer ( tty ) ) ;
printk ( " cy_throttle %s \n " , tty - > name ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_nthrottle " ) ) {
return ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( I_IXOFF ( tty ) ) {
info - > x_char = STOP_CHAR ( tty ) ;
/* Should use the "Send Special Character" feature!!! */
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
base_addr [ CyMSVR1 ] = 0 ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
} /* cy_throttle */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static void cy_unthrottle ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
unsigned long flags ;
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_THROTTLE
2007-02-10 12:45:08 +03:00
char buf [ 64 ] ;
printk ( " throttle %s: %d.... \n " , tty_name ( tty , buf ) ,
tty - > ldisc . chars_in_buffer ( tty ) ) ;
printk ( " cy_unthrottle %s \n " , tty - > name ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_nthrottle " ) ) {
return ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( I_IXOFF ( tty ) ) {
info - > x_char = START_CHAR ( tty ) ;
/* Should use the "Send Special Character" feature!!! */
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
base_addr [ CyMSVR1 ] = CyRTS ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
} /* cy_unthrottle */
2005-04-17 02:20:36 +04:00
static int
2007-02-10 12:45:08 +03:00
get_serial_info ( struct cyclades_port * info ,
struct serial_struct __user * retinfo )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct serial_struct tmp ;
2005-04-17 02:20:36 +04:00
/* CP('g'); */
2007-02-10 12:45:08 +03:00
if ( ! retinfo )
return - EFAULT ;
memset ( & tmp , 0 , sizeof ( tmp ) ) ;
tmp . type = info - > type ;
tmp . line = info - > line ;
tmp . port = info - > line ;
tmp . irq = 0 ;
tmp . flags = info - > flags ;
tmp . baud_base = 0 ; /*!!! */
tmp . close_delay = info - > close_delay ;
tmp . custom_divisor = 0 ; /*!!! */
tmp . hub6 = 0 ; /*!!! */
return copy_to_user ( retinfo , & tmp , sizeof ( * retinfo ) ) ? - EFAULT : 0 ;
} /* get_serial_info */
2005-04-17 02:20:36 +04:00
static int
2007-02-10 12:45:08 +03:00
set_serial_info ( struct cyclades_port * info ,
struct serial_struct __user * new_info )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct serial_struct new_serial ;
struct cyclades_port old_info ;
2005-04-17 02:20:36 +04:00
/* CP('s'); */
2007-02-10 12:45:08 +03:00
if ( ! new_info )
return - EFAULT ;
if ( copy_from_user ( & new_serial , new_info , sizeof ( new_serial ) ) )
return - EFAULT ;
old_info = * info ;
if ( ! capable ( CAP_SYS_ADMIN ) ) {
if ( ( new_serial . close_delay ! = info - > close_delay ) | |
( ( new_serial . flags & ASYNC_FLAGS & ~ ASYNC_USR_MASK ) ! =
( info - > flags & ASYNC_FLAGS & ~ ASYNC_USR_MASK ) ) )
return - EPERM ;
info - > flags = ( ( info - > flags & ~ ASYNC_USR_MASK ) |
( new_serial . flags & ASYNC_USR_MASK ) ) ;
goto check_and_exit ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/*
* OK , past this point , all the error checking has been done .
* At this point , we start making changes . . . . .
*/
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
info - > flags = ( ( info - > flags & ~ ASYNC_FLAGS ) |
( new_serial . flags & ASYNC_FLAGS ) ) ;
info - > close_delay = new_serial . close_delay ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
check_and_exit :
if ( info - > flags & ASYNC_INITIALIZED ) {
config_setup ( info ) ;
return 0 ;
}
return startup ( info ) ;
} /* set_serial_info */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static int cy_tiocmget ( struct tty_struct * tty , struct file * file )
{
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
int channel ;
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
unsigned long flags ;
unsigned char status ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
status = base_addr [ CyMSVR1 ] | base_addr [ CyMSVR2 ] ;
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
return ( ( status & CyRTS ) ? TIOCM_RTS : 0 )
| ( ( status & CyDTR ) ? TIOCM_DTR : 0 )
| ( ( status & CyDCD ) ? TIOCM_CAR : 0 )
| ( ( status & CyDSR ) ? TIOCM_DSR : 0 )
| ( ( status & CyCTS ) ? TIOCM_CTS : 0 ) ;
} /* cy_tiocmget */
2005-04-17 02:20:36 +04:00
static int
cy_tiocmset ( struct tty_struct * tty , struct file * file ,
unsigned int set , unsigned int clear )
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
int channel ;
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
unsigned long flags ;
channel = info - > line ;
if ( set & TIOCM_RTS ) {
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
base_addr [ CyMSVR1 ] = CyRTS ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
if ( set & TIOCM_DTR ) {
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
/* CP('S');CP('2'); */
2007-02-10 12:45:08 +03:00
base_addr [ CyMSVR2 ] = CyDTR ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_DTR
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: raising DTR \n " , __LINE__ ) ;
printk ( " status: 0x%x, 0x%x \n " , base_addr [ CyMSVR1 ] ,
base_addr [ CyMSVR2 ] ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
if ( clear & TIOCM_RTS ) {
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
base_addr [ CyMSVR1 ] = 0 ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
if ( clear & TIOCM_DTR ) {
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
/* CP('C');CP('2'); */
2007-02-10 12:45:08 +03:00
base_addr [ CyMSVR2 ] = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_DTR
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: dropping DTR \n " , __LINE__ ) ;
printk ( " status: 0x%x, 0x%x \n " , base_addr [ CyMSVR1 ] ,
base_addr [ CyMSVR2 ] ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
return 0 ;
} /* set_modem_info */
static void send_break ( struct cyclades_port * info , int duration )
{ /* Let the transmit ISR take care of this (since it
requires stuffing characters into the output stream ) .
*/
info - > x_break = duration ;
if ( ! info - > xmit_cnt ) {
start_xmit ( info ) ;
}
} /* send_break */
2005-04-17 02:20:36 +04:00
static int
2007-02-10 12:45:08 +03:00
get_mon_info ( struct cyclades_port * info , struct cyclades_monitor __user * mon )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
if ( copy_to_user ( mon , & info - > mon , sizeof ( struct cyclades_monitor ) ) )
return - EFAULT ;
info - > mon . int_count = 0 ;
info - > mon . char_count = 0 ;
info - > mon . char_max = 0 ;
info - > mon . char_last = 0 ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
static int set_threshold ( struct cyclades_port * info , unsigned long __user * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
unsigned long value ;
int channel ;
if ( get_user ( value , arg ) )
return - EFAULT ;
channel = info - > line ;
info - > cor4 & = ~ CyREC_FIFO ;
info - > cor4 | = value & CyREC_FIFO ;
base_addr [ CyCOR4 ] = info - > cor4 ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2007-02-10 12:45:08 +03:00
get_threshold ( struct cyclades_port * info , unsigned long __user * value )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
unsigned long tmp ;
channel = info - > line ;
tmp = base_addr [ CyCOR4 ] & CyREC_FIFO ;
return put_user ( tmp , value ) ;
2005-04-17 02:20:36 +04:00
}
static int
2007-02-10 12:45:08 +03:00
set_default_threshold ( struct cyclades_port * info , unsigned long __user * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
unsigned long value ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( get_user ( value , arg ) )
return - EFAULT ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
info - > default_threshold = value & 0x0f ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2007-02-10 12:45:08 +03:00
get_default_threshold ( struct cyclades_port * info , unsigned long __user * value )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
return put_user ( info - > default_threshold , value ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
static int set_timeout ( struct cyclades_port * info , unsigned long __user * arg )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
unsigned long value ;
if ( get_user ( value , arg ) )
return - EFAULT ;
channel = info - > line ;
base_addr [ CyRTPRL ] = value & 0xff ;
base_addr [ CyRTPRH ] = ( value > > 8 ) & 0xff ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
static int get_timeout ( struct cyclades_port * info , unsigned long __user * value )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
unsigned long tmp ;
channel = info - > line ;
tmp = base_addr [ CyRTPRL ] ;
return put_user ( tmp , value ) ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
static int set_default_timeout ( struct cyclades_port * info , unsigned long value )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
info - > default_timeout = value & 0xff ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int
2007-02-10 12:45:08 +03:00
get_default_timeout ( struct cyclades_port * info , unsigned long __user * value )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
return put_user ( info - > default_timeout , value ) ;
2005-04-17 02:20:36 +04:00
}
static int
2007-02-10 12:45:08 +03:00
cy_ioctl ( struct tty_struct * tty , struct file * file ,
unsigned int cmd , unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
unsigned long val ;
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
int ret_val = 0 ;
void __user * argp = ( void __user * ) arg ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OTHER
2007-02-10 12:45:08 +03:00
printk ( " cy_ioctl %s, cmd = %x arg = %lx \n " , tty - > name , cmd , arg ) ; /* */
# endif
switch ( cmd ) {
case CYGETMON :
ret_val = get_mon_info ( info , argp ) ;
break ;
case CYGETTHRESH :
ret_val = get_threshold ( info , argp ) ;
break ;
case CYSETTHRESH :
ret_val = set_threshold ( info , argp ) ;
break ;
case CYGETDEFTHRESH :
ret_val = get_default_threshold ( info , argp ) ;
break ;
case CYSETDEFTHRESH :
ret_val = set_default_threshold ( info , argp ) ;
break ;
case CYGETTIMEOUT :
ret_val = get_timeout ( info , argp ) ;
break ;
case CYSETTIMEOUT :
ret_val = set_timeout ( info , argp ) ;
break ;
case CYGETDEFTIMEOUT :
ret_val = get_default_timeout ( info , argp ) ;
break ;
case CYSETDEFTIMEOUT :
ret_val = set_default_timeout ( info , ( unsigned long ) arg ) ;
break ;
case TCSBRK : /* SVID version: non-zero arg --> no break */
ret_val = tty_check_change ( tty ) ;
if ( ret_val )
break ;
tty_wait_until_sent ( tty , 0 ) ;
if ( ! arg )
send_break ( info , HZ / 4 ) ; /* 1/4 second */
break ;
case TCSBRKP : /* support for POSIX tcsendbreak() */
ret_val = tty_check_change ( tty ) ;
if ( ret_val )
break ;
tty_wait_until_sent ( tty , 0 ) ;
send_break ( info , arg ? arg * ( HZ / 10 ) : HZ / 4 ) ;
2005-04-17 02:20:36 +04:00
break ;
/* The following commands are incompletely implemented!!! */
2007-02-10 12:45:08 +03:00
case TIOCGSOFTCAR :
ret_val =
put_user ( C_CLOCAL ( tty ) ? 1 : 0 ,
( unsigned long __user * ) argp ) ;
break ;
case TIOCSSOFTCAR :
ret_val = get_user ( val , ( unsigned long __user * ) argp ) ;
if ( ret_val )
break ;
tty - > termios - > c_cflag =
( ( tty - > termios - > c_cflag & ~ CLOCAL ) | ( val ? CLOCAL : 0 ) ) ;
break ;
case TIOCGSERIAL :
ret_val = get_serial_info ( info , argp ) ;
break ;
case TIOCSSERIAL :
ret_val = set_serial_info ( info , argp ) ;
break ;
default :
ret_val = - ENOIOCTLCMD ;
}
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OTHER
2007-02-10 12:45:08 +03:00
printk ( " cy_ioctl done \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
return ret_val ;
} /* cy_ioctl */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static void cy_set_termios ( struct tty_struct * tty , struct ktermios * old_termios )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OTHER
2007-02-10 12:45:08 +03:00
printk ( " cy_set_termios %s \n " , tty - > name ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( tty - > termios - > c_cflag = = old_termios - > c_cflag )
return ;
config_setup ( info ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( ( old_termios - > c_cflag & CRTSCTS ) & &
! ( tty - > termios - > c_cflag & CRTSCTS ) ) {
tty - > stopped = 0 ;
cy_start ( tty ) ;
}
2005-04-17 02:20:36 +04:00
# ifdef tytso_patch_94Nov25_1726
2007-02-10 12:45:08 +03:00
if ( ! ( old_termios - > c_cflag & CLOCAL ) & &
( tty - > termios - > c_cflag & CLOCAL ) )
wake_up_interruptible ( & info - > open_wait ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
} /* cy_set_termios */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
static void cy_close ( struct tty_struct * tty , struct file * filp )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
2005-04-17 02:20:36 +04:00
/* CP('C'); */
# ifdef SERIAL_DEBUG_OTHER
2007-02-10 12:45:08 +03:00
printk ( " cy_close %s \n " , tty - > name ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( ! info | | serial_paranoia_check ( info , tty - > name , " cy_close " ) ) {
return ;
}
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " cy_close %s, count = %d \n " , tty - > name , info - > count ) ;
# endif
if ( ( tty - > count = = 1 ) & & ( info - > count ! = 1 ) ) {
/*
* Uh , oh . tty - > count is 1 , which means that the tty
* structure will be freed . Info - > count should always
* be one in these conditions . If it ' s greater than
* one , we ' ve got real problems , since it means the
* serial port won ' t be shutdown .
*/
printk ( " cy_close: bad serial port count; tty->count is 1, "
" info->count is %d \n " , info - > count ) ;
info - > count = 1 ;
}
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_COUNT
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: decrementing count to %d \n " , __LINE__ ,
info - > count - 1 ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( - - info - > count < 0 ) {
printk ( " cy_close: bad serial port count for ttys%d: %d \n " ,
info - > line , info - > count ) ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_COUNT
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: setting count to 0 \n " , __LINE__ ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
info - > count = 0 ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
if ( info - > count )
return ;
info - > flags | = ASYNC_CLOSING ;
if ( info - > flags & ASYNC_INITIALIZED )
tty_wait_until_sent ( tty , 3000 ) ; /* 30 seconds timeout */
shutdown ( info ) ;
if ( tty - > driver - > flush_buffer )
tty - > driver - > flush_buffer ( tty ) ;
tty_ldisc_flush ( tty ) ;
info - > event = 0 ;
info - > tty = NULL ;
if ( info - > blocked_open ) {
if ( info - > close_delay ) {
msleep_interruptible ( jiffies_to_msecs
( info - > close_delay ) ) ;
}
wake_up_interruptible ( & info - > open_wait ) ;
}
info - > flags & = ~ ( ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING ) ;
wake_up_interruptible ( & info - > close_wait ) ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OTHER
2007-02-10 12:45:08 +03:00
printk ( " cy_close done \n " ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
} /* cy_close */
2005-04-17 02:20:36 +04:00
/*
* cy_hangup ( ) - - - called by tty_hangup ( ) when a hangup is signaled .
*/
2007-02-10 12:45:08 +03:00
void cy_hangup ( struct tty_struct * tty )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info = ( struct cyclades_port * ) tty - > driver_data ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OTHER
2007-02-10 12:45:08 +03:00
printk ( " cy_hangup %s \n " , tty - > name ) ; /* */
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_hangup " ) )
return ;
shutdown ( info ) ;
2005-04-17 02:20:36 +04:00
#if 0
2007-02-10 12:45:08 +03:00
info - > event = 0 ;
info - > count = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_COUNT
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: setting count to 0 \n " , __LINE__ ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
info - > tty = 0 ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
info - > flags & = ~ ASYNC_NORMAL_ACTIVE ;
wake_up_interruptible ( & info - > open_wait ) ;
} /* cy_hangup */
2005-04-17 02:20:36 +04:00
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* cy_open ( ) and friends
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
static int
2007-02-10 12:45:08 +03:00
block_til_ready ( struct tty_struct * tty , struct file * filp ,
struct cyclades_port * info )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
DECLARE_WAITQUEUE ( wait , current ) ;
unsigned long flags ;
int channel ;
int retval ;
volatile u_char * base_addr = ( u_char * ) BASE_ADDR ;
/*
* If the device is in the middle of being closed , then block
* until it ' s done , and then try again .
*/
if ( info - > flags & ASYNC_CLOSING ) {
interruptible_sleep_on ( & info - > close_wait ) ;
if ( info - > flags & ASYNC_HUP_NOTIFY ) {
return - EAGAIN ;
} else {
return - ERESTARTSYS ;
}
}
/*
* If non - blocking mode is set , then make the check up front
* and then exit .
*/
if ( filp - > f_flags & O_NONBLOCK ) {
info - > flags | = ASYNC_NORMAL_ACTIVE ;
return 0 ;
}
/*
* Block waiting for the carrier detect and the line to become
* free ( i . e . , not in use by the callout ) . While we are in
* this loop , info - > count is dropped by one , so that
* cy_close ( ) knows when to free things . We restore it upon
* exit , either normal or abnormal .
*/
retval = 0 ;
add_wait_queue ( & info - > open_wait , & wait ) ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " block_til_ready before block: %s, count = %d \n " ,
tty - > name , info - > count ) ;
/**/
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
info - > count - - ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_COUNT
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: decrementing count to %d \n " , __LINE__ , info - > count ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
info - > blocked_open + + ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
channel = info - > line ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
while ( 1 ) {
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
base_addr [ CyMSVR1 ] = CyRTS ;
2005-04-17 02:20:36 +04:00
/* CP('S');CP('4'); */
2007-02-10 12:45:08 +03:00
base_addr [ CyMSVR2 ] = CyDTR ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_DTR
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: raising DTR \n " , __LINE__ ) ;
printk ( " status: 0x%x, 0x%x \n " , base_addr [ CyMSVR1 ] ,
base_addr [ CyMSVR2 ] ) ;
# endif
local_irq_restore ( flags ) ;
set_current_state ( TASK_INTERRUPTIBLE ) ;
if ( tty_hung_up_p ( filp )
| | ! ( info - > flags & ASYNC_INITIALIZED ) ) {
if ( info - > flags & ASYNC_HUP_NOTIFY ) {
retval = - EAGAIN ;
} else {
retval = - ERESTARTSYS ;
}
break ;
}
local_irq_save ( flags ) ;
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */
2007-02-10 12:45:08 +03:00
if ( ! ( info - > flags & ASYNC_CLOSING )
& & ( C_CLOCAL ( tty )
| | ( base_addr [ CyMSVR1 ] & CyDCD ) ) ) {
local_irq_restore ( flags ) ;
break ;
}
local_irq_restore ( flags ) ;
if ( signal_pending ( current ) ) {
retval = - ERESTARTSYS ;
break ;
}
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " block_til_ready blocking: %s, count = %d \n " ,
tty - > name , info - > count ) ;
/**/
# endif
schedule ( ) ;
}
2007-05-08 11:30:52 +04:00
__set_current_state ( TASK_RUNNING ) ;
2007-02-10 12:45:08 +03:00
remove_wait_queue ( & info - > open_wait , & wait ) ;
if ( ! tty_hung_up_p ( filp ) ) {
info - > count + + ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_COUNT
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: incrementing count to %d \n " , __LINE__ ,
info - > count ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
}
info - > blocked_open - - ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " block_til_ready after blocking: %s, count = %d \n " ,
tty - > name , info - > count ) ;
/**/
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( retval )
return retval ;
info - > flags | = ASYNC_NORMAL_ACTIVE ;
return 0 ;
} /* block_til_ready */
2005-04-17 02:20:36 +04:00
/*
* This routine is called whenever a serial port is opened . It
* performs the serial - specific initialization for the tty structure .
*/
2007-02-10 12:45:08 +03:00
int cy_open ( struct tty_struct * tty , struct file * filp )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info ;
int retval , line ;
2005-04-17 02:20:36 +04:00
/* CP('O'); */
2007-02-10 12:45:08 +03:00
line = tty - > index ;
if ( ( line < 0 ) | | ( NR_PORTS < = line ) ) {
return - ENODEV ;
}
info = & cy_port [ line ] ;
if ( info - > line < 0 ) {
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OTHER
2007-02-10 12:45:08 +03:00
printk ( " cy_open %s \n " , tty - > name ) ; /* */
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( serial_paranoia_check ( info , tty - > name , " cy_open " ) ) {
return - ENODEV ;
}
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " cy_open %s, count = %d \n " , tty - > name , info - > count ) ;
/**/
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
info - > count + + ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_COUNT
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: incrementing count to %d \n " , __LINE__ , info - > count ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
tty - > driver_data = info ;
info - > tty = tty ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/*
* Start up serial port
*/
retval = startup ( info ) ;
if ( retval ) {
return retval ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
retval = block_til_ready ( tty , filp , info ) ;
if ( retval ) {
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " cy_open returning after block_til_ready with %d \n " ,
retval ) ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
return retval ;
}
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_OPEN
2007-02-10 12:45:08 +03:00
printk ( " cy_open done \n " ) ;
/**/
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
return 0 ;
} /* cy_open */
2005-04-17 02:20:36 +04:00
/*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* serial167_init ( ) and friends
*
* serial167_init ( ) is called at boot - time to initialize the serial driver .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/*
* This routine prints out the appropriate serial driver version
* number , and identifies which options were configured into this
* driver .
*/
2007-02-10 12:45:08 +03:00
static void show_version ( void )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
printk ( " MVME166/167 cd2401 driver \n " ) ;
} /* show_version */
2005-04-17 02:20:36 +04:00
/* initialize chips on card -- return number of valid
chips ( which is number of ports / 4 ) */
/*
* This initialises the hardware to a reasonable state . It should
* probe the chip first so as to copy 166 - Bug setup as a default for
* port 0. It initialises CMR to CyASYNC ; that is never done again , so
* as to limit the number of CyINIT_CHAN commands in normal running .
*
* . . . I wonder what I should do if this fails . . .
*/
2007-02-10 12:45:08 +03:00
void mvme167_serial_console_setup ( int cflag )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
2005-04-17 02:20:36 +04:00
int ch ;
u_char spd ;
u_char rcor , rbpr , badspeed = 0 ;
unsigned long flags ;
local_irq_save ( flags ) ;
/*
* First probe channel zero of the chip , to see what speed has
* been selected .
*/
base_addr [ CyCAR ] = 0 ;
rcor = base_addr [ CyRCOR ] < < 5 ;
rbpr = base_addr [ CyRBPR ] ;
for ( spd = 0 ; spd < sizeof ( baud_bpr ) ; spd + + )
if ( rbpr = = baud_bpr [ spd ] & & rcor = = baud_co [ spd ] )
break ;
if ( spd > = sizeof ( baud_bpr ) ) {
spd = 14 ; /* 19200 */
badspeed = 1 ; /* Failed to identify speed */
}
initial_console_speed = spd ;
/* OK, we have chosen a speed, now reset and reinitialise */
2007-02-10 12:45:08 +03:00
my_udelay ( 20000L ) ; /* Allow time for any active o/p to complete */
if ( base_addr [ CyCCR ] ! = 0x00 ) {
local_irq_restore ( flags ) ;
/* printk(" chip is never idle (CCR != 0)\n"); */
return ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyCCR ] = CyCHIP_RESET ; /* Reset the chip */
my_udelay ( 1000L ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
if ( base_addr [ CyGFRCR ] = = 0x00 ) {
local_irq_restore ( flags ) ;
/* printk(" chip is not responding (GFRCR stayed 0)\n"); */
return ;
}
2005-04-17 02:20:36 +04:00
/*
* System clock is 20 Mhz , divided by 2048 , so divide by 10 for a 1.0 ms
* tick
*/
base_addr [ CyTPR ] = 10 ;
2007-02-10 12:45:08 +03:00
base_addr [ CyPILR1 ] = 0x01 ; /* Interrupt level for modem change */
base_addr [ CyPILR2 ] = 0x02 ; /* Interrupt level for tx ints */
base_addr [ CyPILR3 ] = 0x03 ; /* Interrupt level for rx ints */
2005-04-17 02:20:36 +04:00
/*
* Attempt to set up all channels to something reasonable , and
* bang out a INIT_CHAN command . We should then be able to limit
* the ammount of fiddling we have to do in normal running .
*/
2007-02-10 12:45:08 +03:00
for ( ch = 3 ; ch > = 0 ; ch - - ) {
base_addr [ CyCAR ] = ( u_char ) ch ;
2005-04-17 02:20:36 +04:00
base_addr [ CyIER ] = 0 ;
base_addr [ CyCMR ] = CyASYNC ;
2007-02-10 12:45:08 +03:00
base_addr [ CyLICR ] = ( u_char ) ch < < 2 ;
2005-04-17 02:20:36 +04:00
base_addr [ CyLIVR ] = 0x5c ;
base_addr [ CyTCOR ] = baud_co [ spd ] ;
base_addr [ CyTBPR ] = baud_bpr [ spd ] ;
base_addr [ CyRCOR ] = baud_co [ spd ] > > 5 ;
base_addr [ CyRBPR ] = baud_bpr [ spd ] ;
base_addr [ CySCHR1 ] = ' Q ' & 0x1f ;
base_addr [ CySCHR2 ] = ' X ' & 0x1f ;
base_addr [ CySCRL ] = 0 ;
base_addr [ CySCRH ] = 0 ;
base_addr [ CyCOR1 ] = Cy_8_BITS | CyPARITY_NONE ;
base_addr [ CyCOR2 ] = 0 ;
base_addr [ CyCOR3 ] = Cy_1_STOP ;
base_addr [ CyCOR4 ] = baud_cor4 [ spd ] ;
base_addr [ CyCOR5 ] = 0 ;
base_addr [ CyCOR6 ] = 0 ;
base_addr [ CyCOR7 ] = 0 ;
base_addr [ CyRTPRL ] = 2 ;
base_addr [ CyRTPRH ] = 0 ;
2007-02-10 12:45:08 +03:00
base_addr [ CyMSVR1 ] = 0 ;
base_addr [ CyMSVR2 ] = 0 ;
write_cy_cmd ( base_addr , CyINIT_CHAN | CyDIS_RCVR | CyDIS_XMTR ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Now do specials for channel zero . . . .
*/
2007-02-10 12:45:08 +03:00
base_addr [ CyMSVR1 ] = CyRTS ;
base_addr [ CyMSVR2 ] = CyDTR ;
2005-04-17 02:20:36 +04:00
base_addr [ CyIER ] = CyRxData ;
2007-02-10 12:45:08 +03:00
write_cy_cmd ( base_addr , CyENB_RCVR | CyENB_XMTR ) ;
2005-04-17 02:20:36 +04:00
local_irq_restore ( flags ) ;
my_udelay ( 20000L ) ; /* Let it all settle down */
2007-02-10 12:45:08 +03:00
printk ( " CD2401 initialised, chip is rev 0x%02x \n " , base_addr [ CyGFRCR ] ) ;
2005-04-17 02:20:36 +04:00
if ( badspeed )
2007-02-10 12:45:08 +03:00
printk
( " WARNING: Failed to identify line speed, rcor=%02x,rbpr=%02x \n " ,
rcor > > 5 , rbpr ) ;
} /* serial_console_init */
2005-04-17 02:20:36 +04:00
2006-10-02 13:17:18 +04:00
static const struct tty_operations cy_ops = {
2005-04-17 02:20:36 +04:00
. open = cy_open ,
. close = cy_close ,
. write = cy_write ,
. put_char = cy_put_char ,
. flush_chars = cy_flush_chars ,
. write_room = cy_write_room ,
. chars_in_buffer = cy_chars_in_buffer ,
. flush_buffer = cy_flush_buffer ,
. ioctl = cy_ioctl ,
. throttle = cy_throttle ,
. unthrottle = cy_unthrottle ,
. set_termios = cy_set_termios ,
. stop = cy_stop ,
. start = cy_start ,
. hangup = cy_hangup ,
. tiocmget = cy_tiocmget ,
. tiocmset = cy_tiocmset ,
} ;
2007-02-10 12:45:08 +03:00
2005-04-17 02:20:36 +04:00
/* The serial driver boot-time initialization code!
Hardware I / O ports are mapped to character special devices on a
first found , first allocated manner . That is , this code searches
for Cyclom cards in the system . As each is found , it is probed
to discover how many chips ( and thus how many ports ) are present .
These ports are mapped to the tty ports 64 and upward in monotonic
fashion . If an 8 - port card is replaced with a 16 - port card , the
port mapping on a following card will shift .
This approach is different from what is used in the other serial
device driver because the Cyclom is more properly a multiplexer ,
not just an aggregation of serial ports on one card .
If there are more cards with more ports than have been statically
allocated above , a warning is printed and the extra ports are ignored .
*/
2007-02-10 12:45:08 +03:00
static int __init serial167_init ( void )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
struct cyclades_port * info ;
int ret = 0 ;
int good_ports = 0 ;
int port_num = 0 ;
int index ;
int DefSpeed ;
2005-04-17 02:20:36 +04:00
# ifdef notyet
2007-02-10 12:45:08 +03:00
struct sigaction sa ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
if ( ! ( mvme16x_config & MVME16x_CONFIG_GOT_CD2401 ) )
return 0 ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
cy_serial_driver = alloc_tty_driver ( NR_PORTS ) ;
if ( ! cy_serial_driver )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
#if 0
2007-02-10 12:45:08 +03:00
scrn [ 1 ] = ' \0 ' ;
2005-04-17 02:20:36 +04:00
# endif
2007-02-10 12:45:08 +03:00
show_version ( ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/* Has "console=0,9600n8" been used in bootinfo to change speed? */
if ( serial_console_cflag )
DefSpeed = serial_console_cflag & 0017 ;
else {
DefSpeed = initial_console_speed ;
serial_console_info = & cy_port [ 0 ] ;
serial_console_cflag = DefSpeed | CS8 ;
2005-04-17 02:20:36 +04:00
#if 0
2007-02-10 12:45:08 +03:00
serial_console = 64 ; /*callout_driver.minor_start */
# endif
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/* Initialize the tty_driver structure */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
cy_serial_driver - > owner = THIS_MODULE ;
cy_serial_driver - > name = " ttyS " ;
cy_serial_driver - > major = TTY_MAJOR ;
cy_serial_driver - > minor_start = 64 ;
cy_serial_driver - > type = TTY_DRIVER_TYPE_SERIAL ;
cy_serial_driver - > subtype = SERIAL_TYPE_NORMAL ;
cy_serial_driver - > init_termios = tty_std_termios ;
cy_serial_driver - > init_termios . c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL ;
cy_serial_driver - > flags = TTY_DRIVER_REAL_RAW ;
tty_set_operations ( cy_serial_driver , & cy_ops ) ;
ret = tty_register_driver ( cy_serial_driver ) ;
if ( ret ) {
printk ( KERN_ERR " Couldn't register MVME166/7 serial driver \n " ) ;
put_tty_driver ( cy_serial_driver ) ;
return ret ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
port_num = 0 ;
info = cy_port ;
for ( index = 0 ; index < 1 ; index + + ) {
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
good_ports = 4 ;
if ( port_num < NR_PORTS ) {
while ( good_ports - - & & port_num < NR_PORTS ) {
2005-04-17 02:20:36 +04:00
/*** initialize port ***/
2007-02-10 12:45:08 +03:00
info - > magic = CYCLADES_MAGIC ;
info - > type = PORT_CIRRUS ;
info - > card = index ;
info - > line = port_num ;
info - > flags = STD_COM_FLAGS ;
info - > tty = NULL ;
info - > xmit_fifo_size = 12 ;
info - > cor1 = CyPARITY_NONE | Cy_8_BITS ;
info - > cor2 = CyETC ;
info - > cor3 = Cy_1_STOP ;
info - > cor4 = 0x08 ; /* _very_ small receive threshold */
info - > cor5 = 0 ;
info - > cor6 = 0 ;
info - > cor7 = 0 ;
info - > tbpr = baud_bpr [ DefSpeed ] ; /* Tx BPR */
info - > tco = baud_co [ DefSpeed ] ; /* Tx CO */
info - > rbpr = baud_bpr [ DefSpeed ] ; /* Rx BPR */
info - > rco = baud_co [ DefSpeed ] > > 5 ; /* Rx CO */
info - > close_delay = 0 ;
info - > x_char = 0 ;
info - > event = 0 ;
info - > count = 0 ;
2005-04-17 02:20:36 +04:00
# ifdef SERIAL_DEBUG_COUNT
2007-02-10 12:45:08 +03:00
printk ( " cyc: %d: setting count to 0 \n " ,
__LINE__ ) ;
# endif
info - > blocked_open = 0 ;
info - > default_threshold = 0 ;
info - > default_timeout = 0 ;
INIT_WORK ( & info - > tqueue , do_softint ) ;
init_waitqueue_head ( & info - > open_wait ) ;
init_waitqueue_head ( & info - > close_wait ) ;
/* info->session */
/* info->pgrp */
2005-04-17 02:20:36 +04:00
/*** !!!!!!!! this may expose new bugs !!!!!!!!! *********/
2007-02-10 12:45:08 +03:00
info - > read_status_mask =
CyTIMEOUT | CySPECHAR | CyBREAK | CyPARITY |
CyFRAME | CyOVERRUN ;
/* info->timeout */
printk ( " ttyS%d " , info - > line ) ;
port_num + + ;
info + + ;
if ( ! ( port_num & 7 ) ) {
printk ( " \n " ) ;
}
}
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
printk ( " \n " ) ;
}
while ( port_num < NR_PORTS ) {
info - > line = - 1 ;
port_num + + ;
info + + ;
}
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_REMOTE_DEBUG
2007-02-10 12:45:08 +03:00
debug_setup ( ) ;
# endif
ret = request_irq ( MVME167_IRQ_SER_ERR , cd2401_rxerr_interrupt , 0 ,
" cd2401_errors " , cd2401_rxerr_interrupt ) ;
if ( ret ) {
printk ( KERN_ERR " Could't get cd2401_errors IRQ " ) ;
goto cleanup_serial_driver ;
}
ret = request_irq ( MVME167_IRQ_SER_MODEM , cd2401_modem_interrupt , 0 ,
" cd2401_modem " , cd2401_modem_interrupt ) ;
if ( ret ) {
printk ( KERN_ERR " Could't get cd2401_modem IRQ " ) ;
goto cleanup_irq_cd2401_errors ;
}
ret = request_irq ( MVME167_IRQ_SER_TX , cd2401_tx_interrupt , 0 ,
" cd2401_txints " , cd2401_tx_interrupt ) ;
if ( ret ) {
printk ( KERN_ERR " Could't get cd2401_txints IRQ " ) ;
goto cleanup_irq_cd2401_modem ;
}
ret = request_irq ( MVME167_IRQ_SER_RX , cd2401_rx_interrupt , 0 ,
" cd2401_rxints " , cd2401_rx_interrupt ) ;
if ( ret ) {
printk ( KERN_ERR " Could't get cd2401_rxints IRQ " ) ;
goto cleanup_irq_cd2401_txints ;
}
/* Now we have registered the interrupt handlers, allow the interrupts */
pcc2chip [ PccSCCMICR ] = 0x15 ; /* Serial ints are level 5 */
pcc2chip [ PccSCCTICR ] = 0x15 ;
pcc2chip [ PccSCCRICR ] = 0x15 ;
pcc2chip [ PccIMLR ] = 3 ; /* Allow PCC2 ints above 3!? */
return 0 ;
2005-04-17 02:20:36 +04:00
cleanup_irq_cd2401_txints :
2007-02-10 12:45:08 +03:00
free_irq ( MVME167_IRQ_SER_TX , cd2401_tx_interrupt ) ;
2005-04-17 02:20:36 +04:00
cleanup_irq_cd2401_modem :
2007-02-10 12:45:08 +03:00
free_irq ( MVME167_IRQ_SER_MODEM , cd2401_modem_interrupt ) ;
2005-04-17 02:20:36 +04:00
cleanup_irq_cd2401_errors :
2007-02-10 12:45:08 +03:00
free_irq ( MVME167_IRQ_SER_ERR , cd2401_rxerr_interrupt ) ;
2005-04-17 02:20:36 +04:00
cleanup_serial_driver :
2007-02-10 12:45:08 +03:00
if ( tty_unregister_driver ( cy_serial_driver ) )
printk ( KERN_ERR
" Couldn't unregister MVME166/7 serial driver \n " ) ;
put_tty_driver ( cy_serial_driver ) ;
return ret ;
} /* serial167_init */
2005-04-17 02:20:36 +04:00
module_init ( serial167_init ) ;
# ifdef CYCLOM_SHOW_STATUS
2007-02-10 12:45:08 +03:00
static void show_status ( int line_num )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int channel ;
struct cyclades_port * info ;
unsigned long flags ;
info = & cy_port [ line_num ] ;
channel = info - > line ;
printk ( " channel %d \n " , channel ) ;
/**/ printk ( " cy_port \n " ) ;
printk ( " card line flags = %d %d %x \n " ,
info - > card , info - > line , info - > flags ) ;
printk
( " *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x \n " ,
( long ) info - > tty , info - > read_status_mask , info - > timeout ,
info - > xmit_fifo_size ) ;
printk ( " cor1,cor2,cor3,cor4,cor5,cor6,cor7 = %x %x %x %x %x %x %x \n " ,
info - > cor1 , info - > cor2 , info - > cor3 , info - > cor4 , info - > cor5 ,
info - > cor6 , info - > cor7 ) ;
printk ( " tbpr,tco,rbpr,rco = %d %d %d %d \n " , info - > tbpr , info - > tco ,
info - > rbpr , info - > rco ) ;
printk ( " close_delay event count = %d %d %d \n " , info - > close_delay ,
info - > event , info - > count ) ;
printk ( " x_char blocked_open = %x %x \n " , info - > x_char ,
info - > blocked_open ) ;
printk ( " open_wait = %lx %lx %lx \n " , ( long ) info - > open_wait ) ;
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
/* Global Registers */
printk ( " CyGFRCR %x \n " , base_addr [ CyGFRCR ] ) ;
printk ( " CyCAR %x \n " , base_addr [ CyCAR ] ) ;
printk ( " CyRISR %x \n " , base_addr [ CyRISR ] ) ;
printk ( " CyTISR %x \n " , base_addr [ CyTISR ] ) ;
printk ( " CyMISR %x \n " , base_addr [ CyMISR ] ) ;
printk ( " CyRIR %x \n " , base_addr [ CyRIR ] ) ;
printk ( " CyTIR %x \n " , base_addr [ CyTIR ] ) ;
printk ( " CyMIR %x \n " , base_addr [ CyMIR ] ) ;
printk ( " CyTPR %x \n " , base_addr [ CyTPR ] ) ;
2007-02-10 12:45:08 +03:00
base_addr [ CyCAR ] = ( u_char ) channel ;
2005-04-17 02:20:36 +04:00
/* Virtual Registers */
#if 0
printk ( " CyRIVR %x \n " , base_addr [ CyRIVR ] ) ;
printk ( " CyTIVR %x \n " , base_addr [ CyTIVR ] ) ;
printk ( " CyMIVR %x \n " , base_addr [ CyMIVR ] ) ;
printk ( " CyMISR %x \n " , base_addr [ CyMISR ] ) ;
# endif
/* Channel Registers */
printk ( " CyCCR %x \n " , base_addr [ CyCCR ] ) ;
printk ( " CyIER %x \n " , base_addr [ CyIER ] ) ;
printk ( " CyCOR1 %x \n " , base_addr [ CyCOR1 ] ) ;
printk ( " CyCOR2 %x \n " , base_addr [ CyCOR2 ] ) ;
printk ( " CyCOR3 %x \n " , base_addr [ CyCOR3 ] ) ;
printk ( " CyCOR4 %x \n " , base_addr [ CyCOR4 ] ) ;
printk ( " CyCOR5 %x \n " , base_addr [ CyCOR5 ] ) ;
#if 0
printk ( " CyCCSR %x \n " , base_addr [ CyCCSR ] ) ;
printk ( " CyRDCR %x \n " , base_addr [ CyRDCR ] ) ;
# endif
printk ( " CySCHR1 %x \n " , base_addr [ CySCHR1 ] ) ;
printk ( " CySCHR2 %x \n " , base_addr [ CySCHR2 ] ) ;
#if 0
printk ( " CySCHR3 %x \n " , base_addr [ CySCHR3 ] ) ;
printk ( " CySCHR4 %x \n " , base_addr [ CySCHR4 ] ) ;
printk ( " CySCRL %x \n " , base_addr [ CySCRL ] ) ;
printk ( " CySCRH %x \n " , base_addr [ CySCRH ] ) ;
printk ( " CyLNC %x \n " , base_addr [ CyLNC ] ) ;
printk ( " CyMCOR1 %x \n " , base_addr [ CyMCOR1 ] ) ;
printk ( " CyMCOR2 %x \n " , base_addr [ CyMCOR2 ] ) ;
# endif
printk ( " CyRTPRL %x \n " , base_addr [ CyRTPRL ] ) ;
printk ( " CyRTPRH %x \n " , base_addr [ CyRTPRH ] ) ;
printk ( " CyMSVR1 %x \n " , base_addr [ CyMSVR1 ] ) ;
printk ( " CyMSVR2 %x \n " , base_addr [ CyMSVR2 ] ) ;
printk ( " CyRBPR %x \n " , base_addr [ CyRBPR ] ) ;
printk ( " CyRCOR %x \n " , base_addr [ CyRCOR ] ) ;
printk ( " CyTBPR %x \n " , base_addr [ CyTBPR ] ) ;
printk ( " CyTCOR %x \n " , base_addr [ CyTCOR ] ) ;
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
} /* show_status */
2005-04-17 02:20:36 +04:00
# endif
#if 0
/* Dummy routine in mvme16x/config.c for now */
/* Serial console setup. Called from linux/init/main.c */
void console_setup ( char * str , int * ints )
{
char * s ;
int baud , bits , parity ;
int cflag = 0 ;
/* Sanity check. */
2007-02-10 12:45:08 +03:00
if ( ints [ 0 ] > 3 | | ints [ 1 ] > 3 )
return ;
2005-04-17 02:20:36 +04:00
/* Get baud, bits and parity */
baud = 2400 ;
bits = 8 ;
parity = ' n ' ;
2007-02-10 12:45:08 +03:00
if ( ints [ 2 ] )
baud = ints [ 2 ] ;
2005-04-17 02:20:36 +04:00
if ( ( s = strchr ( str , ' , ' ) ) ) {
do {
s + + ;
2007-02-10 12:45:08 +03:00
} while ( * s > = ' 0 ' & & * s < = ' 9 ' ) ;
if ( * s )
parity = * s + + ;
if ( * s )
bits = * s - ' 0 ' ;
2005-04-17 02:20:36 +04:00
}
/* Now construct a cflag setting. */
2007-02-10 12:45:08 +03:00
switch ( baud ) {
case 1200 :
cflag | = B1200 ;
break ;
case 9600 :
cflag | = B9600 ;
break ;
case 19200 :
cflag | = B19200 ;
break ;
case 38400 :
cflag | = B38400 ;
break ;
case 2400 :
default :
cflag | = B2400 ;
break ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
switch ( bits ) {
case 7 :
cflag | = CS7 ;
break ;
default :
case 8 :
cflag | = CS8 ;
break ;
2005-04-17 02:20:36 +04:00
}
2007-02-10 12:45:08 +03:00
switch ( parity ) {
case ' o ' :
case ' O ' :
cflag | = PARODD ;
break ;
case ' e ' :
case ' E ' :
cflag | = PARENB ;
break ;
2005-04-17 02:20:36 +04:00
}
serial_console_info = & cy_port [ ints [ 1 ] ] ;
serial_console_cflag = cflag ;
2007-02-10 12:45:08 +03:00
serial_console = ints [ 1 ] + 64 ; /*callout_driver.minor_start */
2005-04-17 02:20:36 +04:00
}
# endif
/*
* The following is probably out of date for 2.1 . x serial console stuff .
*
* The console is registered early on from arch / m68k / kernel / setup . c , and
* it therefore relies on the chip being setup correctly by 166 - Bug . This
* seems reasonable , as the serial port has been used to invoke the system
* boot . It also means that this function must not rely on any data
* initialisation performed by serial167_init ( ) etc .
*
* Of course , once the console has been registered , we had better ensure
* that serial167_init ( ) doesn ' t leave the chip non - functional .
*
* The console must be locked when we get here .
*/
2007-02-10 12:45:08 +03:00
void serial167_console_write ( struct console * co , const char * str ,
unsigned count )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
volatile u_char sink ;
u_char ier ;
int port ;
u_char do_lf = 0 ;
int i = 0 ;
local_irq_save ( flags ) ;
/* Ensure transmitter is enabled! */
port = 0 ;
2007-02-10 12:45:08 +03:00
base_addr [ CyCAR ] = ( u_char ) port ;
2005-04-17 02:20:36 +04:00
while ( base_addr [ CyCCR ] )
;
base_addr [ CyCCR ] = CyENB_XMTR ;
ier = base_addr [ CyIER ] ;
base_addr [ CyIER ] = CyTxMpty ;
while ( 1 ) {
2007-02-10 12:45:08 +03:00
if ( pcc2chip [ PccSCCTICR ] & 0x20 ) {
2005-04-17 02:20:36 +04:00
/* We have a Tx int. Acknowledge it */
sink = pcc2chip [ PccTPIACKR ] ;
if ( ( base_addr [ CyLICR ] > > 2 ) = = port ) {
if ( i = = count ) {
/* Last char of string is now output */
base_addr [ CyTEOIR ] = CyNOTRANS ;
break ;
}
if ( do_lf ) {
base_addr [ CyTDR ] = ' \n ' ;
str + + ;
i + + ;
do_lf = 0 ;
2007-02-10 12:45:08 +03:00
} else if ( * str = = ' \n ' ) {
2005-04-17 02:20:36 +04:00
base_addr [ CyTDR ] = ' \r ' ;
do_lf = 1 ;
2007-02-10 12:45:08 +03:00
} else {
2005-04-17 02:20:36 +04:00
base_addr [ CyTDR ] = * str + + ;
i + + ;
}
base_addr [ CyTEOIR ] = 0 ;
2007-02-10 12:45:08 +03:00
} else
2005-04-17 02:20:36 +04:00
base_addr [ CyTEOIR ] = CyNOTRANS ;
}
}
base_addr [ CyIER ] = ier ;
local_irq_restore ( flags ) ;
}
2007-02-10 12:45:08 +03:00
static struct tty_driver * serial167_console_device ( struct console * c ,
int * index )
2005-04-17 02:20:36 +04:00
{
* index = c - > index ;
return cy_serial_driver ;
}
static int __init serial167_console_setup ( struct console * co , char * options )
{
return 0 ;
}
static struct console sercons = {
2007-02-10 12:45:08 +03:00
. name = " ttyS " ,
. write = serial167_console_write ,
. device = serial167_console_device ,
. setup = serial167_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
2005-04-17 02:20:36 +04:00
} ;
static int __init serial167_console_init ( void )
{
if ( vme_brdtype = = VME_TYPE_MVME166 | |
2007-02-10 12:45:08 +03:00
vme_brdtype = = VME_TYPE_MVME167 | |
vme_brdtype = = VME_TYPE_MVME177 ) {
2005-04-17 02:20:36 +04:00
mvme167_serial_console_setup ( 0 ) ;
register_console ( & sercons ) ;
}
return 0 ;
}
2007-02-10 12:45:08 +03:00
2005-04-17 02:20:36 +04:00
console_initcall ( serial167_console_init ) ;
# ifdef CONFIG_REMOTE_DEBUG
2007-02-10 12:45:08 +03:00
void putDebugChar ( int c )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
volatile u_char sink ;
u_char ier ;
int port ;
local_irq_save ( flags ) ;
/* Ensure transmitter is enabled! */
port = DEBUG_PORT ;
2007-02-10 12:45:08 +03:00
base_addr [ CyCAR ] = ( u_char ) port ;
2005-04-17 02:20:36 +04:00
while ( base_addr [ CyCCR ] )
;
base_addr [ CyCCR ] = CyENB_XMTR ;
ier = base_addr [ CyIER ] ;
base_addr [ CyIER ] = CyTxMpty ;
while ( 1 ) {
2007-02-10 12:45:08 +03:00
if ( pcc2chip [ PccSCCTICR ] & 0x20 ) {
2005-04-17 02:20:36 +04:00
/* We have a Tx int. Acknowledge it */
sink = pcc2chip [ PccTPIACKR ] ;
if ( ( base_addr [ CyLICR ] > > 2 ) = = port ) {
base_addr [ CyTDR ] = c ;
base_addr [ CyTEOIR ] = 0 ;
break ;
2007-02-10 12:45:08 +03:00
} else
2005-04-17 02:20:36 +04:00
base_addr [ CyTEOIR ] = CyNOTRANS ;
}
}
base_addr [ CyIER ] = ier ;
local_irq_restore ( flags ) ;
}
int getDebugChar ( )
{
2007-02-10 12:45:08 +03:00
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
volatile u_char sink ;
u_char ier ;
int port ;
int i , c ;
i = debugiq . out ;
if ( i ! = debugiq . in ) {
c = debugiq . buf [ i ] ;
if ( + + i = = DEBUG_LEN )
i = 0 ;
debugiq . out = i ;
return c ;
}
/* OK, nothing in queue, wait in poll loop */
local_irq_save ( flags ) ;
/* Ensure receiver is enabled! */
port = DEBUG_PORT ;
2007-02-10 12:45:08 +03:00
base_addr [ CyCAR ] = ( u_char ) port ;
2005-04-17 02:20:36 +04:00
#if 0
while ( base_addr [ CyCCR ] )
;
base_addr [ CyCCR ] = CyENB_RCVR ;
# endif
ier = base_addr [ CyIER ] ;
base_addr [ CyIER ] = CyRxData ;
while ( 1 ) {
2007-02-10 12:45:08 +03:00
if ( pcc2chip [ PccSCCRICR ] & 0x20 ) {
2005-04-17 02:20:36 +04:00
/* We have a Rx int. Acknowledge it */
sink = pcc2chip [ PccRPIACKR ] ;
if ( ( base_addr [ CyLICR ] > > 2 ) = = port ) {
int cnt = base_addr [ CyRFOC ] ;
2007-02-10 12:45:08 +03:00
while ( cnt - - > 0 ) {
2005-04-17 02:20:36 +04:00
c = base_addr [ CyRDR ] ;
if ( c = = 0 )
2007-02-10 12:45:08 +03:00
printk
( " !! debug char is null (cnt=%d) !! " ,
cnt ) ;
2005-04-17 02:20:36 +04:00
else
2007-02-10 12:45:08 +03:00
queueDebugChar ( c ) ;
2005-04-17 02:20:36 +04:00
}
base_addr [ CyREOIR ] = 0 ;
i = debugiq . out ;
if ( i = = debugiq . in )
2007-02-10 12:45:08 +03:00
panic ( " Debug input queue empty! " ) ;
2005-04-17 02:20:36 +04:00
c = debugiq . buf [ i ] ;
if ( + + i = = DEBUG_LEN )
i = 0 ;
debugiq . out = i ;
break ;
2007-02-10 12:45:08 +03:00
} else
2005-04-17 02:20:36 +04:00
base_addr [ CyREOIR ] = CyNOTRANS ;
}
}
base_addr [ CyIER ] = ier ;
local_irq_restore ( flags ) ;
return ( c ) ;
}
2007-02-10 12:45:08 +03:00
void queueDebugChar ( int c )
2005-04-17 02:20:36 +04:00
{
int i ;
i = debugiq . in ;
debugiq . buf [ i ] = c ;
if ( + + i = = DEBUG_LEN )
i = 0 ;
if ( i ! = debugiq . out )
debugiq . in = i ;
}
2007-02-10 12:45:08 +03:00
static void debug_setup ( )
2005-04-17 02:20:36 +04:00
{
2007-02-10 12:45:08 +03:00
unsigned long flags ;
volatile unsigned char * base_addr = ( u_char * ) BASE_ADDR ;
int i , cflag ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
cflag = B19200 ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_save ( flags ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
for ( i = 0 ; i < 4 ; i + + ) {
base_addr [ CyCAR ] = i ;
base_addr [ CyLICR ] = i < < 2 ;
}
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
debugiq . in = debugiq . out = 0 ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyCAR ] = DEBUG_PORT ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/* baud rate */
i = cflag & CBAUD ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyIER ] = 0 ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyCMR ] = CyASYNC ;
base_addr [ CyLICR ] = DEBUG_PORT < < 2 ;
base_addr [ CyLIVR ] = 0x5c ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/* tx and rx baud rate */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyTCOR ] = baud_co [ i ] ;
base_addr [ CyTBPR ] = baud_bpr [ i ] ;
base_addr [ CyRCOR ] = baud_co [ i ] > > 5 ;
base_addr [ CyRBPR ] = baud_bpr [ i ] ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
/* set line characteristics according configuration */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CySCHR1 ] = 0 ;
base_addr [ CySCHR2 ] = 0 ;
base_addr [ CySCRL ] = 0 ;
base_addr [ CySCRH ] = 0 ;
base_addr [ CyCOR1 ] = Cy_8_BITS | CyPARITY_NONE ;
base_addr [ CyCOR2 ] = 0 ;
base_addr [ CyCOR3 ] = Cy_1_STOP ;
base_addr [ CyCOR4 ] = baud_cor4 [ i ] ;
base_addr [ CyCOR5 ] = 0 ;
base_addr [ CyCOR6 ] = 0 ;
base_addr [ CyCOR7 ] = 0 ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
write_cy_cmd ( base_addr , CyINIT_CHAN ) ;
write_cy_cmd ( base_addr , CyENB_RCVR ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyCAR ] = DEBUG_PORT ; /* !!! Is this needed? */
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyRTPRL ] = 2 ;
base_addr [ CyRTPRH ] = 0 ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyMSVR1 ] = CyRTS ;
base_addr [ CyMSVR2 ] = CyDTR ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
base_addr [ CyIER ] = CyRxData ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
local_irq_restore ( flags ) ;
2005-04-17 02:20:36 +04:00
2007-02-10 12:45:08 +03:00
} /* debug_setup */
2005-04-17 02:20:36 +04:00
# endif
MODULE_LICENSE ( " GPL " ) ;