patch-2.2.13 linux/drivers/char/cyclades.c

Next file: linux/drivers/char/lp.c
Previous file: linux/drivers/char/console.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.12/linux/drivers/char/cyclades.c linux/drivers/char/cyclades.c
@@ -1,7 +1,7 @@
-#define BLOCKMOVE
+#undef	BLOCKMOVE
 #define	Z_WAKE
 static char rcsid[] =
-"$Revision: 2.2.2.3 $$Date: 1999/06/28 11:13:29 $";
+"$Revision: 2.3.2.2 $$Date: 1999/10/01 11:27:43 $";
 
 /*
  *  linux/drivers/char/cyclades.c
@@ -9,9 +9,8 @@
  * This file contains the driver for the Cyclades Cyclom-Y multiport
  * serial boards.
  *
- * Maintained by Ivan Passos (ivan@cyclades.com),
- * Marcio Saito (marcio@cyclades.com) and 
- * Randolph Bentson (bentson@grieg.seaslug.org).
+ * Initially written by Randolph Bentson (bentson@grieg.seaslug.org).
+ * Maintained by Ivan Passos (ivan@cyclades.com).
  *
  * For Technical support and installation problems, please send e-mail
  * to support@cyclades.com.
@@ -31,6 +30,39 @@
  *   void cleanup_module(void);
  *
  * $Log: cyclades.c,v $
+ * Revision 2.3.2.2   1999/10/01 11:27:43 ivan
+ * Fixed bug in cyz_poll that would make all ports but port 0 
+ * unable to transmit/receive data (Cyclades-Z only);
+ * Implemented logic to prevent the RX buffer from being stuck with data
+ * due to a driver / firmware race condition in interrupt op mode
+ * (Cyclades-Z only);
+ * Fixed bug in block_til_ready logic that would lead to a system crash;
+ * Revisited cy_close spinlock usage;
+ *
+ * Revision 2.3.2.1   1999/09/28 11:01:22 ivan
+ * Revisited CONFIG_PCI conditional compilation for PCI board support;
+ * Implemented TIOCGICOUNT and TIOCMIWAIT ioctl support;
+ * _Major_ cleanup on the Cyclades-Z interrupt support code / logic;
+ * Removed CTS handling from the driver -- this is now completely handled
+ * by the firmware (Cyclades-Z only);
+ * Flush RX on-board buffers on a port open (Cyclades-Z only);
+ * Fixed handling of ASYNC_SPD_* TTY flags;
+ * Module unload now unmaps all memory area allocated by ioremap;
+ *
+ * Revision 2.3.1.1   1999/07/15 16:45:53 ivan
+ * Removed CY_PROC conditional compilation;
+ * Implemented SMP-awareness for the driver;
+ * Implemented a new ISA IRQ autoprobe that uses the irq_probe_[on|off] 
+ * functions;
+ * The driver now accepts memory addresses (maddr=0xMMMMM) and IRQs
+ * (irq=NN) as parameters (only for ISA boards);
+ * Fixed bug in set_line_char that would prevent the Cyclades-Z 
+ * ports from being configured at speeds above 115.2Kbps;
+ * Fixed bug in cy_set_termios that would prevent XON/XOFF flow control
+ * switching from working properly;
+ * The driver now only prints IRQ info for the Cyclades-Z if it's 
+ * configured to work in interrupt mode;
+ *
  * Revision 2.2.2.3   1999/06/28 11:13:29 ivan
  * Added support for interrupt mode operation for the Z cards;
  * Removed the driver inactivity control for the Z;
@@ -557,7 +589,6 @@
 #undef	CY_16Y_HACK
 #undef	CY_ENABLE_MONITORING
 #undef	CY_PCI_DEBUG
-#undef	CY_PROC
 
 #if 0
 #define PAUSE __asm__("nop");
@@ -613,6 +644,18 @@
 #include <asm/uaccess.h>
 #include <asm/bitops.h>
 
+#include <asm/spinlock.h>
+
+#define	CY_LOCK(info,flags)					\
+		do {						\
+		spin_lock_irqsave(&cy_card[info->card].card_lock, flags); \
+		} while (0)
+		
+#define	CY_UNLOCK(info,flags)					\
+		do {						\
+		spin_unlock_irqrestore(&cy_card[info->card].card_lock, flags); \
+		} while (0)
+
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/pci.h>
@@ -631,7 +674,8 @@
 
 #define cy_put_user	put_user
 
-static unsigned long cy_get_user(unsigned long *addr)
+static unsigned long 
+cy_get_user(unsigned long *addr)
 {
 	unsigned long result = 0;
 	int error = get_user (result, addr);
@@ -668,11 +712,6 @@
 static int serial_refcount;
 
 #ifndef CONFIG_COBALT_27
-static volatile int cy_irq_triggered;
-static volatile int cy_triggered;
-static int cy_wild_int_mask;
-static volatile ucchar *intr_base_addr;
-
 /* This is the address lookup table. The driver will probe for
    Cyclom-Y/ISA boards at all addresses in here. If you want the
    driver to probe addresses at a different address, add it to
@@ -695,6 +734,14 @@
 };
 #define NR_ISA_ADDRS (sizeof(cy_isa_addresses)/sizeof(unsigned char*))
 
+#ifdef MODULE
+static int maddr[NR_CARDS] = { 0, };
+static int irq[NR_CARDS]  = { 0, };
+
+MODULE_PARM(maddr, "1-" __MODULE_STRING(NR_CARDS) "l");
+MODULE_PARM(irq, "1-" __MODULE_STRING(NR_CARDS) "i");
+#endif
+
 #endif /* CONFIG_COBALT_27 */
 
 /* This is the per-card data structure containing address, irq, number of
@@ -802,6 +849,7 @@
 static unsigned short	cy_pci_nboard = 0;
 static unsigned short	cy_isa_nboard = 0;
 static unsigned short	cy_nboard = 0;
+#ifdef CONFIG_PCI
 static unsigned short	cy_pci_dev_id[] = {
 			    PCI_DEVICE_ID_CYCLOM_Y_Lo,	/* PCI < 1Mb */
 			    PCI_DEVICE_ID_CYCLOM_Y_Hi,	/* PCI > 1Mb */
@@ -813,12 +861,13 @@
 			    PCI_DEVICE_ID_CYCLOM_Z_Hi,	/* Z PCI > 1Mb */
 			    0				/* end of table */
 			};
-
+#endif
 
 static void cy_start(struct tty_struct *);
 static void set_line_char(struct cyclades_port *);
+static int cyz_issue_cmd(struct cyclades_card *, uclong, ucchar, uclong);
 #ifndef CONFIG_COBALT_27
-static void cy_probe(int, void *, struct pt_regs *);
+static unsigned detect_isa_irq (volatile ucchar *);
 #endif /* CONFIG_COBALT_27 */
 #ifdef CYCLOM_SHOW_STATUS
 static void show_status(int);
@@ -837,6 +886,9 @@
 cyz_timerlist = {
     NULL, NULL, 0, 0, cyz_poll
 };
+#else /* CONFIG_CYZ_INTR */
+static void cyz_rx_restart(unsigned long);
+static struct timer_list cyz_rx_full_timer[NR_PORTS];
 #endif /* CONFIG_CYZ_INTR */
 
 /**************************************************
@@ -941,6 +993,17 @@
     if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
         wake_up_interruptible(&info->open_wait);
     }
+#ifdef CONFIG_CYZ_INTR
+    if (test_and_clear_bit(Cy_EVENT_Z_RX_FULL, &info->event)) {
+	cyz_rx_full_timer[info->line].expires = jiffies + 1;
+	cyz_rx_full_timer[info->line].function = cyz_rx_restart;
+	cyz_rx_full_timer[info->line].data = (unsigned long)info;
+	add_timer(&cyz_rx_full_timer[info->line]);
+    }
+#endif
+    if (test_and_clear_bit(Cy_EVENT_DELTA_WAKEUP, &info->event)) {
+	wake_up_interruptible(&info->delta_msr_wait);
+    }
     if (test_and_clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
         if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
         && tty->ldisc.write_wakeup){
@@ -963,226 +1026,80 @@
    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.
+
+   This function is only called from inside spinlock-protected code.
  */
 static int
 cyy_issue_cmd(volatile ucchar *base_addr, u_char cmd, int index)
 {
-  unsigned long flags;
   volatile int  i;
 
-    save_flags(flags); cli();
-        /* Check to see that the previous command has completed */
-        for(i = 0 ; i < 100 ; i++){
-            if (cy_readb(base_addr+(CyCCR<<index)) == 0){
-                break;
-            }
-            udelay(10L);
-        }
-        /* if the CCR never cleared, the previous command
-            didn't finish within the "reasonable time" */
-        if ( i == 100 ) {
-            restore_flags(flags);
-            return (-1);
-        }
+    /* Check to see that the previous command has completed */
+    for(i = 0 ; i < 100 ; i++){
+	if (cy_readb(base_addr+(CyCCR<<index)) == 0){
+	    break;
+	}
+	udelay(10L);
+    }
+    /* if the CCR never cleared, the previous command
+       didn't finish within the "reasonable time" */
+    if (i == 100)	return (-1);
+
+    /* Issue the new command */
+    cy_writeb((u_long)base_addr+(CyCCR<<index), cmd);
 
-        /* Issue the new command */
-        cy_writeb((u_long)base_addr+(CyCCR<<index), cmd);
-    restore_flags(flags);
     return(0);
 } /* cyy_issue_cmd */
 
 #ifndef CONFIG_COBALT_27	/* ISA interrupt detection code */
-
-static int probe_ready;
-
-/*
- * Grab all interrupts in preparation for doing an automatic irq
- * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a
- * mask of irq's which were grabbed and should therefore be freed
- * using free_all_interrupts().
- */
-static int
-grab_all_interrupts(int dontgrab)
+static unsigned 
+detect_isa_irq (volatile ucchar *address)
 {
-  int irq_lines = 0;
-  int i, mask;
-    
-    for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
-        if (!(mask & dontgrab)
-        && !request_irq(i, cy_probe,
-	             SA_INTERRUPT, "serial probe", NULL)) {
-            irq_lines |= mask;
-        }
-    }
-    return irq_lines;
-} /* grab_all_interrupts */
+  int irq;
+  unsigned long irqs, flags;
+  int save_xir, save_car;
+  int index = 0; /* IRQ probing is only for ISA */
 
-/*
- * Release all interrupts grabbed by grab_all_interrupts
- */
-static void
-free_all_interrupts(int irq_lines)
-{
-  int i;
-    
-    for (i = 0; i < 16; i++) {
-        if (irq_lines & (1 << i)) {
-            free_irq(i,NULL);
-	}
-    }
-} /* free_all_interrupts */
+    /* forget possible initially masked and pending IRQ */
+    irq = probe_irq_off(probe_irq_on());
 
-/*
- * This routine returns a bitfield of "wild interrupts".  Basically,
- * any unclaimed interrupts which is flapping around.
- */
-static int
-check_wild_interrupts(void)
-{
-  int   i, mask;
-  int   wild_interrupts = 0;
-  int   irq_lines;
-  unsigned long timeout;
-  unsigned long flags;
-        
-    /*Turn on interrupts (they may be off) */
-    save_flags(flags); sti();
+    /* Clear interrupts on the board first */
+    cy_writeb((u_long)address + (Cy_ClrIntr<<index), 0);
+			      /* Cy_ClrIntr is 0x1800 */
 
-        irq_lines = grab_all_interrupts(0);
-       
-        /*
-         * Delay for 0.1 seconds -- we use a busy loop since this may 
-         * occur during the bootup sequence
-         */
-        timeout = jiffies+(HZ/10);
-        while (time_after_eq(timeout, jiffies))
-            ;
-        
-        cy_triggered = 0;       /* Reset after letting things settle */
-
-        timeout = jiffies+(HZ/10);
-        while (time_after_eq(timeout, jiffies))
-                ;
-        
-        for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
-            if ((cy_triggered & (1 << i)) &&
-                (irq_lines & (1 << i))) {
-                    wild_interrupts |= mask;
-            }
-        }
-        free_all_interrupts(irq_lines);
-    restore_flags(flags);
-    return wild_interrupts;
-} /* check_wild_interrupts */
+    irqs = probe_irq_on();
+    /* Wait ... */
+    udelay(5000L);
 
-/*
- * This routine is called by do_auto_irq(); it attempts to determine
- * which interrupt a serial port is configured to use.  It is not
- * fool-proof, but it works a large part of the time.
- */
-static int
-get_auto_irq(volatile ucchar *address)
-{
-  unsigned long   	timeout;
-  volatile ucchar 	*base_addr;
-  int           	index;
-  unsigned long		flags;
-
-    index = 0;  /* IRQ probing is only for ISA */
-    base_addr = address;
-    intr_base_addr = address;
-        
-    /*
-     * Enable interrupts and see who answers
-     */
-    cy_irq_triggered = 0;
+    /* Enable the Tx interrupts on the CD1400 */
     save_flags(flags); cli();
-        cy_writeb((u_long)base_addr+(CyCAR<<index), 0);
-        cyy_issue_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR,index);
-        cy_writeb((u_long)base_addr+(CySRER<<index), 
-             cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
-        probe_ready = 1;
-    restore_flags(flags);
-    
-    timeout = jiffies+(HZ/50);
-    while (time_after_eq(timeout, jiffies)) {
-        if (cy_irq_triggered)
-            break;
-    }
-    probe_ready = 0;
-    return(cy_irq_triggered);
-} /* get_auto_irq */
-
-/*
- * Calls get_auto_irq() multiple times, to make sure we don't get
- * faked out by random interrupts
- */
-static int
-do_auto_irq(volatile ucchar *address)
-{
-  int                   irq_lines = 0;
-  int                   irq_try_1 = 0, irq_try_2 = 0;
-  int                   retries;
-  unsigned long		flags;
-
-    /* Turn on interrupts (they may be off) */
-    save_flags(flags); sti();
-
-        probe_ready = 0;
-
-        cy_wild_int_mask = check_wild_interrupts();
+	cy_writeb((u_long)address + (CyCAR<<index), 0);
+	cyy_issue_cmd(address, CyCHAN_CTL|CyENB_XMTR, index);
 
-        irq_lines = grab_all_interrupts(cy_wild_int_mask);
-        
-        for (retries = 0; retries < 5; retries++) {
-            if (!irq_try_1)
-                irq_try_1 = get_auto_irq(address);
-            if (!irq_try_2)
-                irq_try_2 = get_auto_irq(address);
-            if (irq_try_1 && irq_try_2) {
-                if (irq_try_1 == irq_try_2)
-                    break;
-                irq_try_1 = irq_try_2 = 0;
-            }
-        }
+	cy_writeb((u_long)address + (CyCAR<<index), 0);
+	cy_writeb((u_long)address + (CySRER<<index), 
+		cy_readb(address + (CySRER<<index)) | CyTxMpty);
     restore_flags(flags);
-    free_all_interrupts(irq_lines);
-    return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
-} /* do_auto_irq */
 
+    /* Wait ... */
+    udelay(5000L);
 
-/*
- * This interrupt routine is used
- * while we are probing for submarines.
- */
-static void
-cy_probe(int irq, void *dev_id, struct pt_regs *regs)
-{
-  int save_xir, save_car;
-  int index = 0;        /* probing interrupts is only for ISA */
-
-    if (!probe_ready) {
-        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
-        return;
-    }
+    /* Check which interrupt is in use */
+    irq = probe_irq_off(irqs);
 
-    cy_irq_triggered = irq;
-    cy_triggered |= 1 << irq;
-
-        if(cy_readb(intr_base_addr+(CySVRR<<index)) != 0) {
-            save_xir = (u_char) cy_readb(intr_base_addr+(CyTIR<<index));
-            save_car = cy_readb(intr_base_addr+(CyCAR<<index));
-            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_xir & 0x3));
-            cy_writeb((u_long)intr_base_addr+(CySRER<<index),
-                cy_readb(intr_base_addr+(CySRER<<index)) & ~CyTxMpty);
-            cy_writeb((u_long)intr_base_addr+(CyTIR<<index), (save_xir & 0x3f));
-            cy_writeb((u_long)intr_base_addr+(CyCAR<<index), (save_car));
-        }
-        cy_writeb((u_long)intr_base_addr+(Cy_ClrIntr<<index), 0);
-	                          /* Cy_ClrIntr is 0x1800 */
-    return;
-} /* cy_probe */
+    /* Clean up */
+    save_xir = (u_char) cy_readb(address + (CyTIR<<index));
+    save_car = cy_readb(address + (CyCAR<<index));
+    cy_writeb((u_long)address + (CyCAR<<index), (save_xir & 0x3));
+    cy_writeb((u_long)address + (CySRER<<index),
+	cy_readb(address + (CySRER<<index)) & ~CyTxMpty);
+    cy_writeb((u_long)address + (CyTIR<<index), (save_xir & 0x3f));
+    cy_writeb((u_long)address + (CyCAR<<index), (save_car));
+    cy_writeb((u_long)address + (Cy_ClrIntr<<index), 0);
+			      /* Cy_ClrIntr is 0x1800 */
 
+    return (irq > 0)? irq : 0;
+}
 #endif /* CONFIG_COBALT_27 */
 
 /* The real interrupt service routine is called
@@ -1245,6 +1162,7 @@
 		    printk("cyy_interrupt: rcvd intr, chip %d\n\r", chip);
 #endif
                     /* determine the channel & change to that context */
+		    spin_lock(&cinfo->card_lock);
                     save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     i = channel + chip * 4 + cinfo->first_line;
@@ -1252,6 +1170,7 @@
                     info->last_active = jiffies;
                     save_car = cy_readb(base_addr+(CyCAR<<index));
                     cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
+		    spin_unlock(&cinfo->card_lock);
 
                     /* if there is nowhere to put the data, discard it */
                     if(info->tty == 0){
@@ -1269,7 +1188,19 @@
                         j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask);
                         if ( j == CyIVRRxEx ) { /* exception */
                             data = cy_readb(base_addr+(CyRDSR<<index));
+
+			    /* For statistics only */
+			    if (data & CyBREAK)
+				info->icount.brk++;
+			    else if(data & CyFRAME)
+				info->icount.frame++;
+			    else if(data & CyPARITY)
+				info->icount.parity++;
+			    else if(data & CyOVERRUN)
+				info->icount.overrun++;
+
                             if(data & info->ignore_status_mask){
+				info->icount.rx++;
                                 continue;
                             }
                             if (tty->flip.count < TTY_FLIPBUF_SIZE){
@@ -1277,9 +1208,10 @@
                                 if (data & info->read_status_mask){
                                     if(data & CyBREAK){
                                         *tty->flip.flag_buf_ptr++ =
-							    TTY_BREAK;
+	    						    TTY_BREAK;
                                         *tty->flip.char_buf_ptr++ =
 					  cy_readb(base_addr+(CyRDSR<<index));
+					info->icount.rx++;
                                         if (info->flags & ASYNC_SAK){
                                             do_SAK(tty);
                                         }
@@ -1288,17 +1220,20 @@
 							    TTY_FRAME;
                                         *tty->flip.char_buf_ptr++ =
 					  cy_readb(base_addr+(CyRDSR<<index));
+					info->icount.rx++;
 					info->idle_stats.frame_errs++;
                                     }else if(data & CyPARITY){
                                         *tty->flip.flag_buf_ptr++ =
 							    TTY_PARITY;
                                         *tty->flip.char_buf_ptr++ =
 					  cy_readb(base_addr+(CyRDSR<<index));
+					info->icount.rx++;
 					info->idle_stats.parity_errs++;
                                     }else if(data & CyOVERRUN){
                                         *tty->flip.flag_buf_ptr++ =
 							    TTY_OVERRUN;
                                         *tty->flip.char_buf_ptr++ = 0;
+					info->icount.rx++;
                                         /* If the flip buffer itself is
                                            overflowing, we still lose
                                            the next incoming character.
@@ -1310,6 +1245,7 @@
 							     TTY_NORMAL;
                                            *tty->flip.char_buf_ptr++ =
 					    cy_readb(base_addr+(CyRDSR<<index));
+					    info->icount.rx++;
                                         }
 					info->idle_stats.overruns++;
                                     /* These two conditions may imply */
@@ -1319,15 +1255,18 @@
                                     }else{
                                         *tty->flip.flag_buf_ptr++ = 0;
                                         *tty->flip.char_buf_ptr++ = 0;
+					info->icount.rx++;
                                     }
                                 }else{
                                     *tty->flip.flag_buf_ptr++ = 0;
                                     *tty->flip.char_buf_ptr++ = 0;
+				    info->icount.rx++;
                                 }
                             }else{
                                 /* there was a software buffer
 				   overrun and nothing could be
 				   done about it!!! */
+				info->icount.buf_overrun++;
 				info->idle_stats.overruns++;
                             }
                         } else { /* normal character reception */
@@ -1351,6 +1290,7 @@
                                 data = cy_readb(base_addr+(CyRDSR<<index));
                                 *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
                                 *tty->flip.char_buf_ptr++ = data;
+				info->icount.rx++;
 #ifdef CY_16Y_HACK
                                 udelay(10L);
 #endif
@@ -1359,8 +1299,10 @@
                         queue_task(&tty->flip.tqueue, &tq_timer);
                     }
                     /* end of service */
+		    spin_lock(&cinfo->card_lock);
                     cy_writeb((u_long)base_addr+(CyRIR<<index), (save_xir & 0x3f));
                     cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
+		    spin_unlock(&cinfo->card_lock);
                 }
 
 
@@ -1373,34 +1315,40 @@
 #endif
 
                     /* determine the channel & change to that context */
+		    spin_lock(&cinfo->card_lock);
                     save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     i = channel + chip * 4 + cinfo->first_line;
                     save_car = cy_readb(base_addr+(CyCAR<<index));
                     cy_writeb((u_long)base_addr+(CyCAR<<index), save_xir);
+		    spin_unlock(&cinfo->card_lock);
 
                     /* validate the port# (as configured and open) */
                     if( (i < 0) || (NR_PORTS <= i) ){
+			spin_lock(&cinfo->card_lock);
                         cy_writeb((u_long)base_addr+(CySRER<<index),
                              cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
+			spin_unlock(&cinfo->card_lock);
                         goto txend;
                     }
                     info = &cy_port[i];
                     info->last_active = jiffies;
                     if(info->tty == 0){
+			spin_lock(&cinfo->card_lock);
                         cy_writeb((u_long)base_addr+(CySRER<<index),
                              cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
+			spin_unlock(&cinfo->card_lock);
                         goto txdone;
                     }
 
                     /* load the on-chip space for outbound data */
                     char_count = info->xmit_fifo_size;
 
-
                     if(info->x_char) { /* send special char */
                         outch = info->x_char;
                         cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
                         char_count--;
+			info->icount.tx++;
                         info->x_char = 0;
                     }
 
@@ -1421,21 +1369,27 @@
 
                     while (char_count-- > 0){
 			if (!info->xmit_cnt){
+			    spin_lock(&cinfo->card_lock);
                             cy_writeb((u_long)base_addr+(CySRER<<index),
 				cy_readb(base_addr+(CySRER<<index)) & 
 					~CyTxMpty);
+			    spin_unlock(&cinfo->card_lock);
 			    goto txdone;
 			}
 			if (info->xmit_buf == 0){
+			    spin_lock(&cinfo->card_lock);
                             cy_writeb((u_long)base_addr+(CySRER<<index),
 				cy_readb(base_addr+(CySRER<<index)) & 
 					~CyTxMpty);
+			    spin_unlock(&cinfo->card_lock);
                             goto txdone;
 			}
 			if (info->tty->stopped || info->tty->hw_stopped){
+			    spin_lock(&cinfo->card_lock);
                             cy_writeb((u_long)base_addr+(CySRER<<index),
 				cy_readb(base_addr+(CySRER<<index)) & 
 					~CyTxMpty);
+			    spin_unlock(&cinfo->card_lock);
                             goto txdone;
 			}
                         /* Because the Embedded Transmit Commands have
@@ -1455,6 +1409,7 @@
                             info->xmit_tail = (info->xmit_tail + 1)
                                                       & (SERIAL_XMIT_SIZE - 1);
                             cy_writeb((u_long)base_addr+(CyTDR<<index), outch);
+			    info->icount.tx++;
                         }else{
                             if(char_count > 1){
                                 info->xmit_cnt--;
@@ -1463,6 +1418,7 @@
                                 cy_writeb((u_long)base_addr+(CyTDR<<index), 
 					  outch);
                                 cy_writeb((u_long)base_addr+(CyTDR<<index), 0);
+				info->icount.tx++;
                                 char_count--;
                             }else{
                             }
@@ -1475,14 +1431,17 @@
                     }
         txend:
                     /* end of service */
+		    spin_lock(&cinfo->card_lock);
                     cy_writeb((u_long)base_addr+(CyTIR<<index), 
 			      (save_xir & 0x3f));
                     cy_writeb((u_long)base_addr+(CyCAR<<index), (save_car));
+		    spin_unlock(&cinfo->card_lock);
                 }
 
                 if (status & CySRModem) {        /* modem interrupt */
 
                     /* determine the channel & change to that context */
+		    spin_lock(&cinfo->card_lock);
                     save_xir = (u_char) cy_readb(base_addr+(CyMIR<<index));
                     channel = (u_short ) (save_xir & CyIRChannel);
                     info = &cy_port[channel + chip * 4
@@ -1493,10 +1452,21 @@
 
                     mdm_change = cy_readb(base_addr+(CyMISR<<index));
                     mdm_status = cy_readb(base_addr+(CyMSVR1<<index));
+		    spin_unlock(&cinfo->card_lock);
 
                     if(info->tty == 0){/* no place for data, ignore it*/
                         ;
                     }else{
+			if (mdm_change & CyANY_DELTA) {
+			    /* For statistics only */
+			    if (mdm_change & CyDCD)	info->icount.dcd++;
+			    if (mdm_change & CyCTS)	info->icount.cts++;
+			    if (mdm_change & CyDSR)	info->icount.dsr++;
+			    if (mdm_change & CyRI)	info->icount.rng++;
+
+			    cy_sched_event(info, Cy_EVENT_DELTA_WAKEUP);
+			}
+
                         if((mdm_change & CyDCD)
                         && (info->flags & ASYNC_CHECK_CD)){
                             if(mdm_status & CyDCD){
@@ -1517,9 +1487,11 @@
                                     /* cy_start isn't used
 				         because... !!! */
                                     info->tty->hw_stopped = 0;
+				  spin_lock(&cinfo->card_lock);
                                   cy_writeb((u_long)base_addr+(CySRER<<index),
                                        cy_readb(base_addr+(CySRER<<index)) | 
                                        CyTxMpty);
+				  spin_unlock(&cinfo->card_lock);
                                     cy_sched_event(info,
 				        Cy_EVENT_WRITE_WAKEUP);
                                 }
@@ -1528,29 +1500,35 @@
                                     /* cy_stop isn't used
 				         because ... !!! */
                                     info->tty->hw_stopped = 1;
+				  spin_lock(&cinfo->card_lock);
                                   cy_writeb((u_long)base_addr+(CySRER<<index),
                                        cy_readb(base_addr+(CySRER<<index)) & 
                                        ~CyTxMpty);
+				  spin_unlock(&cinfo->card_lock);
                                 }
                             }
                         }
-                        if(mdm_status & CyDSR){
+                        if(mdm_change & CyDSR){
                         }
-                        if(mdm_status & CyRI){
+                        if(mdm_change & CyRI){
                         }
                     }
                     /* end of service */
+		    spin_lock(&cinfo->card_lock);
                     cy_writeb((u_long)base_addr+(CyMIR<<index), 
 			      (save_xir & 0x3f));
                     cy_writeb((u_long)base_addr+(CyCAR<<index), save_car);
+		    spin_unlock(&cinfo->card_lock);
                 }
             }          /* end while status != 0 */
         }            /* end loop for chips... */
     } while(had_work);
 
    /* clear interrupts */
