[PATCH 4/8] tty/serial: at91: add cts control via gpio

Richard Genoud richard.genoud at gmail.com
Fri Feb 7 09:59:11 EST 2014


On sam9x5, dedicated CTS (and RTS) pins are unusable together with the
LCDC, the EMAC, or the MMC because they share the same line.

This patch permits to use a GPIO to control the CTS line.

Signed-off-by: Richard Genoud <richard.genoud at gmail.com>
---
 .../devicetree/bindings/serial/atmel-usart.txt     |   3 +
 arch/arm/mach-at91/at91rm9200_devices.c            |   5 +
 arch/arm/mach-at91/at91sam9260_devices.c           |   7 ++
 arch/arm/mach-at91/at91sam9261_devices.c           |   4 +
 arch/arm/mach-at91/at91sam9263_devices.c           |   4 +
 arch/arm/mach-at91/at91sam9g45_devices.c           |   5 +
 arch/arm/mach-at91/at91sam9rl_devices.c            |   5 +
 drivers/tty/serial/atmel_serial.c                  | 112 +++++++++++++++++++--
 include/linux/platform_data/atmel.h                |   1 +
 9 files changed, 136 insertions(+), 10 deletions(-)

diff --git a/Documentation/devicetree/bindings/serial/atmel-usart.txt b/Documentation/devicetree/bindings/serial/atmel-usart.txt
index 17c1042b2df8..6c0898e4b58e 100644
--- a/Documentation/devicetree/bindings/serial/atmel-usart.txt
+++ b/Documentation/devicetree/bindings/serial/atmel-usart.txt
@@ -15,6 +15,8 @@ Optional properties:
 - atmel,use-dma-tx: use of PDC or DMA for transmitting data
 - rts-gpios: specify a GPIO for RTS line. It will use specified PIO instead of the peripheral
   function pin for the USART RTS feature. If unsure, don't specify this property.
+- cts-gpios: specify a GPIO for CTS line. It will use specified PIO instead of the peripheral
+  function pin for the USART CTS feature. If unsure, don't specify this property.
 - add dma bindings for dma transfer:
 	- dmas: DMA specifier, consisting of a phandle to DMA controller node,
 		memory peripheral interface and USART DMA channel ID, FIFO configuration.
@@ -36,6 +38,7 @@ Example:
 		atmel,use-dma-rx;
 		atmel,use-dma-tx;
 		rts-gpios = <&pioD 15 0>;
+		cts-gpios = <&pioD 16 0>;
 	};
 
 - use DMA:
diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c
index 605add05af7e..4688a85cabc4 100644
--- a/arch/arm/mach-at91/at91rm9200_devices.c
+++ b/arch/arm/mach-at91/at91rm9200_devices.c
@@ -923,6 +923,7 @@ static struct atmel_uart_data dbgu_data = {
 	.use_dma_tx	= 0,
 	.use_dma_rx	= 0,		/* DBGU not capable of receive DMA */
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -962,6 +963,7 @@ static struct atmel_uart_data uart0_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1013,6 +1015,7 @@ static struct atmel_uart_data uart1_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1065,6 +1068,7 @@ static struct atmel_uart_data uart2_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -1109,6 +1113,7 @@ static struct atmel_uart_data uart3_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart3_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index b52527c78b12..5e8f0d1add1d 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -820,6 +820,7 @@ static struct atmel_uart_data dbgu_data = {
 	.use_dma_tx	= 0,
 	.use_dma_rx	= 0,		/* DBGU not capable of receive DMA */
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -859,6 +860,7 @@ static struct atmel_uart_data uart0_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -911,6 +913,7 @@ static struct atmel_uart_data uart1_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -955,6 +958,7 @@ static struct atmel_uart_data uart2_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -999,6 +1003,7 @@ static struct atmel_uart_data uart3_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart3_dmamask = DMA_BIT_MASK(32);
@@ -1043,6 +1048,7 @@ static struct atmel_uart_data uart4_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart4_dmamask = DMA_BIT_MASK(32);
@@ -1082,6 +1088,7 @@ static struct atmel_uart_data uart5_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart5_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index 6c1a2ecc306f..f0ccc835e331 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -881,6 +881,7 @@ static struct atmel_uart_data dbgu_data = {
 	.use_dma_tx	= 0,
 	.use_dma_rx	= 0,		/* DBGU not capable of receive DMA */
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -920,6 +921,7 @@ static struct atmel_uart_data uart0_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -964,6 +966,7 @@ static struct atmel_uart_data uart1_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1008,6 +1011,7 @@ static struct atmel_uart_data uart2_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart2_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index 97cc2a0d6f90..1e696ceacfaf 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -1325,6 +1325,7 @@ static struct atmel_uart_data dbgu_data = {
 	.use_dma_tx	= 0,
 	.use_dma_rx	= 0,		/* DBGU not capable of receive DMA */
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -1364,6 +1365,7 @@ static struct atmel_uart_data uart0_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1408,6 +1410,7 @@ static struct atmel_uart_data uart1_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1452,6 +1455,7 @@ static struct atmel_uart_data uart2_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart2_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
index c10149588e21..b75eb826b803 100644
--- a/arch/arm/mach-at91/at91sam9g45_devices.c
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -1588,6 +1588,7 @@ static struct atmel_uart_data dbgu_data = {
 	.use_dma_tx	= 0,
 	.use_dma_rx	= 0,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -1627,6 +1628,7 @@ static struct atmel_uart_data uart0_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1671,6 +1673,7 @@ static struct atmel_uart_data uart1_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1715,6 +1718,7 @@ static struct atmel_uart_data uart2_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -1759,6 +1763,7 @@ static struct atmel_uart_data uart3_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart3_dmamask = DMA_BIT_MASK(32);
diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c
index 4120af972b61..67bf5811b38f 100644
--- a/arch/arm/mach-at91/at91sam9rl_devices.c
+++ b/arch/arm/mach-at91/at91sam9rl_devices.c
@@ -957,6 +957,7 @@ static struct atmel_uart_data dbgu_data = {
 	.use_dma_tx	= 0,
 	.use_dma_rx	= 0,		/* DBGU not capable of receive DMA */
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 dbgu_dmamask = DMA_BIT_MASK(32);
@@ -996,6 +997,7 @@ static struct atmel_uart_data uart0_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart0_dmamask = DMA_BIT_MASK(32);
@@ -1048,6 +1050,7 @@ static struct atmel_uart_data uart1_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart1_dmamask = DMA_BIT_MASK(32);
@@ -1092,6 +1095,7 @@ static struct atmel_uart_data uart2_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart2_dmamask = DMA_BIT_MASK(32);
@@ -1136,6 +1140,7 @@ static struct atmel_uart_data uart3_data = {
 	.use_dma_tx	= 1,
 	.use_dma_rx	= 1,
 	.rts_gpio	= -EINVAL,
+	.cts_gpio	= -EINVAL,
 };
 
 static u64 uart3_dmamask = DMA_BIT_MASK(32);
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 7ef99d7e070b..7a6b0506c050 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -43,6 +43,7 @@
 #include <linux/platform_data/atmel.h>
 #include <linux/timer.h>
 #include <linux/gpio.h>
+#include <linux/irq.h>
 
 #include <asm/io.h>
 #include <asm/ioctls.h>
@@ -55,6 +56,8 @@
 #define SUPPORT_SYSRQ
 #endif
 
+#define INVALID_IRQ  ((unsigned)-1)
+
 #include <linux/serial_core.h>
 
 static void atmel_start_rx(struct uart_port *port);
@@ -128,6 +131,8 @@ struct atmel_uart_char {
 
 struct gpio_lines {
 	int rts;	/* optional RTS GPIO */
+	int cts;	/* optional CTS GPIO */
+	int cts_irq;
 };
 
 /*
@@ -168,6 +173,7 @@ struct atmel_uart_port {
 	struct serial_rs485	rs485;		/* rs485 settings */
 	struct gpio_lines	gpio;
 	unsigned int		tx_done_mask;
+	bool			ms_irq_enabled;
 	bool			is_usart;	/* usart or uart */
 	struct timer_list	uart_timer;	/* uart timer */
 	int (*prepare_rx)(struct uart_port *port);
@@ -241,6 +247,23 @@ 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 = 0;
+
+	status = UART_GET_CSR(port);
+
+	if (gpio_is_valid(atmel_port->gpio.cts)) {
+		if (gpio_get_value(atmel_port->gpio.cts))
+			status |= ATMEL_US_CTS;
+		else
+			status &= ~ATMEL_US_CTS;
+	}
+
+	return status;
+}
+
 /* Enable or disable the rs485 support */
 void atmel_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
 {
@@ -352,7 +375,7 @@ static u_int atmel_get_mctrl(struct uart_port *port)
 {
 	unsigned int status, ret = 0;
 
-	status = UART_GET_CSR(port);
+	status = atmel_get_lines_status(port);
 
 	/*
 	 * The control signals are active low.
@@ -453,8 +476,25 @@ static void atmel_stop_rx(struct uart_port *port)
  */
 static void atmel_enable_ms(struct uart_port *port)
 {
-	UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC
-			| ATMEL_US_DCDIC | ATMEL_US_CTSIC);
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
+	uint32_t ier;
+
+	/*
+	 * Interrupt should not be enabled twice
+	 */
+	if (atmel_port->ms_irq_enabled)
+		return;
+
+	atmel_port->ms_irq_enabled = true;
+
+	ier = ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC;
+
+	if (atmel_port->gpio.cts_irq != INVALID_IRQ)
+		enable_irq(atmel_port->gpio.cts_irq);
+	else
+		ier |= ATMEL_US_CTSIC;
+
+	UART_PUT_IER(port, ier);
 }
 
 /*
@@ -522,7 +562,7 @@ static void atmel_rx_chars(struct uart_port *port)
 	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	unsigned int status, ch;
 
-	status = UART_GET_CSR(port);
+	status = atmel_get_lines_status(port);
 	while (status & ATMEL_US_RXRDY) {
 		ch = UART_GET_CHAR(port);
 
@@ -556,7 +596,7 @@ static void atmel_rx_chars(struct uart_port *port)
 		}
 
 		atmel_buffer_rx_char(port, status, ch);
-		status = UART_GET_CSR(port);
+		status = atmel_get_lines_status(port);
 	}
 
 	tasklet_schedule(&atmel_port->tasklet);
@@ -1043,11 +1083,22 @@ atmel_handle_status(struct uart_port *port, unsigned int pending,
 static irqreturn_t atmel_interrupt(int irq, void *dev_id)
 {
 	struct uart_port *port = dev_id;
+	struct atmel_uart_port *atmel_port = to_atmel_uart_port(port);
 	unsigned int status, pending, pass_counter = 0;
+	bool gpio_handled = false;
 
 	do {
-		status = UART_GET_CSR(port);
+		status = atmel_get_lines_status(port);
 		pending = status & UART_GET_IMR(port);
+		if (!gpio_handled) {
+			/*
+			 * Dealing with GPIO interrupt
+			 */
+			if ((irq !=  INVALID_IRQ) &&
+			    (irq == atmel_port->gpio.cts_irq))
+				pending |= ATMEL_US_CTSIC;
+			gpio_handled = true;
+		}
 		if (!pending)
 			break;
 
@@ -1527,6 +1578,22 @@ static void atmel_get_ip_name(struct uart_port *port)
 	}
 }
 
+static int atmel_request_gpio_irq(struct uart_port *port, int irq,
+				  const char *name)
+{
+	int err = 0;
+
+	if (irq == INVALID_IRQ)
+		goto out;
+
+	irq_set_status_flags(irq, IRQ_NOAUTOEN);
+	err = request_irq(irq, atmel_interrupt, IRQ_TYPE_EDGE_BOTH, name, port);
+	if (err)
+		dev_err(port->dev, "atmel_startup - Can't get %s\n", name);
+out:
+	return err;
+}
+
 /*
  * Perform initialization and enable port for reception
  */
@@ -1543,6 +1610,7 @@ static int atmel_startup(struct uart_port *port)
 	 * handle an unexpected interrupt
 	 */
 	UART_PUT_IDR(port, -1);
+	atmel_port->ms_irq_enabled = false;
 
 	/*
 	 * Allocate the IRQ
@@ -1555,6 +1623,14 @@ static int atmel_startup(struct uart_port *port)
 	}
 
 	/*
+	 * Get the GPIO lines IRQ
+	 */
+	retval = atmel_request_gpio_irq(port, atmel_port->gpio.cts_irq,
+					"atmel_cts_irq");
+	if (retval)
+		goto free_ctrl_irq;
+
+	/*
 	 * Initialize DMA (if necessary)
 	 */
 	atmel_init_property(atmel_port, pdev);
@@ -1572,7 +1648,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;
 
 	/*
@@ -1618,6 +1694,11 @@ static int atmel_startup(struct uart_port *port)
 	}
 
 	return 0;
+
+free_ctrl_irq:
+	free_irq(port->irq, port);
+
+	return retval;
 }
 
 /*
@@ -1665,9 +1746,13 @@ static void atmel_shutdown(struct uart_port *port)
 	atmel_port->rx_ring.tail = 0;
 
 	/*
-	 * Free the interrupt
+	 * Free the interrupts
 	 */
 	free_irq(port->irq, port);
+	if (atmel_port->gpio.cts_irq != INVALID_IRQ)
+		free_irq(atmel_port->gpio.cts_irq, port);
+
+	atmel_port->ms_irq_enabled = false;
 }
 
 /*
@@ -2367,6 +2452,8 @@ static int atmel_init_gpios(struct atmel_uart_port *atmel_port,
 
 	ret = atmel_request_gpio(&pdev->dev, atmel_port->gpio.rts,
 				 "RTS", NULL);
+	ret += atmel_request_gpio(&pdev->dev, atmel_port->gpio.cts,
+				  "CTS", &atmel_port->gpio.cts_irq);
 	return ret;
 }
 
@@ -2406,10 +2493,15 @@ static int atmel_serial_probe(struct platform_device *pdev)
 	port->backup_imr = 0;
 	port->uart.line = ret;
 	port->gpio.rts = -EINVAL; /* Invalid, zero could be valid */
-	if (pdata)
+	port->gpio.cts = -EINVAL;
+	port->gpio.cts_irq = INVALID_IRQ;
+	if (pdata) {
 		port->gpio.rts = pdata->rts_gpio;
-	else if (np)
+		port->gpio.cts = pdata->cts_gpio;
+	} else if (np) {
 		port->gpio.rts = of_get_named_gpio(np, "rts-gpios", 0);
+		port->gpio.cts = of_get_named_gpio(np, "cts-gpios", 0);
+	}
 
 	ret = atmel_init_gpios(port, pdev);
 	if (ret)
diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h
index e26b0c14edea..166a99ca911f 100644
--- a/include/linux/platform_data/atmel.h
+++ b/include/linux/platform_data/atmel.h
@@ -85,6 +85,7 @@ struct atmel_uart_data {
 	void __iomem		*regs;		/* virt. base address, if any */
 	struct serial_rs485	rs485;		/* rs485 settings */
 	int			rts_gpio;	/* optional RTS GPIO */
+	int			cts_gpio;	/* optional CTS GPIO */
 };
 
  /* Touchscreen Controller */
-- 
1.8.5




More information about the linux-arm-kernel mailing list