aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/atmel_serial.c
diff options
context:
space:
mode:
authorRichard Genoud <richard.genoud@gmail.com>2014-05-13 20:20:44 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-28 12:49:44 -0700
commite0b0baadb7a4509bdcd5ba37d0be61e2c4bb0d48 (patch)
treeea9d6937dddf11ed65c8b9c2f8847213f73e2d2e /drivers/tty/serial/atmel_serial.c
parenttty/serial: Add GPIOLIB helpers for controlling modem lines (diff)
downloadlinux-dev-e0b0baadb7a4509bdcd5ba37d0be61e2c4bb0d48.tar.xz
linux-dev-e0b0baadb7a4509bdcd5ba37d0be61e2c4bb0d48.zip
tty/serial: at91: use mctrl_gpio helpers
On sam9x5, dedicated CTS (and RTS) pins are unusable together with the LCDC, the EMAC, or the MMC because they share the same line. Moreover, the USART controller doesn't handle DTR/DSR/DCD/RI signals, so we have to control them via GPIO. This patch permits to use GPIOs to control the CTS/RTS/DTR/DSR/DCD/RI signals. Signed-off-by: Richard Genoud <richard.genoud@gmail.com> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial/atmel_serial.c')
-rw-r--r--drivers/tty/serial/atmel_serial.c105
1 files changed, 70 insertions, 35 deletions
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 53eeea13ff16..43ca659c1d4b 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -43,6 +43,8 @@
#include <linux/platform_data/atmel.h>
#include <linux/timer.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/err.h>
#include <asm/io.h>
#include <asm/ioctls.h>
@@ -57,6 +59,8 @@
#include <linux/serial_core.h>
+#include "serial_mctrl_gpio.h"
+
static void atmel_start_rx(struct uart_port *port);
static void atmel_stop_rx(struct uart_port *port);
@@ -162,7 +166,7 @@ struct atmel_uart_port {
struct circ_buf rx_ring;
struct serial_rs485 rs485; /* rs485 settings */
- int rts_gpio; /* optional RTS GPIO */
+ struct mctrl_gpios *gpios;
unsigned int tx_done_mask;
bool is_usart; /* usart or uart */
struct timer_list uart_timer; /* uart timer */
@@ -237,6 +241,50 @@ static bool atmel_use_dma_rx(struct uart_port *port)
return atmel_port->use_dma_rx;
}
+static unsigned int atmel_get_lines_status(struct uart_port *port)
+{
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned int status, ret = 0;
+
+ status = UART_GET_CSR(port);
+
+ mctrl_gpio_get(atmel_port->gpios, &ret);
+
+ if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
+ UART_GPIO_CTS))) {
+ if (ret & TIOCM_CTS)
+ status &= ~ATMEL_US_CTS;
+ else
+ status |= ATMEL_US_CTS;
+ }
+
+ if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
+ UART_GPIO_DSR))) {
+ if (ret & TIOCM_DSR)
+ status &= ~ATMEL_US_DSR;
+ else
+ status |= ATMEL_US_DSR;
+ }
+
+ if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
+ UART_GPIO_RI))) {
+ if (ret & TIOCM_RI)
+ status &= ~ATMEL_US_RI;
+ else
+ status |= ATMEL_US_RI;
+ }
+
+ if (!IS_ERR_OR_NULL(mctrl_gpio_to_gpiod(atmel_port->gpios,
+ UART_GPIO_DCD))) {
+ if (ret & TIOCM_CD)
+ status &= ~ATMEL_US_DCD;
+ else
+ status |= ATMEL_US_DCD;
+ }
+
+ return status;
+}
+
/* Enable or disable the rs485 support */
void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
{
@@ -296,17 +344,6 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
unsigned int mode;
struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
- /*
- * AT91RM9200 Errata #39: RTS0 is not internally connected
- * to PA21. We need to drive the pin as a GPIO.
- */
- if (gpio_is_valid(atmel_port->rts_gpio)) {
- if (mctrl & TIOCM_RTS)
- gpio_set_value(atmel_port->rts_gpio, 0);
- else
- gpio_set_value(atmel_port->rts_gpio, 1);
- }
-
if (mctrl & TIOCM_RTS)
control |= ATMEL_US_RTSEN;
else
@@ -319,6 +356,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
UART_PUT_CR(port, control);
+ mctrl_gpio_set(atmel_port->gpios, mctrl);
+
/* Local loopback mode? */
mode = UART_GET_MR(port) & ~ATMEL_US_CHMODE;
if (mctrl & TIOCM_LOOP)
@@ -346,7 +385,8 @@ static void atmel_set_mctrl(struct uart_port *port, u_int mctrl)
*/
static u_int atmel_get_mctrl(struct uart_port *port)
{
- unsigned int status, ret = 0;
+ struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+ unsigned int ret = 0, status;
status = UART_GET_CSR(port);
@@ -362,7 +402,7 @@ static u_int atmel_get_mctrl(struct uart_port *port)
if (!(status & ATMEL_US_RI))
ret |= TIOCM_RI;
- return ret;
+ return mctrl_gpio_get(atmel_port->gpios, &ret);
}
/*
@@ -1042,7 +1082,7 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
unsigned int status, pending, pass_counter = 0;
do {
- status = UART_GET_CSR(port);
+ status = atmel_get_lines_status(port);
pending = status & UART_GET_IMR(port);
if (!pending)
break;
@@ -1568,7 +1608,7 @@ static int atmel_startup(struct uart_port *port)
}
/* Save current CSR for comparison in atmel_tasklet_func() */
- atmel_port->irq_status_prev = UART_GET_CSR(port);
+ atmel_port->irq_status_prev = atmel_get_lines_status(port);
atmel_port->irq_status = atmel_port->irq_status_prev;
/*
@@ -2324,6 +2364,15 @@ static int atmel_serial_resume(struct platform_device *pdev)
#define atmel_serial_resume NULL
#endif
+static int atmel_init_gpios(struct atmel_uart_port *p, struct device *dev)
+{
+ p->gpios = mctrl_gpio_init(dev, 0);
+ if (IS_ERR_OR_NULL(p->gpios))
+ return -1;
+
+ return 0;
+}
+
static int atmel_serial_probe(struct platform_device *pdev)
{
struct atmel_uart_port *port;
@@ -2359,25 +2408,11 @@ static int atmel_serial_probe(struct platform_device *pdev)
port = &atmel_ports[ret];
port->backup_imr = 0;
port->uart.line = ret;
- port->rts_gpio = -EINVAL; /* Invalid, zero could be valid */
- if (pdata)
- port->rts_gpio = pdata->rts_gpio;
- else if (np)
- port->rts_gpio = of_get_named_gpio(np, "rts-gpios", 0);
-
- if (gpio_is_valid(port->rts_gpio)) {
- ret = devm_gpio_request(&pdev->dev, port->rts_gpio, "RTS");
- if (ret) {
- dev_err(&pdev->dev, "error requesting RTS GPIO\n");
- goto err;
- }
- /* Default to 1 as RTS is active low */
- ret = gpio_direction_output(port->rts_gpio, 1);
- if (ret) {
- dev_err(&pdev->dev, "error setting up RTS GPIO\n");
- goto err;
- }
- }
+
+ ret = atmel_init_gpios(port, &pdev->dev);
+ if (ret < 0)
+ dev_err(&pdev->dev, "%s",
+ "Failed to initialize GPIOs. The serial port may not work as expected");
ret = atmel_init_port(port, pdev);
if (ret)