Commit a1b5b43f authored by Geert Uytterhoeven's avatar Geert Uytterhoeven Committed by Greg Kroah-Hartman

serial: sh-sci: Replace buggy big #ifdef by runtime logic

The #ifdef logic to clear SCxSR bits using RMW on SCIFA/SCIFB and SCIF
variants with some SCIFA features (sh7705/SH7720/sh7721) has several
drawbacks:
  - It wasn't updated for newer R-Mobile variants (APE6),
  - It doesn't correctly handle SoCs with both SCIF and SCIFA/B (e.g.
    R-Car Gen2, but also legacy sh7723/sh7724),
  - It doesn't play well with ARM multi-platform kernels: on R-Car Gen2,
    SCIF/SCIFA/SCIFB/HSCIF were handled differently, depending on
    whether r8a7740 or sh73a0 support was enabled or not,

Replace the #ifdef logic by runtime logic to fix this.

SCIFA/SCIFB and SCIF on sh7705/sh7720/sh7721 use RMW to clear error
bits, other variants use plain stores, as before.

Note that this changes behavior for SCIFA on sh7723/sh7724 (these SoCs
have both SCIF and SCIFA), which didn't use RMW before.
Signed-off-by: default avatarGeert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9a23a1d1
...@@ -489,6 +489,22 @@ static void sci_port_disable(struct sci_port *sci_port) ...@@ -489,6 +489,22 @@ static void sci_port_disable(struct sci_port *sci_port)
pm_runtime_put_sync(sci_port->port.dev); pm_runtime_put_sync(sci_port->port.dev);
} }
static void sci_clear_SCxSR(struct uart_port *port, unsigned int mask)
{
if (port->type == PORT_SCI) {
/* Just store the mask */
serial_port_out(port, SCxSR, mask);
} else if (to_sci_port(port)->overrun_mask == SCIFA_ORER) {
/* SCIFA/SCIFB and SCIF on SH7705/SH7720/SH7721 */
/* Only clear the status bits we want to clear */
serial_port_out(port, SCxSR,
serial_port_in(port, SCxSR) & mask);
} else {
/* Store the mask, clear parity/framing errors */
serial_port_out(port, SCxSR, mask & ~(SCIF_FERC | SCIF_PERC));
}
}
#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE) #if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
#ifdef CONFIG_CONSOLE_POLL #ifdef CONFIG_CONSOLE_POLL
...@@ -500,7 +516,7 @@ static int sci_poll_get_char(struct uart_port *port) ...@@ -500,7 +516,7 @@ static int sci_poll_get_char(struct uart_port *port)
do { do {
status = serial_port_in(port, SCxSR); status = serial_port_in(port, SCxSR);
if (status & SCxSR_ERRORS(port)) { if (status & SCxSR_ERRORS(port)) {
serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port));
continue; continue;
} }
break; break;
...@@ -513,7 +529,7 @@ static int sci_poll_get_char(struct uart_port *port) ...@@ -513,7 +529,7 @@ static int sci_poll_get_char(struct uart_port *port)
/* Dummy read */ /* Dummy read */
serial_port_in(port, SCxSR); serial_port_in(port, SCxSR);
serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
return c; return c;
} }
...@@ -528,7 +544,7 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c) ...@@ -528,7 +544,7 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
} while (!(status & SCxSR_TDxE(port))); } while (!(status & SCxSR_TDxE(port)));
serial_port_out(port, SCxTDR, c); serial_port_out(port, SCxTDR, c);
serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port)); sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port) & ~SCxSR_TEND(port));
} }
#endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */ #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
...@@ -655,7 +671,7 @@ static void sci_transmit_chars(struct uart_port *port) ...@@ -655,7 +671,7 @@ static void sci_transmit_chars(struct uart_port *port)
port->icount.tx++; port->icount.tx++;
} while (--count > 0); } while (--count > 0);
serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port));
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port); uart_write_wakeup(port);
...@@ -666,7 +682,7 @@ static void sci_transmit_chars(struct uart_port *port) ...@@ -666,7 +682,7 @@ static void sci_transmit_chars(struct uart_port *port)
if (port->type != PORT_SCI) { if (port->type != PORT_SCI) {
serial_port_in(port, SCxSR); /* Dummy read */ serial_port_in(port, SCxSR); /* Dummy read */
serial_port_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_TDxE_CLEAR(port));
} }
ctrl |= SCSCR_TIE; ctrl |= SCSCR_TIE;
...@@ -750,7 +766,7 @@ static void sci_receive_chars(struct uart_port *port) ...@@ -750,7 +766,7 @@ static void sci_receive_chars(struct uart_port *port)
} }
serial_port_in(port, SCxSR); /* dummy read */ serial_port_in(port, SCxSR); /* dummy read */
serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
copied += count; copied += count;
port->icount.rx += count; port->icount.rx += count;
...@@ -761,7 +777,7 @@ static void sci_receive_chars(struct uart_port *port) ...@@ -761,7 +777,7 @@ static void sci_receive_chars(struct uart_port *port)
tty_flip_buffer_push(tport); tty_flip_buffer_push(tport);
} else { } else {
serial_port_in(port, SCxSR); /* dummy read */ serial_port_in(port, SCxSR); /* dummy read */
serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
} }
} }
...@@ -982,14 +998,14 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr) ...@@ -982,14 +998,14 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr)
if (sci_handle_errors(port)) { if (sci_handle_errors(port)) {
/* discard character in rx buffer */ /* discard character in rx buffer */
serial_port_in(port, SCxSR); serial_port_in(port, SCxSR);
serial_port_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port));
} }
} else { } else {
sci_handle_fifo_overrun(port); sci_handle_fifo_overrun(port);
sci_rx_interrupt(irq, ptr); sci_rx_interrupt(irq, ptr);
} }
serial_port_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port));
/* Kick the transmission */ /* Kick the transmission */
sci_tx_interrupt(irq, ptr); sci_tx_interrupt(irq, ptr);
...@@ -1003,7 +1019,7 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr) ...@@ -1003,7 +1019,7 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr)
/* Handle BREAKs */ /* Handle BREAKs */
sci_handle_breaks(port); sci_handle_breaks(port);
serial_port_out(port, SCxSR, SCxSR_BREAK_CLEAR(port)); sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port));
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
...@@ -119,28 +119,11 @@ enum { ...@@ -119,28 +119,11 @@ enum {
#define SCxSR_ERRORS(port) (to_sci_port(port)->error_mask) #define SCxSR_ERRORS(port) (to_sci_port(port)->error_mask)
#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \ #define SCxSR_RDxF_CLEAR(port) \
defined(CONFIG_CPU_SUBTYPE_SH7720) || \ (((port)->type == PORT_SCI) ? SCI_RDxF_CLEAR : SCIF_RDxF_CLEAR)
defined(CONFIG_CPU_SUBTYPE_SH7721) || \ #define SCxSR_ERROR_CLEAR(port) \
defined(CONFIG_ARCH_SH73A0) || \ (((port)->type == PORT_SCI) ? SCI_ERROR_CLEAR : SCIF_ERROR_CLEAR)
defined(CONFIG_ARCH_R8A7740) #define SCxSR_TDxE_CLEAR(port) \
(((port)->type == PORT_SCI) ? SCI_TDxE_CLEAR : SCIF_TDxE_CLEAR)
# define SCxSR_RDxF_CLEAR(port) \ #define SCxSR_BREAK_CLEAR(port) \
(serial_port_in(port, SCxSR) & SCIF_RDxF_CLEAR) (((port)->type == PORT_SCI) ? SCI_BREAK_CLEAR : SCIF_BREAK_CLEAR)
# define SCxSR_ERROR_CLEAR(port) \
(serial_port_in(port, SCxSR) & SCIF_ERROR_CLEAR)
# define SCxSR_TDxE_CLEAR(port) \
(serial_port_in(port, SCxSR) & SCIF_TDxE_CLEAR)
# define SCxSR_BREAK_CLEAR(port) \
(serial_port_in(port, SCxSR) & SCIF_BREAK_CLEAR)
#else
# define SCxSR_RDxF_CLEAR(port) \
((((port)->type == PORT_SCI) ? SCI_RDxF_CLEAR : SCIF_RDxF_CLEAR) & 0xff)
# define SCxSR_ERROR_CLEAR(port) \
((((port)->type == PORT_SCI) ? SCI_ERROR_CLEAR : SCIF_ERROR_CLEAR) & 0xff)
# define SCxSR_TDxE_CLEAR(port) \
((((port)->type == PORT_SCI) ? SCI_TDxE_CLEAR : SCIF_TDxE_CLEAR) & 0xff)
# define SCxSR_BREAK_CLEAR(port) \
((((port)->type == PORT_SCI) ? SCI_BREAK_CLEAR : SCIF_BREAK_CLEAR) & 0xff)
#endif
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