[PATCH] tty: serial: omap: use mctrl_gpio helpers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Yegor Yefremov <yegorslists@xxxxxxxxxxxxxx>

This patch permits to use GPIOs to control the CTS/RTS/DTR/DSR/DCD/RI
signals.

Signed-off-by: Yegor Yefremov <yegorslists@xxxxxxxxxxxxxx>
---
 .../devicetree/bindings/serial/omap_serial.txt     |    9 +
 drivers/tty/serial/Kconfig                         |    1 +
 drivers/tty/serial/omap-serial.c                   |  168 +++++++++++++++++++-
 3 files changed, 171 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/serial/omap_serial.txt b/Documentation/devicetree/bindings/serial/omap_serial.txt
index 54c2a15..10fcba8 100644
--- a/Documentation/devicetree/bindings/serial/omap_serial.txt
+++ b/Documentation/devicetree/bindings/serial/omap_serial.txt
@@ -16,6 +16,9 @@ Optional properties:
 - dmas : DMA specifier, consisting of a phandle to the DMA controller
          node and a DMA channel number.
 - dma-names : "rx" for receive channel, "tx" for transmit channel.
+- {rts,cts,dtr,dsr,rng,dcd}-gpios: specify a GPIO for RTS/CTS/DTR/DSR/RI/DCD
+  line respectively. It will use specified PIO instead of the peripheral
+  function pin for the UART feature. If unsure, don't specify this property.
 
 Example:
 
@@ -27,4 +30,10 @@ Example:
                         dma-names = "tx", "rx";
                         ti,hwmods = "uart4";
                         clock-frequency = <48000000>;
+                        cts-gpios = <&gpio3 5 GPIO_ACTIVE_LOW>;
+                        rts-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>;
+                        dtr-gpios = <&gpio1 12 GPIO_ACTIVE_LOW>;
+                        dsr-gpios = <&gpio1 13 GPIO_ACTIVE_LOW>;
+                        dcd-gpios = <&gpio1 14 GPIO_ACTIVE_LOW>;
+                        rng-gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
                 };
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index f8120c1..bbe0e06 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1106,6 +1106,7 @@ config SERIAL_OMAP
 	tristate "OMAP serial port support"
 	depends on ARCH_OMAP2PLUS
 	select SERIAL_CORE
+	select SERIAL_MCTRL_GPIO
 	help
 	  If you have a machine based on an Texas Instruments OMAP CPU you
 	  can enable its onboard serial ports by enabling this option.
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 7f49172..42751b0 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -43,9 +43,13 @@
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
 #include <linux/platform_data/serial-omap.h>
+#include <linux/gpio/consumer.h>
+#include <linux/err.h>
 
 #include <dt-bindings/gpio/gpio.h>
 
+#include "serial_mctrl_gpio.h"
+
 #define OMAP_MAX_HSUART_PORTS	10
 
 #define UART_BUILD_REVISION(x, y)	(((x) << 8) | (y))
