[RFC] tty: serial: omap: use mctrl_gpio helpers
yegorslists at googlemail.com
yegorslists at googlemail.com
Thu Apr 24 02:24:31 PDT 2014
From: Yegor Yefremov <yegorslists at googlemail.com>
This patch permits to use GPIOs to control the CTS/RTS/DTR/DSR/DCD/RI
signals.
Signed-off-by: Yegor Yefremov <yegorslists at 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
More information about the linux-arm-kernel
mailing list