md: allow an md array to appear with 0 drives if it has external metadata
[safe/jmp/linux-2.6] / drivers / char / synclink_gt.c
index 372a37e..1f954ac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: synclink_gt.c,v 4.36 2006/08/28 20:47:14 paulkf Exp $
+ * $Id: synclink_gt.c,v 4.50 2007/07/25 19:29:25 paulkf Exp $
  *
  * Device driver for Microgate SyncLink GT serial adapters.
  *
@@ -73,6 +73,7 @@
 #include <linux/bitops.h>
 #include <linux/workqueue.h>
 #include <linux/hdlc.h>
+#include <linux/synclink.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -81,8 +82,6 @@
 #include <asm/types.h>
 #include <asm/uaccess.h>
 
-#include "linux/synclink.h"
-
 #if defined(CONFIG_HDLC) || (defined(CONFIG_HDLC_MODULE) && defined(CONFIG_SYNCLINK_GT_MODULE))
 #define SYNCLINK_GENERIC_HDLC 1
 #else
@@ -93,7 +92,7 @@
  * module identification
  */
 static char *driver_name     = "SyncLink GT";
-static char *driver_version  = "$Revision: 4.36 $";
+static char *driver_version  = "$Revision: 4.50 $";
 static char *tty_driver_name = "synclink_gt";
 static char *tty_dev_prefix  = "ttySLG";
 MODULE_LICENSE("GPL");
@@ -206,10 +205,10 @@ static void flush_cond_wait(struct cond_wait **head);
  */
 struct slgt_desc
 {
-       unsigned short count;
-       unsigned short status;
-       unsigned int pbuf;  /* physical address of data buffer */
-       unsigned int next;  /* physical address of next descriptor */
+       __le16 count;
+       __le16 status;
+       __le32 pbuf;  /* physical address of data buffer */
+       __le32 next;  /* physical address of next descriptor */
 
        /* driver book keeping */
        char *buf;          /* virtual  address of data buffer */
@@ -477,6 +476,7 @@ static void tx_set_idle(struct slgt_info *info);
 static unsigned int free_tbuf_count(struct slgt_info *info);
 static void reset_tbufs(struct slgt_info *info);
 static void tdma_reset(struct slgt_info *info);
+static void tdma_start(struct slgt_info *info);
 static void tx_load(struct slgt_info *info, const char *buf, unsigned int count);
 
 static void get_signals(struct slgt_info *info);
@@ -904,6 +904,8 @@ start:
                spin_lock_irqsave(&info->lock,flags);
                if (!info->tx_active)
                        tx_start(info);
+               else
+                       tdma_start(info);
                spin_unlock_irqrestore(&info->lock,flags);
        }
 
@@ -1562,6 +1564,9 @@ static int hdlcdev_open(struct net_device *dev)
        int rc;
        unsigned long flags;
 
+       if (!try_module_get(THIS_MODULE))
+               return -EBUSY;
+
        DBGINFO(("%s hdlcdev_open\n", dev->name));
 
        /* generic HDLC layer open processing */
@@ -1631,6 +1636,7 @@ static int hdlcdev_close(struct net_device *dev)
        info->netcount=0;
        spin_unlock_irqrestore(&info->netlock, flags);
 
+       module_put(THIS_MODULE);
        return 0;
 }
 
@@ -2033,37 +2039,41 @@ static void bh_transmit(struct slgt_info *info)
                tty_wakeup(tty);
 }
 