+   spin_lock(&cinfo->card_lock);
    cy_writeb((u_long)card_base_addr + (Cy_ClrIntr<<index), 0);
                                 /* Cy_ClrIntr is 0x1800 */
+   spin_unlock(&cinfo->card_lock);
 } /* cyy_interrupt */
 
 /***********************************************************/
@@ -1624,37 +1602,184 @@
     return(0);
 } /* cyz_issue_cmd */
 
+static void
+cyz_handle_rx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl)
+{
+  struct cyclades_card *cinfo = &cy_card[info->card];
+  struct tty_struct *tty = info->tty;
+  volatile int char_count;
+#ifdef BLOCKMOVE
+  int small_count;
+#else
+  char data;
+#endif
+  volatile uclong rx_put, rx_get, rx_bufsize;
 
-#if 0
-static int
-cyz_update_channel( struct cyclades_card *cinfo,
-	    u_long channel, u_char mode, u_char cmd)
-{ 
-  struct FIRM_ID *firm_id =
-      (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
-  struct ZFW_CTRL *zfw_ctrl;
-  struct CH_CTRL *ch_ctrl;
+/* Removed due to compilation problems in Alpha systems */
+//    if ((char_count = CHARS_IN_BUF(buf_ctrl))){
 
-    if (!ISZLOADED(*cinfo)){
-	return (-1);
-    }
-    zfw_ctrl = (struct ZFW_CTRL *)
-	       (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
-    ch_ctrl = zfw_ctrl->ch_ctrl;
+    rx_get = cy_readl(&buf_ctrl->rx_get);
+    rx_put = cy_readl(&buf_ctrl->rx_put);
+    rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
+    if (rx_put >= rx_get)
+	char_count = rx_put - rx_get;
+    else
+	char_count = rx_put - rx_get + rx_bufsize;
 
-    cy_writel(&ch_ctrl[channel].op_mode, (uclong)mode);
+    if ( char_count ) {
+	info->last_active = jiffies;
+	info->jiffies[1] = jiffies;
 
-    return cyz_issue_cmd(cinfo, channel, cmd, 0L);
+#ifdef CY_ENABLE_MONITORING
+	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
+	if(tty == 0){
+	    /* flush received characters */
+	    rx_get = (rx_get + char_count) & (rx_bufsize - 1);
+	    info->rflush_count++;
+	}else{
+#ifdef BLOCKMOVE
+	    /* we'd like to use memcpy(t, f, n) and memset(s, c, count)
+	       for performance, but because of buffer boundaries, there
+	       may be several steps to the operation */
+	    while(0 < (small_count = 
+		       cy_min((rx_bufsize - rx_get),
+		       cy_min((TTY_FLIPBUF_SIZE - tty->flip.count), char_count))
+		 )) {
+		memcpy_fromio(tty->flip.char_buf_ptr,
+			      (char *)(cinfo->base_addr
+				       + cy_readl(&buf_ctrl->rx_bufaddr)
+				       + rx_get),
+			      small_count);
 
-} /* cyz_update_channel */
+		tty->flip.char_buf_ptr += small_count;
+		memset(tty->flip.flag_buf_ptr, TTY_NORMAL, small_count);
+		tty->flip.flag_buf_ptr += small_count;
+		rx_get = (rx_get + small_count) & (rx_bufsize - 1);
+		char_count -= small_count;
+		info->icount.rx += small_count;
+		info->idle_stats.recv_bytes += small_count;
+		tty->flip.count += small_count;
+	    }
+#else
+	    while(char_count--){
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE){
+#ifdef CONFIG_CYZ_INTR
+		    cy_sched_event(info, Cy_EVENT_Z_RX_FULL);
+#endif
+		    break;
+		}
+		data = cy_readb(cinfo->base_addr +
+			cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
+		rx_get = (rx_get + 1) & (rx_bufsize - 1);
+		tty->flip.count++;
+		*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+		*tty->flip.char_buf_ptr++ = data;
+		info->idle_stats.recv_bytes++;
+		info->icount.rx++;
+	    }
 #endif
+	    info->idle_stats.recv_idle = jiffies;
+	    queue_task(&tty->flip.tqueue, &tq_timer);
+	}
+	/* Update rx_get */
+	cy_writel(&buf_ctrl->rx_get, rx_get);
+    }
+}
 
-#ifdef CONFIG_CYZ_INTR
 static void
-cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+cyz_handle_tx(struct cyclades_port *info, volatile struct BUF_CTRL *buf_ctrl)
+{
+  struct cyclades_card *cinfo = &cy_card[info->card];
+  struct tty_struct *tty = info->tty;
+  char data;
+  volatile int char_count;
+#ifdef BLOCKMOVE
+  int small_count;
+#endif
+  volatile uclong tx_put, tx_get, tx_bufsize;
+
+/* Removed due to compilation problems in Alpha systems */
+//    if ((char_count = SPACE_IN_BUF(buf_ctrl))){
+
+    tx_get = cy_readl(&buf_ctrl->tx_get);
+    tx_put = cy_readl(&buf_ctrl->tx_put);
+    tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
+    if (tx_put >= tx_get)
+	char_count = tx_get - tx_put - 1 + tx_bufsize;
+    else
+	char_count = tx_get - tx_put - 1;
+
+    if ( char_count ) {
+
+	if( tty == 0 ){
+	    goto ztxdone;
+	}
+
+	if(info->x_char) { /* send special char */
+	    data = info->x_char;
+
+	    cy_writeb((cinfo->base_addr + 
+		cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
+	    tx_put = (tx_put + 1) & (tx_bufsize - 1);
+	    info->x_char = 0;
+	    char_count--;
+	    info->icount.tx++;
+	    info->last_active = jiffies;
+	    info->jiffies[2] = jiffies;
+	}
+#ifdef BLOCKMOVE
+	while(0 < (small_count = 
+		   cy_min((tx_bufsize - tx_put),
+		   cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
+			cy_min(info->xmit_cnt, char_count))))){
+
+	    memcpy_toio((char *)(cinfo->base_addr
+				 + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
+			&info->xmit_buf[info->xmit_tail],
+			small_count);
+
+	    tx_put = (tx_put + small_count) & (tx_bufsize - 1);
+	    char_count -= small_count;
+	    info->icount.tx += small_count;
+	    info->xmit_cnt -= small_count;
+	    info->xmit_tail = 
+		(info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
+	    info->last_active = jiffies;
+	    info->jiffies[2] = jiffies;
+	}
+#else
+	while (info->xmit_cnt && char_count){
+	    data = info->xmit_buf[info->xmit_tail];
+	    info->xmit_cnt--;
+	    info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
+
+	    cy_writeb(cinfo->base_addr + 
+		cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, data);
+	    tx_put = (tx_put + 1) & (tx_bufsize - 1);
+	    char_count--;
+	    info->icount.tx++;
+	    info->last_active = jiffies;
+	    info->jiffies[2] = jiffies;
+	}
+#endif
+    ztxdone:
+	if (info->xmit_cnt < WAKEUP_CHARS) {
+	    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+	}
+	/* Update tx_put */
+	cy_writel(&buf_ctrl->tx_put, tx_put);
+    }
+}
+
+static void
+cyz_handle_cmd(struct cyclades_card *cinfo)
 {
   struct tty_struct *tty;
-  struct cyclades_card *cinfo;
   struct cyclades_port *info;
   static volatile struct FIRM_ID *firm_id;
   static volatile struct ZFW_CTRL *zfw_ctrl;
@@ -1665,541 +1790,200 @@
   ucchar cmd;
   uclong param;
   uclong hw_ver, fw_ver;
-  char data;
-  volatile int char_count, special_count;
-#ifdef BLOCKMOVE
-  int small_count;
-#endif
-  volatile uclong tx_put, tx_get, tx_bufsize;
-  volatile uclong rx_put, rx_get, rx_bufsize;
+  int special_count;
+  int delta_count;
 
-    if((cinfo = (struct cyclades_card *)dev_id) == 0){
-#ifdef CY_DEBUG_INTERRUPTS
-	printk("cyz_interrupt: spurious interrupt %d\n\r", irq);
-#endif
-        return; /* spurious interrupt */
-    }
-
-	firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
-        if (!ISZLOADED(*cinfo)) {
-#ifdef CY_DEBUG_INTERRUPTS
-	    printk("cyz_interrupt: board not yet loaded (INT %d).\n\r", irq);
-#endif
-	    return;
+    firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
+    zfw_ctrl = (struct ZFW_CTRL *)
+	       (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+    board_ctrl = &(zfw_ctrl->board_ctrl);
+    fw_ver = cy_readl(&board_ctrl->fw_version);
+    hw_ver = cy_readl(&((struct RUNTIME_9060 *)(cinfo->ctl_addr))->mail_box_0);
+
+    while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
+	special_count = 0;
+	delta_count = 0;
+	info = &cy_port[channel + cinfo->first_line];
+	if((tty = info->tty) == 0) {
+	    continue;
 	}
+	ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
+	buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
 
-	zfw_ctrl = (struct ZFW_CTRL *)
-	           (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
-	board_ctrl = &(zfw_ctrl->board_ctrl);
-	fw_ver = cy_readl(&board_ctrl->fw_version);
-	hw_ver = cy_readl(&((struct RUNTIME_9060 *)
-                            (cinfo->ctl_addr))->mail_box_0);
-
-	while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1) {
-	    special_count = 0;
-	    info = &cy_port[channel + cinfo->first_line];
-            if((tty = info->tty) == 0) continue;
-	    ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
-	    buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
-
-	    switch(cmd){
+	switch(cmd) {
 	    case C_CM_PR_ERROR:
 		tty->flip.count++;
 		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
 		*tty->flip.char_buf_ptr++ = 0;
+		info->icount.rx++;
 		special_count++;
-	        break;
+		break;
 	    case C_CM_FR_ERROR:
 		tty->flip.count++;
 		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
 		*tty->flip.char_buf_ptr++ = 0;
+		info->icount.rx++;
 		special_count++;
-	        break;
+		break;
 	    case C_CM_RXBRK:
 		tty->flip.count++;
 		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
 		*tty->flip.char_buf_ptr++ = 0;
+		info->icount.rx++;
 		special_count++;
-	        break;
+		break;
 	    case C_CM_MDCD:
+		info->icount.dcd++;
+		delta_count++;
 		if (info->flags & ASYNC_CHECK_CD){
 		    if ((fw_ver > 241 ? 
-                          ((u_long)param) : 
-                          cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) {
-			/* SP("Open Wakeup\n"); */
-			cy_sched_event(info,
-			    Cy_EVENT_OPEN_WAKEUP);
-		    }else if(!((info->flags
-				& ASYNC_CALLOUT_ACTIVE)
-			 &&(info->flags
-			    & ASYNC_CALLOUT_NOHUP))){
-			/* SP("Hangup\n"); */
-			cy_sched_event(info,
-			    Cy_EVENT_HANGUP);
+			  ((u_long)param) : 
+			  cy_readl(&ch_ctrl->rs_status)) & C_RS_DCD) {
+			cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP);
+		    }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE)
+			     &&(info->flags & ASYNC_CALLOUT_NOHUP))){
+			cy_sched_event(info, Cy_EVENT_HANGUP);
 		    }
 		}
-	        break;
+		break;
 	    case C_CM_MCTS:
-		if (info->flags & ASYNC_CTS_FLOW) {
-		    if(info->tty->hw_stopped){
-			if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){
-			    /* cy_start isn't used because... 
-			       HW flow is handled by the board */
-			    /* SP("Write Wakeup\n"); */
-			    cy_sched_event(info,
-				Cy_EVENT_WRITE_WAKEUP);
-			}
-		    }else{
-			if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){
-			    /* cy_stop isn't used because 
-			       HW flow is handled by the board */
-			    /* SP("Write stop\n"); */
-			}
-		    }
-		}
-	        break;
+		info->icount.cts++;
+		delta_count++;
+		break;
 	    case C_CM_MRI:
-	        break;
+		info->icount.rng++;
+		delta_count++;
+		break;
 	    case C_CM_MDSR:
-	        break;
+		info->icount.dsr++;
+		delta_count++;
+		break;
 #ifdef Z_WAKE
 	    case C_CM_IOCTLW:
 		cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP);
-	        break;
+		break;
 #endif
+#ifdef CONFIG_CYZ_INTR
 	    case C_CM_RXHIWM:
 	    case C_CM_RXNNDT:
+	    case C_CM_INTBACK2:
 		/* Reception Interrupt */
 #ifdef CY_DEBUG_INTERRUPTS
-	    printk("cyz_interrupt: rcvd intr, card %d, port %ld\n\r", 
-		info->card, channel);
+		printk("cyz_interrupt: rcvd intr, card %d, port %ld\n\r", 
+			info->card, channel);
 #endif
-
-	    rx_get = cy_readl(&buf_ctrl->rx_get);
-	    rx_put = cy_readl(&buf_ctrl->rx_put);
-	    rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
-	    if (rx_put >= rx_get)
-	    	char_count = rx_put - rx_get;
-	    else
-	    	char_count = rx_put - rx_get + rx_bufsize;
-
-	    if ( char_count ){
-
-#ifdef CY_ENABLE_MONITORING
-		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
-		info->idle_stats.recv_bytes += char_count;
-		info->idle_stats.recv_idle   = jiffies;
-		if( tty == 0){
-		    /* flush received characters */
-		    rx_get = (rx_get + char_count) & (rx_bufsize - 1);
-		    /* SP("-"); */
-		    info->rflush_count++;
-		}else{
-#ifdef BLOCKMOVE
-		/* we'd like to use memcpy(t, f, n) and memset(s, c, count)
-		   for performance, but because of buffer boundaries, there
-		   may be several steps to the operation */
-		    while(0 < (small_count
-			= cy_min((rx_bufsize - rx_get),
-			         cy_min((TTY_FLIPBUF_SIZE - tty->flip.count),
-				        char_count)))){
-
-			memcpy_fromio(tty->flip.char_buf_ptr,
-			              (char *)(cinfo->base_addr
-				       + cy_readl(&buf_ctrl->rx_bufaddr)
-				       + rx_get),
-			              small_count);
-
-			tty->flip.char_buf_ptr += small_count;
-			memset(tty->flip.flag_buf_ptr,
-			       TTY_NORMAL,
-			       small_count);
-			tty->flip.flag_buf_ptr += small_count;
-			rx_get = (rx_get + small_count) & (rx_bufsize - 1);
-			char_count -= small_count;
-			tty->flip.count += small_count;
-		    }
-#else
-		    while(char_count--){
-			if (tty->flip.count >= TTY_FLIPBUF_SIZE){
-				break;
-			}
-			data = cy_readb(cinfo->base_addr +
-			           cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
-			rx_get = (rx_get + 1) & (rx_bufsize - 1);
-			tty->flip.count++;
-			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
-			*tty->flip.char_buf_ptr++ = data;
-		    }
-#endif
-		    queue_task(&tty->flip.tqueue, &tq_timer);
-		}
-		/* Update rx_get */
-		cy_writel(&buf_ctrl->rx_get, rx_get);
-	    }
+		cyz_handle_rx(info, buf_ctrl);
 		break;
 	    case C_CM_TXBEMPTY:
 	    case C_CM_TXLOWWM:
 	    case C_CM_INTBACK:
 		/* Transmission Interrupt */
 #ifdef CY_DEBUG_INTERRUPTS
-	    printk("cyz_interrupt: xmit intr, card %d, port %ld\n\r", 
-		info->card, channel);
-#endif
-
-	    tx_get = cy_readl(&buf_ctrl->tx_get);
-	    tx_put = cy_readl(&buf_ctrl->tx_put);
-	    tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
-	    if (tx_put >= tx_get)
-	    	char_count = tx_get - tx_put - 1 + tx_bufsize;
-	    else
-	    	char_count = tx_get - tx_put - 1;
-
-	    if ( char_count ){
-
-		if( tty == 0 ){
-		    goto ztxdone;
-		}
-
-		if(info->x_char) { /* send special char */
-		    data = info->x_char;
-
-		    cy_writeb((cinfo->base_addr +
-		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
-		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
-		    info->x_char = 0;
-		    char_count--;
-		}
-#ifdef BLOCKMOVE
-		while(0 < (small_count
-		    = cy_min((tx_bufsize - tx_put),
-			     cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
-			             cy_min(info->xmit_cnt, char_count))))){
-
-		    memcpy_toio((char *)(cinfo->base_addr
-			         + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
-			        &info->xmit_buf[info->xmit_tail],
-			        small_count);
-
-		    tx_put = (tx_put + small_count) & (tx_bufsize - 1);
-		    char_count -= small_count;
-		    info->xmit_cnt -= small_count;
-		    info->xmit_tail =
-		       (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
-		}
-#else
-		while (info->xmit_cnt && char_count){
-		    data = info->xmit_buf[info->xmit_tail];
-		    info->xmit_cnt--;
-		    info->xmit_tail =
-			(info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
-
-		    cy_writeb(cinfo->base_addr +
-		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, 
-			      data);
-		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
-		    char_count--;
-		}
-
+		printk("cyz_interrupt: xmit intr, card %d, port %ld\n\r", 
+			info->card, channel);
 #endif
-	    ztxdone:
-		if (info->xmit_cnt < WAKEUP_CHARS) {
-		    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
-		}
-		/* Update tx_put */
-		cy_writel(&buf_ctrl->tx_put, tx_put);
-	    }
+		cyz_handle_tx(info, buf_ctrl);
 		break;
+#endif /* CONFIG_CYZ_INTR */
 	    case C_CM_FATAL:
 		/* should do something with this !!! */
-	        break;
-	    }
-	    if(special_count){
-		queue_task(&tty->flip.tqueue, &tq_timer);
-	    }
+		break;
+	    default:
+		break;
 	}
+	if(delta_count)
+	    cy_sched_event(info, Cy_EVENT_DELTA_WAKEUP);
+	if(special_count)
+	    queue_task(&tty->flip.tqueue, &tq_timer);
+    }
+}
+
+#ifdef CONFIG_CYZ_INTR
+static void
+cyz_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct cyclades_card *cinfo;
+
+    if((cinfo = (struct cyclades_card *)dev_id) == 0){
+#ifdef CY_DEBUG_INTERRUPTS
+	printk("cyz_interrupt: spurious interrupt %d\n\r", irq);
+#endif
+        return; /* spurious interrupt */
+    }
+
+    if (!ISZLOADED(*cinfo)) {
+#ifdef CY_DEBUG_INTERRUPTS
+	printk("cyz_interrupt: board not yet loaded (IRQ%d).\n\r", irq);
+#endif
+	return;
+    }
+
+    /* Handle the interrupts */
+    cyz_handle_cmd(cinfo);
 
     return;
 } /* cyz_interrupt */
 
+static void
+cyz_rx_restart(unsigned long arg)
+{
+    struct cyclades_port *info = (struct cyclades_port *)arg;
+    int retval;
+    int card = info->card;
+    uclong channel = (info->line) - (cy_card[card].first_line);
+
+    cyz_rx_full_timer[info->card].expires = jiffies + HZ;
+    retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_INTBACK2, 0L);
+    if (retval != 0){
+	printk("cyc:cyz_rx_restart retval was %x\n", retval);
+    }
+}
+
 #else /* CONFIG_CYZ_INTR */
 
 static void
 cyz_poll(unsigned long arg)
 {
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info;
+  struct tty_struct *tty;
   static volatile struct FIRM_ID *firm_id;
   static volatile struct ZFW_CTRL *zfw_ctrl;
   static volatile struct BOARD_CTRL *board_ctrl;
   static volatile struct CH_CTRL *ch_ctrl;
   static volatile struct BUF_CTRL *buf_ctrl;
-  struct cyclades_card *cinfo;
-  struct cyclades_port *info;
-  struct tty_struct *tty;
   int card, port;
-  int char_count;
-#ifdef BLOCKMOVE
-  int small_count;
-#endif
-  char data;
-  uclong channel;
-  ucchar cmd;
-  uclong param;
-  uclong hw_ver, fw_ver;
-  volatile uclong tx_put, tx_get, tx_bufsize;
-  volatile uclong rx_put, rx_get, rx_bufsize;
 
     cyz_timerlist.expires = jiffies + (HZ);
     for (card = 0 ; card < NR_CARDS ; card++){
 	cinfo = &cy_card[card];
-	if (!IS_CYC_Z(*cinfo)) continue;
 
+	if (!IS_CYC_Z(*cinfo)) continue;
+	if (!ISZLOADED(*cinfo)) continue;
 
-	firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
-        if (!ISZLOADED(*cinfo)) {
+	/* Skip first polling cycle to avoid racing conditions with the FW */
+	if (!cinfo->intr_enabled) {
+	    cinfo->intr_enabled = 1;
 	    continue;
 	}
 
+	firm_id = (struct FIRM_ID *)(cinfo->base_addr + ID_ADDRESS);
 	zfw_ctrl = (struct ZFW_CTRL *)
-	           (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
+		   (cinfo->base_addr + cy_readl(&firm_id->zfwctrl_addr));
 	board_ctrl = &(zfw_ctrl->board_ctrl);
-	fw_ver = cy_readl(&board_ctrl->fw_version);
-	hw_ver = cy_readl(&((struct RUNTIME_9060 *)
-                            (cinfo->ctl_addr))->mail_box_0);
-
-	while(cyz_fetch_msg(cinfo, &channel, &cmd, &param) == 1){
-	    char_count = 0;
-	    info = &cy_port[ channel + cinfo->first_line ];
-            if((tty = info->tty) == 0) continue;
-	    ch_ctrl = &(zfw_ctrl->ch_ctrl[channel]);
-	    buf_ctrl = &(zfw_ctrl->buf_ctrl[channel]);
-	    info->jiffies[0] = jiffies;
 
-	    switch(cmd){
-	    case C_CM_PR_ERROR:
-		tty->flip.count++;
-		*tty->flip.flag_buf_ptr++ = TTY_PARITY;
-		*tty->flip.char_buf_ptr++ = 0;
-		char_count++;
-	        break;
-	    case C_CM_FR_ERROR:
-		tty->flip.count++;
-		*tty->flip.flag_buf_ptr++ = TTY_FRAME;
-		*tty->flip.char_buf_ptr++ = 0;
-		char_count++;
-	        break;
-	    case C_CM_RXBRK:
-		tty->flip.count++;
-		*tty->flip.flag_buf_ptr++ = TTY_BREAK;
-		*tty->flip.char_buf_ptr++ = 0;
-		char_count++;
-	        break;
-	    case C_CM_MDCD:
-		if (info->flags & ASYNC_CHECK_CD){
-		    if ((fw_ver > 241 ? 
-                          ((u_long)param) : 
-                          cy_readl(&ch_ctrl[channel].rs_status)) & C_RS_DCD) {
-			cy_sched_event(info,
-			    Cy_EVENT_OPEN_WAKEUP);
-		    }else if(!((info->flags
-				& ASYNC_CALLOUT_ACTIVE)
-			 &&(info->flags
-			    & ASYNC_CALLOUT_NOHUP))){
-			cy_sched_event(info,
-			    Cy_EVENT_HANGUP);
-		    }
-		}
-	        break;
-	    case C_CM_MCTS:
-		if (info->flags & ASYNC_CTS_FLOW) {
-		    if(info->tty->hw_stopped){
-			if( cy_readl(&ch_ctrl[channel].rs_status) & C_RS_DCD){
-			    /* cy_start isn't used because... 
-			       HW flow is handled by the board */
-			    cy_sched_event(info,
-				Cy_EVENT_WRITE_WAKEUP);
-			}
-		    }else{
-			if(!(cy_readl(&ch_ctrl[channel].rs_status) & C_RS_CTS)){
-			    /* cy_stop isn't used because 
-			       HW flow is handled by the board */
-			}
-		    }
-		}
-	        break;
-	    case C_CM_MRI:
-	        break;
-	    case C_CM_MDSR:
-	        break;
-#ifdef Z_WAKE
-	    case C_CM_IOCTLW:
-		cy_sched_event(info, Cy_EVENT_SHUTDOWN_WAKEUP);
-	        break;
-#endif
-	    case C_CM_FATAL:
-		/* should do something with this !!! */
-	        break;
-	    }
-	    if(char_count){
-		queue_task(&tty->flip.tqueue, &tq_timer);
-	    }
-	}
+	cyz_handle_cmd(cinfo);
+
 	for (port = 0; port < cy_readl(&board_ctrl->n_channel); port++){
 	    info = &cy_port[ port + cinfo->first_line ];
             tty = info->tty;
 	    ch_ctrl = &(zfw_ctrl->ch_ctrl[port]);
 	    buf_ctrl = &(zfw_ctrl->buf_ctrl[port]);
 
-/* Removed due to compilation problems in Alpha systems */
-//	    if ((char_count = CHARS_IN_BUF(buf_ctrl))){
-
-	    rx_get = cy_readl(&buf_ctrl->rx_get);
-	    rx_put = cy_readl(&buf_ctrl->rx_put);
-	    rx_bufsize = cy_readl(&buf_ctrl->rx_bufsize);
-	    if (rx_put >= rx_get)
-	    	char_count = rx_put - rx_get;
-	    else
-	    	char_count = rx_put - rx_get + rx_bufsize;
-
-	    if ( char_count ){
-
-		info->last_active = jiffies;
-		info->jiffies[1] = jiffies;
-
-#ifdef CY_ENABLE_MONITORING
-		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
-		info->idle_stats.recv_bytes += char_count;
-		info->idle_stats.recv_idle   = jiffies;
-		if( tty == 0){
-		    /* flush received characters */
-		    rx_get = (rx_get + char_count) & (rx_bufsize - 1);
-		    info->rflush_count++;
-		}else{
-#ifdef BLOCKMOVE
-		/* we'd like to use memcpy(t, f, n) and memset(s, c, count)
-		   for performance, but because of buffer boundaries, there
-		   may be several steps to the operation */
-		    while(0 < (small_count
-			= cy_min((rx_bufsize - rx_get),
-			         cy_min((TTY_FLIPBUF_SIZE - tty->flip.count),
-				        char_count)))){
-
-			memcpy_fromio(tty->flip.char_buf_ptr,
-			              (char *)(cinfo->base_addr
-				       + cy_readl(&buf_ctrl->rx_bufaddr)
-				       + rx_get),
-			              small_count);
-
-			tty->flip.char_buf_ptr += small_count;
-			memset(tty->flip.flag_buf_ptr,
-			       TTY_NORMAL,
-			       small_count);
-			tty->flip.flag_buf_ptr += small_count;
-			rx_get = (rx_get + small_count) & (rx_bufsize - 1);
-			char_count -= small_count;
-			tty->flip.count += small_count;
-		    }
-#else
-		    while(char_count--){
-			if (tty->flip.count >= TTY_FLIPBUF_SIZE){
-				break;
-			}
-			data = cy_readb(cinfo->base_addr +
-			           cy_readl(&buf_ctrl->rx_bufaddr) + rx_get);
-			rx_get = (rx_get + 1) & (rx_bufsize - 1);
-			tty->flip.count++;
-			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
-			*tty->flip.char_buf_ptr++ = data;
-		    }
-#endif
-		    queue_task(&tty->flip.tqueue, &tq_timer);
-		}
-		/* Update rx_get */
-		cy_writel(&buf_ctrl->rx_get, rx_get);
-	    }
-
-/* Removed due to compilation problems in Alpha systems */
-//	    if ((char_count = SPACE_IN_BUF(buf_ctrl))){
-
-	    tx_get = cy_readl(&buf_ctrl->tx_get);
-	    tx_put = cy_readl(&buf_ctrl->tx_put);
-	    tx_bufsize = cy_readl(&buf_ctrl->tx_bufsize);
-	    if (tx_put >= tx_get)
-	    	char_count = tx_get - tx_put - 1 + tx_bufsize;
-	    else
-	    	char_count = tx_get - tx_put - 1;
-
-	    if ( char_count ){
-
-		if( tty == 0 ){
-		    goto ztxdone;
-		}
-
-		if(info->x_char) { /* send special char */
-		    data = info->x_char;
-
-		    cy_writeb((cinfo->base_addr +
-		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put), data);
-		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
-		    info->x_char = 0;
-		    char_count--;
-		    info->last_active = jiffies;
-		    info->jiffies[2] = jiffies;
-		}
-#ifdef BLOCKMOVE
-		while(0 < (small_count
-		    = cy_min((tx_bufsize - tx_put),
-			     cy_min ((SERIAL_XMIT_SIZE - info->xmit_tail),
-			             cy_min(info->xmit_cnt, char_count))))){
-
-		    memcpy_toio((char *)(cinfo->base_addr
-			         + cy_readl(&buf_ctrl->tx_bufaddr) + tx_put),
-			        &info->xmit_buf[info->xmit_tail],
-			        small_count);
-
-		    tx_put = (tx_put + small_count) & (tx_bufsize - 1);
-		    char_count -= small_count;
-		    info->xmit_cnt -= small_count;
-		    info->xmit_tail =
-		       (info->xmit_tail + small_count) & (SERIAL_XMIT_SIZE - 1);
-		    info->last_active = jiffies;
-		    info->jiffies[2] = jiffies;
-		}
-#else
-		while (info->xmit_cnt && char_count){
-		    data = info->xmit_buf[info->xmit_tail];
-		    info->xmit_cnt--;
-		    info->xmit_tail =
-			(info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1);
-
-		    cy_writeb(cinfo->base_addr +
-		              cy_readl(&buf_ctrl->tx_bufaddr) + tx_put, 
-			      data);
-		    tx_put = (tx_put + 1) & (tx_bufsize - 1);
-		    char_count--;
-		    info->last_active = jiffies;
-		    info->jiffies[2] = jiffies;
-		}
-
-#endif
-	    ztxdone:
-		if (info->xmit_cnt < WAKEUP_CHARS) {
-		    cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
-		}
-		/* Update tx_put */
-		cy_writel(&buf_ctrl->tx_put, tx_put);
-	    }
+	    cyz_handle_rx(info, buf_ctrl);
+	    cyz_handle_tx(info, buf_ctrl);
 	}
-	/* poll every 40 ms */
+	/* poll every 'cyz_polling_cycle' period */
 	cyz_timerlist.expires = jiffies + cyz_polling_cycle;
     }
     add_timer(&cyz_timerlist);
@@ -2225,11 +2009,14 @@
   int card,chip,channel,index;
   unsigned long page;
 
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+
     page = get_free_page(GFP_KERNEL);
     if (!page)
 	return -ENOMEM;
 
-    save_flags(flags); cli();
+    CY_LOCK(info, flags);
 
     if (info->flags & ASYNC_INITIALIZED){
 	free_page(page);
@@ -2249,10 +2036,10 @@
     else
 	info->xmit_buf = (unsigned char *) page;
 
+    CY_UNLOCK(info, flags);
+
     set_line_char(info);
 
-    card = info->card;
-    channel = (info->line) - (cy_card[card].first_line);
     if (!IS_CYC_Z(cy_card[card])) {
 	chip = channel>>2;
 	channel &= 0x03;
@@ -2265,6 +2052,8 @@
 	     card, chip, channel, (long)base_addr);/**/
 #endif
 
+	CY_LOCK(info, flags);
+
 	cy_writeb((ulong)base_addr+(CyCAR<<index), (u_char)channel);
 
 	cy_writeb((ulong)base_addr+(CyRTPR<<index), (info->default_timeout
@@ -2297,7 +2086,7 @@
 	info->idle_stats.recv_idle =
 	info->idle_stats.xmit_idle = jiffies;
 
-	restore_flags(flags);
+	CY_UNLOCK(info, flags);
 
     } else {
       struct FIRM_ID *firm_id;
@@ -2306,8 +2095,6 @@
       struct CH_CTRL *ch_ctrl;
       int retval;
 
-	restore_flags(flags);
-
 	base_addr = (unsigned char*) (cy_card[card].base_addr);
 
         firm_id = (struct FIRM_ID *) (base_addr + ID_ADDRESS);
@@ -2326,35 +2113,42 @@
 	     card, channel, (long)base_addr);/**/
 #endif
 
+	CY_LOCK(info, flags);
+
 	cy_writel(&ch_ctrl[channel].op_mode, C_CH_ENABLE);
 #ifdef Z_WAKE
 #ifdef CONFIG_CYZ_INTR
 	cy_writel(&ch_ctrl[channel].intr_enable, 
 		  C_IN_TXBEMPTY|C_IN_TXLOWWM|C_IN_RXHIWM|C_IN_RXNNDT|
 		  C_IN_IOCTLW|
-		  C_IN_MDCD|C_IN_MCTS);
+		  C_IN_MDCD);
 #else
 	cy_writel(&ch_ctrl[channel].intr_enable, 
 		  C_IN_IOCTLW|
-		  C_IN_MDCD|C_IN_MCTS);
+		  C_IN_MDCD);
 #endif /* CONFIG_CYZ_INTR */
 #else
 #ifdef CONFIG_CYZ_INTR
 	cy_writel(&ch_ctrl[channel].intr_enable, 
 		  C_IN_TXBEMPTY|C_IN_TXLOWWM|C_IN_RXHIWM|C_IN_RXNNDT|
-		  C_IN_MDCD|C_IN_MCTS);
+		  C_IN_MDCD);
 #else
 	cy_writel(&ch_ctrl[channel].intr_enable, 
-		  C_IN_MDCD|C_IN_MCTS);
+		  C_IN_MDCD);
 #endif /* CONFIG_CYZ_INTR */
 #endif /* Z_WAKE */
 
-	retval = cyz_issue_cmd( &cy_card[card],
-	    channel, C_CM_IOCTL, 0L);	/* was C_CM_RESET */
+	retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L);
 	if (retval != 0){
 	    printk("cyc:startup(1) retval was %x\n", retval);
 	}
 
+	/* Flush RX buffers before raising DTR and RTS */
+	retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_FLUSH_RX, 0L);
+	if (retval != 0){
+	    printk("cyc:startup(2) retval was %x\n", retval);
+	}
+
 	/* set timeout !!! */
 	/* set RTS and DTR !!! */
 	cy_writel(&ch_ctrl[channel].rs_control,
@@ -2362,7 +2156,7 @@
 	retval = cyz_issue_cmd(&cy_card[info->card],
 	    channel, C_CM_IOCTLM, 0L);
 	if (retval != 0){
-	    printk("cyc:startup(2) retval was %x\n", retval);
+	    printk("cyc:startup(3) retval was %x\n", retval);
 	}
 #ifdef CY_DEBUG_DTR
 	    printk("cyc:startup raising Z DTR\n");
@@ -2380,6 +2174,8 @@
 	info->idle_stats.in_use    =
 	info->idle_stats.recv_idle =
 	info->idle_stats.xmit_idle = jiffies;
+
+	CY_UNLOCK(info, flags);
     }
 
 #ifdef CY_DEBUG_OPEN
@@ -2388,7 +2184,7 @@
 	return 0;
 
 errout:
-	restore_flags(flags);
+	CY_UNLOCK(info, flags);
 	return retval;
 } /* startup */
 
@@ -2410,21 +2206,21 @@
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-	save_flags(flags); cli();
+	CY_LOCK(info, flags);
 	    cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
 	    cy_writeb((u_long)base_addr+(CySRER<<index), 
                cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
-	restore_flags(flags);
+	CY_UNLOCK(info, flags);
     } else {
 #ifdef CONFIG_CYZ_INTR
       int retval;
 
-	save_flags(flags); cli();
+	CY_LOCK(info, flags);
 	    retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_INTBACK, 0L);
 	    if (retval != 0){
 		printk("cyc:start_xmit retval was %x\n", retval);
 	    }
-	restore_flags(flags);
+	CY_UNLOCK(info, flags);
 #else /* CONFIG_CYZ_INTR */
 	/* Don't have to do anything at this time */
 #endif /* CONFIG_CYZ_INTR */
@@ -2461,7 +2257,10 @@
 		card, chip, channel, (long)base_addr);
 #endif
 
