[PATCH] OMAP/serial: Add support for driving a GPIO as DTR.

From: NeilBrown
Date: Sun Jul 29 2012 - 20:30:41 EST



Hi all,
this is my current patch which answers my question from May:
Question: How to power-manage UART-attached devices.

It teaches omap2/serial about the possibility of a GPIO which is to be
driven as a DTR line.
This allows me to power on/off devices that are accessed via a serial port
(GPS, Bluetooth) when the port is opened/closed. I simply write a driver
which exports a GPIO line and does the required magic when it is driven
high or low.

- because '0' is a valid GPIO number, I added an extra port_info field
call 'DTR_present' so existing code wouldn't accidentally request to
use GPIO-0 as a DTR - is there a better way to handle this?
- I added a DTR_inverted field so you could choose the polarity of the
GPIO line ... I'm not really sure this is needed so I'll probably drop it
unless encouraged otherwise.

- If the gpio 'can_sleep', then I need a work-queue to effect the change, and
I currently use the 'qos_work' rather than adding another work handler.
As both the qos_work and the gpio work tasks are idempotent and rare, this
seems reasonable. Is it OK? should I make another work_struct? or
something else?

Thanks,
NeilBrown




---------------------
OMAP hardware doesn't provide a phyisical DTR line, but
some configurations may need a DTR line which tracks whether
the device is open or not.

So allow a gpio to be configured as the DTR line.

Signed-off-by: NeilBrown <neilb@xxxxxxx>

diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index c1b93c7..25d53b2 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -304,6 +304,9 @@ void __init omap_serial_init_port(struct omap_board_data *bdata,
omap_up.dma_rx_timeout = info->dma_rx_timeout;
omap_up.dma_rx_poll_rate = info->dma_rx_poll_rate;
omap_up.autosuspend_timeout = info->autosuspend_timeout;
+ omap_up.DTR_gpio = info->DTR_gpio;
+ omap_up.DTR_inverted = info->DTR_inverted;
+ omap_up.DTR_present = info->DTR_present;

pdata = &omap_up;
pdata_size = sizeof(struct omap_uart_port_info);
diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
index 1a52725..52d3de4 100644
--- a/arch/arm/plat-omap/include/plat/omap-serial.h
+++ b/arch/arm/plat-omap/include/plat/omap-serial.h
@@ -69,6 +69,9 @@ struct omap_uart_port_info {
unsigned int dma_rx_timeout;
unsigned int autosuspend_timeout;
unsigned int dma_rx_poll_rate;
+ int DTR_gpio;
+ int DTR_inverted;
+ int DTR_present;

int (*get_context_loss_count)(struct device *);
void (*set_forceidle)(struct platform_device *);
@@ -131,6 +134,10 @@ struct uart_omap_port {
u32 errata;
u8 wakeups_enabled;

+ int DTR_gpio;
+ int DTR_inverted;
+ int DTR_active;
+
struct pm_qos_request pm_qos_request;
u32 latency;
u32 calc_latency;
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index d3cda0c..aa603f2 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -39,6 +39,7 @@
#include <linux/irq.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
+#include <linux/gpio.h>

#include <plat/dma.h>
#include <plat/dmtimer.h>
@@ -507,6 +508,16 @@ static void serial_omap_set_mctrl(struct uart_port *port, unsigned int mctrl)
up->mcr |= mcr;
serial_out(up, UART_MCR, up->mcr);
pm_runtime_put(&up->pdev->dev);
+
+ if (gpio_is_valid(up->DTR_gpio) &&
+ !!(mctrl & TIOCM_DTR) != up->DTR_active) {
+ up->DTR_active = !up->DTR_active;
+ if (gpio_cansleep(up->DTR_gpio))
+ schedule_work(&up->qos_work);
+ else
+ gpio_set_value(up->DTR_gpio,
+ up->DTR_active != up->DTR_inverted);
+ }
}

static void serial_omap_break_ctl(struct uart_port *port, int break_state)
@@ -715,6 +726,9 @@ static void serial_omap_uart_qos_work(struct work_struct *work)
qos_work);

pm_qos_update_request(&up->pm_qos_request, up->latency);
+ if (gpio_is_valid(up->DTR_gpio))
+ gpio_set_value_cansleep(up->DTR_gpio,
+ up->DTR_active != up->DTR_inverted);
}

static void
@@ -1435,7 +1449,7 @@ static int serial_omap_probe(struct platform_device *pdev)
struct uart_omap_port *up;
struct resource *mem, *irq, *dma_tx, *dma_rx;
struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data;
- int ret = -ENOSPC;
+ int ret;

if (pdev->dev.of_node)
omap_up_info = of_get_uart_port_info(&pdev->dev);
@@ -1466,10 +1480,29 @@ static int serial_omap_probe(struct platform_device *pdev)
if (!dma_tx)
return -ENXIO;

+ if (gpio_is_valid(omap_up_info->DTR_gpio) &&
+ omap_up_info->DTR_present) {
+ ret = gpio_request(omap_up_info->DTR_gpio, "omap-serial");
+ if (ret < 0)
+ return ret;
+ ret = gpio_direction_output(omap_up_info->DTR_gpio,
+ omap_up_info->DTR_inverted);
+ if (ret < 0)
+ return ret;
+ }
+
up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
if (!up)
return -ENOMEM;

+ if (gpio_is_valid(omap_up_info->DTR_gpio) &&
+ omap_up_info->DTR_present) {
+ up->DTR_gpio = omap_up_info->DTR_gpio;
+ up->DTR_inverted = omap_up_info->DTR_inverted;
+ } else
+ up->DTR_gpio = -EINVAL;
+ up->DTR_active = 0;
+
up->pdev = pdev;
up->port.dev = &pdev->dev;
up->port.type = PORT_OMAP;

Attachment: signature.asc
Description: PGP signature