-static void dsr_change(struct slgt_info *info)
+static void dsr_change(struct slgt_info *info, unsigned short status)
 {
-       get_signals(info);
+       if (status & BIT3) {
+               info->signals |= SerialSignal_DSR;
+               info->input_signal_events.dsr_up++;
+       } else {
+               info->signals &= ~SerialSignal_DSR;
+               info->input_signal_events.dsr_down++;
+       }
        DBGISR(("dsr_change %s signals=%04X\n", info->device_name, info->signals));
        if ((info->dsr_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
                slgt_irq_off(info, IRQ_DSR);
                return;
        }
        info->icount.dsr++;
-       if (info->signals & SerialSignal_DSR)
-               info->input_signal_events.dsr_up++;
-       else
-               info->input_signal_events.dsr_down++;
        wake_up_interruptible(&info->status_event_wait_q);
        wake_up_interruptible(&info->event_wait_q);
        info->pending_bh |= BH_STATUS;
 }
 
-static void cts_change(struct slgt_info *info)
+static void cts_change(struct slgt_info *info, unsigned short status)
 {
-       get_signals(info);
+       if (status & BIT2) {
+               info->signals |= SerialSignal_CTS;
+               info->input_signal_events.cts_up++;
+       } else {
+               info->signals &= ~SerialSignal_CTS;
+               info->input_signal_events.cts_down++;
+       }
        DBGISR(("cts_change %s signals=%04X\n", info->device_name, info->signals));
        if ((info->cts_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
                slgt_irq_off(info, IRQ_CTS);
                return;
        }
        info->icount.cts++;
-       if (info->signals & SerialSignal_CTS)
-               info->input_signal_events.cts_up++;
-       else
-               info->input_signal_events.cts_down++;
        wake_up_interruptible(&info->status_event_wait_q);
        wake_up_interruptible(&info->event_wait_q);
        info->pending_bh |= BH_STATUS;
@@ -2084,20 +2094,21 @@ static void cts_change(struct slgt_info *info)
        }
 }
 
-static void dcd_change(struct slgt_info *info)
+static void dcd_change(struct slgt_info *info, unsigned short status)
 {
-       get_signals(info);
+       if (status & BIT1) {
+               info->signals |= SerialSignal_DCD;
+               info->input_signal_events.dcd_up++;
+       } else {
+               info->signals &= ~SerialSignal_DCD;
+               info->input_signal_events.dcd_down++;
+       }
        DBGISR(("dcd_change %s signals=%04X\n", info->device_name, info->signals));
        if ((info->dcd_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
                slgt_irq_off(info, IRQ_DCD);
                return;
        }
        info->icount.dcd++;
-       if (info->signals & SerialSignal_DCD) {
-               info->input_signal_events.dcd_up++;
-       } else {
-               info->input_signal_events.dcd_down++;
-       }
 #if SYNCLINK_GENERIC_HDLC
        if (info->netcount) {
                if (info->signals & SerialSignal_DCD)
@@ -2120,20 +2131,21 @@ static void dcd_change(struct slgt_info *info)
        }
 }
 
-static void ri_change(struct slgt_info *info)
+static void ri_change(struct slgt_info *info, unsigned short status)
 {
-       get_signals(info);
+       if (status & BIT0) {
+               info->signals |= SerialSignal_RI;
+               info->input_signal_events.ri_up++;
+       } else {
+               info->signals &= ~SerialSignal_RI;
+               info->input_signal_events.ri_down++;
+       }
        DBGISR(("ri_change %s signals=%04X\n", info->device_name, info->signals));
        if ((info->ri_chkcount)++ == IO_PIN_SHUTDOWN_LIMIT) {
                slgt_irq_off(info, IRQ_RI);
                return;
        }
-       info->icount.dcd++;
-       if (info->signals & SerialSignal_RI) {
-               info->input_signal_events.ri_up++;
-       } else {
-               info->input_signal_events.ri_down++;
-       }
+       info->icount.rng++;
        wake_up_interruptible(&info->status_event_wait_q);
        wake_up_interruptible(&info->event_wait_q);
        info->pending_bh |= BH_STATUS;
@@ -2184,13 +2196,13 @@ static void isr_serial(struct slgt_info *info)
        }
 
        if (status & IRQ_DSR)
-               dsr_change(info);
+               dsr_change(info, status);
        if (status & IRQ_CTS)
-               cts_change(info);
+               cts_change(info, status);
        if (status & IRQ_DCD)
-               dcd_change(info);
+               dcd_change(info, status);
        if (status & IRQ_RI)
-               ri_change(info);
+               ri_change(info, status);
 }
 
 static void isr_rdma(struct slgt_info *info)
@@ -3871,44 +3883,58 @@ static void tx_start(struct slgt_info *info)
                        slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
                        /* clear tx idle and underrun status bits */
                        wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
-
-                       if (!(rd_reg32(info, TDCSR) & BIT0)) {
-                               /* tx DMA stopped, restart tx DMA */
-                               tdma_reset(info);
-                               /* set 1st descriptor address */
-                               wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
-                               switch(info->params.mode) {
-                               case MGSL_MODE_RAW:
-                               case MGSL_MODE_MONOSYNC:
-                               case MGSL_MODE_BISYNC:
-                                       wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
-                                       break;
-                               default:
-                                       wr_reg32(info, TDCSR, BIT0); /* DMA enable */
-                               }
-                       }
-
                        if (info->params.mode == MGSL_MODE_HDLC)
                                mod_timer(&info->tx_timer, jiffies +
                                                msecs_to_jiffies(5000));
                } else {
-                       tdma_reset(info);
-                       /* set 1st descriptor address */
-                       wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
-
                        slgt_irq_off(info, IRQ_TXDATA);
                        slgt_irq_on(info, IRQ_TXIDLE);
                        /* clear tx idle status bit */
                        wr_reg16(info, SSR, IRQ_TXIDLE);
-
-                       /* enable tx DMA */
-                       wr_reg32(info, TDCSR, BIT0);
                }
-
+               tdma_start(info);
                info->tx_active = 1;
        }
 }
 
+/*
+ * start transmit DMA if inactive and there are unsent buffers
+ */
+static void tdma_start(struct slgt_info *info)
+{
+       unsigned int i;
+
+       if (rd_reg32(info, TDCSR) & BIT0)
+               return;
+
+       /* transmit DMA inactive, check for unsent buffers */
+       i = info->tbuf_start;
+       while (!desc_count(info->tbufs[i])) {
+               if (++i == info->tbuf_count)
+                       i = 0;
+               if (i == info->tbuf_current)
+                       return;
+       }
+       info->tbuf_start = i;
+
+       /* there are unsent buffers, start transmit DMA */
+
+       /* reset needed if previous error condition */
+       tdma_reset(info);
+
+       /* set 1st descriptor address */
+       wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
+       switch(info->params.mode) {
+       case MGSL_MODE_RAW:
+       case MGSL_MODE_MONOSYNC:
+       case MGSL_MODE_BISYNC:
+               wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
+               break;
+       default:
+               wr_reg32(info, TDCSR, BIT0); /* DMA enable */
+       }
+}
+
 static void tx_stop(struct slgt_info *info)
 {
        unsigned short val;
@@ -4642,8 +4668,8 @@ static unsigned int free_tbuf_count(struct slgt_info *info)
                        i=0;
        } while (i != info->tbuf_current);
 
-       /* last buffer with zero count may be in use, assume it is */
-       if (count)
+       /* if tx DMA active, last zero count buffer is in use */
+       if (count && (rd_reg32(info, TDCSR) & BIT0))
                --count;
 
        return count;