ad525x_dpot: extend write argument to 16bits
[safe/jmp/linux-2.6] / drivers / char / synclink_gt.c
index 5e25649..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>
@@ -214,6 +215,7 @@ struct slgt_desc
 #define set_desc_next(a,b) (a).next   = cpu_to_le32((unsigned int)(b))
 #define set_desc_count(a,b)(a).count  = cpu_to_le16((unsigned short)(b))
 #define set_desc_eof(a,b)  (a).status = cpu_to_le16((b) ? (le16_to_cpu((a).status) | BIT0) : (le16_to_cpu((a).status) & ~BIT0))
+#define set_desc_status(a, b) (a).status = cpu_to_le16((unsigned short)(b))
 #define desc_count(a)      (le16_to_cpu((a).count))
 #define desc_status(a)     (le16_to_cpu((a).status))
 #define desc_complete(a)   (le16_to_cpu((a).status) & BIT15)
@@ -297,6 +299,7 @@ struct slgt_info {
        u32 max_frame_size;       /* as set by device config */
 
        unsigned int rbuf_fill_level;
+       unsigned int rx_pio;
        unsigned int if_mode;
        unsigned int base_clock;
 
@@ -331,6 +334,8 @@ struct slgt_info {
        struct slgt_desc *rbufs;
        unsigned int rbuf_current;
        unsigned int rbuf_index;
+       unsigned int rbuf_fill_index;
+       unsigned short rbuf_fill_count;
 
        unsigned int tbuf_count;
        struct slgt_desc *tbufs;
@@ -463,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);
@@ -791,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;
 }
@@ -856,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;
        }
@@ -955,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);
 }
 
@@ -971,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);
 }
@@ -1007,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);
 }
 
 /*
@@ -1471,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;
 }
 
 /**
@@ -2110,6 +2100,40 @@ static void ri_change(struct slgt_info *info, unsigned short status)
        info->pending_bh |= BH_STATUS;
 }
 
+static void isr_rxdata(struct slgt_info *info)
+{
+       unsigned int count = info->rbuf_fill_count;
+       unsigned int i = info->rbuf_fill_index;
+       unsigned short reg;
+
+       while (rd_reg16(info, SSR) & IRQ_RXDATA) {
+               reg = rd_reg16(info, RDR);
+               DBGISR(("isr_rxdata %s RDR=%04X\n", info->device_name, reg));
+               if (desc_complete(info->rbufs[i])) {
+                       /* all buffers full */
+                       rx_stop(info);
+                       info->rx_restart = 1;
+                       continue;
+               }
+               info->rbufs[i].buf[count++] = (unsigned char)reg;
+               /* async mode saves status byte to buffer for each data byte */
+               if (info->params.mode == MGSL_MODE_ASYNC)
+                       info->rbufs[i].buf[count++] = (unsigned char)(reg >> 8);
+               if (count == info->rbuf_fill_level || (reg & BIT10)) {
+                       /* buffer full or end of frame */
+                       set_desc_count(info->rbufs[i], count);
+                       set_desc_status(info->rbufs[i], BIT15 | (reg >> 8));
+                       info->rbuf_fill_count = count = 0;
+                       if (++i == info->rbuf_count)
+                               i = 0;
+                       info->pending_bh |= BH_RECEIVE;
+               }
+       }
+
+       info->rbuf_fill_index = i;
+       info->rbuf_fill_count = count;
+}
+
 static void isr_serial(struct slgt_info *info)
 {
        unsigned short status = rd_reg16(info, SSR);
@@ -2122,9 +2146,11 @@ 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))
+                       isr_rxdata(info);
                if ((status & IRQ_RXBREAK) && (status & RXBREAK)) {
                        info->icount.brk++;
                        /* process break detection if tty control allows */
@@ -2141,7 +2167,8 @@ static void isr_serial(struct slgt_info *info)
        } else {
                if (status & (IRQ_TXIDLE + IRQ_TXUNDER))
                        isr_txeom(info, status);
-
+               if (info->rx_pio && (status & IRQ_RXDATA))
+                       isr_rxdata(info);
                if (status & IRQ_RXIDLE) {
                        if (status & RXIDLE)
                                info->icount.rxidle++;
@@ -2215,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 */
@@ -2236,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);
 
@@ -2642,6 +2702,10 @@ static int rx_enable(struct slgt_info *info, int enable)
                        return -EINVAL;
                }
                info->rbuf_fill_level = rbuf_fill_level;
