From: Yegor Yefremov <yegorsli...@googlemail.com>

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

Signed-off-by: Yegor Yefremov <yegorsli...@googlemail.com>
---
 drivers/tty/serial/Kconfig       |    1 +
 drivers/tty/serial/omap-serial.c |  168 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 162 insertions(+), 7 deletions(-)

diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 6e748dc..3eeaa09 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -1088,6 +1088,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 08b6b94..87dcad7 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  6
 
 #define UART_BUILD_REVISION(x, y)      (((x) << 8) | (y))
@@ -169,6 +173,9 @@ struct uart_omap_port {
 
        struct serial_rs485     rs485;
        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;
@@ -294,6 +301,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);
@@ -310,6 +338,10 @@ static void serial_omap_stop_tx(struct uart_port *port)
        /* Handle RS-485 */
        if (up->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
@@ -320,10 +352,10 @@ static void serial_omap_stop_tx(struct uart_port *port)
                        up->scr &= ~OMAP_UART_SCR_TX_EMPTY;
                        serial_out(up, UART_OMAP_SCR, up->scr);
                        res = (up->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 (up->rs485.delay_rts_after_send > 0)
                                        mdelay(up->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
@@ -425,14 +457,18 @@ static void serial_omap_start_tx(struct uart_port *port)
 
        /* Handle RS-485 */
        if (up->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 = (up->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 (up->rs485.delay_rts_before_send > 0)
                                mdelay(up->rs485.delay_rts_before_send);
                }
@@ -581,10 +617,45 @@ 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)
@@ -632,6 +703,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);
@@ -669,7 +779,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)
@@ -698,6 +809,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);
+
        if (gpio_is_valid(up->DTR_gpio) &&
            !!(mctrl & TIOCM_DTR) != up->DTR_active) {
                up->DTR_active = !up->DTR_active;
@@ -733,6 +846,8 @@ static int serial_omap_startup(struct uart_port *port)
        unsigned long flags = 0;
        int retval;
 
+       up->ms_irq_enabled = false;
+
        /*
         * Allocate the IRQ
         */
@@ -752,6 +867,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);
@@ -842,6 +966,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)
@@ -1370,6 +1496,9 @@ serial_omap_config_rs485(struct uart_port *port, struct 
serial_rs485 *rs485conf)
        unsigned long flags;
        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);
        spin_lock_irqsave(&up->port.lock, flags);
@@ -1386,12 +1515,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 = (up->rs485.flags & SER_RS485_ENABLED) ?
                        SER_RS485_RTS_AFTER_SEND : SER_RS485_RTS_ON_SEND;
                val = (up->rs485.flags & val) ? 1 : 0;
-               gpio_set_value(up->rts_gpio, val);
+               gpiod_set_value(rts_gpiod, val);
        } else
                up->rs485.flags &= ~SER_RS485_ENABLED;
 
@@ -1642,6 +1771,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 uart_omap_port   *up;
@@ -1727,6 +1876,11 @@ static int serial_omap_probe(struct platform_device 
*pdev)
                goto err_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-omap" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to