Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17b8f2a3 authored by Uwe Kleine-König's avatar Uwe Kleine-König Committed by Greg Kroah-Hartman
Browse files

serial: imx: add support for half duplex rs485



The transmitter is expected to be controlled by the UART's RTS pin.

Signed-off-by: default avatarUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent afe9cbb1
Loading
Loading
Loading
Loading
+95 −12
Original line number Diff line number Diff line
@@ -365,8 +365,23 @@ 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);
		if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
			temp &= ~UCR2_CTS;
		else
			temp |= UCR2_CTS;
		writel(temp, port->membase + UCR2);

		temp = readl(port->membase + UCR4);
		temp &= ~UCR4_TCEN;
		writel(temp, port->membase + UCR4);
	}
}

/*
@@ -560,6 +575,20 @@ 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);
		if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
			temp &= ~UCR2_CTS;
		else
			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);
@@ -715,6 +744,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)
@@ -723,8 +753,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)
@@ -733,7 +765,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++;
@@ -785,11 +816,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 (!(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)
@@ -1264,11 +1297,26 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
	if (termios->c_cflag & CRTSCTS) {
		if (sport->have_rtscts) {
			ucr2 &= ~UCR2_IRTS;

			if (port->rs485.flags & SER_RS485_ENABLED)
				/*
				 * RTS is mandatory for rs485 operation, so keep
				 * it under manual control and keep transmitter
				 * disabled.
				 */
				if (!(port->rs485.flags &
				      SER_RS485_RTS_AFTER_SEND))
					ucr2 |= UCR2_CTS;
			else
				ucr2 |= UCR2_CTSC;

		} else {
			termios->c_cflag &= ~CRTSCTS;
		}
	}
	} else if (port->rs485.flags & SER_RS485_ENABLED)
		/* disable transmitter */
		if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND))
			ucr2 |= UCR2_CTS;

	if (termios->c_cflag & CSTOPB)
		ucr2 |= UCR2_STPB;
@@ -1490,6 +1538,38 @@ 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;
	rs485conf->flags |= SER_RS485_RX_DURING_TX;

	/* 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;
		if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
			temp &= ~UCR2_CTS;
		else
			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,
@@ -1847,6 +1927,9 @@ 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.rs485.flags =
		SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX;
	sport->port.flags = UPF_BOOT_AUTOCONF;
	init_timer(&sport->timer);
	sport->timer.function = imx_timeout;