Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+46 −29
Original line number Diff line number Diff line
/*
 * $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.
 *
@@ -93,7 +93,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");
@@ -477,6 +477,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 +905,8 @@ static int write(struct tty_struct *tty,
		spin_lock_irqsave(&info->lock,flags);
		if (!info->tx_active)
		 	tx_start(info);
		else
			tdma_start(info);
		spin_unlock_irqrestore(&info->lock,flags);
 	}

@@ -3871,10 +3874,45 @@ 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);
		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)) {
				/* tx DMA stopped, restart tx DMA */
	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) {
@@ -3888,27 +3926,6 @@ static void tx_start(struct slgt_info *info)
	}
}

			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);
		}

		info->tx_active = 1;
	}
}

static void tx_stop(struct slgt_info *info)
{
	unsigned short val;
@@ -4642,8 +4659,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;