-	save_flags(flags); cli();
+	CY_LOCK(info, flags);
+
+	    /* Clear delta_msr_wait queue to avoid mem leaks. */
+	    wake_up_interruptible(&info->delta_msr_wait);
 
 	    if (info->xmit_buf){
 		unsigned char * temp;
@@ -2488,7 +2287,7 @@
 		set_bit(TTY_IO_ERROR, &info->tty->flags);
 	    }
 	    info->flags &= ~ASYNC_INITIALIZED;
-	restore_flags(flags);
+	CY_UNLOCK(info, flags);
     } else {
       struct FIRM_ID *firm_id;
       struct ZFW_CTRL *zfw_ctrl;
@@ -2513,7 +2312,7 @@
 	board_ctrl = &(zfw_ctrl->board_ctrl);
 	ch_ctrl = zfw_ctrl->ch_ctrl;
 
-	save_flags(flags); cli();
+	CY_LOCK(info, flags);
 
 	    if (info->xmit_buf){
 		unsigned char * temp;
@@ -2529,7 +2328,7 @@
 		retval = cyz_issue_cmd(&cy_card[info->card],
 			channel, C_CM_IOCTLM, 0L);
 		if (retval != 0){
-		    printk("cyc:shutdown retval (2) was %x\n", retval);
+		    printk("cyc:shutdown retval was %x\n", retval);
 		}
 #ifdef CY_DEBUG_DTR
 		printk("cyc:shutdown dropping Z DTR\n");
@@ -2541,7 +2340,7 @@
 	    }
 	    info->flags &= ~ASYNC_INITIALIZED;
 
-	restore_flags(flags);
+	CY_UNLOCK(info, flags);
     }
 
 #ifdef CY_DEBUG_OPEN
