diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index 57d6b29..aa71c13 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include #include #include @@ -160,6 +162,7 @@ struct uart_omap_port { u32 calc_latency; struct work_struct qos_work; struct pinctrl *pins; + struct serial_rs485 rs485; }; #define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) @@ -268,11 +271,11 @@ static void serial_omap_enable_ms(struct uart_port *port) pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); } - +static inline void wait_for_xmitr(struct uart_omap_port *up); static void serial_omap_stop_tx(struct uart_port *port) { struct uart_omap_port *up = to_uart_omap_port(port); - + int val; pm_runtime_get_sync(up->dev); if (up->ier & UART_IER_THRI) { up->ier &= ~UART_IER_THRI; @@ -283,6 +286,15 @@ static void serial_omap_stop_tx(struct uart_port *port) pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); + wait_for_xmitr(up); + if (up->rs485.flags & SER_RS485_ENABLED) { + if(up->rs485.delay_rts_after_send>0){ + udelay(up->rs485.delay_rts_after_send); + } + /* Disable RS485 TX EN */ + val = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + gpio_set_value(up->rs485.gpio_pin, val); + } } static void serial_omap_stop_rx(struct uart_port *port) @@ -345,12 +357,23 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up) static void serial_omap_start_tx(struct uart_port *port) { struct uart_omap_port *up = to_uart_omap_port(port); + int val; + + if (up->rs485.flags & SER_RS485_ENABLED) { + /* Enable RS485 TX EN */ + val = (up->rs485.flags & SER_RS485_RTS_ON_SEND) ? 0 : 1; + gpio_set_value(up->rs485.gpio_pin, val); + if(up->rs485.delay_rts_before_send>0){ + udelay(up->rs485.delay_rts_before_send); + } + } pm_runtime_get_sync(up->dev); serial_omap_enable_ier_thri(up); serial_omap_set_noidle(up); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); + } static void serial_omap_throttle(struct uart_port *port) @@ -702,6 +725,7 @@ static void serial_omap_shutdown(struct uart_port *port) { struct uart_omap_port *up = to_uart_omap_port(port); unsigned long flags = 0; + int val; dev_dbg(up->port.dev, "serial_omap_shutdown+%d\n", up->port.line); @@ -722,6 +746,12 @@ static void serial_omap_shutdown(struct uart_port *port) */ serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC); serial_omap_clear_fifos(up); + + /* if in RS485 mode, make sure we disable the driver */ + if (up->rs485.flags & SER_RS485_ENABLED) { + val = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + gpio_set_value(up->rs485.gpio_pin, val); + } /* * Read data port to reset things, and then free the irq @@ -1250,6 +1280,93 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up) #endif + +static int +serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + int r = 0; + int val; + struct uart_omap_port *p = (struct uart_omap_port *)port; + + spin_lock(&port->lock); + + /* TODO - disable transmitter ? */ + + if (rs485conf->flags & SER_RS485_ENABLED) { + val = (p->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + /* if using GPIO, request the resource and set it up */ + if (rs485conf->flags & SER_RS485_USE_GPIO) { + /* get gpio resources if not already set */ + if (!(p->rs485.flags & SER_RS485_USE_GPIO) || + (p->rs485.gpio_pin != rs485conf->gpio_pin)) { + + r = gpio_request(rs485conf->gpio_pin, + "RS485 TXE"); + if (r) { + dev_warn(port->dev, + "Could not request GPIO %d : %d\n", + rs485conf->gpio_pin, r); + r = -EFAULT; + goto exit_bail; + + } + + r = gpio_direction_output(rs485conf->gpio_pin, val); + if (r) { + dev_warn(port->dev, + "Could not drive GPIO %d : %d\n", + rs485conf->gpio_pin, r); + r = -EFAULT; + goto exit_bail; + } + + /* free up old pin */ +//TODO: What if old pin is same as current?!!?!? + //if (p->rs485.flags & SER_RS485_USE_GPIO) + //gpio_free(p->rs485.gpio_pin); + } + } else { /* RTS pin requested */ + dev_warn(port->dev, "Must use GPIO for RS485 Support\n"); + goto exit_bail; + } + } + p->rs485 = *rs485conf; + +exit_bail: + spin_unlock(&port->lock); + return r; + +} + +static int +serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + switch (cmd) { + case TIOCSRS485: + printk("rs485\n"); + if (copy_from_user(&rs485conf, (struct serial_rs485 *)arg, + sizeof(rs485conf))) + return -EFAULT; + serial_omap_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + printk("rs485\n"); + if (copy_to_user((struct serial_rs485 *)arg, + &((struct uart_omap_port *)port)->rs485, + sizeof(rs485conf))) + return -EFAULT; + break; + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + + static struct uart_ops serial_omap_pops = { .tx_empty = serial_omap_tx_empty, .set_mctrl = serial_omap_set_mctrl, @@ -1271,6 +1388,7 @@ static struct uart_ops serial_omap_pops = { .request_port = serial_omap_request_port, .config_port = serial_omap_config_port, .verify_port = serial_omap_verify_port, + .ioctl = serial_omap_ioctl, #ifdef CONFIG_CONSOLE_POLL .poll_put_char = serial_omap_poll_put_char, .poll_get_char = serial_omap_poll_get_char, diff --git a/include/uapi/linux/serial.h b/include/uapi/linux/serial.h index 5e0d0ed..1830ceb 100644 --- a/include/uapi/linux/serial.h +++ b/include/uapi/linux/serial.h @@ -109,18 +109,16 @@ struct serial_icounter_struct { */ struct serial_rs485 { - __u32 flags; /* RS485 feature flags */ -#define SER_RS485_ENABLED (1 << 0) /* If enabled */ -#define SER_RS485_RTS_ON_SEND (1 << 1) /* Logical level for - RTS pin when - sending */ -#define SER_RS485_RTS_AFTER_SEND (1 << 2) /* Logical level for - RTS pin after sent*/ -#define SER_RS485_RX_DURING_TX (1 << 4) - __u32 delay_rts_before_send; /* Delay before send (milliseconds) */ - __u32 delay_rts_after_send; /* Delay after send (milliseconds) */ - __u32 padding[5]; /* Memory is cheap, new structs - are a royal PITA .. */ -}; - + __u32 flags; /* RS485 feature flags */ + #define SER_RS485_ENABLED (1 << 0) + #define SER_RS485_RTS_ON_SEND (1 << 1) + #define SER_RS485_RTS_AFTER_SEND (1 << 2) + #define SER_RS485_RTS_BEFORE_SEND (1 << 3) + #define SER_RS485_USE_GPIO (1 << 5) + __u32 delay_rts_before_send; /* Microseconds */ + __u32 delay_rts_after_send; /* Microseconds */ + __u32 gpio_pin; /* GPIO Pin Index */ + __u32 padding[4]; /* Memory is cheap, new structs + are a royal PITA .. */ + }; #endif /* _UAPI_LINUX_SERIAL_H */