From patchwork Thu Apr 24 09:24:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yegor Yefremov X-Patchwork-Id: 4047301 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id EE597BFF02 for ; Thu, 24 Apr 2014 09:56:30 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CEA9A201BA for ; Thu, 24 Apr 2014 09:56:29 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 9616E2011E for ; Thu, 24 Apr 2014 09:56:28 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WdGMX-0008GQ-IT; Thu, 24 Apr 2014 09:54:37 +0000 Received: from ns.visionsystems.de ([62.145.30.242] helo=mail.visionsystems.de) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WdGMU-0007a9-9e for linux-arm-kernel@lists.infradead.org; Thu, 24 Apr 2014 09:54:35 +0000 Received: from localhost (localhost [127.0.0.1]) by mail.visionsystems.de (Postfix) with ESMTP id 89C7E2E443F; Thu, 24 Apr 2014 11:25:00 +0200 (CEST) Received: from mail.visionsystems.de ([127.0.0.1]) by localhost (mail.visionsystems.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id 07025-04; Thu, 24 Apr 2014 11:24:46 +0200 (CEST) Received: from visionsystems.de (kallisto.visionsystems.local [192.168.1.3]) by mail.visionsystems.de (Postfix) with ESMTP id 97A212E4447; Thu, 24 Apr 2014 11:24:46 +0200 (CEST) Received: from development1.visionsystems.local ([192.168.1.36]) by visionsystems.de with Microsoft SMTPSVC(6.0.3790.4675); Thu, 24 Apr 2014 11:24:42 +0200 From: yegorslists@googlemail.com To: linux-serial@vger.kernel.org Subject: [RFC] tty: serial: omap: use mctrl_gpio helpers Date: Thu, 24 Apr 2014 11:24:31 +0200 Message-Id: <1398331471-16745-2-git-send-email-yegorslists@googlemail.com> X-Mailer: git-send-email 1.7.7 In-Reply-To: <1398331471-16745-1-git-send-email-yegorslists@googlemail.com> References: <1398331471-16745-1-git-send-email-yegorslists@googlemail.com> X-OriginalArrivalTime: 24 Apr 2014 09:24:42.0229 (UTC) FILETIME=[06025A50:01CF5F9F] X-Virus-Scanned: amavisd-new at visionsystems.de X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140424_025434_893222_6DB1FF32 X-CRM114-Status: GOOD ( 25.51 ) X-Spam-Score: 0.6 (/) Cc: gregkh@linuxfoundation.org, linux-omap@vger.kernel.org, linux-arm-kernel@lists.infradead.org, Yegor Yefremov X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.5 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, FREEMAIL_FROM, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Yegor Yefremov This patch permits to use GPIOs to control the CTS/RTS/DTR/DSR/DCD/RI signals. Signed-off-by: Yegor Yefremov --- 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 #include #include +#include +#include #include +#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;