@@ -2568,6 +2367,9 @@
   int retval;
   char *base_addr;
 
+    cinfo = &cy_card[info->card];
+    channel = info->line - cinfo->first_line;
+
     /*
      * If the device is in the middle of being closed, then block
      * until it's done, and then try again.
@@ -2627,18 +2429,16 @@
     printk("cyc block_til_ready before block: ttyC%d, count = %d\n",
            info->line, info->count);/**/
 #endif
-    save_flags(flags); cli();
+    CY_LOCK(info, flags);
     if (!tty_hung_up_p(filp))
 	info->count--;
-    restore_flags(flags);
+    CY_UNLOCK(info, flags);
 #ifdef CY_DEBUG_COUNT
     printk("cyc block_til_ready: (%d): decrementing count to %d\n",
         current->pid, info->count);
 #endif
     info->blocked_open++;
 
-    cinfo = &cy_card[info->card];
-    channel = info->line - cinfo->first_line;
     if (!IS_CYC_Z(*cinfo)) {
 	chip = channel>>2;
 	channel &= 0x03;
@@ -2647,7 +2447,7 @@
 			    + (cy_chip_offset[chip]<<index));
 
 	while (1) {
-	    save_flags(flags); cli();
+	    CY_LOCK(info, flags);
 		if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
 		    (tty->termios->c_cflag & CBAUD)){
 		    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
@@ -2660,24 +2460,27 @@
                         cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
 		}
-	    restore_flags(flags);
+	    CY_UNLOCK(info, flags);
+
 	    current->state = TASK_INTERRUPTIBLE;
 	    if (tty_hung_up_p(filp)
 	    || !(info->flags & ASYNC_INITIALIZED) ){
-		return ((info->flags & ASYNC_HUP_NOTIFY) ? 
+		retval = ((info->flags & ASYNC_HUP_NOTIFY) ? 
 		    -EAGAIN : -ERESTARTSYS);
 		break;
 	    }
-	    save_flags(flags); cli();
+
+	    CY_LOCK(info, flags);
 		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
 		if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
 		&& !(info->flags & ASYNC_CLOSING)
 		&& (C_CLOCAL(tty)
 		    || (cy_readb(base_addr+(CyMSVR1<<index)) & CyDCD))) {
-			restore_flags(flags);
+			CY_UNLOCK(info, flags);
 			break;
 		}
