[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