serial: sh-sci: Do not start transfers from sci_startup()
[linux-2.6-block.git] / drivers / tty / serial / sh-sci.c
index 0130feb069aee02fbf5beeecacd9f674cde2e2ff..d88c84cd88a48b3885674ad474b68c9d318666bf 100644 (file)
@@ -57,6 +57,7 @@
 #include <asm/sh_bios.h>
 #endif
 
+#include "serial_mctrl_gpio.h"
 #include "sh-sci.h"
 
 /* Offsets into the sci_port->irqs array */
@@ -111,6 +112,7 @@ struct sci_port {
        unsigned int            error_clear;
        unsigned int            sampling_rate_mask;
        resource_size_t         reg_size;
+       struct mctrl_gpios      *gpios;
 
        /* Break timer */
        struct timer_list       break_timer;
@@ -139,6 +141,8 @@ struct sci_port {
        struct timer_list               rx_timer;
        unsigned int                    rx_timeout;
 #endif
+
+       bool autorts;
 };
 
 #define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
@@ -701,7 +705,6 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
 static void sci_init_pins(struct uart_port *port, unsigned int cflag)
 {
        struct sci_port *s = to_sci_port(port);
-       const struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR;
 
        /*
         * Use port-specific handler if provided.
@@ -711,21 +714,28 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag)
                return;
        }
 
-       /*
-        * For the generic path SCSPTR is necessary. Bail out if that's
-        * unavailable, too.
-        */
-       if (!reg->size)
-               return;
-
-       if ((s->cfg->capabilities & SCIx_HAVE_RTSCTS) &&
-           ((!(cflag & CRTSCTS)))) {
-               unsigned short status;
+       if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+               u16 ctrl = serial_port_in(port, SCPCR);
+
+               /* Enable RXD and TXD pin functions */
+               ctrl &= ~(SCPCR_RXDC | SCPCR_TXDC);
+               if (to_sci_port(port)->cfg->capabilities & SCIx_HAVE_RTSCTS) {
+                       /* RTS# is output, driven 1 */
+                       ctrl |= SCPCR_RTSC;
+                       serial_port_out(port, SCPDR,
+                               serial_port_in(port, SCPDR) | SCPDR_RTSD);
+                       /* Enable CTS# pin function */
+                       ctrl &= ~SCPCR_CTSC;
+               }
+               serial_port_out(port, SCPCR, ctrl);
+       } else if (sci_getreg(port, SCSPTR)->size) {
+               u16 status = serial_port_in(port, SCSPTR);
 
-               status = serial_port_in(port, SCSPTR);
-               status &= ~SCSPTR_CTSIO;
-               status |= SCSPTR_RTSIO;
-               serial_port_out(port, SCSPTR, status); /* Set RTS = 1 */
+               /* RTS# is output, driven 1 */
+               status |= SCSPTR_RTSIO | SCSPTR_RTSDT;
+               /* CTS# and SCK are inputs */
+               status &= ~(SCSPTR_CTSIO | SCSPTR_SCKIO);
+               serial_port_out(port, SCSPTR, status);
        }
 }
 
@@ -1803,6 +1813,46 @@ static unsigned int sci_tx_empty(struct uart_port *port)
        return (status & SCxSR_TEND(port)) && !in_tx_fifo ? TIOCSER_TEMT : 0;
 }
 
+static void sci_set_rts(struct uart_port *port, bool state)
+{
+       if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+               u16 data = serial_port_in(port, SCPDR);
+
+               /* Active low */
+               if (state)
+                       data &= ~SCPDR_RTSD;
+               else
+                       data |= SCPDR_RTSD;
+               serial_port_out(port, SCPDR, data);
+
+               /* RTS# is output */
+               serial_port_out(port, SCPCR,
+                               serial_port_in(port, SCPCR) | SCPCR_RTSC);
+       } else if (sci_getreg(port, SCSPTR)->size) {
+               u16 ctrl = serial_port_in(port, SCSPTR);
+
+               /* Active low */
+               if (state)
+                       ctrl &= ~SCSPTR_RTSDT;
+               else
+                       ctrl |= SCSPTR_RTSDT;
+               serial_port_out(port, SCSPTR, ctrl);
+       }
+}
+
+static bool sci_get_cts(struct uart_port *port)
+{
+       if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+               /* Active low */
+               return !(serial_port_in(port, SCPDR) & SCPDR_CTSD);
+       } else if (sci_getreg(port, SCSPTR)->size) {
+               /* Active low */
+               return !(serial_port_in(port, SCSPTR) & SCSPTR_CTSDT);
+       }
+
+       return true;
+}
+
 /*
  * 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
@@ -1817,6 +1867,8 @@ static unsigned int sci_tx_empty(struct uart_port *port)
  */
 static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
+       struct sci_port *s = to_sci_port(port);
+
        if (mctrl & TIOCM_LOOP) {
                const struct plat_sci_reg *reg;
 
@@ -1829,25 +1881,72 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
                                        serial_port_in(port, SCFCR) |
                                        SCFCR_LOOP);
        }
