ad525x_dpot: extend write argument to 16bits
[safe/jmp/linux-2.6] / drivers / char / synclink_gt.c
index 1386625..4561ce2 100644 (file)
@@ -62,6 +62,7 @@
 #include <linux/mm.h>
 #include <linux/seq_file.h>
 #include <linux/slab.h>
+#include <linux/smp_lock.h>
 #include <linux/netdevice.h>
 #include <linux/vmalloc.h>
 #include <linux/init.h>
@@ -467,8 +468,7 @@ static unsigned int free_tbuf_count(struct slgt_info *info);
 static unsigned int tbuf_bytes(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 bool tx_load(struct slgt_info *info, const char *buf, unsigned int count);
 
 static void get_signals(struct slgt_info *info);
 static void set_signals(struct slgt_info *info);
@@ -795,55 +795,50 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
        }
 }
 
+static void update_tx_timer(struct slgt_info *info)
+{
+       /*
+        * use worst case speed of 1200bps to calculate transmit timeout
+        * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
+        */
+       if (info->params.mode == MGSL_MODE_HDLC) {
+               int timeout  = (tbuf_bytes(info) * 7) + 1000;
+               mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
+       }
+}
+
 static int write(struct tty_struct *tty,
                 const unsigned char *buf, int count)
 {
        int ret = 0;
        struct slgt_info *info = tty->driver_data;
        unsigned long flags;
-       unsigned int bufs_needed;
 
        if (sanity_check(info, tty->name, "write"))
-               goto cleanup;
+               return -EIO;
+
        DBGINFO(("%s write count=%d\n", info->device_name, count));
 
-       if (!info->tx_buf)
-               goto cleanup;
+       if (!info->tx_buf || (count > info->max_frame_size))
+               return -EIO;
 
-       if (count > info->max_frame_size) {
-               ret = -EIO;
-               goto cleanup;
-       }
+       if (!count || tty->stopped || tty->hw_stopped)
+               return 0;
 
-       if (!count)
-               goto cleanup;
+       spin_lock_irqsave(&info->lock, flags);
 
-       if (!info->tx_active && info->tx_count) {
+       if (info->tx_count) {
                /* send accumulated data from send_char() */
-               tx_load(info, info->tx_buf, info->tx_count);
-               goto start;
+               if (!tx_load(info, info->tx_buf, info->tx_count))
+                       goto cleanup;
+               info->tx_count = 0;
        }
-       bufs_needed = (count/DMABUFSIZE);
-       if (count % DMABUFSIZE)
-               ++bufs_needed;
-       if (bufs_needed > free_tbuf_count(info))
-               goto cleanup;
 
-       ret = info->tx_count = count;
-       tx_load(info, buf, count);
-       goto start;
-
-start:
-       if (info->tx_count && !tty->stopped && !tty->hw_stopped) {
-               spin_lock_irqsave(&info->lock,flags);
-               if (!info->tx_active)
-                       tx_start(info);
-               else
-                       tdma_start(info);
-               spin_unlock_irqrestore(&info->lock,flags);
-       }
+       if (tx_load(info, buf, count))
+               ret = count;
 
 cleanup:
+       spin_unlock_irqrestore(&info->lock, flags);
        DBGINFO(("%s write rc=%d\n", info->device_name, ret));
        return ret;
 }
@@ -860,7 +855,7 @@ static int put_char(struct tty_struct *tty, unsigned char ch)
        if (!info->tx_buf)
                return 0;
        spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_active && (info->tx_count < info->max_frame_size)) {
+       if (info->tx_count < info->max_frame_size) {
                info->tx_buf[info->tx_count++] = ch;
                ret = 1;
        }
@@ -959,10 +954,8 @@ static void flush_chars(struct tty_struct *tty)
        DBGINFO(("%s flush_chars start transmit\n", info->device_name));
 
        spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_active && info->tx_count) {
-               tx_load(info, info->tx_buf,info->tx_count);
-               tx_start(info);
-       }
+       if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
+               info->tx_count = 0;
        spin_unlock_irqrestore(&info->lock,flags);
 }
 
@@ -975,10 +968,9 @@ static void flush_buffer(struct tty_struct *tty)
                return;
        DBGINFO(("%s flush_buffer\n", info->device_name));
 
-       spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_active)
-               info->tx_count = 0;
-       spin_unlock_irqrestore(&info->lock,flags);
+       spin_lock_irqsave(&info->lock, flags);
+       info->tx_count = 0;
+       spin_unlock_irqrestore(&info->lock, flags);
 
        tty_wakeup(tty);
 }