@@ -164,6 +168,9 @@ struct uart_omap_port {
 	u32			features;
 
 	int			rts_gpio;
+	struct mctrl_gpios	*gpios;
+	int			gpio_irq[UART_GPIO_MAX];
+	bool			ms_irq_enabled;
 
 	struct pm_qos_request	pm_qos_request;
 	u32			latency;
@@ -301,6 +308,27 @@ static void serial_omap_enable_ms(struct uart_port *port)
 	dev_dbg(up->port.dev, "serial_omap_enable_ms+%d\n", up->port.line);
 
 	pm_runtime_get_sync(up->dev);
+
+	/*
+	* Interrupt should not be enabled twice
+	*/
+	if (up->ms_irq_enabled)
+		return;
+
+	up->ms_irq_enabled = true;
+
+	if (up->gpio_irq[UART_GPIO_CTS] >= 0)
+		enable_irq(up->gpio_irq[UART_GPIO_CTS]);
+
+	if (up->gpio_irq[UART_GPIO_DSR] >= 0)
+		enable_irq(up->gpio_irq[UART_GPIO_DSR]);
+
+	if (up->gpio_irq[UART_GPIO_RI] >= 0)
+		enable_irq(up->gpio_irq[UART_GPIO_RI]);
+
+	if (up->gpio_irq[UART_GPIO_DCD] >= 0)
+		enable_irq(up->gpio_irq[UART_GPIO_DCD]);
+
 	up->ier |= UART_IER_MSI;
 	serial_out(up, UART_IER, up->ier);
 	pm_runtime_mark_last_busy(up->dev);
@@ -317,6 +345,11 @@ static void serial_omap_stop_tx(struct uart_port *port)
 	/* Handle RS-485 */
 	if (port->rs485.flags & SER_RS485_ENABLED) {
 		if (up->scr & OMAP_UART_SCR_TX_EMPTY) {
+			struct gpio_desc *rts_gpiod;
+
+			rts_gpiod = mctrl_gpio_to_gpiod(up->gpios,
+					UART_GPIO_RTS);
+
 			/* THR interrupt is fired when both TX FIFO and TX
 			 * shift register are empty. This means there's nothing
 			 * left to transmit now, so make sure the THR interrupt
@@ -328,11 +361,11 @@ static void serial_omap_stop_tx(struct uart_port *port)
 			serial_out(up, UART_OMAP_SCR, up->scr);
 			res = (port->rs485.flags & SER_RS485_RTS_AFTER_SEND) ?
 				1 : 0;
-			if (gpio_get_value(up->rts_gpio) != res) {
+			if (gpiod_get_value(rts_gpiod) != res) {
 				if (port->rs485.delay_rts_after_send > 0)
 					mdelay(
 					port->rs485.delay_rts_after_send);
-				gpio_set_value(up->rts_gpio, res);
+				gpiod_set_value(rts_gpiod, res);
 			}
 		} else {
 			/* We're asked to stop, but there's still stuff in the
@@ -431,14 +464,18 @@ static void serial_omap_start_tx(struct uart_port *port)
 
 	/* Handle RS-485 */
 	if (port->rs485.flags & SER_RS485_ENABLED) {
+		struct gpio_desc *rts_gpiod;
+
+		rts_gpiod = mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS);
+
 		/* Fire THR interrupts when FIFO is below trigger level */
 		up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
 		serial_out(up, UART_OMAP_SCR, up->scr);
 
 		/* if rts not already enabled */
 		res = (port->rs485.flags & SER_RS485_RTS_ON_SEND) ? 1 : 0;
-		if (gpio_get_value(up->rts_gpio) != res) {
-			gpio_set_value(up->rts_gpio, res);
+		if (gpiod_get_value(rts_gpiod) != res) {
+			gpiod_set_value(rts_gpiod, res);
 			if (port->rs485.delay_rts_before_send > 0)
 				mdelay(port->rs485.delay_rts_before_send);
 		}
@@ -587,10 +624,44 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id)
 	unsigned int type;
 	irqreturn_t ret = IRQ_NONE;
 	int max_count = 256;
+	bool gpio_handled = false;
+	bool gpio_any_delta = false;
 
 	spin_lock(&up->port.lock);
 	pm_runtime_get_sync(up->dev);
 
+	if (!gpio_handled) {
+		/*
+		* Dealing with GPIO interrupt
+		*/
+		if (irq == up->gpio_irq[UART_GPIO_RI]) {
+			up->port.icount.rng++;
+			gpio_any_delta = true;
+		}
+
+		if (irq == up->gpio_irq[UART_GPIO_DSR]) {
+			up->port.icount.dsr++;
+			gpio_any_delta = true;
+		}
+
+		if (irq == up->gpio_irq[UART_GPIO_DCD]) {
+			uart_handle_dcd_change
+				(&up->port, UART_MSR_DCD);
+			gpio_any_delta = true;
+		}
+
+		if (irq == up->gpio_irq[UART_GPIO_CTS]) {
+			uart_handle_cts_change
+				(&up->port, UART_MSR_CTS);
+			gpio_any_delta = true;
+		}
+
+		if (gpio_any_delta)
+			wake_up_interruptible(&up->port.state->port.delta_msr_wait);
+
+		gpio_handled = true;
+	}
+
 	do {
 		iir = serial_in(up, UART_IIR);
 		if (iir & UART_IIR_NO_INT)
@@ -638,6 +709,45 @@ static irqreturn_t serial_omap_irq(int irq, void *dev_id)
 	return ret;
 }
 
+static void serial_omap_free_gpio_irq(struct uart_port *port)
+{
+	struct uart_omap_port *up = to_uart_omap_port(port);
+	enum mctrl_gpio_idx i;
+
+	for (i = 0; i < UART_GPIO_MAX; i++)
+		if (up->gpio_irq[i] >= 0)
+			free_irq(up->gpio_irq[i], port);
+}
+
+static int serial_omap_request_gpio_irq(struct uart_port *port)
+{
+	struct uart_omap_port *up = to_uart_omap_port(port);
+	int *irq = up->gpio_irq;
+	enum mctrl_gpio_idx i;
+	int err = 0;
+
+	for (i = 0; (i < UART_GPIO_MAX) && !err; i++) {
+		if (irq[i] < 0)
+			continue;
+
+		irq_set_status_flags(irq[i], IRQ_NOAUTOEN);
+		err = request_irq(irq[i], serial_omap_irq, IRQ_TYPE_EDGE_BOTH,
+				"omap_serial", port);
+		if (err)
+			dev_err(port->dev, "omap_startup - Can't get %d irq\n",
+				irq[i]);
+	}
+
+	/*
+	 * If something went wrong, rollback.
+	 */
+	while (err && (--i >= 0))
+		if (irq[i] >= 0)
+			free_irq(irq[i], port);
+
+	return err;
+}
+
 static unsigned int serial_omap_tx_empty(struct uart_port *port)
 {
 	struct uart_omap_port *up = to_uart_omap_port(port);
@@ -675,7 +785,8 @@ static unsigned int serial_omap_get_mctrl(struct uart_port *port)
 		ret |= TIOCM_DSR;
 	if (status & UART_MSR_CTS)
 		ret |= TIOCM_CTS;
-	return ret;
+
+	return mctrl_gpio_get(up->gpios, &ret);
 }
 
 static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
@@ -714,6 +825,8 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
 
 	pm_runtime_mark_last_busy(up->dev);
 	pm_runtime_put_autosuspend(up->dev);
+
+	mctrl_gpio_set(up->gpios, mctrl);
 }
 
 static void serial_omap_break_ctl(struct uart_port *port, int break_state)
@@ -740,6 +853,8 @@ static int serial_omap_startup(struct uart_port *port)
 	unsigned long flags = 0;
 	int retval;
 
+	up->ms_irq_enabled = false;
+
 	/*
 	 * Allocate the IRQ
 	 */
@@ -759,6 +874,15 @@ static int serial_omap_startup(struct uart_port *port)
 		disable_irq(up->wakeirq);
 	}
 
+	retval = serial_omap_request_gpio_irq(port);
+	if (retval) {
+		free_irq(up->port.irq, up);
+		if (up->wakeirq)
+			free_irq(up->wakeirq, up);
+
+		return retval;
+	}
+
 	dev_dbg(up->port.dev, "serial_omap_startup+%d\n", up->port.line);
 
 	pm_runtime_get_sync(up->dev);
@@ -847,6 +971,8 @@ static void serial_omap_shutdown(struct uart_port *port)
 	free_irq(up->port.irq, up);
 	if (up->wakeirq)
 		free_irq(up->wakeirq, up);
+	serial_omap_free_gpio_irq(port);
+	up->ms_irq_enabled = false;
 }
 
 static void serial_omap_uart_qos_work(struct work_struct *work)
