aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/synclink_gt.c
diff options
context:
space:
mode:
authorPaul Fulghum <paulkf@microgate.com>2009-06-24 13:34:51 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-06-24 12:55:50 -0400
commitce89294c056805019d8369b3b74bb52ef51b4708 (patch)
tree533da71951aa6e6ccd88cd2569f4640836adea90 /drivers/char/synclink_gt.c
parent2a13373cf84477460365c32842cda9a6374b845d (diff)
synclink_gt: fix transmit race and timeout
Fix race condition when adding transmit data to active DMA buffer ring that can cause transmit stall. Update transmit timeout when adding data to active DMA buffer ring. Base transmit timeout on amount of buffered data instead of using fixed value. Signed-off-by: Paul Fulghum <paulkf@microgate.com> Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char/synclink_gt.c')
-rw-r--r--drivers/char/synclink_gt.c72
1 files changed, 30 insertions, 42 deletions
diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c
index 1386625fc4ca..a2e67e6df3a1 100644
--- a/drivers/char/synclink_gt.c
+++ b/drivers/char/synclink_gt.c
@@ -467,7 +467,6 @@ static unsigned int free_tbuf_count(struct slgt_info *info);
467static unsigned int tbuf_bytes(struct slgt_info *info); 467static unsigned int tbuf_bytes(struct slgt_info *info);
468static void reset_tbufs(struct slgt_info *info); 468static void reset_tbufs(struct slgt_info *info);
469static void tdma_reset(struct slgt_info *info); 469static void tdma_reset(struct slgt_info *info);
470static void tdma_start(struct slgt_info *info);
471static void tx_load(struct slgt_info *info, const char *buf, unsigned int count); 470static void tx_load(struct slgt_info *info, const char *buf, unsigned int count);
472 471
473static void get_signals(struct slgt_info *info); 472static void get_signals(struct slgt_info *info);
@@ -795,6 +794,18 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios)
795 } 794 }
796} 795}
797 796
797static void update_tx_timer(struct slgt_info *info)
798{
799 /*
800 * use worst case speed of 1200bps to calculate transmit timeout
801 * based on data in buffers (tbuf_bytes) and FIFO (128 bytes)
802 */
803 if (info->params.mode == MGSL_MODE_HDLC) {
804 int timeout = (tbuf_bytes(info) * 7) + 1000;
805 mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout));
806 }
807}
808
798static int write(struct tty_struct *tty, 809static int write(struct tty_struct *tty,
799 const unsigned char *buf, int count) 810 const unsigned char *buf, int count)
800{ 811{
@@ -838,8 +849,18 @@ start:
838 spin_lock_irqsave(&info->lock,flags); 849 spin_lock_irqsave(&info->lock,flags);
839 if (!info->tx_active) 850 if (!info->tx_active)
840 tx_start(info); 851 tx_start(info);
841 else 852 else if (!(rd_reg32(info, TDCSR) & BIT0)) {
842 tdma_start(info); 853 /* transmit still active but transmit DMA stopped */
854 unsigned int i = info->tbuf_current;
855 if (!i)
856 i = info->tbuf_count;
857 i--;
858 /* if DMA buf unsent must try later after tx idle */
859 if (desc_count(info->tbufs[i]))
860 ret = 0;
861 }
862 if (ret > 0)
863 update_tx_timer(info);
843 spin_unlock_irqrestore(&info->lock,flags); 864 spin_unlock_irqrestore(&info->lock,flags);
844 } 865 }
845 866
@@ -1502,10 +1523,9 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev)
1502 /* save start time for transmit timeout detection */ 1523 /* save start time for transmit timeout detection */
1503 dev->trans_start = jiffies; 1524 dev->trans_start = jiffies;
1504 1525
1505 /* start hardware transmitter if necessary */
1506 spin_lock_irqsave(&info->lock,flags); 1526 spin_lock_irqsave(&info->lock,flags);
1507 if (!info->tx_active) 1527 tx_start(info);
1508 tx_start(info); 1528 update_tx_timer(info);
1509 spin_unlock_irqrestore(&info->lock,flags); 1529 spin_unlock_irqrestore(&info->lock,flags);
1510 1530
1511 return 0; 1531 return 0;
@@ -3946,50 +3966,19 @@ static void tx_start(struct slgt_info *info)
3946 slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE); 3966 slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
3947 /* clear tx idle and underrun status bits */ 3967 /* clear tx idle and underrun status bits */
3948 wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); 3968 wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER));
3949 if (info->params.mode == MGSL_MODE_HDLC)
3950 mod_timer(&info->tx_timer, jiffies +
3951 msecs_to_jiffies(5000));
3952 } else { 3969 } else {
3953 slgt_irq_off(info, IRQ_TXDATA); 3970 slgt_irq_off(info, IRQ_TXDATA);
3954 slgt_irq_on(info, IRQ_TXIDLE); 3971 slgt_irq_on(info, IRQ_TXIDLE);
3955 /* clear tx idle status bit */ 3972 /* clear tx idle status bit */
3956 wr_reg16(info, SSR, IRQ_TXIDLE); 3973 wr_reg16(info, SSR, IRQ_TXIDLE);
3957 } 3974 }
3958 tdma_start(info); 3975 /* set 1st descriptor address and start DMA */
3976 wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
3977 wr_reg32(info, TDCSR, BIT2 + BIT0);
3959 info->tx_active = true; 3978 info->tx_active = true;
3960 } 3979 }
3961} 3980}
3962 3981
3963/*
3964 * start transmit DMA if inactive and there are unsent buffers
3965 */
3966static void tdma_start(struct slgt_info *info)
3967{
3968 unsigned int i;
3969
3970 if (rd_reg32(info, TDCSR) & BIT0)
3971 return;
3972
3973 /* transmit DMA inactive, check for unsent buffers */
3974 i = info->tbuf_start;
3975 while (!desc_count(info->tbufs[i])) {
3976 if (++i == info->tbuf_count)
3977 i = 0;
3978 if (i == info->tbuf_current)
3979 return;
3980 }
3981 info->tbuf_start = i;
3982
3983 /* there are unsent buffers, start transmit DMA */
3984
3985 /* reset needed if previous error condition */
3986 tdma_reset(info);
3987
3988 /* set 1st descriptor address */
3989 wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
3990 wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
3991}
3992
3993static void tx_stop(struct slgt_info *info) 3982static void tx_stop(struct slgt_info *info)
3994{ 3983{
3995 unsigned short val; 3984 unsigned short val;
@@ -5004,8 +4993,7 @@ static void tx_timeout(unsigned long context)
5004 info->icount.txtimeout++; 4993 info->icount.txtimeout++;
5005 } 4994 }
5006 spin_lock_irqsave(&info->lock,flags); 4995 spin_lock_irqsave(&info->lock,flags);
5007 info->tx_active = false; 4996 tx_stop(info);
5008 info->tx_count = 0;
5009 spin_unlock_irqrestore(&info->lock,flags); 4997 spin_unlock_irqrestore(&info->lock,flags);
5010 4998
5011#if SYNCLINK_GENERIC_HDLC 4999#if SYNCLINK_GENERIC_HDLC