+               if (rbuf_fill_level < 128)
+                       info->rx_pio = 1; /* PIO mode */
+               else
+                       info->rx_pio = 0; /* DMA mode */
                rx_stop(info); /* restart receiver to use new fill level */
        }
 
@@ -3099,13 +3163,16 @@ static int carrier_raised(struct tty_port *port)
        return (info->signals & SerialSignal_DCD) ? 1 : 0;
 }
 
-static void raise_dtr_rts(struct tty_port *port)
+static void dtr_rts(struct tty_port *port, int on)
 {
        unsigned long flags;
        struct slgt_info *info = container_of(port, struct slgt_info, port);
 
        spin_lock_irqsave(&info->lock,flags);
-       info->signals |= SerialSignal_RTS + SerialSignal_DTR;
+       if (on)
+               info->signals |= SerialSignal_RTS + SerialSignal_DTR;
+       else
+               info->signals &= ~(SerialSignal_RTS + SerialSignal_DTR);
        set_signals(info);
        spin_unlock_irqrestore(&info->lock,flags);
 }
@@ -3419,7 +3486,7 @@ static void add_device(struct slgt_info *info)
 
 static const struct tty_port_operations slgt_port_ops = {
        .carrier_raised = carrier_raised,
-       .raise_dtr_rts = raise_dtr_rts,
+       .dtr_rts = dtr_rts,
 };
 
 /*
@@ -3841,15 +3908,27 @@ static void rx_start(struct slgt_info *info)
        rdma_reset(info);
        reset_rbufs(info);
 
-       /* set 1st descriptor address */
-       wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
-
-       if (info->params.mode != MGSL_MODE_ASYNC) {
-               /* enable rx DMA and DMA interrupt */
-               wr_reg32(info, RDCSR, (BIT2 + BIT0));
+       if (info->rx_pio) {
+               /* rx request when rx FIFO not empty */
+               wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) & ~BIT14));
+               slgt_irq_on(info, IRQ_RXDATA);
+               if (info->params.mode == MGSL_MODE_ASYNC) {
+                       /* enable saving of rx status */
+                       wr_reg32(info, RDCSR, BIT6);
+               }
        } else {
-               /* enable saving of rx status, rx DMA and DMA interrupt */
-               wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
+               /* rx request when rx FIFO half full */
+               wr_reg16(info, SCR, (unsigned short)(rd_reg16(info, SCR) | BIT14));
+               /* set 1st descriptor address */
+               wr_reg32(info, RDDAR, info->rbufs[0].pdesc);
+
+               if (info->params.mode != MGSL_MODE_ASYNC) {
+                       /* enable rx DMA and DMA interrupt */
+                       wr_reg32(info, RDCSR, (BIT2 + BIT0));
+               } else {
+                       /* enable saving of rx status, rx DMA and DMA interrupt */
+                       wr_reg32(info, RDCSR, (BIT6 + BIT2 + BIT0));
+               }
        }
 
        slgt_irq_on(info, IRQ_RXOVER);
@@ -3869,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) {
@@ -3886,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;
@@ -4467,6 +4515,8 @@ static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last
 static void reset_rbufs(struct slgt_info *info)
 {
        free_rbufs(info, 0, info->rbuf_count - 1);
+       info->rbuf_fill_index = 0;
+       info->rbuf_fill_count = 0;
 }
 
 /*
@@ -4721,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);
@@ -4757,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)
@@ -4883,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 */
@@ -4942,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