From b7d7f54a8cb79c7bb3dcb7bc538a7f6a71b33e74 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Thu, 24 Apr 2014 10:57:23 +0200 Subject: [PATCH] tty: serial: omap: use mctrl_gpio helpers 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 649b784..f7bfbbe 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1127,6 +1127,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 18c30ca..d10f960 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)) @@ -165,6 +169,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; @@ -302,6 +309,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); @@ -318,6 +346,11 @@ 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 @@ -328,10 +361,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 @@ -430,14 +463,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); } @@ -586,10 +623,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) @@ -637,6 +708,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); @@ -674,7 +784,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) @@ -702,6 +813,8 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl) serial_out(up, UART_MCR, up->mcr); 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) @@ -728,6 +841,8 @@ static int serial_omap_startup(struct uart_port *port) unsigned long flags = 0; int retval; + up->ms_irq_enabled = false; + /* * Allocate the IRQ */ @@ -747,6 +862,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); @@ -837,6 +961,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) @@ -1362,6 +1488,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); @@ -1378,12 +1507,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; @@ -1634,6 +1763,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); @@ -1693,6 +1842,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