driver:serial: add RS485 support

This commit is contained in:
2020-10-25 21:43:15 +08:00
parent 508fa321b7
commit 93f9e93c44

View File

@ -37,6 +37,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
@ -224,6 +225,7 @@ struct imx_port {
unsigned short trcv_delay; /* transceiver delay */
struct clk *clk_ipg;
struct clk *clk_per;
int txen_gpio;
const struct imx_uart_data *devdata;
/* DMA fields */
@ -396,13 +398,20 @@ static void imx_stop_tx(struct uart_port *port)
/* 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)
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND){
temp &= ~UCR2_CTS;
else
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 1);
}else{
temp |= UCR2_CTS;
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 0);
}
writel(temp, port->membase + UCR2);
/* disable shifter empty irq */
temp = readl(port->membase + UCR4);
temp &= ~UCR4_TCEN;
writel(temp, port->membase + UCR4);
@ -596,14 +605,21 @@ static void imx_start_tx(struct uart_port *port)
unsigned long temp;
if (port->rs485.flags & SER_RS485_ENABLED) {
/* enable transmitter and shifter empty irq */
/* enable transmitter */
temp = readl(port->membase + UCR2);
if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
if (port->rs485.flags & SER_RS485_RTS_ON_SEND){
temp &= ~UCR2_CTS;
else
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 1);
}else{
temp |= UCR2_CTS;
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 0);
}
writel(temp, port->membase + UCR2);
/* enable shifter empty irq */
temp = readl(port->membase + UCR4);
temp |= UCR4_TCEN;
writel(temp, port->membase + UCR4);
@ -811,6 +827,9 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
if (mctrl & TIOCM_RTS)
temp |= UCR2_CTS | UCR2_CTSC;
writel(temp, sport->port.membase + UCR2);
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 0);
}
temp = readl(sport->port.membase + uts_reg(sport)) & ~UTS_LOOP;
@ -1325,25 +1344,38 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
if (sport->have_rtscts) {
ucr2 &= ~UCR2_IRTS;
if (port->rs485.flags & SER_RS485_ENABLED) {
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))
if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND)){
ucr2 |= UCR2_CTS;
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 0);
}else{
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 1);
}
} else {
ucr2 |= UCR2_CTSC;
}
} else {
termios->c_cflag &= ~CRTSCTS;
}
} else if (port->rs485.flags & SER_RS485_ENABLED)
} else if (port->rs485.flags & SER_RS485_ENABLED) {
/* disable transmitter */
if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND))
if (!(port->rs485.flags & SER_RS485_RTS_AFTER_SEND)){
ucr2 &= ~UCR2_CTS;
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 0);
}else{
ucr2 |= UCR2_CTS;
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 1);
}
}
if (termios->c_cflag & CSTOPB)
ucr2 |= UCR2_STPB;
@ -1586,19 +1618,25 @@ static int imx_rs485_config(struct uart_port *port,
rs485conf->flags |= SER_RS485_RX_DURING_TX;
/* RTS is required to control the transmitter */
if (!sport->have_rtscts)
if (sport->txen_gpio == -1 && !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)
if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND){
temp &= ~UCR2_CTS;
else
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 1);
}else{
temp |= UCR2_CTS;
if (sport->txen_gpio != -1)
gpio_set_value(sport->txen_gpio, 0);
}
writel(temp, sport->port.membase + UCR2);
}
@ -1938,6 +1976,19 @@ 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_get_property(np, "fsl,rs485-gpio-txen", NULL))
sport->txen_gpio = of_get_named_gpio(np, "fsl,rs485-gpio-txen", 0);
else
sport->txen_gpio = -1;
if (gpio_is_valid(sport->txen_gpio))
devm_gpio_request_one(&pdev->dev, sport->txen_gpio, GPIOF_OUT_INIT_LOW, "rs485-txen");
else
sport->txen_gpio = -1;
if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time"))
sport->port.rs485.flags |= SER_RS485_ENABLED;
sport->devdata = of_id->data;
return 0;