-	    restore_flags(flags);
+	    CY_UNLOCK(info, flags);
+
 	    if (signal_pending(current)) {
 		retval = -ERESTARTSYS;
 		break;
@@ -2723,7 +2526,7 @@
 	    current->state = TASK_INTERRUPTIBLE;
 	    if (tty_hung_up_p(filp)
 	    || !(info->flags & ASYNC_INITIALIZED) ){
-		return ((info->flags & ASYNC_HUP_NOTIFY) ?
+		retval = ((info->flags & ASYNC_HUP_NOTIFY) ?
 		    -EAGAIN : -ERESTARTSYS);
 		break;
 	    }
@@ -2813,6 +2616,10 @@
 	       interrupts should be enabled as soon as the first open happens 
 	       to one of its ports. */
 	    if (!cy_card[info->card].intr_enabled) {
+		/* Enable interrupts on the PLX chip */
+		cy_writew(cy_card[info->card].ctl_addr+0x68,
+			cy_readw(cy_card[info->card].ctl_addr+0x68)|0x0900);
+		/* Enable interrupts on the FW */
 		retval = cyz_issue_cmd(&cy_card[info->card], 
 					0, C_CM_IRQ_ENBL, 0L);
 		if (retval != 0){
@@ -2897,7 +2704,8 @@
 /*
  * cy_wait_until_sent() --- wait until the transmitter is empty
  */
-static void cy_wait_until_sent(struct tty_struct *tty, int timeout)
+static void 
+cy_wait_until_sent(struct tty_struct *tty, int timeout)
 {
   struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
   unsigned char *base_addr;
@@ -2981,9 +2789,9 @@
  * This routine is called when a particular tty device is closed.
  */
 static void
-cy_close(struct tty_struct * tty, struct file * filp)
+cy_close(struct tty_struct *tty, struct file *filp)
 {
-  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
   unsigned long flags;
 
 #ifdef CY_DEBUG_OTHER
@@ -2994,12 +2802,11 @@
         return;
     }
 
-    save_flags(flags); cli();
-
+    CY_LOCK(info, flags);
     /* If the TTY is being hung up, nothing to do */
     if (tty_hung_up_p(filp)) {
 	MOD_DEC_USE_COUNT;
-        restore_flags(flags);
+	CY_UNLOCK(info, flags);
         return;
     }
         
@@ -3030,7 +2837,7 @@
     }
     if (info->count) {
 	MOD_DEC_USE_COUNT;
-        restore_flags(flags);
+	CY_UNLOCK(info, flags);
         return;
     }
     info->flags |= ASYNC_CLOSING;
@@ -3048,9 +2855,11 @@
     * the line discipline to only process XON/XOFF characters.
     */
     tty->closing = 1;
+    CY_UNLOCK(info, flags);
     if (info->closing_wait != CY_CLOSING_WAIT_NONE) {
 	tty_wait_until_sent(tty, info->closing_wait);
     }
+    CY_LOCK(info, flags);
 
     if (!IS_CYC_Z(cy_card[info->card])) {
 	int channel = info->line - cy_card[info->card].first_line;
@@ -3066,7 +2875,9 @@
 	if (info->flags & ASYNC_INITIALIZED) {
 	    /* Waiting for on-board buffers to be empty before closing 
 	       the port */
+	    CY_UNLOCK(info, flags);
 	    cy_wait_until_sent(tty, info->timeout);
+	    CY_LOCK(info, flags);
 	}
     } else {
 #ifdef Z_WAKE
@@ -3084,27 +2895,34 @@
 	    retval = cyz_issue_cmd(&cy_card[info->card], channel, 
 				   C_CM_IOCTLW, 0L);
 	    if (retval != 0){
-		printk("cyc:shutdown retval (1) was %x\n", retval);
+		printk("cyc:cy_close retval was %x\n", retval);
 	    }
+	    CY_UNLOCK(info, flags);
 	    interruptible_sleep_on(&info->shutdown_wait);
+	    CY_LOCK(info, flags);
 	}
 #endif
     }
 
+    CY_UNLOCK(info, flags);
     shutdown(info);
     if (tty->driver.flush_buffer)
         tty->driver.flush_buffer(tty);
     if (tty->ldisc.flush_buffer)
         tty->ldisc.flush_buffer(tty);
+    CY_LOCK(info, flags);
+
     tty->closing = 0;
     info->event = 0;
     info->tty = 0;
     if (info->blocked_open) {
+	CY_UNLOCK(info, flags);
         if (info->close_delay) {
             current->state = TASK_INTERRUPTIBLE;
             schedule_timeout(info->close_delay);
         }
         wake_up_interruptible(&info->open_wait);
+	CY_LOCK(info, flags);
     }
     info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
                      ASYNC_CLOSING);
@@ -3115,7 +2933,7 @@
 #endif
 
     MOD_DEC_USE_COUNT;
-    restore_flags(flags);
+    CY_UNLOCK(info, flags);
     return;
 } /* cy_close */
 
@@ -3153,8 +2971,7 @@
         return 0;
     }
 
-    save_flags(flags);               
-
+    CY_LOCK(info, flags);
     if (from_user) {
 	down(&tmp_buf_sem);
 	while (1) {
@@ -3170,13 +2987,11 @@
 		}
 		break;
 	    }
-	    cli();               
 	    c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
 			SERIAL_XMIT_SIZE - info->xmit_head));
 	    memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
 	    info->xmit_head = ((info->xmit_head + c) & (SERIAL_XMIT_SIZE-1));
 	    info->xmit_cnt += c;
-	    restore_flags(flags);
 	    buf += c;
 	    count -= c;
 	    ret += c;
@@ -3184,22 +2999,20 @@
 	up(&tmp_buf_sem);
     } else {
 	while (1) {
-	    cli();
 	    c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, 
 			SERIAL_XMIT_SIZE - info->xmit_head));
 	    if (c <= 0) {
-		restore_flags(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;
-	    restore_flags(flags);
 	    buf += c;
 	    count -= c;
 	    ret += c;
 	}
     }
+    CY_UNLOCK(info, flags);
 
     info->idle_stats.xmit_bytes += ret;
     info->idle_stats.xmit_idle   = jiffies;
@@ -3234,9 +3047,9 @@
     if (!tty || !info->xmit_buf)
         return;
 
-    save_flags(flags); cli();
+    CY_LOCK(info, flags);
         if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
-            restore_flags(flags);
+	    CY_UNLOCK(info, flags);
             return;
         }
 
@@ -3245,7 +3058,7 @@
         info->xmit_cnt++;
 	info->idle_stats.xmit_bytes++;
 	info->idle_stats.xmit_idle = jiffies;
-    restore_flags(flags);
+    CY_UNLOCK(info, flags);
 } /* cy_put_char */
 
 
@@ -3257,9 +3070,6 @@
 cy_flush_chars(struct tty_struct *tty)
 {
   struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
-  unsigned long flags;
-  unsigned char *base_addr;
-  int card,chip,channel,index;
                                 
 #ifdef CY_DEBUG_IO
     printk("cyc:cy_flush_chars ttyC%d\n", info->line); /* */
@@ -3272,25 +3082,7 @@
     || tty->hw_stopped || !info->xmit_buf)
         return;
 
-    card = info->card;
-    channel = info->line - cy_card[card].first_line;
-    if (!IS_CYC_Z(cy_card[card])) {
-	chip = channel>>2;
-	channel &= 0x03;
-	index = cy_card[card].bus_index;
-	base_addr = (unsigned char*)
-		       (cy_card[card].base_addr
-		       + (cy_chip_offset[chip]<<index));
-
-	save_flags(flags); cli();
-	    cy_writeb((u_long)base_addr+(CyCAR<<index), channel);
-	    cy_writeb((u_long)base_addr+(CySRER<<index),
-               cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
-	restore_flags(flags);
-    } else {
-	/* Since polling is already in place,
-	    nothing further need be done.  */
-    }
+    start_xmit(info);
 } /* cy_flush_chars */
 
 
@@ -3399,6 +3191,20 @@
     cflag = info->tty->termios->c_cflag;
     iflag = info->tty->termios->c_iflag;
 
+    /*
+     * Set up the tty->alt_speed kludge
+     */
+    if (info->tty) {
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+	    info->tty->alt_speed = 57600;
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+	    info->tty->alt_speed = 115200;
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+	    info->tty->alt_speed = 230400;
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+	    info->tty->alt_speed = 460800;
+    }
+
     card = info->card;
     channel = (info->line) - (cy_card[card].first_line);
     chip_number = channel / 4;
@@ -3408,7 +3214,11 @@
 	index = cy_card[card].bus_index;
 
 	/* baud rate */
-	baud = tty_get_baud_rate(info->tty);
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+	    baud = info->baud;
+	} else {
+	    baud = tty_get_baud_rate(info->tty);
+	}
 	if (baud > CD1400_MAX_SPEED) {
 	    baud = CD1400_MAX_SPEED;
 	}
@@ -3511,7 +3321,7 @@
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-	save_flags(flags); cli();
+	CY_LOCK(info, flags);
 	    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
 
 	   /* tx and rx baud rate */
@@ -3601,14 +3411,15 @@
 	    if (info->tty){
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
 	    }
+	CY_UNLOCK(info, flags);
 
-	restore_flags(flags);
     } else {
       struct FIRM_ID *firm_id;
       struct ZFW_CTRL *zfw_ctrl;
       struct BOARD_CTRL *board_ctrl;
       struct CH_CTRL *ch_ctrl;
       struct BUF_CTRL *buf_ctrl;
+      uclong sw_flow;
       int retval;
 
         firm_id = (struct FIRM_ID *)
@@ -3624,9 +3435,13 @@
 	buf_ctrl = &zfw_ctrl->buf_ctrl[channel];
 
 	/* baud rate */
-	baud = tty_get_baud_rate(info->tty);
-	if (baud > CD1400_MAX_SPEED) {
-	    baud = CD1400_MAX_SPEED;
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+	    baud = info->baud;
+	} else {
+	    baud = tty_get_baud_rate(info->tty);
+	}
+	if (baud > CYZ_MAX_SPEED) {
+	    baud = CYZ_MAX_SPEED;
 	}
 	cy_writel(&ch_ctrl->comm_baud , baud);
 
@@ -3666,14 +3481,24 @@
 
 	/* CTS flow control flag */
 	if (cflag & CRTSCTS){
-	    info->flags |= ASYNC_CTS_FLOW;
 	    cy_writel(&ch_ctrl->hw_flow,
                cy_readl(&ch_ctrl->hw_flow) | C_RS_CTS | C_RS_RTS);
 	}else{
-	    info->flags &= ~ASYNC_CTS_FLOW;
 	    cy_writel(&ch_ctrl->hw_flow,
                cy_readl(&ch_ctrl->hw_flow) & ~(C_RS_CTS | C_RS_RTS));
 	}
+	/* As the HW flow control is done in firmware, the driver doesn't
+	   need to care about it */
+	info->flags &= ~ASYNC_CTS_FLOW;
+
+	/* XON/XOFF/XANY flow control flags */
+	sw_flow = 0;
+	if (iflag & IXON){
+	    sw_flow |= C_FL_OXX;
+	    if (iflag & IXANY)
+		sw_flow |= C_FL_OIXANY;
+	}
+	cy_writel(&ch_ctrl->sw_flow, sw_flow);
 
 	retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_IOCTL, 0L);
 	if (retval != 0){
@@ -3688,14 +3513,6 @@
 	    info->flags |= ASYNC_CHECK_CD;
 	}
 
-	if (iflag & IXON){
-	    cy_writel(&ch_ctrl->sw_flow, 
-		cy_readl(&ch_ctrl->sw_flow) | C_FL_OXX);
-	} else {
-	    cy_writel(&ch_ctrl->sw_flow, 
-		cy_readl(&ch_ctrl->sw_flow) & ~C_FL_OXX);
-	}
-
 	if(baud == 0){ /* baud rate is zero, turn off line */
 	    cy_writel(&ch_ctrl->rs_control,
                cy_readl(&ch_ctrl->rs_control) & ~C_RS_DTR);
@@ -3721,20 +3538,6 @@
 	}
     }
 
-    /*
-     * Set up the tty->alt_speed kludge
-     */
-    if (info->tty) {
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
-	    info->tty->alt_speed = 57600;
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
-	    info->tty->alt_speed = 115200;
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
-	    info->tty->alt_speed = 230400;
-	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
-	    info->tty->alt_speed = 460800;
-    }
-
 
 } /* set_line_char */
 
@@ -3831,12 +3634,11 @@
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-	save_flags(flags); cli();
+	CY_LOCK(info, flags);
 	    cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
 	    status = cy_readb(base_addr+(CyMSVR1<<index));
 	    status |= cy_readb(base_addr+(CyMSVR2<<index));