@@ -1011,12 +1003,10 @@ static void tx_release(struct tty_struct *tty)
        if (sanity_check(info, tty->name, "tx_release"))
                return;
        DBGINFO(("%s tx_release\n", info->device_name));
-       spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_active && info->tx_count) {
-               tx_load(info, info->tx_buf, info->tx_count);
-               tx_start(info);
-       }
-       spin_unlock_irqrestore(&info->lock,flags);
+       spin_lock_irqsave(&info->lock, flags);
+       if (info->tx_count && tx_load(info, info->tx_buf, info->tx_count))
+               info->tx_count = 0;
+       spin_unlock_irqrestore(&info->lock, flags);
 }
 
 /*
@@ -1475,40 +1465,36 @@ static int hdlcdev_attach(struct net_device *dev, unsigned short encoding,
  *
  * skb  socket buffer containing HDLC frame
  * dev  pointer to network device structure
- *
- * returns 0 if success, otherwise error code
  */
-static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t hdlcdev_xmit(struct sk_buff *skb,
+                                     struct net_device *dev)
 {
        struct slgt_info *info = dev_to_port(dev);
        unsigned long flags;
 
        DBGINFO(("%s hdlc_xmit\n", dev->name));
 
+       if (!skb->len)
+               return NETDEV_TX_OK;
+
        /* stop sending until this frame completes */
        netif_stop_queue(dev);
 
-       /* copy data to device buffers */
-       info->tx_count = skb->len;
-       tx_load(info, skb->data, skb->len);
-
        /* update network statistics */
        dev->stats.tx_packets++;
        dev->stats.tx_bytes += skb->len;
 
-       /* done with socket buffer, so free it */
-       dev_kfree_skb(skb);
-
        /* save start time for transmit timeout detection */
        dev->trans_start = jiffies;
 
-       /* start hardware transmitter if necessary */
-       spin_lock_irqsave(&info->lock,flags);
-       if (!info->tx_active)
-               tx_start(info);
-       spin_unlock_irqrestore(&info->lock,flags);
+       spin_lock_irqsave(&info->lock, flags);
+       tx_load(info, skb->data, skb->len);
+       spin_unlock_irqrestore(&info->lock, flags);
 
-       return 0;
+       /* done with socket buffer, so free it */
+       dev_kfree_skb(skb);
+
+       return NETDEV_TX_OK;
 }
 
 /**
@@ -2160,7 +2146,7 @@ static void isr_serial(struct slgt_info *info)
 
        if (info->params.mode == MGSL_MODE_ASYNC) {
                if (status & IRQ_TXIDLE) {
-                       if (info->tx_count)
+                       if (info->tx_active)
                                isr_txeom(info, status);
                }
                if (info->rx_pio && (status & IRQ_RXDATA))
@@ -2256,13 +2242,42 @@ static void isr_tdma(struct slgt_info *info)
        }
 }
 
+/*
+ * return true if there are unsent tx DMA buffers, otherwise false
+ *
+ * if there are unsent buffers then info->tbuf_start
+ * is set to index of first unsent buffer
+ */
+static bool unsent_tbufs(struct slgt_info *info)
+{
+       unsigned int i = info->tbuf_current;
+       bool rc = false;
+
+       /*
+        * search backwards from last loaded buffer (precedes tbuf_current)
+        * for first unsent buffer (desc_count > 0)
+        */
+
+       do {
+               if (i)
+                       i--;
+               else
+                       i = info->tbuf_count - 1;
+               if (!desc_count(info->tbufs[i]))
+                       break;
+               info->tbuf_start = i;
+               rc = true;
+       } while (i != info->tbuf_current);
+
+       return rc;
+}
+
 static void isr_txeom(struct slgt_info *info, unsigned short status)
 {
        DBGISR(("%s txeom status=%04x\n", info->device_name, status));
 
        slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
        tdma_reset(info);
-       reset_tbufs(info);
        if (status & IRQ_TXUNDER) {
                unsigned short val = rd_reg16(info, TCR);
                wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
@@ -2277,8 +2292,12 @@ static void isr_txeom(struct slgt_info *info, unsigned short status)
                                info->icount.txok++;
                }
 
+               if (unsent_tbufs(info)) {
+                       tx_start(info);
+                       update_tx_timer(info);
+                       return;
+               }
                info->tx_active = false;
-               info->tx_count = 0;
 
                del_timer(&info->tx_timer);
 
@@ -3929,7 +3948,7 @@ static void tx_start(struct slgt_info *info)
                info->tx_enabled = true;
        }
 
