summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUwe Kleine-König <u.kleine-koenig@pengutronix.de>2015-09-22 20:53:31 +0200
committerUwe Kleine-König <u.kleine-koenig@pengutronix.de>2015-09-30 08:52:20 +0200
commit0d34e02b57e3fb2fb65a6e619cc37db10b6fbe7e (patch)
tree43b1d11ba6fbe657ef14d908eb69b165d893aab2
parent043bdcd2cebb9df73471569d28e000a828e9bfc6 (diff)
downloadlinux-topic/serial-handshaking.tar.gz
linux-topic/serial-handshaking.tar.xz
serial: imx: implement handshaking using gpios with the mctrl_gpio helpertopic/serial-handshaking
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
-rw-r--r--drivers/tty/serial/Kconfig1
-rw-r--r--drivers/tty/serial/imx.c89
2 files changed, 70 insertions, 20 deletions
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index c0ed0c733c9d..dc40ffb61b25 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -574,6 +574,7 @@ config SERIAL_IMX
depends on ARCH_MXC || COMPILE_TEST
select SERIAL_CORE
select RATIONAL
+ select SERIAL_MCTRL_GPIO if GPIOLIB
help
If you have a machine based on a Motorola IMX CPU you
can enable its onboard serial port by enabling this option.
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 0a58fd07c840..c2a38eb38805 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -44,6 +44,8 @@
#include <linux/platform_data/serial-imx.h>
#include <linux/platform_data/dma-imx.h>
+#include "serial_mctrl_gpio.h"
+
/* Register definitions */
#define URXD0 0x0 /* Receiver Register */
#define URTX0 0x40 /* Transmitter Register */
@@ -208,6 +210,8 @@ struct imx_port {
struct clk *clk_per;
const struct imx_uart_data *devdata;
+ struct mctrl_gpios *gpios;
+
/* DMA fields */
unsigned int dma_is_inited:1;
unsigned int dma_is_enabled:1;
@@ -310,6 +314,26 @@ static void imx_port_ucrs_restore(struct uart_port *port,
}
#endif
+static void imx_port_rts_active(struct imx_port *sport, unsigned long *ucr2)
+{
+ *ucr2 &= ~UCR2_CTSC;
+ *ucr2 |= UCR2_CTS;
+
+ mctrl_gpio_set(sport->gpios, sport->port.mctrl | TIOCM_RTS);
+}
+
+static void imx_port_rts_inactive(struct imx_port *sport, unsigned long *ucr2)
+{
+ *ucr2 &= ~(UCR2_CTSC | UCR2_CTS);
+
+ mctrl_gpio_set(sport->gpios, sport->port.mctrl & ~TIOCM_RTS);
+}
+
+static void imx_port_rts_auto(struct imx_port *sport, unsigned long *ucr2)
+{
+ *ucr2 |= UCR2_CTSC;
+}
+
/*
* interrupts disabled on entry
*/
@@ -333,9 +357,9 @@ static void imx_stop_tx(struct uart_port *port)
readl(port->membase + USR2) & USR2_TXDC) {
temp = readl(port->membase + UCR2);
if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
- temp &= ~UCR2_CTS;
+ imx_port_rts_inactive(sport, &temp);
else
- temp |= UCR2_CTS;
+ imx_port_rts_active(sport, &temp);
writel(temp, port->membase + UCR2);
temp = readl(port->membase + UCR4);
@@ -377,6 +401,8 @@ static void imx_enable_ms(struct uart_port *port)
struct imx_port *sport = (struct imx_port *)port;
mod_timer(&sport->timer, jiffies);
+
+ mctrl_gpio_enable_ms(sport->gpios);
}
static void imx_dma_tx(struct imx_port *sport);
@@ -536,14 +562,14 @@ 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 */
temp = readl(port->membase + UCR2);
if (port->rs485.flags & SER_RS485_RTS_ON_SEND)
- temp &= ~UCR2_CTS;
+ imx_port_rts_inactive(sport, &temp);
else
- temp |= UCR2_CTS;
+ imx_port_rts_active(sport, &temp);
writel(temp, port->membase + UCR2);
+ /* enable transmitter and shifter empty irq */
temp = readl(port->membase + UCR4);
temp |= UCR4_TCEN;
writel(temp, port->membase + UCR4);
@@ -754,9 +780,8 @@ static unsigned int imx_tx_empty(struct uart_port *port)
/*
* We have a modem side uart, so the meanings of RTS and CTS are inverted.
*/
-static unsigned int imx_get_mctrl(struct uart_port *port)
+static unsigned int imx_get_hwmctrl(struct imx_port *sport)
{
- struct imx_port *sport = (struct imx_port *)port;
unsigned int tmp = TIOCM_DSR;
unsigned usr1 = readl(sport->port.membase + USR1);
@@ -774,6 +799,16 @@ static unsigned int imx_get_mctrl(struct uart_port *port)
return tmp;
}
+static unsigned int imx_get_mctrl(struct uart_port *port)
+{
+ struct imx_port *sport = (struct imx_port *)port;
+ unsigned int ret = imx_get_hwmctrl(sport);
+
+ mctrl_gpio_get(sport->gpios, &ret);
+
+ return ret;
+}
+
static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
struct imx_port *sport = (struct imx_port *)port;
@@ -796,6 +831,8 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
if (mctrl & TIOCM_LOOP)
temp |= UTS_LOOP;
writel(temp, sport->port.membase + uts_reg(sport));
+
+ mctrl_gpio_set(sport->gpios, mctrl);
}
/*
@@ -828,7 +865,7 @@ static void imx_mctrl_check(struct imx_port *sport)
{
unsigned int status, changed;
- status = imx_get_mctrl(&sport->port);
+ status = imx_get_hwmctrl(sport);
changed = status ^ sport->old_status;
if (changed == 0)
@@ -1212,6 +1249,8 @@ static void imx_shutdown(struct uart_port *port)
imx_uart_dma_exit(sport);
}
+ mctrl_gpio_disable_ms(sport->gpios);
+
spin_lock_irqsave(&sport->port.lock, flags);
temp = readl(sport->port.membase + UCR2);
temp &= ~(UCR2_TXEN);
@@ -1289,9 +1328,10 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
{
struct imx_port *sport = (struct imx_port *)port;
unsigned long flags;
- unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
+ unsigned long ucr2, old_ucr1, old_txrxen;
+ unsigned int baud, quot;
unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
- unsigned int div, ufcr;
+ unsigned long div, ufcr;
unsigned long num, denom;
uint64_t tdiv64;
@@ -1320,11 +1360,13 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
* it under manual control and keep transmitter
* disabled.
*/
- if (!(port->rs485.flags &
- SER_RS485_RTS_AFTER_SEND))
- ucr2 |= UCR2_CTS;
+ if (port->rs485.flags &
+ SER_RS485_RTS_AFTER_SEND)
+ imx_port_rts_inactive(sport, &ucr2);
+ else
+ imx_port_rts_active(sport, &ucr2);
} else {
- ucr2 |= UCR2_CTSC;
+ imx_port_rts_auto(sport, &ucr2);
}
/* Can we enable the DMA support? */
@@ -1334,10 +1376,14 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
} 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))
- ucr2 |= UCR2_CTS;
+ if (port->rs485.flags & SER_RS485_RTS_AFTER_SEND)
+ imx_port_rts_inactive(sport, &ucr2);
+ else
+ imx_port_rts_active(sport, &ucr2);
+ }
+
if (termios->c_cflag & CSTOPB)
ucr2 |= UCR2_STPB;
@@ -1580,11 +1626,10 @@ static int imx_rs485_config(struct uart_port *port,
/* disable transmitter */
temp = readl(sport->port.membase + UCR2);
- temp &= ~UCR2_CTSC;
if (rs485conf->flags & SER_RS485_RTS_AFTER_SEND)
- temp &= ~UCR2_CTS;
+ imx_port_rts_inactive(sport, &temp);
else
- temp |= UCR2_CTS;
+ imx_port_rts_active(sport, &temp);
writel(temp, sport->port.membase + UCR2);
}
@@ -1920,6 +1965,10 @@ static int serial_imx_probe(struct platform_device *pdev)
sport->timer.function = imx_timeout;
sport->timer.data = (unsigned long)sport;
+ sport->gpios = mctrl_gpio_init(&sport->port, 0);
+ if (IS_ERR(sport->gpios))
+ return PTR_ERR(sport->gpios);
+
sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(sport->clk_ipg)) {
ret = PTR_ERR(sport->clk_ipg);