-	restore_flags(flags);
-
+	CY_UNLOCK(info, flags);
 
         if (info->rtsdtr_inv) {
 	    result =  ((status  & CyRTS) ? TIOCM_DTR : 0)
@@ -3907,17 +3709,17 @@
 	switch (cmd) {
 	case TIOCMBIS:
 	    if (arg & TIOCM_RTS){
-		save_flags(flags); cli();
+		CY_LOCK(info, flags);
 		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
                 if (info->rtsdtr_inv) {
 		    cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
                 } else {
 		    cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
                 }
-		restore_flags(flags);
+		CY_UNLOCK(info, flags);
 	    }
 	    if (arg & TIOCM_DTR){
-		save_flags(flags); cli();
+		CY_LOCK(info, flags);
 		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
                 if (info->rtsdtr_inv) {
 		    cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
@@ -3930,12 +3732,12 @@
 		    cy_readb(base_addr+(CyMSVR1<<index)), 
                     cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
-		restore_flags(flags);
+		CY_UNLOCK(info, flags);
 	    }
 	    break;
 	case TIOCMBIC:
 	    if (arg & TIOCM_RTS){
-		save_flags(flags); cli();
+		CY_LOCK(info, flags);
 		cy_writeb((u_long)base_addr+(CyCAR<<index), 
                           (u_char)channel);
                 if (info->rtsdtr_inv) {
@@ -3943,10 +3745,10 @@
                 } else {
 		    	cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
                 }
-		restore_flags(flags);
+		CY_UNLOCK(info, flags);
 	    }
 	    if (arg & TIOCM_DTR){
-		save_flags(flags); cli();
+		CY_LOCK(info, flags);
 		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
                 if (info->rtsdtr_inv) {
 			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
@@ -3959,31 +3761,31 @@
 		    cy_readb(base_addr+(CyMSVR1<<index)), 
                     cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
-		restore_flags(flags);
+		CY_UNLOCK(info, flags);
 	    }
 	    break;
 	case TIOCMSET:
 	    if (arg & TIOCM_RTS){
-		save_flags(flags); cli();
+		CY_LOCK(info, flags);
 	        cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
                 if (info->rtsdtr_inv) {
 			cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
                 } else {
 			cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
                 }
-		restore_flags(flags);
+		CY_UNLOCK(info, flags);
 	    }else{
-		save_flags(flags); cli();
+		CY_LOCK(info, flags);
 		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
                 if (info->rtsdtr_inv) {
 			cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
                 } else {
 			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
                 }
-		restore_flags(flags);
+		CY_UNLOCK(info, flags);
 	    }
 	    if (arg & TIOCM_DTR){
-		save_flags(flags); cli();
+		CY_LOCK(info, flags);
 		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
                 if (info->rtsdtr_inv) {
 			cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
@@ -3996,9 +3798,9 @@
 		    cy_readb(base_addr+(CyMSVR1<<index)), 
                     cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
-		restore_flags(flags);
+		CY_UNLOCK(info, flags);
 	    }else{
-		save_flags(flags); cli();
+		CY_LOCK(info, flags);
 		cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
                 if (info->rtsdtr_inv) {
 			cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
@@ -4012,7 +3814,7 @@
 		    cy_readb(base_addr+(CyMSVR1<<index)), 
                     cy_readb(base_addr+(CyMSVR2<<index)));
 #endif
-		restore_flags(flags);
+		CY_UNLOCK(info, flags);
 	    }
 	    break;
 	default:
@@ -4032,50 +3834,66 @@
 	    switch (cmd) {
 	    case TIOCMBIS:
 		if (arg & TIOCM_RTS){
+		    CY_LOCK(info, flags);
 		    cy_writel(&ch_ctrl[channel].rs_control,
                        cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
+		    CY_UNLOCK(info, flags);
 		}
 		if (arg & TIOCM_DTR){
+		    CY_LOCK(info, flags);
 		    cy_writel(&ch_ctrl[channel].rs_control,
                        cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
 #ifdef CY_DEBUG_DTR
 		    printk("cyc:set_modem_info raising Z DTR\n");
 #endif
+		    CY_UNLOCK(info, flags);
 		}
 		break;
 	    case TIOCMBIC:
 		if (arg & TIOCM_RTS){
+		    CY_LOCK(info, flags);
 		    cy_writel(&ch_ctrl[channel].rs_control,
                        cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
+		    CY_UNLOCK(info, flags);
 		}
 		if (arg & TIOCM_DTR){
+		    CY_LOCK(info, flags);
 		    cy_writel(&ch_ctrl[channel].rs_control,
                        cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
 #ifdef CY_DEBUG_DTR
 		    printk("cyc:set_modem_info clearing Z DTR\n");
 #endif
+		    CY_UNLOCK(info, flags);
 		}
 		break;
 	    case TIOCMSET:
 		if (arg & TIOCM_RTS){
+		    CY_LOCK(info, flags);
 		    cy_writel(&ch_ctrl[channel].rs_control,
                        cy_readl(&ch_ctrl[channel].rs_control) | C_RS_RTS);
+		    CY_UNLOCK(info, flags);
 		}else{
+		    CY_LOCK(info, flags);
 		    cy_writel(&ch_ctrl[channel].rs_control,
                        cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_RTS);
+		    CY_UNLOCK(info, flags);
 		}
 		if (arg & TIOCM_DTR){
+		    CY_LOCK(info, flags);
 		    cy_writel(&ch_ctrl[channel].rs_control,
                        cy_readl(&ch_ctrl[channel].rs_control) | C_RS_DTR);
 #ifdef CY_DEBUG_DTR
 		    printk("cyc:set_modem_info raising Z DTR\n");
 #endif
+		    CY_UNLOCK(info, flags);
 		}else{
+		    CY_LOCK(info, flags);
 		    cy_writel(&ch_ctrl[channel].rs_control,
                        cy_readl(&ch_ctrl[channel].rs_control) & ~C_RS_DTR);
 #ifdef CY_DEBUG_DTR
 		    printk("cyc:set_modem_info clearing Z DTR\n");
 #endif
+		    CY_UNLOCK(info, flags);
 		}
 		break;
 	    default:
@@ -4084,12 +3902,14 @@
 	}else{
 	    return -ENODEV;
 	}
+	CY_LOCK(info, flags);
         retval = cyz_issue_cmd(&cy_card[info->card],
 				    channel, C_CM_IOCTLM,0L);
 	if (retval != 0){
 	    printk("cyc:set_modem_info retval at %d was %x\n",
 	        __LINE__, retval);
 	}
+	CY_UNLOCK(info, flags);
     }
     return 0;
 } /* set_modem_info */
@@ -4106,7 +3926,7 @@
     if (serial_paranoia_check(info, tty->device, "cy_break"))
 	return;
 
-    save_flags(flags); cli();
+    CY_LOCK(info, flags);
     if (!IS_CYC_Z(cy_card[info->card])) {
         /* Let the transmit ISR take care of this (since it
 	   requires stuffing characters into the output stream).
@@ -4115,14 +3935,18 @@
 	    if (!info->breakon) {
 		info->breakon = 1;
 		if (!info->xmit_cnt) {
+		    CY_UNLOCK(info, flags);
 		    start_xmit(info);
+		    CY_LOCK(info, flags);
 		}
 	    }
 	} else {
 	    if (!info->breakoff) {
 		info->breakoff = 1;
 		if (!info->xmit_cnt) {
+		    CY_UNLOCK(info, flags);
 		    start_xmit(info);
+		    CY_LOCK(info, flags);
 		}
 	    }
 	}
@@ -4147,8 +3971,7 @@
 	    }
 	}
     }
-    restore_flags(flags);
-
+    CY_UNLOCK(info, flags);
 } /* cy_break */
 
 static int
@@ -4170,6 +3993,7 @@
 {
   unsigned char *base_addr;
   int card,channel,chip,index;
+  unsigned long flags;
    
     card = info->card;
     channel = info->line - cy_card[card].first_line;
@@ -4183,8 +4007,11 @@
 
 	info->cor3 &= ~CyREC_FIFO;
 	info->cor3 |= value & CyREC_FIFO;
-	cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
-	cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index);
+
+	CY_LOCK(info, flags);
+	    cy_writeb((u_long)base_addr+(CyCOR3<<index), info->cor3);
+	    cyy_issue_cmd(base_addr,CyCOR_CHANGE|CyCOR3ch,index);
+	CY_UNLOCK(info, flags);
     } else {
 	// Nothing to do!
     }
@@ -4238,6 +4065,7 @@
 {
   unsigned char *base_addr;
   int card,channel,chip,index;
+  unsigned long flags;
    
     card = info->card;
     channel = info->line - cy_card[card].first_line;
@@ -4249,7 +4077,9 @@
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-	cy_writeb((u_long)base_addr+(CyRTPR<<index), value & 0xff);
+	CY_LOCK(info, flags);
+	    cy_writeb((u_long)base_addr+(CyRTPR<<index), value & 0xff);
+	CY_UNLOCK(info, flags);
     } else {
 	// Nothing to do!
     }
@@ -4307,7 +4137,10 @@
             unsigned int cmd, unsigned long arg)
 {
   struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+  struct cyclades_icount cprev, cnow;		/* kernel counter temps */
+  struct serial_icounter_struct *p_cuser;	/* user space */
   int ret_val = 0;
+  unsigned long flags;
 
     if (serial_paranoia_check(info, tty->device, "cy_ioctl"))
 	return -ENODEV;
@@ -4363,6 +4196,7 @@
             if (copy_to_user((void *)arg, (void *)&cy_card[info->card], 
 			sizeof (struct cyclades_card))) {
 		ret_val = -EFAULT;
+		break;
 	    }
 	    ret_val = 0;
             break;
@@ -4399,6 +4233,77 @@
         case TIOCSSERIAL:
             ret_val = set_serial_info(info, (struct serial_struct *) arg);
             break;
+	/*
+	 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change 
+	 * - mask passed in arg for lines of interest
+	 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
+	 * Caller should use TIOCGICOUNT to see which one it was
+	 */
+	case TIOCMIWAIT:
+	    CY_LOCK(info, flags);
+	    /* note the counters on entry */
+	    cprev = info->icount;
+	    CY_UNLOCK(info, flags);
+	    while (1) {
+		interruptible_sleep_on(&info->delta_msr_wait);
+		/* see if a signal did it */
+		if (signal_pending(current)) {
+		    return -ERESTARTSYS;
+		}
+
+		CY_LOCK(info, flags);
+		cnow = info->icount; /* atomic copy */
+		CY_UNLOCK(info, flags);
+
+		if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
+		    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) {
+		    return -EIO; /* no change => error */
+		}
+		if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || 
+		     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || 
+		     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) || 
+		     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
+		    return 0;
+		}
+		cprev = cnow;
+	    }
+	    /* NOTREACHED */
+
+	/*
+	 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
+	 * Return: write counters to the user passed counter struct
+	 * NB: both 1->0 and 0->1 transitions are counted except for
+	 *     RI where only 0->1 is counted.
+	 */
+	case TIOCGICOUNT:
+	    CY_LOCK(info, flags);
+	    cnow = info->icount;
+	    CY_UNLOCK(info, flags);
+	    p_cuser = (struct serial_icounter_struct *) arg;
+	    ret_val = put_user(cnow.cts, &p_cuser->cts);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.dsr, &p_cuser->dsr);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.rng, &p_cuser->rng);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.dcd, &p_cuser->dcd);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.rx, &p_cuser->rx);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.tx, &p_cuser->tx);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.frame, &p_cuser->frame);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.overrun, &p_cuser->overrun);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.parity, &p_cuser->parity);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.brk, &p_cuser->brk);
+	    if (ret_val) return ret_val;
+	    ret_val = put_user(cnow.buf_overrun, &p_cuser->buf_overrun);
+	    if (ret_val) return ret_val;
+	    ret_val = 0;
+	    break;
         default:
             ret_val = -ENOIOCTLCMD;
     }
@@ -4426,7 +4331,9 @@
     printk("cyc:cy_set_termios ttyC%d\n", info->line);
 #endif
 
-    if (tty->termios->c_cflag == old_termios->c_cflag)
+    if ((tty->termios->c_cflag == old_termios->c_cflag) &&
+	((tty->termios->c_iflag & (IXON|IXANY)) == 
+	 (old_termios->c_iflag & (IXON|IXANY))))
         return;
     set_line_char(info);
 
@@ -4489,14 +4396,14 @@
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-	save_flags(flags); cli();
+	CY_LOCK(info, flags);
 	cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
 	if (info->rtsdtr_inv) {
 		cy_writeb((u_long)base_addr+(CyMSVR2<<index), ~CyDTR);
 	} else {
 		cy_writeb((u_long)base_addr+(CyMSVR1<<index), ~CyRTS);
 	}
-	restore_flags(flags);
+	CY_UNLOCK(info, flags);
     } else {
 	// Nothing to do!
     }
@@ -4548,14 +4455,14 @@
 		       (cy_card[card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-	save_flags(flags); cli();
+	CY_LOCK(info, flags);
 	cy_writeb((u_long)base_addr+(CyCAR<<index), (u_char)channel);
 	if (info->rtsdtr_inv) {
 		cy_writeb((u_long)base_addr+(CyMSVR2<<index), CyDTR);
 	} else {
 		cy_writeb((u_long)base_addr+(CyMSVR1<<index), CyRTS);
 	}
-	restore_flags(flags);
+	CY_UNLOCK(info, flags);
     }else{
 	// Nothing to do!
     }
@@ -4593,12 +4500,12 @@
                    (cy_card[info->card].base_addr
                            + (cy_chip_offset[chip]<<index));
 
-        save_flags(flags); cli();
+	CY_LOCK(info, flags);
             cy_writeb((u_long)base_addr+(CyCAR<<index),
 	       (u_char)(channel & 0x0003)); /* index channel */
             cy_writeb((u_long)base_addr+(CySRER<<index), 
                cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty);
-        restore_flags(flags);
+	CY_UNLOCK(info, flags);
     } else {
 	// Nothing to do!
     }
@@ -4633,12 +4540,12 @@
                        (cy_card[info->card].base_addr
 		       + (cy_chip_offset[chip]<<index));
 
-        save_flags(flags); cli();
+	CY_LOCK(info, flags);
             cy_writeb((u_long)base_addr+(CyCAR<<index),
 	       (u_char)(channel & 0x0003)); /* index channel */
             cy_writeb((u_long)base_addr+(CySRER<<index), 
                cy_readb(base_addr+(CySRER<<index)) | CyTxMpty);
