提交 3089f381 编写于 作者: G Guennadi Liakhovetski 提交者: Paul Mundt

SH: extend SCI DMA support to work on SCIFA ports

SCIFA ports have additional bits to control DMA requests and they must have
respective interrupt sources enabled, as the datasheet suggests, the only way
to avoid actually taking interrupts in addition to DMA events is by masking the
IRQ on the CPU.
Signed-off-by: NGuennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: NPaul Mundt <lethal@linux-sh.org>
上级 b2623a61
...@@ -106,6 +106,7 @@ struct sci_port { ...@@ -106,6 +106,7 @@ struct sci_port {
struct work_struct work_tx; struct work_struct work_tx;
struct work_struct work_rx; struct work_struct work_rx;
struct timer_list rx_timer; struct timer_list rx_timer;
unsigned int rx_timeout;
#endif #endif
}; };
...@@ -673,22 +674,22 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr) ...@@ -673,22 +674,22 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
struct sci_port *s = to_sci_port(port); struct sci_port *s = to_sci_port(port);
if (s->chan_rx) { if (s->chan_rx) {
unsigned long tout;
u16 scr = sci_in(port, SCSCR); u16 scr = sci_in(port, SCSCR);
u16 ssr = sci_in(port, SCxSR); u16 ssr = sci_in(port, SCxSR);
/* Disable future Rx interrupts */ /* Disable future Rx interrupts */
sci_out(port, SCSCR, scr & ~SCI_CTRL_FLAGS_RIE); if (port->type == PORT_SCIFA) {
disable_irq_nosync(irq);
scr |= 0x4000;
} else {
scr &= ~SCI_CTRL_FLAGS_RIE;
}
sci_out(port, SCSCR, scr);
/* Clear current interrupt */ /* Clear current interrupt */
sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port))); sci_out(port, SCxSR, ssr & ~(1 | SCxSR_RDxF(port)));
/* Calculate delay for 1.5 DMA buffers */ dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n",
tout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 / jiffies, s->rx_timeout);
port->fifosize / 2; mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
dev_dbg(port->dev, "Rx IRQ: setup timeout in %lu ms\n",
tout * 1000 / HZ);
if (tout < 2)
tout = 2;
mod_timer(&s->rx_timer, jiffies + tout);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -925,13 +926,17 @@ static void sci_dma_tx_complete(void *arg) ...@@ -925,13 +926,17 @@ static void sci_dma_tx_complete(void *arg)
s->cookie_tx = -EINVAL; s->cookie_tx = -EINVAL;
s->desc_tx = NULL; s->desc_tx = NULL;
spin_unlock_irqrestore(&port->lock, flags);
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port); uart_write_wakeup(port);
if (uart_circ_chars_pending(xmit)) if (!uart_circ_empty(xmit)) {
schedule_work(&s->work_tx); schedule_work(&s->work_tx);
} else if (port->type == PORT_SCIFA) {
u16 ctrl = sci_in(port, SCSCR);
sci_out(port, SCSCR, ctrl & ~SCI_CTRL_FLAGS_TIE);
}
spin_unlock_irqrestore(&port->lock, flags);
} }
/* Locking: called with port lock held */ /* Locking: called with port lock held */
...@@ -975,13 +980,13 @@ static void sci_dma_rx_complete(void *arg) ...@@ -975,13 +980,13 @@ static void sci_dma_rx_complete(void *arg)
unsigned long flags; unsigned long flags;
int count; int count;
dev_dbg(port->dev, "%s(%d)\n", __func__, port->line); dev_dbg(port->dev, "%s(%d) active #%d\n", __func__, port->line, s->active_rx);
spin_lock_irqsave(&port->lock, flags); spin_lock_irqsave(&port->lock, flags);
count = sci_dma_rx_push(s, tty, s->buf_len_rx); count = sci_dma_rx_push(s, tty, s->buf_len_rx);
mod_timer(&s->rx_timer, jiffies + msecs_to_jiffies(5)); mod_timer(&s->rx_timer, jiffies + s->rx_timeout);
spin_unlock_irqrestore(&port->lock, flags); spin_unlock_irqrestore(&port->lock, flags);
...@@ -1053,6 +1058,8 @@ static void sci_submit_rx(struct sci_port *s) ...@@ -1053,6 +1058,8 @@ static void sci_submit_rx(struct sci_port *s)
sci_rx_dma_release(s, true); sci_rx_dma_release(s, true);
return; return;
} }
dev_dbg(s->port.dev, "%s(): cookie %d to #%d\n", __func__,
s->cookie_rx[i], i);
} }
s->active_rx = s->cookie_rx[0]; s->active_rx = s->cookie_rx[0];
...@@ -1110,10 +1117,10 @@ static void work_fn_rx(struct work_struct *work) ...@@ -1110,10 +1117,10 @@ static void work_fn_rx(struct work_struct *work)
return; return;
} }
dev_dbg(port->dev, "%s: cookie %d #%d\n", __func__,
s->cookie_rx[new], new);
s->active_rx = s->cookie_rx[!new]; s->active_rx = s->cookie_rx[!new];
dev_dbg(port->dev, "%s: cookie %d #%d, new active #%d\n", __func__,
s->cookie_rx[new], new, s->active_rx);
} }
static void work_fn_tx(struct work_struct *work) static void work_fn_tx(struct work_struct *work)
...@@ -1175,23 +1182,28 @@ static void work_fn_tx(struct work_struct *work) ...@@ -1175,23 +1182,28 @@ static void work_fn_tx(struct work_struct *work)
static void sci_start_tx(struct uart_port *port) static void sci_start_tx(struct uart_port *port)
{ {
struct sci_port *s = to_sci_port(port);
unsigned short ctrl; unsigned short ctrl;
#ifdef CONFIG_SERIAL_SH_SCI_DMA #ifdef CONFIG_SERIAL_SH_SCI_DMA
struct sci_port *s = to_sci_port(port); if (port->type == PORT_SCIFA) {
u16 new, scr = sci_in(port, SCSCR);
if (s->chan_tx) { if (s->chan_tx)
if (!uart_circ_empty(&s->port.state->xmit) && s->cookie_tx < 0) new = scr | 0x8000;
schedule_work(&s->work_tx); else
new = scr & ~0x8000;
return; if (new != scr)
sci_out(port, SCSCR, new);
} }
if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) &&
s->cookie_tx < 0)
schedule_work(&s->work_tx);
#endif #endif
if (!s->chan_tx || port->type == PORT_SCIFA) {
/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR); ctrl = sci_in(port, SCSCR);
ctrl |= SCI_CTRL_FLAGS_TIE; sci_out(port, SCSCR, ctrl | SCI_CTRL_FLAGS_TIE);
sci_out(port, SCSCR, ctrl); }
} }
static void sci_stop_tx(struct uart_port *port) static void sci_stop_tx(struct uart_port *port)
...@@ -1200,6 +1212,8 @@ static void sci_stop_tx(struct uart_port *port) ...@@ -1200,6 +1212,8 @@ static void sci_stop_tx(struct uart_port *port)
/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR); ctrl = sci_in(port, SCSCR);
if (port->type == PORT_SCIFA)
ctrl &= ~0x8000;
ctrl &= ~SCI_CTRL_FLAGS_TIE; ctrl &= ~SCI_CTRL_FLAGS_TIE;
sci_out(port, SCSCR, ctrl); sci_out(port, SCSCR, ctrl);
} }
...@@ -1210,6 +1224,8 @@ static void sci_start_rx(struct uart_port *port) ...@@ -1210,6 +1224,8 @@ static void sci_start_rx(struct uart_port *port)
/* Set RIE (Receive Interrupt Enable) bit in SCSCR */ /* Set RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl |= sci_in(port, SCSCR); ctrl |= sci_in(port, SCSCR);
if (port->type == PORT_SCIFA)
ctrl &= ~0x4000;
sci_out(port, SCSCR, ctrl); sci_out(port, SCSCR, ctrl);
} }
...@@ -1219,6 +1235,8 @@ static void sci_stop_rx(struct uart_port *port) ...@@ -1219,6 +1235,8 @@ static void sci_stop_rx(struct uart_port *port)
/* Clear RIE (Receive Interrupt Enable) bit in SCSCR */ /* Clear RIE (Receive Interrupt Enable) bit in SCSCR */
ctrl = sci_in(port, SCSCR); ctrl = sci_in(port, SCSCR);
if (port->type == PORT_SCIFA)
ctrl &= ~0x4000;
ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE); ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE);
sci_out(port, SCSCR, ctrl); sci_out(port, SCSCR, ctrl);
} }
...@@ -1253,8 +1271,12 @@ static void rx_timer_fn(unsigned long arg) ...@@ -1253,8 +1271,12 @@ static void rx_timer_fn(unsigned long arg)
{ {
struct sci_port *s = (struct sci_port *)arg; struct sci_port *s = (struct sci_port *)arg;
struct uart_port *port = &s->port; struct uart_port *port = &s->port;
u16 scr = sci_in(port, SCSCR); u16 scr = sci_in(port, SCSCR);
if (port->type == PORT_SCIFA) {
scr &= ~0x4000;
enable_irq(s->irqs[1]);
}
sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE); sci_out(port, SCSCR, scr | SCI_CTRL_FLAGS_RIE);
dev_dbg(port->dev, "DMA Rx timed out\n"); dev_dbg(port->dev, "DMA Rx timed out\n");
schedule_work(&s->work_rx); schedule_work(&s->work_rx);
...@@ -1404,8 +1426,12 @@ static void sci_shutdown(struct uart_port *port) ...@@ -1404,8 +1426,12 @@ static void sci_shutdown(struct uart_port *port)
static void sci_set_termios(struct uart_port *port, struct ktermios *termios, static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
struct ktermios *old) struct ktermios *old)
{ {
#ifdef CONFIG_SERIAL_SH_SCI_DMA
struct sci_port *s = to_sci_port(port);
#endif
unsigned int status, baud, smr_val, max_baud; unsigned int status, baud, smr_val, max_baud;
int t = -1; int t = -1;
u16 scfcr = 0;
/* /*
* earlyprintk comes here early on with port->uartclk set to zero. * earlyprintk comes here early on with port->uartclk set to zero.
...@@ -1428,7 +1454,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -1428,7 +1454,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */
if (port->type != PORT_SCI) if (port->type != PORT_SCI)
sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); sci_out(port, SCFCR, scfcr | SCFCR_RFRST | SCFCR_TFRST);
smr_val = sci_in(port, SCSMR) & 3; smr_val = sci_in(port, SCSMR) & 3;
if ((termios->c_cflag & CSIZE) == CS7) if ((termios->c_cflag & CSIZE) == CS7)
...@@ -1459,10 +1485,32 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, ...@@ -1459,10 +1485,32 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios,
} }
sci_init_pins(port, termios->c_cflag); sci_init_pins(port, termios->c_cflag);
sci_out(port, SCFCR, (termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0); sci_out(port, SCFCR, scfcr | ((termios->c_cflag & CRTSCTS) ? SCFCR_MCE : 0));
sci_out(port, SCSCR, SCSCR_INIT(port)); sci_out(port, SCSCR, SCSCR_INIT(port));
#ifdef CONFIG_SERIAL_SH_SCI_DMA
/*
* Calculate delay for 1.5 DMA buffers: see
* drivers/serial/serial_core.c::uart_update_timeout(). With 10 bits
* (CS8), 250Hz, 115200 baud and 64 bytes FIFO, the above function
* calculates 1 jiffie for the data plus 5 jiffies for the "slop(e)."
* Then below we calculate 3 jiffies (12ms) for 1.5 DMA buffers (3 FIFO
* sizes), but it has been found out experimentally, that this is not
* enough: the driver too often needlessly runs on a DMA timeout. 20ms
* as a minimum seem to work perfectly.
*/
if (s->chan_rx) {
s->rx_timeout = (port->timeout - HZ / 50) * s->buf_len_rx * 3 /
port->fifosize / 2;
dev_dbg(port->dev,
"DMA Rx t-out %ums, tty t-out %u jiffies\n",
s->rx_timeout * 1000 / HZ, port->timeout);
if (s->rx_timeout < msecs_to_jiffies(20))
s->rx_timeout = msecs_to_jiffies(20);
}
#endif
if ((termios->c_cflag & CREAD) != 0) if ((termios->c_cflag & CREAD) != 0)
sci_start_rx(port); sci_start_rx(port);
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册