@@ -1373,6 +1499,9 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
 	struct uart_omap_port *up = to_uart_omap_port(port);
 	unsigned int mode;
 	int val;
+	struct gpio_desc *rts_gpiod;
+
+	rts_gpiod = mctrl_gpio_to_gpiod(up->gpios, UART_GPIO_RTS);
 
 	pm_runtime_get_sync(up->dev);
 
@@ -1388,12 +1517,12 @@ serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf)
 	 * Just as a precaution, only allow rs485
 	 * to be enabled if the gpio pin is valid
 	 */
-	if (gpio_is_valid(up->rts_gpio)) {
+	if (!IS_ERR_OR_NULL(rts_gpiod)) {
 		/* enable / disable rts */
 		val = (port->rs485.flags & SER_RS485_ENABLED) ?
 			SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND;
 		val = (port->rs485.flags & val) ? 1 : 0;
-		gpio_set_value(up->rts_gpio, val);
+		gpiod_set_value(rts_gpiod, val);
 	} else
 		port->rs485.flags &= ~SER_RS485_ENABLED;
 
@@ -1616,6 +1745,26 @@ static int serial_omap_probe_rs485(struct uart_omap_port *up,
 	return 0;
 }
 
+static int serial_omap_init_gpios(struct uart_omap_port *up, struct device *dev)
+{
+	enum mctrl_gpio_idx i;
+	struct gpio_desc *gpiod;
+
+	up->gpios = mctrl_gpio_init(dev, 0);
+	if (IS_ERR_OR_NULL(up->gpios))
+		return -1;
+
+	for (i = 0; i < UART_GPIO_MAX; i++) {
+		gpiod = mctrl_gpio_to_gpiod(up->gpios, i);
+		if (gpiod && (gpiod_get_direction(gpiod) == GPIOF_DIR_IN))
+			up->gpio_irq[i] = gpiod_to_irq(gpiod);
+		else
+			up->gpio_irq[i] = -EINVAL;
+	}
+
+	return 0;
+}
+
 static int serial_omap_probe(struct platform_device *pdev)
 {
 	struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev);
@@ -1682,6 +1831,11 @@ static int serial_omap_probe(struct platform_device *pdev)
 		dev_info(up->port.dev, "no wakeirq for uart%d\n",
 			 up->port.line);
 
+	ret = serial_omap_init_gpios(up, &pdev->dev);
+	if (ret < 0)
+		dev_err(&pdev->dev, "%s",
+			"Failed to initialize GPIOs. The serial port may not work as expected");
+
 	ret = serial_omap_probe_rs485(up, pdev->dev.of_node);
 	if (ret < 0)
 		goto err_rs485;
-- 
1.7.7

--
To unsubscribe from this list: send the line "unsubscribe linux-serial" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Kernel Newbies]     [Security]     [Netfilter]     [Bugtraq]     [Linux PPP]     [Linux FS]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Linmodem]     [Device Mapper]     [Linux Kernel for ARM]

  Powered by Linux