summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/sh-sci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/sh-sci.c')
-rw-r--r--drivers/tty/serial/sh-sci.c115
1 files changed, 90 insertions, 25 deletions
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index caa09a0c48f4..7c9457962a3d 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -588,14 +588,28 @@ static void sci_start_tx(struct uart_port *port)
if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) &&
dma_submit_error(s->cookie_tx)) {
+ if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
+ /* Switch irq from SCIF to DMA */
+ disable_irq(s->irqs[SCIx_TXI_IRQ]);
+
s->cookie_tx = 0;
schedule_work(&s->work_tx);
}
#endif
- if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+ if (!s->chan_tx || s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE ||
+ port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
/* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
ctrl = serial_port_in(port, SCSCR);
+
+ /*
+ * For SCI, TE (transmit enable) must be set after setting TIE
+ * (transmit interrupt enable) or in the same instruction to start
+ * the transmit process.
+ */
+ if (port->type == PORT_SCI)
+ ctrl |= SCSCR_TE;
+
serial_port_out(port, SCSCR, ctrl | SCSCR_TIE);
}
}
@@ -833,6 +847,11 @@ static void sci_transmit_chars(struct uart_port *port)
} else if (!uart_circ_empty(xmit) && !stopped) {
c = xmit->buf[xmit->tail];
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ } else if (port->type == PORT_SCI && uart_circ_empty(xmit)) {
+ ctrl = serial_port_in(port, SCSCR);
+ ctrl &= ~SCSCR_TE;
+ serial_port_out(port, SCSCR, ctrl);
+ return;
} else {
break;
}
@@ -846,9 +865,16 @@ static void sci_transmit_chars(struct uart_port *port)
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
- if (uart_circ_empty(xmit))
- sci_stop_tx(port);
+ if (uart_circ_empty(xmit)) {
+ if (port->type == PORT_SCI) {
+ ctrl = serial_port_in(port, SCSCR);
+ ctrl &= ~SCSCR_TIE;
+ ctrl |= SCSCR_TEIE;
+ serial_port_out(port, SCSCR, ctrl);
+ }
+ sci_stop_tx(port);
+ }
}
static void sci_receive_chars(struct uart_port *port)
@@ -1192,9 +1218,15 @@ static void sci_dma_tx_complete(void *arg)
schedule_work(&s->work_tx);
} else {
s->cookie_tx = -EINVAL;
- if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
+ s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
u16 ctrl = serial_port_in(port, SCSCR);
serial_port_out(port, SCSCR, ctrl & ~SCSCR_TIE);
+ if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
+ /* Switch irq from DMA to SCIF */
+ dmaengine_pause(s->chan_tx_saved);
+ enable_irq(s->irqs[SCIx_TXI_IRQ]);
+ }
}
}
@@ -1266,9 +1298,13 @@ static void sci_dma_rx_reenable_irq(struct sci_port *s)
/* Direct new serial port interrupts back to CPU */
scr = serial_port_in(port, SCSCR);
- if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
- scr &= ~SCSCR_RDRQE;
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
+ s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
enable_irq(s->irqs[SCIx_RXI_IRQ]);
+ if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
+ scif_set_rtrg(port, s->rx_trigger);
+ else
+ scr &= ~SCSCR_RDRQE;
}
serial_port_out(port, SCSCR, scr | SCSCR_RIE);
}
@@ -1505,7 +1541,8 @@ static enum hrtimer_restart sci_dma_rx_timer_fn(struct hrtimer *t)
tty_flip_buffer_push(&port->state->port);
}
- if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
+ s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
sci_dma_rx_submit(s, true);
sci_dma_rx_reenable_irq(s);
@@ -1531,15 +1568,12 @@ static struct dma_chan *sci_request_dma_chan(struct uart_port *port,
memset(&cfg, 0, sizeof(cfg));
cfg.direction = dir;
- if (dir == DMA_MEM_TO_DEV) {
- cfg.dst_addr = port->mapbase +
- (sci_getreg(port, SCxTDR)->offset << port->regshift);
- cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- } else {
- cfg.src_addr = port->mapbase +
- (sci_getreg(port, SCxRDR)->offset << port->regshift);
- cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
- }
+ cfg.dst_addr = port->mapbase +
+ (sci_getreg(port, SCxTDR)->offset << port->regshift);
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
+ cfg.src_addr = port->mapbase +
+ (sci_getreg(port, SCxRDR)->offset << port->regshift);
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
ret = dmaengine_slave_config(chan, &cfg);
if (ret) {
@@ -1574,7 +1608,7 @@ static void sci_request_dma(struct uart_port *port)
* Don't request a dma channel if no channel was specified
* in the device tree.
*/
- if (!of_find_property(port->dev->of_node, "dmas", NULL))
+ if (!of_property_present(port->dev->of_node, "dmas"))
return;
chan = sci_request_dma_chan(port, DMA_MEM_TO_DEV);
@@ -1632,7 +1666,8 @@ static void sci_request_dma(struct uart_port *port)
s->chan_rx_saved = s->chan_rx = chan;
- if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
+ s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE)
sci_dma_rx_submit(s, false);
}
}
@@ -1685,9 +1720,15 @@ static irqreturn_t sci_rx_interrupt(int irq, void *ptr)
u16 ssr = serial_port_in(port, SCxSR);
/* Disable future Rx interrupts */
- if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
- disable_irq_nosync(irq);
- scr |= SCSCR_RDRQE;
+ if (port->type == PORT_SCIFA || port->type == PORT_SCIFB ||
+ s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
+ disable_irq_nosync(s->irqs[SCIx_RXI_IRQ]);
+ if (s->cfg->regtype == SCIx_RZ_SCIFA_REGTYPE) {
+ scif_set_rtrg(port, 1);
+ scr |= SCSCR_RIE;
+ } else {
+ scr |= SCSCR_RDRQE;
+ }
} else {
if (sci_dma_rx_submit(s, false) < 0)
goto handle_pio;
@@ -1737,6 +1778,24 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr)
return IRQ_HANDLED;
}
+static irqreturn_t sci_tx_end_interrupt(int irq, void *ptr)
+{
+ struct uart_port *port = ptr;
+ unsigned long flags;
+ unsigned short ctrl;
+
+ if (port->type != PORT_SCI)
+ return sci_tx_interrupt(irq, ptr);
+
+ spin_lock_irqsave(&port->lock, flags);
+ ctrl = serial_port_in(port, SCSCR);
+ ctrl &= ~(SCSCR_TE | SCSCR_TEIE);
+ serial_port_out(port, SCSCR, ctrl);
+ spin_unlock_irqrestore(&port->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
static irqreturn_t sci_br_interrupt(int irq, void *ptr)
{
struct uart_port *port = ptr;
@@ -1873,7 +1932,7 @@ static const struct sci_irq_desc {
[SCIx_TEI_IRQ] = {
.desc = "tx end",
- .handler = sci_tx_interrupt,
+ .handler = sci_tx_end_interrupt,
},
/*
@@ -2580,8 +2639,14 @@ done:
sci_set_mctrl(port, port->mctrl);
}
- scr_val |= SCSCR_RE | SCSCR_TE |
- (s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0));
+ /*
+ * For SCI, TE (transmit enable) must be set after setting TIE
+ * (transmit interrupt enable) or in the same instruction to
+ * start the transmitting process. So skip setting TE here for SCI.
+ */
+ if (port->type != PORT_SCI)
+ scr_val |= SCSCR_TE;
+ scr_val |= SCSCR_RE | (s->cfg->scscr & ~(SCSCR_CKE1 | SCSCR_CKE0));
serial_port_out(port, SCSCR, scr_val | s->hscif_tot);
if ((srr + 1 == 5) &&
(port->type == PORT_SCIFA || port->type == PORT_SCIFB)) {
@@ -3149,7 +3214,7 @@ static int sci_remove(struct platform_device *dev)
#define SCI_OF_TYPE(data) ((unsigned long)(data) >> 16)
#define SCI_OF_REGTYPE(data) ((unsigned long)(data) & 0xffff)
-static const struct of_device_id of_sci_match[] = {
+static const struct of_device_id of_sci_match[] __maybe_unused = {
/* SoC-specific types */
{
.compatible = "renesas,scif-r7s72100",