-        restore_flags(flags);
+	CY_UNLOCK(info, flags);
     } else {
 	// Nothing to do!
     }
@@ -4660,19 +4567,22 @@
 
     if (serial_paranoia_check(info, tty->device, "cy_flush_buffer"))
         return;
-    save_flags(flags); cli();
-    info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
-    restore_flags(flags);
 
     card = info->card;
     channel = (info->line) - (cy_card[card].first_line);
 
+    CY_LOCK(info, flags);
+    info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+    CY_UNLOCK(info, flags);
+
     if (IS_CYC_Z(cy_card[card])) { /* If it is a Z card, flush the on-board 
 				      buffers as well */
+	CY_LOCK(info, flags);
 	retval = cyz_issue_cmd(&cy_card[card], channel, C_CM_FLUSH_TX, 0L);
 	if (retval != 0) {
 	    printk("cyc: flush_buffer retval was %x\n", retval);
 	}
+	CY_UNLOCK(info, flags);
     }
     wake_up_interruptible(&tty->write_wait);
     if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
@@ -4811,9 +4721,24 @@
   unsigned short	cy_isa_irq,nboard;
   volatile ucchar	*cy_isa_address;
   unsigned short	i,j,cy_isa_nchan;
+#ifdef MODULE
+  int isparam = 0;
+#endif
 
         nboard = 0;
 
+#ifdef MODULE
+	/* Check for module parameters */
+	for(i = 0 ; i < NR_CARDS; i++) {
+	    if (maddr[i] || i) {
+		isparam = 1;
+		cy_isa_addresses[i] = (ucchar *)maddr[i];
+	    }
+	    if (!maddr[i])
+		break;
+	}
+#endif
+
         /* scan the address table probing for Cyclom-Y/ISA boards */
         for (i = 0 ; i < NR_ISA_ADDRS ; i++) {
                 cy_isa_address = cy_isa_addresses[i];
@@ -4832,8 +4757,13 @@
                         continue;
                 }
 
+#ifdef MODULE
+		if (isparam && irq[i])
+		    cy_isa_irq = irq[i];
+		else
+#endif
                 /* find out the board's irq by probing */
-                cy_isa_irq = do_auto_irq(cy_isa_address);
+                cy_isa_irq = detect_isa_irq(cy_isa_address);
                 if (cy_isa_irq == 0) {
                         printk("Cyclom-Y/ISA found at 0x%lx ",
                                 (unsigned long) cy_isa_address);
@@ -4894,7 +4824,8 @@
 } /* cy_detect_isa */
 #endif /* CONFIG_COBALT_27 */
 
-static void plx_init(uclong addr, uclong initctl)
+static void 
+plx_init(uclong addr, uclong initctl)
 {
     /* Reset PLX */
     cy_writel(addr + initctl, cy_readl(addr + initctl) | 0x40000000);
@@ -4926,6 +4857,7 @@
   unsigned short        device_id,dev_index = 0;
   uclong		mailbox;
   uclong		Ze_addr0[NR_CARDS], Ze_addr2[NR_CARDS], ZeIndex = 0;
+  unsigned char         Ze_irq[NR_CARDS];
 
         if(pci_present() == 0) {    /* PCI bus not present */
                 return(0);
@@ -5057,6 +4989,11 @@
 		    default: /* Old boards, use PLX_9060 */
 
 		    plx_init(cy_pci_addr0, 0x6c);
+		    /* For some yet unknown reason, once the PLX9060 reloads
+		       the EEPROM, the IRQ is lost and, thus, we have to
+		       re-write it to the PCI config. registers.
+		       This will remain here until we find a permanent fix. */
+		    pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, cy_pci_irq);
 
 		    cy_writew(cy_pci_addr0+0x68, 
 			cy_readw(cy_pci_addr0+0x68)|0x0900);
@@ -5097,7 +5034,16 @@
                 cy_pci_addr0 = (ulong)ioremap(cy_pci_addr0, CyPCI_Zctl);
 #endif
 
+		/* Disable interrupts on the PLX before resetting it */
+		cy_writew(cy_pci_addr0+0x68,
+			cy_readw(cy_pci_addr0+0x68) & ~0x0900);
+
 		plx_init(cy_pci_addr0, 0x6c);
+		/* For some yet unknown reason, once the PLX9060 reloads
+		   the EEPROM, the IRQ is lost and, thus, we have to
+		   re-write it to the PCI config. registers.
+		   This will remain here until we find a permanent fix. */
+		pci_write_config_byte(pdev, PCI_INTERRUPT_LINE, cy_pci_irq);
 
 		mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) 
 			   cy_pci_addr0)->mail_box_0);
@@ -5120,6 +5066,7 @@
 		    } else {
 			Ze_addr0[ZeIndex] = cy_pci_addr0;
 			Ze_addr2[ZeIndex] = cy_pci_addr2;
+			Ze_irq[ZeIndex] = cy_pci_irq;
 			ZeIndex++;
 		    }
 		    i--;
@@ -5206,24 +5153,21 @@
                 cy_card[j].num_chips = -1;
 
                 /* print message */
+#ifdef CONFIG_CYZ_INTR
 		/* don't report IRQ if board is no IRQ */
-		if( (cy_pci_irq != 0) && (cy_pci_irq != 255) ) {
+		if( (cy_pci_irq != 0) && (cy_pci_irq != 255) )
 		    printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
 			j+1,(ulong)cy_pci_addr2,
 			(ulong)(cy_pci_addr2 + CyPCI_Zwin - 1),
 			(int)cy_pci_irq);
-		}else{
+		else
+#endif /* CONFIG_CYZ_INTR */
 		    printk("Cyclades-8Zo/PCI #%d: 0x%lx-0x%lx, ",
 			j+1,(ulong)cy_pci_addr2,
 			(ulong)(cy_pci_addr2 + CyPCI_Zwin - 1));
-		}
+
                 printk("%d channels starting from port %d.\n",
 		    cy_pci_nchan,cy_next_channel);
-#ifdef CONFIG_CYZ_INTR
-		/* Enable interrupts on the PLX chip */
-		cy_writew(cy_pci_addr0+0x68,
-			cy_readw(cy_pci_addr0+0x68)|0x0900);
-#endif /* CONFIG_CYZ_INTR */
                 cy_next_channel += cy_pci_nchan;
     }
         }
@@ -5231,9 +5175,11 @@
         for (; ZeIndex != 0 && i < NR_CARDS; i++) {
 	    cy_pci_addr0 = Ze_addr0[0];
 	    cy_pci_addr2 = Ze_addr2[0];
+	    cy_pci_irq = Ze_irq[0];
 	    for (j = 0 ; j < ZeIndex-1 ; j++) {
 		Ze_addr0[j] = Ze_addr0[j+1];
 		Ze_addr2[j] = Ze_addr2[j+1];
+		Ze_irq[j] = Ze_irq[j+1];
 	    }
 	    ZeIndex--;
 		mailbox = (uclong)cy_readl(&((struct RUNTIME_9060 *) 
@@ -5291,24 +5237,21 @@
                 cy_card[j].num_chips = -1;
 
                 /* print message */
+#ifdef CONFIG_CYZ_INTR
 		/* don't report IRQ if board is no IRQ */
-		if( (cy_pci_irq != 0) && (cy_pci_irq != 255) ) {
+		if( (cy_pci_irq != 0) && (cy_pci_irq != 255) )
 		    printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, IRQ%d, ",
 			j+1,(ulong)cy_pci_addr2,
 			(ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1),
 			(int)cy_pci_irq);
-		}else{
+		else
+#endif /* CONFIG_CYZ_INTR */
 		    printk("Cyclades-Ze/PCI #%d: 0x%lx-0x%lx, ",
 			j+1,(ulong)cy_pci_addr2,
 			(ulong)(cy_pci_addr2 + CyPCI_Ze_win - 1));
-		}
+
                 printk("%d channels starting from port %d.\n",
 		    cy_pci_nchan,cy_next_channel);
-#ifdef CONFIG_CYZ_INTR
-		/* Enable interrupts on the PLX chip */
-		cy_writew(cy_pci_addr0+0x68,
-			cy_readw(cy_pci_addr0+0x68)|0x0900);
-#endif /* CONFIG_CYZ_INTR */
                 cy_next_channel += cy_pci_nchan;
         }
 	if (ZeIndex != 0) {
@@ -5427,9 +5370,6 @@
   unsigned long mailbox;
   unsigned short chip_number;
   int nports;
-#ifdef CY_PROC
-  struct proc_dir_entry *ent;
-#endif
 
     init_bh(CYCLADES_BH, do_cyclades_bh);
 
@@ -5533,12 +5473,13 @@
     /* initialize per-port data structures for each valid board found */
     for (board = 0 ; board < cy_nboard ; board++) {
             cinfo = &cy_card[board];
-            if (cinfo->num_chips == -1){ /* Cyclades-Z */
+            if (cinfo->num_chips == -1) { /* Cyclades-Z */
 		number_z_boards++;
 		mailbox = cy_readl(&((struct RUNTIME_9060 *)
 			     cy_card[board].ctl_addr)->mail_box_0);
 		nports = (mailbox == ZE_V1) ? ZE_V1_NPORTS : 8;
 		cinfo->intr_enabled = 0;
+		spin_lock_init(&cinfo->card_lock);
                 for (port = cinfo->first_line ;
                      port < cinfo->first_line + nports;
                      port++)
@@ -5566,6 +5507,11 @@
                     info->rco = 0;
                     info->close_delay = 5*HZ/10;
 		    info->closing_wait = CLOSING_WAIT_DELAY;
+		    info->icount.cts = info->icount.dsr = 
+			info->icount.rng = info->icount.dcd = 0;
+		    info->icount.rx = info->icount.tx = 0;
+		    info->icount.frame = info->icount.parity = 0;
+		    info->icount.overrun = info->icount.brk = 0;
                     info->x_char = 0;
                     info->event = 0;
                     info->count = 0;
@@ -5584,6 +5530,7 @@
                     info->open_wait = 0;
                     info->close_wait = 0;
                     info->shutdown_wait = 0;
+                    info->delta_msr_wait = 0;
                     /* info->session */
                     /* info->pgrp */
                     info->read_status_mask = 0;
@@ -5597,6 +5544,7 @@
                 continue;
             }else{ /* Cyclom-Y of some kind*/
                 index = cinfo->bus_index;
+		spin_lock_init(&cinfo->card_lock);
                 for (port = cinfo->first_line ;
                      port < cinfo->first_line + 4*cinfo->num_chips ;
                      port++)
@@ -5616,6 +5564,11 @@
                     info->cor5 = 0;
                     info->close_delay = 5*HZ/10;
 		    info->closing_wait = CLOSING_WAIT_DELAY;
+		    info->icount.cts = info->icount.dsr = 
+			info->icount.rng = info->icount.dcd = 0;
+		    info->icount.rx = info->icount.tx = 0;
+		    info->icount.frame = info->icount.parity = 0;
+		    info->icount.overrun = info->icount.brk = 0;
 		    chip_number = (port - cinfo->first_line) / 4;
                     if ((info->chip_rev = cy_readb(cinfo->base_addr +
 	               		 (cy_chip_offset[chip_number]<<index) +
@@ -5653,6 +5606,7 @@
                     info->open_wait = 0;
                     info->close_wait = 0;
                     info->shutdown_wait = 0;
+                    info->delta_msr_wait = 0;
                     /* info->session */
                     /* info->pgrp */
                     info->read_status_mask =
@@ -5674,11 +5628,6 @@
     }
 #endif /* CONFIG_CYZ_INTR */
 
-#ifdef CY_PROC
-        ent = create_proc_entry("cyclades", S_IFREG | S_IRUGO, 0);
-        ent->read_proc = cyclades_get_proc_info;
-#endif
-
     return 0;
     
 } /* cy_init */
@@ -5719,23 +5668,22 @@
     restore_flags(flags);
 
     for (i = 0; i < NR_CARDS; i++) {
-        if (cy_card[i].base_addr != 0
+        if (cy_card[i].base_addr != 0) {
+	    iounmap((void *)cy_card[i].base_addr);
+	    if (cy_card[i].ctl_addr != 0)
+		iounmap((void *)cy_card[i].ctl_addr);
+	    if (cy_card[i].irq
 #ifndef CONFIG_CYZ_INTR
-	    && cy_card[i].num_chips != -1 /* not a Z card */
+		&& cy_card[i].num_chips != -1 /* not a Z card */
 #endif /* CONFIG_CYZ_INTR */
-	    && cy_card[i].irq)
-        {
-            free_irq(cy_card[i].irq, &cy_card[i]);
+	    )
+		free_irq(cy_card[i].irq, &cy_card[i]);
         }
     }
     if (tmp_buf) {
 	free_page((unsigned long) tmp_buf);
 	tmp_buf = NULL;
     }
-#ifdef CY_PROC
-    remove_proc_entry("cyclades", 0);
-#endif
-
 } /* cleanup_module */
 #else
 /* called by linux/init/main.c to parse command line options */
@@ -5799,8 +5747,7 @@
     printk("  session pgrp open_wait = %lx %lx %lx\n",
              info->session, info->pgrp, (long)info->open_wait);
 
-
-    save_flags(flags); cli();
+    CY_LOCK(info, flags);
 
         base_addr = (unsigned char*)
                        (cy_card[card].base_addr
@@ -5857,7 +5804,7 @@
         printk(" CyTBPR %x\n", cy_readb(base_addr + CyTBPR<<index));
         printk(" CyTCOR %x\n", cy_readb(base_addr + CyTCOR<<index));
 
-    restore_flags(flags);
+    CY_UNLOCK(info, flags);
 } /* show_status */
 #endif
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)