[PATCH 7/7] serial: imx: add support for half duplex rs485

aurélien bouin a.bouin at gmail.com
Thu Feb 19 01:15:10 PST 2015


Hello,

2015-02-18 21:41 GMT+01:00 Uwe Kleine-König <u.kleine-koenig at pengutronix.de>:
> The transmitter is expected to be controlled by the UART's RTS pin.

You definately can be more open in using the last patch I have sent
the 11th of februrary using rs485.padding[0] that give in parameter
the GPIO to use for transmit pin

> Signed-off-by: Uwe Kleine-König <u.kleine-koenig at pengutronix.de>
> ---
>  drivers/tty/serial/imx.c | 94 +++++++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 81 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
> index 806b3cfb7f55..ad43019a4880 100644
> --- a/drivers/tty/serial/imx.c
> +++ b/drivers/tty/serial/imx.c
> @@ -364,8 +364,20 @@ static void imx_stop_tx(struct uart_port *port)
>         if (sport->dma_is_enabled && sport->dma_is_txing)
>                 return;
>
> -       temp = readl(sport->port.membase + UCR1);
> -       writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
> +       temp = readl(port->membase + UCR1);
> +       writel(temp & ~UCR1_TXMPTYEN, port->membase + UCR1);
> +
> +       /* in rs485 mode disable transmitter if shifter is empty */
> +       if (port->rs485.flags & SER_RS485_ENABLED &&
> +           readl(port->membase + USR2) & USR2_TXDC) {
> +               temp = readl(port->membase + UCR2);
> +               temp |= UCR2_CTS;
> +               writel(temp, port->membase + UCR2);
> +
> +               temp = readl(port->membase + UCR4);
> +               temp &= ~UCR4_TCEN;
> +               writel(temp, port->membase + UCR4);
> +       }
>  }
>
>  /*
> @@ -520,6 +532,17 @@ static void imx_start_tx(struct uart_port *port)
>         struct imx_port *sport = (struct imx_port *)port;
>         unsigned long temp;
>
> +       if (port->rs485.flags & SER_RS485_ENABLED) {
> +               /* enable transmitter and shifter empty irq */
> +               temp = readl(port->membase + UCR2);
> +               temp &= ~UCR2_CTS;
> +               writel(temp, port->membase + UCR2);
> +
> +               temp = readl(port->membase + UCR4);
> +               temp |= UCR4_TCEN;
> +               writel(temp, port->membase + UCR4);
> +       }
> +
>         if (!sport->dma_is_enabled) {
>                 temp = readl(sport->port.membase + UCR1);
>                 writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
> @@ -661,6 +684,7 @@ static irqreturn_t imx_int(int irq, void *dev_id)
>         unsigned int sts2;
>
>         sts = readl(sport->port.membase + USR1);
> +       sts2 = readl(sport->port.membase + USR2);
>
>         if (sts & USR1_RRDY) {
>                 if (sport->dma_is_enabled)
> @@ -669,8 +693,10 @@ static irqreturn_t imx_int(int irq, void *dev_id)
>                         imx_rxint(irq, dev_id);
>         }
>
> -       if (sts & USR1_TRDY &&
> -                       readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
> +       if ((sts & USR1_TRDY &&
> +            readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) ||
> +           (sts2 & USR2_TXDC &&
> +            readl(sport->port.membase + UCR4) & UCR4_TCEN))
>                 imx_txint(irq, dev_id);
>
>         if (sts & USR1_RTSD)
> @@ -679,7 +705,6 @@ static irqreturn_t imx_int(int irq, void *dev_id)
>         if (sts & USR1_AWAKE)
>                 writel(USR1_AWAKE, sport->port.membase + USR1);
>
> -       sts2 = readl(sport->port.membase + USR2);
>         if (sts2 & USR2_ORE) {
>                 dev_err(sport->port.dev, "Rx FIFO overrun\n");
>                 sport->port.icount.overrun++;
> @@ -731,11 +756,13 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
>         struct imx_port *sport = (struct imx_port *)port;
>         unsigned long temp;
>
> -       temp = readl(sport->port.membase + UCR2) & ~(UCR2_CTS | UCR2_CTSC);
> -       if (mctrl & TIOCM_RTS)
> -               temp |= UCR2_CTS | UCR2_CTSC;
> -
> -       writel(temp, sport->port.membase + UCR2);
> +       if (!(port->rs485.flags & SER_RS485_ENABLED)) {
> +               temp = readl(sport->port.membase + UCR2);
> +               temp &= ~(UCR2_CTS | UCR2_CTSC);
> +               if (mctrl & TIOCM_RTS)
> +                       temp |= UCR2_CTS | UCR2_CTSC;
> +               writel(temp, sport->port.membase + UCR2);
> +       }
>
>         temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP;
>         if (mctrl & TIOCM_LOOP)
> @@ -1029,7 +1056,7 @@ static int imx_startup(struct uart_port *port)
>         temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
>         writel(temp, sport->port.membase + UCR1);
>
> -       temp = read(sport->port.membase + UCR4);
> +       temp = readl(sport->port.membase + UCR4);
>         temp |= UCR4_OREN;
>         writel(temp, sport->port.membase + UCR4);
>
> @@ -1144,7 +1171,17 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>         if (termios->c_cflag & CRTSCTS) {
>                 if (sport->have_rtscts) {
>                         ucr2 &= ~UCR2_IRTS;
> -                       ucr2 |= UCR2_CTSC;
> +
> +                       if (port->rs485.flags & SER_RS485_ENABLED)
> +                               /*
> +                                * RTS is mandatory for rs485 operation, so keep
> +                                * it under manual control and keep transmitter
> +                                * disabled.
> +                                */
> +                               ucr2 |= UCR2_CTS;
> +                       else
> +                               ucr2 |= UCR2_CTSC;
> +
>
>                         /* Can we enable the DMA support? */
>                         if (is_imx6q_uart(sport) && !uart_console(port)
> @@ -1153,7 +1190,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
>                 } else {
>                         termios->c_cflag &= ~CRTSCTS;
>                 }
> -       }
> +       } else if (port->rs485.flags & SER_RS485_ENABLED)
> +               /* disable transmitter */
> +               ucr2 |= UCR2_CTS;
>
>         if (termios->c_cflag & CSTOPB)
>                 ucr2 |= UCR2_STPB;
> @@ -1374,6 +1413,34 @@ static void imx_poll_put_char(struct uart_port *port, unsigned char c)
>  }
>  #endif
>
> +static int imx_rs485_config(struct uart_port *port,
> +                           struct serial_rs485 *rs485conf)
> +{
> +       struct imx_port *sport = (struct imx_port *)port;
> +
> +       /* unimplemented */
> +       rs485conf->delay_rts_before_send = 0;
> +       rs485conf->delay_rts_after_send = 0;
> +
> +       /* RTS is required to control the transmitter */
> +       if (!sport->have_rtscts)
> +               rs485conf->flags &= ~SER_RS485_ENABLED;
> +
> +       if (rs485conf->flags & SER_RS485_ENABLED) {
> +               unsigned long temp;
> +
> +               /* disable transmitter */
> +               temp = readl(sport->port.membase + UCR2);
> +               temp &= ~UCR2_CTSC;
> +               temp |= UCR2_CTS;
> +               writel(temp, sport->port.membase + UCR2);
> +       }
> +
> +       port->rs485 = *rs485conf;
> +
> +       return 0;
> +}
> +
>  static struct uart_ops imx_pops = {
>         .tx_empty       = imx_tx_empty,
>         .set_mctrl      = imx_set_mctrl,
> @@ -1731,6 +1798,7 @@ static int serial_imx_probe(struct platform_device *pdev)
>         sport->port.irq = rxirq;
>         sport->port.fifosize = 32;
>         sport->port.ops = &imx_pops;
> +       sport->port.rs485_config = imx_rs485_config;
>         sport->port.flags = UPF_BOOT_AUTOCONF;
>         init_timer(&sport->timer);
>         sport->timer.function = imx_timeout;
> --
> 2.1.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the linux-arm-kernel mailing list