+
+       mctrl_gpio_set(s->gpios, mctrl);
+
+       if (!(s->cfg->capabilities & SCIx_HAVE_RTSCTS))
+               return;
+
+       if (!(mctrl & TIOCM_RTS)) {
+               /* Disable Auto RTS */
+               serial_port_out(port, SCFCR,
+                               serial_port_in(port, SCFCR) & ~SCFCR_MCE);
+
+               /* Clear RTS */
+               sci_set_rts(port, 0);
+       } else if (s->autorts) {
+               if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+                       /* Enable RTS# pin function */
+                       serial_port_out(port, SCPCR,
+                               serial_port_in(port, SCPCR) & ~SCPCR_RTSC);
+               }
+
+               /* Enable Auto RTS */
+               serial_port_out(port, SCFCR,
+                               serial_port_in(port, SCFCR) | SCFCR_MCE);
+       } else {
+               /* Set RTS */
+               sci_set_rts(port, 1);
+       }
 }
 
 static unsigned int sci_get_mctrl(struct uart_port *port)
 {
+       struct sci_port *s = to_sci_port(port);
+       struct mctrl_gpios *gpios = s->gpios;
+       unsigned int mctrl = 0;
+
+       mctrl_gpio_get(gpios, &mctrl);
+
        /*
         * CTS/RTS is handled in hardware when supported, while nothing
-        * else is wired up. Keep it simple and simply assert DSR/CAR.
+        * else is wired up.
         */
-       return TIOCM_DSR | TIOCM_CAR;
+       if (s->autorts) {
+               if (sci_get_cts(port))
+                       mctrl |= TIOCM_CTS;
+       } else if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_CTS))) {
+               mctrl |= TIOCM_CTS;
+       }
+       if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DSR)))
+               mctrl |= TIOCM_DSR;
+       if (IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(gpios, UART_GPIO_DCD)))
+               mctrl |= TIOCM_CAR;
+
+       return mctrl;
+}
+
+static void sci_enable_ms(struct uart_port *port)
+{
+       mctrl_gpio_enable_ms(to_sci_port(port)->gpios);
 }
 
 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) {
+       if (!sci_getreg(port, SCSPTR)->size) {
                /*
                 * Not supported by hardware. Most parts couple break and rx
                 * interrupts together, with break detection always enabled.
@@ -1873,7 +1972,6 @@ static void sci_break_ctl(struct uart_port *port, int break_state)
 static int sci_startup(struct uart_port *port)
 {
        struct sci_port *s = to_sci_port(port);
-       unsigned long flags;
        int ret;
 
        dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
@@ -1884,11 +1982,6 @@ static int sci_startup(struct uart_port *port)
 
        sci_request_dma(port);
 
-       spin_lock_irqsave(&port->lock, flags);
-       sci_start_tx(port);
-       sci_start_rx(port);
-       spin_unlock_irqrestore(&port->lock, flags);
-
        return 0;
 }
 
@@ -1899,6 +1992,9 @@ static void sci_shutdown(struct uart_port *port)
 
        dev_dbg(port->dev, "%s(%d)\n", __func__, port->line);
 
+       s->autorts = false;
+       mctrl_gpio_disable_ms(to_sci_port(port)->gpios);
+
        spin_lock_irqsave(&port->lock, flags);
        sci_stop_rx(port);
        sci_stop_tx(port);
@@ -2218,15 +2314,18 @@ done:
 
        sci_init_pins(port, termios->c_cflag);
 
+       port->status &= ~UPSTAT_AUTOCTS;
+       s->autorts = false;
        reg = sci_getreg(port, SCFCR);
        if (reg->size) {
                unsigned short ctrl = serial_port_in(port, SCFCR);
 
-               if (s->cfg->capabilities & SCIx_HAVE_RTSCTS) {
-                       if (termios->c_cflag & CRTSCTS)
-                               ctrl |= SCFCR_MCE;
-                       else
-                               ctrl &= ~SCFCR_MCE;
+               if ((port->flags & UPF_HARD_FLOW) &&
+                   (termios->c_cflag & CRTSCTS)) {
+                       /* There is no CTS interrupt to restart the hardware */
+                       port->status |= UPSTAT_AUTOCTS;
+                       /* MCE is enabled when RTS is raised */
+                       s->autorts = true;
                }
 
                /*
@@ -2300,6 +2399,9 @@ done:
                sci_start_rx(port);
 
        sci_port_disable(s);
+
+       if (UART_ENABLE_MS(port, termios->c_cflag))
+               sci_enable_ms(port);
 }
 
 static void sci_pm(struct uart_port *port, unsigned int state,
@@ -2425,6 +2527,7 @@ static struct uart_ops sci_uart_ops = {
        .start_tx       = sci_start_tx,
        .stop_tx        = sci_stop_tx,
        .stop_rx        = sci_stop_rx,
+       .enable_ms      = sci_enable_ms,
        .break_ctl      = sci_break_ctl,
        .startup        = sci_startup,
        .shutdown       = sci_shutdown,
@@ -2890,6 +2993,9 @@ sci_parse_dt(struct platform_device *pdev, unsigned int *dev_id)
        p->regtype = SCI_OF_REGTYPE(match->data);
        p->scscr = SCSCR_RE | SCSCR_TE;
 
+       if (of_find_property(np, "uart-has-rtscts", NULL))
+               p->capabilities |= SCIx_HAVE_RTSCTS;
+
        return p;
 }
 
@@ -2912,6 +3018,21 @@ static int sci_probe_single(struct platform_device *dev,
        if (ret)
                return ret;
 
+       sciport->gpios = mctrl_gpio_init(&sciport->port, 0);
+       if (IS_ERR(sciport->gpios) && PTR_ERR(sciport->gpios) != -ENOSYS)
+               return PTR_ERR(sciport->gpios);
+
+       if (p->capabilities & SCIx_HAVE_RTSCTS) {
+               if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(sciport->gpios,
+                                                       UART_GPIO_CTS)) ||
+                   !IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(sciport->gpios,
+                                                       UART_GPIO_RTS))) {
+                       dev_err(&dev->dev, "Conflicting RTS/CTS config\n");
+                       return -EINVAL;
+               }
+               sciport->port.flags |= UPF_HARD_FLOW;
+       }
+
        ret = uart_add_one_port(&sci_uart_driver, &sciport->port);
        if (ret) {
                sci_cleanup_single(sciport);