summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBhuvanchandra DV <bhuvanchandra.dv@toradex.com>2016-08-26 12:15:27 +0530
committerMax Krummenacher <max.krummenacher@toradex.com>2016-09-01 14:22:21 +0200
commit3c522150e4230f36684f4c976abf755db990f0c2 (patch)
treea35f114f8c853b936856a792cee3cfc9a6e63752
parent9f2723e14bab4fb558b4a4eb3e941876e2c55e2a (diff)
tty: serial: imx: Add support for RS-485
Use the RTS(CTS) output to control a RS485 transmitter. The UART obviously needs RTS/CTS enabled for this to work, e.g. fsl,uart-has-rtscts must be present in the device tree. Enable RS485 by either using ioctrl TIOCSRS485 or enable it in the device tree by setting linux,rs485-enabled-at-boot-time. e.g. &uart2 { status = "okay"; linux,rs485-enabled-at-boot-time; }; Signed-off-by: Bhuvanchandra DV <bhuvanchandra.dv@toradex.com> Signed-off-by: Max Krummenacher <max.krummenacher@toradex.com>
-rw-r--r--drivers/tty/serial/imx.c136
1 files changed, 126 insertions, 10 deletions
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 6970f7fb794d..defc57def37c 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -250,6 +250,8 @@ struct imx_port {
unsigned int saved_reg[10];
#define DMA_TX_IS_WORKING 1
unsigned long flags;
+
+ struct serial_rs485 rs485;
};
struct imx_port_ucrs {
@@ -322,6 +324,32 @@ static inline int is_imx6q_uart(struct imx_port *sport)
{
return sport->devdata->devtype == IMX6Q_UART;
}
+
+static void imx_rs485_config(struct imx_port *sport)
+{
+ /* unimplemented */
+ sport->rs485.delay_rts_before_send = 0;
+ sport->rs485.delay_rts_after_send = 0;
+ sport->rs485.flags |= SER_RS485_RX_DURING_TX;
+
+ /* RTS is required to control the transmitter */
+ if (!sport->have_rtscts)
+ sport->rs485.flags &= ~SER_RS485_ENABLED;
+
+ if (sport->rs485.flags & SER_RS485_ENABLED) {
+ unsigned long temp;
+
+ /* disable transmitter */
+ temp = readl(sport->port.membase + UCR2);
+ temp &= ~UCR2_CTSC;
+ if (sport->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ temp |= UCR2_CTS;
+ else
+ temp &= ~UCR2_CTS;
+ writel(temp, sport->port.membase + UCR2);
+ }
+}
+
/*
* Save and restore functions for UCR1, UCR2 and UCR3 registers
*/
@@ -398,6 +426,21 @@ static void imx_stop_tx(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned long temp;
+ /* in rs485 mode disable transmitter if shifter is empty */
+ if (sport->rs485.flags & SER_RS485_ENABLED &&
+ readl(sport->port.membase + USR2) & USR2_TXDC) {
+ temp = readl(sport->port.membase + UCR2);
+ if (sport->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ temp |= UCR2_CTS;
+ else
+ temp &= ~UCR2_CTS;
+ writel(temp, sport->port.membase + UCR2);
+
+ temp = readl(sport->port.membase + UCR4);
+ temp &= ~UCR4_TCEN;
+ writel(temp, sport->port.membase + UCR4);
+ }
+
if (USE_IRDA(sport)) {
/* half duplex - wait for end of transmission */
int n = 256;
@@ -641,6 +684,20 @@ static void imx_start_tx(struct uart_port *port)
writel(temp, sport->port.membase + UCR4);
}
+ if (sport->rs485.flags & SER_RS485_ENABLED) {
+ /* enable transmitter and shifter empty irq */
+ temp = readl(sport->port.membase + UCR2);
+ if (sport->rs485.flags & SER_RS485_RTS_ON_SEND)
+ temp |= UCR2_CTS;
+ else
+ temp &= ~UCR2_CTS;
+ writel(temp, sport->port.membase + UCR2);
+
+ temp = readl(sport->port.membase + UCR4);
+ temp |= UCR4_TCEN;
+ writel(temp, sport->port.membase + UCR4);
+ }
+
if (sport->dma_is_enabled) {
schedule_delayed_work(&sport->tsk_dma_tx, 0);
return;
@@ -776,6 +833,10 @@ static irqreturn_t imx_int(int irq, void *dev_id)
imx_rxint(irq, dev_id);
}
+ if (readl(sport->port.membase + USR2) & USR2_TXDC &&
+ readl(sport->port.membase + UCR4) & UCR4_TCEN)
+ imx_txint(irq, dev_id);
+
if (sts & USR1_TRDY &&
readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
imx_txint(irq, dev_id);
@@ -824,11 +885,14 @@ static unsigned int imx_get_mctrl(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
- if (readl(sport->port.membase + USR1) & USR1_RTSS)
- tmp |= TIOCM_CTS;
+ /* RTS must not be touched in RS485 mode */
+ if (!(sport->rs485.flags & SER_RS485_ENABLED)) {
+ if (readl(sport->port.membase + USR1) & USR1_RTSS)
+ tmp |= TIOCM_CTS;
- if (readl(sport->port.membase + UCR2) & UCR2_CTS)
- tmp |= TIOCM_RTS;
+ if (readl(sport->port.membase + UCR2) & UCR2_CTS)
+ tmp |= TIOCM_RTS;
+ }
if (readl(sport->port.membase + uts_reg(sport)) & UTS_LOOP)
tmp |= TIOCM_LOOP;
@@ -841,11 +905,16 @@ 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;
+ /* RTS must not be touched in RS485 mode */
+ if (!(sport->rs485.flags & SER_RS485_ENABLED)) {
+ temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
- writel(temp, sport->port.membase + UCR2);
+ if (mctrl & TIOCM_RTS)
+ if (!sport->dma_is_enabled)
+ temp |= UCR2_CTS;
+
+ writel(temp, sport->port.membase + UCR2);
+ }
temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP;
if (mctrl & TIOCM_LOOP)
@@ -1318,7 +1387,7 @@ static void imx_shutdown(struct uart_port *port)
* the current transcation, so hold on. There no SDMA interrupt generate,
* the "dma_wait" event cannot be waked up.
*/
- if (sport->have_rtscts) {
+ if (sport->have_rtscts && !(sport->rs485.flags & SER_RS485_ENABLED)) {
temp = readl(sport->port.membase + UCR2) & ~UCR2_CTSC;
temp |= UCR2_CTS;
writel(temp, sport->port.membase + UCR2);
@@ -1463,10 +1532,23 @@ 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 (sport->rs485.flags & SER_RS485_ENABLED) {
+ /*
+ * RTS is mandatory for rs485 operation, so keep
+ * it under manual control
+ */
+ if (sport->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ ucr2 |= UCR2_CTS;
+ } else {
+ ucr2 |= UCR2_CTSC;
+ }
} else {
termios->c_cflag &= ~CRTSCTS;
}
+ } else if (sport->rs485.flags & SER_RS485_ENABLED) {
+ if (sport->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ ucr2 |= UCR2_CTS;
}
if (termios->c_cflag & CSTOPB)
@@ -1705,6 +1787,31 @@ static void imx_poll_put_char(struct uart_port *port, unsigned char c)
}
#endif
+static int imx_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg)
+{
+ struct imx_port *sport = (struct imx_port *)port;
+
+ switch (cmd) {
+ case TIOCSRS485:
+ if (copy_from_user(&(sport->rs485), (struct serial_rs485 *) arg,
+ sizeof(struct serial_rs485)))
+ return -EFAULT;
+ if (sport->rs485.flags & SER_RS485_ENABLED)
+ imx_rs485_config(sport);
+ break;
+
+ case TIOCGRS485:
+ if (copy_to_user((struct serial_rs485 *) arg, &(sport->rs485),
+ sizeof(struct serial_rs485)))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
static struct uart_ops imx_pops = {
.tx_empty = imx_tx_empty,
.set_mctrl = imx_set_mctrl,
@@ -1721,6 +1828,7 @@ static struct uart_ops imx_pops = {
.type = imx_type,
.config_port = imx_config_port,
.verify_port = imx_verify_port,
+ .ioctl = imx_ioctl,
#if defined(CONFIG_CONSOLE_POLL)
.poll_get_char = imx_poll_get_char,
.poll_put_char = imx_poll_put_char,
@@ -2039,6 +2147,11 @@ static int serial_imx_probe_dt(struct imx_port *sport,
if (of_get_property(np, "fsl,dte-mode", NULL))
sport->dte_mode = 1;
+ if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) {
+ sport->rs485.flags |= SER_RS485_ENABLED;
+ sport->rs485.flags |= SER_RS485_RTS_ON_SEND;
+ }
+
sport->devdata = of_id->data;
return 0;
@@ -2135,6 +2248,9 @@ static int serial_imx_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, sport);
+ if (sport->rs485.flags & SER_RS485_ENABLED)
+ imx_rs485_config(sport);
+
return uart_add_one_port(&imx_reg, &sport->port);
}