Commit bb029c67 authored by Paul Fulghum's avatar Paul Fulghum Committed by Linus Torvalds

synclink_gt: fix transmit DMA stall

Fix transmit DMA stall when write() called in window after previous
transmit DMA completes but before previous serial transmission completes.
Signed-off-by: default avatarPaul Fulghum <paulkf@microgate.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ddb437b7
/* /*
* $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. * Device driver for Microgate SyncLink GT serial adapters.
* *
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
* module identification * module identification
*/ */
static char *driver_name = "SyncLink GT"; 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_driver_name = "synclink_gt";
static char *tty_dev_prefix = "ttySLG"; static char *tty_dev_prefix = "ttySLG";
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -477,6 +477,7 @@ static void tx_set_idle(struct slgt_info *info); ...@@ -477,6 +477,7 @@ static void tx_set_idle(struct slgt_info *info);
static unsigned int free_tbuf_count(struct slgt_info *info); static unsigned int free_tbuf_count(struct slgt_info *info);
static void reset_tbufs(struct slgt_info *info); static void reset_tbufs(struct slgt_info *info);
static void tdma_reset(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 tx_load(struct slgt_info *info, const char *buf, unsigned int count);
static void get_signals(struct slgt_info *info); static void get_signals(struct slgt_info *info);
...@@ -904,6 +905,8 @@ static int write(struct tty_struct *tty, ...@@ -904,6 +905,8 @@ static int write(struct tty_struct *tty,
spin_lock_irqsave(&info->lock,flags); spin_lock_irqsave(&info->lock,flags);
if (!info->tx_active) if (!info->tx_active)
tx_start(info); tx_start(info);
else
tdma_start(info);
spin_unlock_irqrestore(&info->lock,flags); spin_unlock_irqrestore(&info->lock,flags);
} }
...@@ -3871,44 +3874,58 @@ static void tx_start(struct slgt_info *info) ...@@ -3871,44 +3874,58 @@ static void tx_start(struct slgt_info *info)
slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE); slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE);
/* clear tx idle and underrun status bits */ /* clear tx idle and underrun status bits */
wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); 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) if (info->params.mode == MGSL_MODE_HDLC)
mod_timer(&info->tx_timer, jiffies + mod_timer(&info->tx_timer, jiffies +
msecs_to_jiffies(5000)); msecs_to_jiffies(5000));
} else { } 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_off(info, IRQ_TXDATA);
slgt_irq_on(info, IRQ_TXIDLE); slgt_irq_on(info, IRQ_TXIDLE);
/* clear tx idle status bit */ /* clear tx idle status bit */
wr_reg16(info, SSR, IRQ_TXIDLE); wr_reg16(info, SSR, IRQ_TXIDLE);
/* enable tx DMA */
wr_reg32(info, TDCSR, BIT0);
} }
tdma_start(info);
info->tx_active = 1; 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) static void tx_stop(struct slgt_info *info)
{ {
unsigned short val; unsigned short val;
...@@ -4642,8 +4659,8 @@ static unsigned int free_tbuf_count(struct slgt_info *info) ...@@ -4642,8 +4659,8 @@ static unsigned int free_tbuf_count(struct slgt_info *info)
i=0; i=0;
} while (i != info->tbuf_current); } while (i != info->tbuf_current);
/* last buffer with zero count may be in use, assume it is */ /* if tx DMA active, last zero count buffer is in use */
if (count) if (count && (rd_reg32(info, TDCSR) & BIT0))
--count; --count;
return count; return count;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment