diff options
author | Geert Uytterhoeven <geert+renesas@glider.be> | 2015-09-18 13:08:24 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-10-04 17:36:10 +0100 |
commit | e1910fcdb545acd374b38785b46cbefb1b6f01e9 (patch) | |
tree | e7ca62b84a42639dad42c98edb8b628d83dbf76d | |
parent | 3575b8583e7867ac246520e24be7f973df113d6a (diff) |
serial: sh-sci: Shuffle functions around
This allows to:
- Remove forward declarations of static functions,
- Coalesce two sections protected by #ifdef CONFIG_SERIAL_SH_SCI_DMA,
- Avoid shuffling functions around in the near future,
- Avoid adding forward declarations in the near future.
No functional changes.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/tty/serial/sh-sci.c | 905 |
1 files changed, 449 insertions, 456 deletions
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index d8b73e791a55..7d8b2644e06d 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -123,11 +123,6 @@ struct sci_port { struct notifier_block freq_transition; }; -/* Function prototypes */ -static void sci_start_tx(struct uart_port *port); -static void sci_stop_tx(struct uart_port *port); -static void sci_start_rx(struct uart_port *port); - #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS static struct sci_port sci_ports[SCI_NPORTS]; @@ -489,6 +484,89 @@ static void sci_port_disable(struct sci_port *sci_port) pm_runtime_put_sync(sci_port->port.dev); } +static inline unsigned long port_rx_irq_mask(struct uart_port *port) +{ + /* + * Not all ports (such as SCIFA) will support REIE. Rather than + * special-casing the port type, we check the port initialization + * IRQ enable mask to see whether the IRQ is desired at all. If + * it's unset, it's logically inferred that there's no point in + * testing for it. + */ + return SCSCR_RIE | (to_sci_port(port)->cfg->scscr & SCSCR_REIE); +} + +static void sci_start_tx(struct uart_port *port) +{ + struct sci_port *s = to_sci_port(port); + unsigned short ctrl; + +#ifdef CONFIG_SERIAL_SH_SCI_DMA + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + u16 new, scr = serial_port_in(port, SCSCR); + if (s->chan_tx) + new = scr | SCSCR_TDRQE; + else + new = scr & ~SCSCR_TDRQE; + if (new != scr) + serial_port_out(port, SCSCR, new); + } + + if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && + dma_submit_error(s->cookie_tx)) { + s->cookie_tx = 0; + schedule_work(&s->work_tx); + } +#endif + + if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { + /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = serial_port_in(port, SCSCR); + serial_port_out(port, SCSCR, ctrl | SCSCR_TIE); + } +} + +static void sci_stop_tx(struct uart_port *port) +{ + unsigned short ctrl; + + /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ + ctrl = serial_port_in(port, SCSCR); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~SCSCR_TDRQE; + + ctrl &= ~SCSCR_TIE; + + serial_port_out(port, SCSCR, ctrl); +} + +static void sci_start_rx(struct uart_port *port) +{ + unsigned short ctrl; + + ctrl = serial_port_in(port, SCSCR) | port_rx_irq_mask(port); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~SCSCR_RDRQE; + + serial_port_out(port, SCSCR, ctrl); +} + +static void sci_stop_rx(struct uart_port *port) +{ + unsigned short ctrl; + + ctrl = serial_port_in(port, SCSCR); + + if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) + ctrl &= ~SCSCR_RDRQE; + + ctrl &= ~port_rx_irq_mask(port); + + serial_port_out(port, SCSCR, ctrl); +} + static void sci_clear_SCxSR(struct uart_port *port, unsigned int mask) { if (port->type == PORT_SCI) { @@ -940,336 +1018,6 @@ static int sci_handle_breaks(struct uart_port *port) return copied; } -static irqreturn_t sci_rx_interrupt(int irq, void *ptr) -{ -#ifdef CONFIG_SERIAL_SH_SCI_DMA - struct uart_port *port = ptr; - struct sci_port *s = to_sci_port(port); - - if (s->chan_rx) { - u16 scr = serial_port_in(port, SCSCR); - 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; - } else { - scr &= ~SCSCR_RIE; - } - serial_port_out(port, SCSCR, scr); - /* Clear current interrupt */ - serial_port_out(port, SCxSR, - ssr & ~(SCIF_DR | SCxSR_RDxF(port))); - dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", - jiffies, s->rx_timeout); - mod_timer(&s->rx_timer, jiffies + s->rx_timeout); - - return IRQ_HANDLED; - } -#endif - - /* I think sci_receive_chars has to be called irrespective - * of whether the I_IXOFF is set, otherwise, how is the interrupt - * to be disabled? - */ - sci_receive_chars(ptr); - - return IRQ_HANDLED; -} - -static irqreturn_t sci_tx_interrupt(int irq, void *ptr) -{ - struct uart_port *port = ptr; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - sci_transmit_chars(port); - spin_unlock_irqrestore(&port->lock, flags); - - return IRQ_HANDLED; -} - -static irqreturn_t sci_er_interrupt(int irq, void *ptr) -{ - struct uart_port *port = ptr; - struct sci_port *s = to_sci_port(port); - - /* Handle errors */ - if (port->type == PORT_SCI) { - if (sci_handle_errors(port)) { - /* discard character in rx buffer */ - serial_port_in(port, SCxSR); - sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); - } - } else { - sci_handle_fifo_overrun(port); - if (!s->chan_rx) - sci_receive_chars(ptr); - } - - sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); - - /* Kick the transmission */ - if (!s->chan_tx) - sci_tx_interrupt(irq, ptr); - - return IRQ_HANDLED; -} - -static irqreturn_t sci_br_interrupt(int irq, void *ptr) -{ - struct uart_port *port = ptr; - - /* Handle BREAKs */ - sci_handle_breaks(port); - sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port)); - - return IRQ_HANDLED; -} - -static inline unsigned long port_rx_irq_mask(struct uart_port *port) -{ - /* - * Not all ports (such as SCIFA) will support REIE. Rather than - * special-casing the port type, we check the port initialization - * IRQ enable mask to see whether the IRQ is desired at all. If - * it's unset, it's logically inferred that there's no point in - * testing for it. - */ - return SCSCR_RIE | (to_sci_port(port)->cfg->scscr & SCSCR_REIE); -} - -static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) -{ - unsigned short ssr_status, scr_status, err_enabled, orer_status = 0; - struct uart_port *port = ptr; - struct sci_port *s = to_sci_port(port); - irqreturn_t ret = IRQ_NONE; - - ssr_status = serial_port_in(port, SCxSR); - scr_status = serial_port_in(port, SCSCR); - if (s->overrun_reg == SCxSR) - orer_status = ssr_status; - else { - if (sci_getreg(port, s->overrun_reg)->size) - orer_status = serial_port_in(port, s->overrun_reg); - } - - err_enabled = scr_status & port_rx_irq_mask(port); - - /* Tx Interrupt */ - if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCSCR_TIE) && - !s->chan_tx) - ret = sci_tx_interrupt(irq, ptr); - - /* - * Rx Interrupt: if we're using DMA, the DMA controller clears RDF / - * DR flags - */ - if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) && - (scr_status & SCSCR_RIE)) - ret = sci_rx_interrupt(irq, ptr); - - /* Error Interrupt */ - if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) - ret = sci_er_interrupt(irq, ptr); - - /* Break Interrupt */ - if ((ssr_status & SCxSR_BRK(port)) && err_enabled) - ret = sci_br_interrupt(irq, ptr); - - /* Overrun Interrupt */ - if (orer_status & s->overrun_mask) { - sci_handle_fifo_overrun(port); - ret = IRQ_HANDLED; - } - - return ret; -} - -/* - * Here we define a transition notifier so that we can update all of our - * ports' baud rate when the peripheral clock changes. - */ -static int sci_notifier(struct notifier_block *self, - unsigned long phase, void *p) -{ - struct sci_port *sci_port; - unsigned long flags; - - sci_port = container_of(self, struct sci_port, freq_transition); - - if (phase == CPUFREQ_POSTCHANGE) { - struct uart_port *port = &sci_port->port; - - spin_lock_irqsave(&port->lock, flags); - port->uartclk = clk_get_rate(sci_port->iclk); - spin_unlock_irqrestore(&port->lock, flags); - } - - return NOTIFY_OK; -} - -static const struct sci_irq_desc { - const char *desc; - irq_handler_t handler; -} sci_irq_desc[] = { - /* - * Split out handlers, the default case. - */ - [SCIx_ERI_IRQ] = { - .desc = "rx err", - .handler = sci_er_interrupt, - }, - - [SCIx_RXI_IRQ] = { - .desc = "rx full", - .handler = sci_rx_interrupt, - }, - - [SCIx_TXI_IRQ] = { - .desc = "tx empty", - .handler = sci_tx_interrupt, - }, - - [SCIx_BRI_IRQ] = { - .desc = "break", - .handler = sci_br_interrupt, - }, - - /* - * Special muxed handler. - */ - [SCIx_MUX_IRQ] = { - .desc = "mux", - .handler = sci_mpxed_interrupt, - }, -}; - -static int sci_request_irq(struct sci_port *port) -{ - struct uart_port *up = &port->port; - int i, j, ret = 0; - - for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) { - const struct sci_irq_desc *desc; - int irq; - - if (SCIx_IRQ_IS_MUXED(port)) { - i = SCIx_MUX_IRQ; - irq = up->irq; - } else { - irq = port->irqs[i]; - - /* - * Certain port types won't support all of the - * available interrupt sources. - */ - if (unlikely(irq < 0)) - continue; - } - - desc = sci_irq_desc + i; - port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s", - dev_name(up->dev), desc->desc); - if (!port->irqstr[j]) - goto out_nomem; - - ret = request_irq(irq, desc->handler, up->irqflags, - port->irqstr[j], port); - if (unlikely(ret)) { - dev_err(up->dev, "Can't allocate %s IRQ\n", desc->desc); - goto out_noirq; - } - } - - return 0; - -out_noirq: - while (--i >= 0) - free_irq(port->irqs[i], port); - -out_nomem: - while (--j >= 0) - kfree(port->irqstr[j]); - - return ret; -} - -static void sci_free_irq(struct sci_port *port) -{ - int i; - - /* - * Intentionally in reverse order so we iterate over the muxed - * IRQ first. - */ - for (i = 0; i < SCIx_NR_IRQS; i++) { - int irq = port->irqs[i]; - - /* - * Certain port types won't support all of the available - * interrupt sources. - */ - if (unlikely(irq < 0)) - continue; - - free_irq(port->irqs[i], port); - kfree(port->irqstr[i]); - - if (SCIx_IRQ_IS_MUXED(port)) { - /* If there's only one IRQ, we're done. */ - return; - } - } -} - -static unsigned int sci_tx_empty(struct uart_port *port) -{ - unsigned short status = serial_port_in(port, SCxSR); - unsigned short in_tx_fifo = sci_txfill(port); - - return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; -} - -/* - * Modem control is a bit of a mixed bag for SCI(F) ports. Generally - * CTS/RTS is supported in hardware by at least one port and controlled - * via SCSPTR (SCxPCR for SCIFA/B parts), or external pins (presently - * handled via the ->init_pins() op, which is a bit of a one-way street, - * lacking any ability to defer pin control -- this will later be - * converted over to the GPIO framework). - * - * Other modes (such as loopback) are supported generically on certain - * port types, but not others. For these it's sufficient to test for the - * existence of the support register and simply ignore the port type. - */ -static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) -{ - if (mctrl & TIOCM_LOOP) { - const struct plat_sci_reg *reg; - - /* - * Standard loopback mode for SCFCR ports. - */ - reg = sci_getreg(port, SCFCR); - if (reg->size) - serial_port_out(port, SCFCR, - serial_port_in(port, SCFCR) | - SCFCR_LOOP); - } -} - -static unsigned int sci_get_mctrl(struct uart_port *port) -{ - /* - * CTS/RTS is handled in hardware when supported, while nothing - * else is wired up. Keep it simple and simply assert DSR/CAR. - */ - return TIOCM_DSR | TIOCM_CAR; -} - #ifdef CONFIG_SERIAL_SH_SCI_DMA static void sci_dma_tx_complete(void *arg) { @@ -1336,6 +1084,24 @@ static int sci_dma_rx_find_active(struct sci_port *s) return -1; } +static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) +{ + struct dma_chan *chan = s->chan_rx; + struct uart_port *port = &s->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + s->chan_rx = NULL; + s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; + spin_unlock_irqrestore(&port->lock, flags); + dmaengine_terminate_all(chan); + dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], + sg_dma_address(&s->sg_rx[0])); + dma_release_channel(chan); + if (enable_pio) + sci_start_rx(port); +} + static void sci_dma_rx_complete(void *arg) { struct sci_port *s = arg; @@ -1362,24 +1128,6 @@ static void sci_dma_rx_complete(void *arg) schedule_work(&s->work_rx); } -static void sci_rx_dma_release(struct sci_port *s, bool enable_pio) -{ - struct dma_chan *chan = s->chan_rx; - struct uart_port *port = &s->port; - unsigned long flags; - - spin_lock_irqsave(&port->lock, flags); - s->chan_rx = NULL; - s->cookie_rx[0] = s->cookie_rx[1] = -EINVAL; - spin_unlock_irqrestore(&port->lock, flags); - dmaengine_terminate_all(chan); - dma_free_coherent(chan->device->dev, s->buf_len_rx * 2, s->rx_buf[0], - sg_dma_address(&s->sg_rx[0])); - dma_release_channel(chan); - if (enable_pio) - sci_start_rx(port); -} - static void sci_tx_dma_release(struct sci_port *s, bool enable_pio) { struct dma_chan *chan = s->chan_tx; @@ -1557,110 +1305,7 @@ static void work_fn_tx(struct work_struct *work) dma_async_issue_pending(chan); } -#endif -static void sci_start_tx(struct uart_port *port) -{ - struct sci_port *s = to_sci_port(port); - unsigned short ctrl; - -#ifdef CONFIG_SERIAL_SH_SCI_DMA - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - u16 new, scr = serial_port_in(port, SCSCR); - if (s->chan_tx) - new = scr | SCSCR_TDRQE; - else - new = scr & ~SCSCR_TDRQE; - if (new != scr) - serial_port_out(port, SCSCR, new); - } - - if (s->chan_tx && !uart_circ_empty(&s->port.state->xmit) && - dma_submit_error(s->cookie_tx)) { - s->cookie_tx = 0; - schedule_work(&s->work_tx); - } -#endif - - if (!s->chan_tx || port->type == PORT_SCIFA || port->type == PORT_SCIFB) { - /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = serial_port_in(port, SCSCR); - serial_port_out(port, SCSCR, ctrl | SCSCR_TIE); - } -} - -static void sci_stop_tx(struct uart_port *port) -{ - unsigned short ctrl; - - /* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */ - ctrl = serial_port_in(port, SCSCR); - - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~SCSCR_TDRQE; - - ctrl &= ~SCSCR_TIE; - - serial_port_out(port, SCSCR, ctrl); -} - -static void sci_start_rx(struct uart_port *port) -{ - unsigned short ctrl; - - ctrl = serial_port_in(port, SCSCR) | port_rx_irq_mask(port); - - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~SCSCR_RDRQE; - - serial_port_out(port, SCSCR, ctrl); -} - -static void sci_stop_rx(struct uart_port *port) -{ - unsigned short ctrl; - - ctrl = serial_port_in(port, SCSCR); - - if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) - ctrl &= ~SCSCR_RDRQE; - - ctrl &= ~port_rx_irq_mask(port); - - serial_port_out(port, SCSCR, ctrl); -} - -static void sci_break_ctl(struct uart_port *port, int break_state) -{ - struct sci_port *s = to_sci_port(port); - const struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR; - unsigned short scscr, scsptr; - - /* check wheter the port has SCSPTR */ - if (!reg->size) { - /* - * Not supported by hardware. Most parts couple break and rx - * interrupts together, with break detection always enabled. - */ - return; - } - - scsptr = serial_port_in(port, SCSPTR); - scscr = serial_port_in(port, SCSCR); - - if (break_state == -1) { - scsptr = (scsptr | SCSPTR_SPB2IO) & ~SCSPTR_SPB2DT; - scscr &= ~SCSCR_TE; - } else { - scsptr = (scsptr | SCSPTR_SPB2DT) & ~SCSPTR_SPB2IO; - scscr |= SCSCR_TE; - } - - serial_port_out(port, SCSPTR, scsptr); - serial_port_out(port, SCSCR, scscr); -} - -#ifdef CONFIG_SERIAL_SH_SCI_DMA static bool filter(struct dma_chan *chan, void *slave) { struct sh_dmae_slave *param = slave; @@ -1794,6 +1439,354 @@ static inline void sci_free_dma(struct uart_port *port) } #endif +static irqreturn_t sci_rx_interrupt(int irq, void *ptr) +{ +#ifdef CONFIG_SERIAL_SH_SCI_DMA + struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + + if (s->chan_rx) { + u16 scr = serial_port_in(port, SCSCR); + 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; + } else { + scr &= ~SCSCR_RIE; + } + serial_port_out(port, SCSCR, scr); + /* Clear current interrupt */ + serial_port_out(port, SCxSR, + ssr & ~(SCIF_DR | SCxSR_RDxF(port))); + dev_dbg(port->dev, "Rx IRQ %lu: setup t-out in %u jiffies\n", + jiffies, s->rx_timeout); + mod_timer(&s->rx_timer, jiffies + s->rx_timeout); + + return IRQ_HANDLED; + } +#endif + + /* I think sci_receive_chars has to be called irrespective + * of whether the I_IXOFF is set, otherwise, how is the interrupt + * to be disabled? + */ + sci_receive_chars(ptr); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_tx_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + sci_transmit_chars(port); + spin_unlock_irqrestore(&port->lock, flags); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_er_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + + /* Handle errors */ + if (port->type == PORT_SCI) { + if (sci_handle_errors(port)) { + /* discard character in rx buffer */ + serial_port_in(port, SCxSR); + sci_clear_SCxSR(port, SCxSR_RDxF_CLEAR(port)); + } + } else { + sci_handle_fifo_overrun(port); + if (!s->chan_rx) + sci_receive_chars(ptr); + } + + sci_clear_SCxSR(port, SCxSR_ERROR_CLEAR(port)); + + /* Kick the transmission */ + if (!s->chan_tx) + sci_tx_interrupt(irq, ptr); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_br_interrupt(int irq, void *ptr) +{ + struct uart_port *port = ptr; + + /* Handle BREAKs */ + sci_handle_breaks(port); + sci_clear_SCxSR(port, SCxSR_BREAK_CLEAR(port)); + + return IRQ_HANDLED; +} + +static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr) +{ + unsigned short ssr_status, scr_status, err_enabled, orer_status = 0; + struct uart_port *port = ptr; + struct sci_port *s = to_sci_port(port); + irqreturn_t ret = IRQ_NONE; + + ssr_status = serial_port_in(port, SCxSR); + scr_status = serial_port_in(port, SCSCR); + if (s->overrun_reg == SCxSR) + orer_status = ssr_status; + else { + if (sci_getreg(port, s->overrun_reg)->size) + orer_status = serial_port_in(port, s->overrun_reg); + } + + err_enabled = scr_status & port_rx_irq_mask(port); + + /* Tx Interrupt */ + if ((ssr_status & SCxSR_TDxE(port)) && (scr_status & SCSCR_TIE) && + !s->chan_tx) + ret = sci_tx_interrupt(irq, ptr); + + /* + * Rx Interrupt: if we're using DMA, the DMA controller clears RDF / + * DR flags + */ + if (((ssr_status & SCxSR_RDxF(port)) || s->chan_rx) && + (scr_status & SCSCR_RIE)) + ret = sci_rx_interrupt(irq, ptr); + + /* Error Interrupt */ + if ((ssr_status & SCxSR_ERRORS(port)) && err_enabled) + ret = sci_er_interrupt(irq, ptr); + + /* Break Interrupt */ + if ((ssr_status & SCxSR_BRK(port)) && err_enabled) + ret = sci_br_interrupt(irq, ptr); + + /* Overrun Interrupt */ + if (orer_status & s->overrun_mask) { + sci_handle_fifo_overrun(port); + ret = IRQ_HANDLED; + } + + return ret; +} + +/* + * Here we define a transition notifier so that we can update all of our + * ports' baud rate when the peripheral clock changes. + */ +static int sci_notifier(struct notifier_block *self, + unsigned long phase, void *p) +{ + struct sci_port *sci_port; + unsigned long flags; + + sci_port = container_of(self, struct sci_port, freq_transition); + + if (phase == CPUFREQ_POSTCHANGE) { + struct uart_port *port = &sci_port->port; + + spin_lock_irqsave(&port->lock, flags); + port->uartclk = clk_get_rate(sci_port->iclk); + spin_unlock_irqrestore(&port->lock, flags); + } + + return NOTIFY_OK; +} + +static const struct sci_irq_desc { + const char *desc; + irq_handler_t handler; +} sci_irq_desc[] = { + /* + * Split out handlers, the default case. + */ + [SCIx_ERI_IRQ] = { + .desc = "rx err", + .handler = sci_er_interrupt, + }, + + [SCIx_RXI_IRQ] = { + .desc = "rx full", + .handler = sci_rx_interrupt, + }, + + [SCIx_TXI_IRQ] = { + .desc = "tx empty", + .handler = sci_tx_interrupt, + }, + + [SCIx_BRI_IRQ] = { + .desc = "break", + .handler = sci_br_interrupt, + }, + + /* + * Special muxed handler. + */ + [SCIx_MUX_IRQ] = { + .desc = "mux", + .handler = sci_mpxed_interrupt, + }, +}; + +static int sci_request_irq(struct sci_port *port) +{ + struct uart_port *up = &port->port; + int i, j, ret = 0; + + for (i = j = 0; i < SCIx_NR_IRQS; i++, j++) { + const struct sci_irq_desc *desc; + int irq; + + if (SCIx_IRQ_IS_MUXED(port)) { + i = SCIx_MUX_IRQ; + irq = up->irq; + } else { + irq = port->irqs[i]; + + /* + * Certain port types won't support all of the + * available interrupt sources. + */ + if (unlikely(irq < 0)) + continue; + } + + desc = sci_irq_desc + i; + port->irqstr[j] = kasprintf(GFP_KERNEL, "%s:%s", + dev_name(up->dev), desc->desc); + if (!port->irqstr[j]) + goto out_nomem; + + ret = request_irq(irq, desc->handler, up->irqflags, + port->irqstr[j], port); + if (unlikely(ret)) { + dev_err(up->dev, "Can't allocate %s IRQ\n", desc->desc); + goto out_noirq; + } + } + + return 0; + +out_noirq: + while (--i >= 0) + free_irq(port->irqs[i], port); + +out_nomem: + while (--j >= 0) + kfree(port->irqstr[j]); + + return ret; +} + +static void sci_free_irq(struct sci_port *port) +{ + int i; + + /* + * Intentionally in reverse order so we iterate over the muxed + * IRQ first. + */ + for (i = 0; i < SCIx_NR_IRQS; i++) { + int irq = port->irqs[i]; + + /* + * Certain port types won't support all of the available + * interrupt sources. + */ + if (unlikely(irq < 0)) + continue; + + free_irq(port->irqs[i], port); + kfree(port->irqstr[i]); + + if (SCIx_IRQ_IS_MUXED(port)) { + /* If there's only one IRQ, we're done. */ + return; + } + } +} + +static unsigned int sci_tx_empty(struct uart_port *port) +{ + unsigned short status = serial_port_in(port, SCxSR); + unsigned short in_tx_fifo = sci_txfill(port); + + return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0; +} + +/* + * Modem control is a bit of a mixed bag for SCI(F) ports. Generally + * CTS/RTS is supported in hardware by at least one port and controlled + * via SCSPTR (SCxPCR for SCIFA/B parts), or external pins (presently + * handled via the ->init_pins() op, which is a bit of a one-way street, + * lacking any ability to defer pin control -- this will later be + * converted over to the GPIO framework). + * + * Other modes (such as loopback) are supported generically on certain + * port types, but not others. For these it's sufficient to test for the + * existence of the support register and simply ignore the port type. + */ +static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + if (mctrl & TIOCM_LOOP) { + const struct plat_sci_reg *reg; + + /* + * Standard loopback mode for SCFCR ports. + */ + reg = sci_getreg(port, SCFCR); + if (reg->size) + serial_port_out(port, SCFCR, + serial_port_in(port, SCFCR) | + SCFCR_LOOP); + } +} + +static unsigned int sci_get_mctrl(struct uart_port *port) +{ + /* + * CTS/RTS is handled in hardware when supported, while nothing + * else is wired up. Keep it simple and simply assert DSR/CAR. + */ + return TIOCM_DSR | TIOCM_CAR; +} + +static void sci_break_ctl(struct uart_port *port, int break_state) +{ + struct sci_port *s = to_sci_port(port); + const struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR; + unsigned short scscr, scsptr; + + /* check wheter the port has SCSPTR */ + if (!reg->size) { + /* + * Not supported by hardware. Most parts couple break and rx + * interrupts together, with break detection always enabled. + */ + return; + } + + scsptr = serial_port_in(port, SCSPTR); + scscr = serial_port_in(port, SCSCR); + + if (break_state == -1) { + scsptr = (scsptr | SCSPTR_SPB2IO) & ~SCSPTR_SPB2DT; + scscr &= ~SCSCR_TE; + } else { + scsptr = (scsptr | SCSPTR_SPB2DT) & ~SCSPTR_SPB2IO; + scscr |= SCSCR_TE; + } + + serial_port_out(port, SCSPTR, scsptr); + serial_port_out(port, SCSCR, scscr); +} + static int sci_startup(struct uart_port *port) { struct sci_port *s = to_sci_port(port); |