-       if (info->tx_count) {
+       if (desc_count(info->tbufs[info->tbuf_start])) {
                info->drop_rts_on_tx_done = false;
 
                if (info->params.mode != MGSL_MODE_ASYNC) {
@@ -3946,50 +3965,19 @@ 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 (info->params.mode == MGSL_MODE_HDLC)
-                               mod_timer(&info->tx_timer, jiffies +
-                                               msecs_to_jiffies(5000));
                } else {
                        slgt_irq_off(info, IRQ_TXDATA);
                        slgt_irq_on(info, IRQ_TXIDLE);
                        /* clear tx idle status bit */
                        wr_reg16(info, SSR, IRQ_TXIDLE);
                }
-               tdma_start(info);
+               /* set 1st descriptor address and start DMA */
+               wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
+               wr_reg32(info, TDCSR, BIT2 + BIT0);
                info->tx_active = true;
        }
 }
 
-/*
- * 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);
-       wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
-}
-
 static void tx_stop(struct slgt_info *info)
 {
        unsigned short val;
@@ -4783,25 +4771,36 @@ static unsigned int tbuf_bytes(struct slgt_info *info)
 }
 
 /*
- * load transmit DMA buffer(s) with data
+ * load data into transmit DMA buffer ring and start transmitter if needed
+ * return true if data accepted, otherwise false (buffers full)
  */
-static void tx_load(struct slgt_info *info, const char *buf, unsigned int size)
+static bool tx_load(struct slgt_info *info, const char *buf, unsigned int size)
 {
        unsigned short count;
        unsigned int i;
        struct slgt_desc *d;
 
-       if (size == 0)
-               return;
+       /* check required buffer space */
+       if (DIV_ROUND_UP(size, DMABUFSIZE) > free_tbuf_count(info))
+               return false;
 
        DBGDATA(info, buf, size, "tx");
 
+       /*
+        * copy data to one or more DMA buffers in circular ring
+        * tbuf_start   = first buffer for this data
+        * tbuf_current = next free buffer
+        *
+        * Copy all data before making data visible to DMA controller by
+        * setting descriptor count of the first buffer.
+        * This prevents an active DMA controller from reading the first DMA
+        * buffers of a frame and stopping before the final buffers are filled.
+        */
+
        info->tbuf_start = i = info->tbuf_current;
 
        while (size) {
                d = &info->tbufs[i];
-               if (++i == info->tbuf_count)
-                       i = 0;
 
                count = (unsigned short)((size > DMABUFSIZE) ? DMABUFSIZE : size);
                memcpy(d->buf, buf, count);
@@ -4819,11 +4818,27 @@ static void tx_load(struct slgt_info *info, const char *buf, unsigned int size)
                else
                        set_desc_eof(*d, 0);
 
-               set_desc_count(*d, count);
+               /* set descriptor count for all but first buffer */
+               if (i != info->tbuf_start)
+                       set_desc_count(*d, count);
                d->buf_count = count;
+
+               if (++i == info->tbuf_count)
+                       i = 0;
        }
 
        info->tbuf_current = i;
+
+       /* set first buffer count to make new data visible to DMA controller */
+       d = &info->tbufs[info->tbuf_start];
+       set_desc_count(*d, d->buf_count);
+
+       /* start transmitter if needed and update transmit timeout */
+       if (!info->tx_active)
+               tx_start(info);
+       update_tx_timer(info);
+
+       return true;
 }
 
 static int register_test(struct slgt_info *info)
@@ -4945,9 +4960,7 @@ static int loopback_test(struct slgt_info *info)
        spin_lock_irqsave(&info->lock,flags);
        async_mode(info);
        rx_start(info);
-       info->tx_count = count;
        tx_load(info, buf, count);
-       tx_start(info);
        spin_unlock_irqrestore(&info->lock, flags);
 
        /* wait for receive complete */
@@ -5004,8 +5017,7 @@ static void tx_timeout(unsigned long context)
                info->icount.txtimeout++;
        }
        spin_lock_irqsave(&info->lock,flags);
-       info->tx_active = false;
-       info->tx_count = 0;
+       tx_stop(info);
        spin_unlock_irqrestore(&info->lock,flags);
 
 #if SYNCLINK_GENERIC_HDLC