diff options
Diffstat (limited to 'drivers/tty/serial/omap-serial.c')
| -rw-r--r-- | drivers/tty/serial/omap-serial.c | 257 |
1 files changed, 236 insertions, 21 deletions
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index f0b9f6b52b32..816d1a23f9d0 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -40,9 +40,11 @@ #include <linux/pm_runtime.h> #include <linux/of.h> #include <linux/gpio.h> -#include <linux/pinctrl/consumer.h> +#include <linux/of_gpio.h> #include <linux/platform_data/serial-omap.h> +#include <dt-bindings/gpio/gpio.h> + #define OMAP_MAX_HSUART_PORTS 6 #define UART_BUILD_REVISION(x, y) (((x) << 8) | (y)) @@ -52,6 +54,11 @@ #define OMAP_UART_REV_52 0x0502 #define OMAP_UART_REV_63 0x0603 +#define OMAP_UART_TX_WAKEUP_EN BIT(7) + +/* Feature flags */ +#define OMAP_UART_WER_HAS_TX_WAKEUP BIT(0) + #define UART_ERRATA_i202_MDR1_ACCESS BIT(0) #define UART_ERRATA_i291_DMA_FORCEIDLE BIT(1) @@ -137,6 +144,7 @@ struct uart_omap_port { unsigned char dlh; unsigned char mdr1; unsigned char scr; + unsigned char wer; int use_dma; /* @@ -151,16 +159,20 @@ struct uart_omap_port { int context_loss_cnt; u32 errata; u8 wakeups_enabled; + u32 features; int DTR_gpio; int DTR_inverted; int DTR_active; + struct serial_rs485 rs485; + int rts_gpio; + struct pm_qos_request pm_qos_request; u32 latency; u32 calc_latency; struct work_struct qos_work; - struct pinctrl *pins; + bool is_suspending; }; #define to_uart_omap_port(p) ((container_of((p), struct uart_omap_port, port))) @@ -194,17 +206,17 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up) static int serial_omap_get_context_loss_count(struct uart_omap_port *up) { - struct omap_uart_port_info *pdata = up->dev->platform_data; + struct omap_uart_port_info *pdata = dev_get_platdata(up->dev); if (!pdata || !pdata->get_context_loss_count) - return 0; + return -EINVAL; return pdata->get_context_loss_count(up->dev); } static void serial_omap_enable_wakeup(struct uart_omap_port *up, bool enable) { - struct omap_uart_port_info *pdata = up->dev->platform_data; + struct omap_uart_port_info *pdata = dev_get_platdata(up->dev); if (!pdata || !pdata->enable_wakeup) return; @@ -271,13 +283,42 @@ static void serial_omap_enable_ms(struct uart_port *port) static void serial_omap_stop_tx(struct uart_port *port) { struct uart_omap_port *up = to_uart_omap_port(port); + struct circ_buf *xmit = &up->port.state->xmit; + int res; pm_runtime_get_sync(up->dev); + + /* handle rs485 */ + if (up->rs485.flags & SER_RS485_ENABLED) { + /* do nothing if current tx not yet completed */ + res = serial_in(up, UART_LSR) & UART_LSR_TEMT; + if (!res) + return; + + /* if there's no more data to send, turn off rts */ + if (uart_circ_empty(xmit)) { + /* if rts not already disabled */ + res = (up->rs485.flags & SER_RS485_RTS_AFTER_SEND) ? 1 : 0; + if (gpio_get_value(up->rts_gpio) != res) { + if (up->rs485.delay_rts_after_send > 0) { + mdelay(up->rs485.delay_rts_after_send); + } + gpio_set_value(up->rts_gpio, res); + } + } + } + if (up->ier & UART_IER_THRI) { up->ier &= ~UART_IER_THRI; serial_out(up, UART_IER, up->ier); } + if ((up->rs485.flags & SER_RS485_ENABLED) && + !(up->rs485.flags & SER_RS485_RX_DURING_TX)) { + up->ier = UART_IER_RLSI | UART_IER_RDI; + serial_out(up, UART_IER, up->ier); + } + pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); } @@ -339,8 +380,26 @@ 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 res; pm_runtime_get_sync(up->dev); + + /* handle rs485 */ + if (up->rs485.flags & SER_RS485_ENABLED) { + /* 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 (up->rs485.delay_rts_before_send > 0) { + mdelay(up->rs485.delay_rts_before_send); + } + } + } + + if ((up->rs485.flags & SER_RS485_ENABLED) && + !(up->rs485.flags & SER_RS485_RX_DURING_TX)) + serial_omap_stop_rx(port); + serial_omap_enable_ier_thri(up); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); @@ -682,7 +741,11 @@ static int serial_omap_startup(struct uart_port *port) serial_out(up, UART_IER, up->ier); /* Enable module level wake up */ - serial_out(up, UART_OMAP_WER, OMAP_UART_WER_MOD_WKUP); + up->wer = OMAP_UART_WER_MOD_WKUP; + if (up->features & OMAP_UART_WER_HAS_TX_WAKEUP) + up->wer |= OMAP_UART_TX_WAKEUP_EN; + + serial_out(up, UART_OMAP_WER, up->wer); pm_runtime_mark_last_busy(up->dev); pm_runtime_put_autosuspend(up->dev); @@ -1253,6 +1316,76 @@ static inline void serial_omap_add_console_port(struct uart_omap_port *up) #endif +/* Enable or disable the rs485 support */ +static void +serial_omap_config_rs485(struct uart_port *port, struct serial_rs485 *rs485conf) +{ + struct uart_omap_port *up = to_uart_omap_port(port); + unsigned long flags; + unsigned int mode; + int val; + + pm_runtime_get_sync(up->dev); + spin_lock_irqsave(&up->port.lock, flags); + + /* Disable interrupts from this port */ + mode = up->ier; + up->ier = 0; + serial_out(up, UART_IER, 0); + + /* store new config */ + up->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)) { + /* 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); + } else + up->rs485.flags &= ~SER_RS485_ENABLED; + + /* Enable interrupts */ + up->ier = mode; + serial_out(up, UART_IER, up->ier); + + spin_unlock_irqrestore(&up->port.lock, flags); + pm_runtime_mark_last_busy(up->dev); + pm_runtime_put_autosuspend(up->dev); +} + +static int +serial_omap_ioctl(struct uart_port *port, unsigned int cmd, unsigned long arg) +{ + struct serial_rs485 rs485conf; + + switch (cmd) { + case TIOCSRS485: + if (copy_from_user(&rs485conf, (struct serial_rs485 *) arg, + sizeof(rs485conf))) + return -EFAULT; + + serial_omap_config_rs485(port, &rs485conf); + break; + + case TIOCGRS485: + if (copy_to_user((struct serial_rs485 *) arg, + &(to_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, @@ -1274,6 +1407,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, @@ -1289,6 +1423,22 @@ static struct uart_driver serial_omap_reg = { }; #ifdef CONFIG_PM_SLEEP +static int serial_omap_prepare(struct device *dev) +{ + struct uart_omap_port *up = dev_get_drvdata(dev); + + up->is_suspending = true; + + return 0; +} + +static void serial_omap_complete(struct device *dev) +{ + struct uart_omap_port *up = dev_get_drvdata(dev); + + up->is_suspending = false; +} + static int serial_omap_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); @@ -1307,14 +1457,17 @@ static int serial_omap_resume(struct device *dev) return 0; } -#endif +#else +#define serial_omap_prepare NULL +#define serial_omap_complete NULL +#endif /* CONFIG_PM_SLEEP */ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) { u32 mvr, scheme; u16 revision, major, minor; - mvr = serial_in(up, UART_OMAP_MVER); + mvr = readl(up->port.membase + (UART_OMAP_MVER << up->port.regshift)); /* Check revision register scheme */ scheme = mvr >> OMAP_UART_MVR_SCHEME_SHIFT; @@ -1353,9 +1506,11 @@ static void omap_serial_fill_features_erratas(struct uart_omap_port *up) case OMAP_UART_REV_52: up->errata |= (UART_ERRATA_i202_MDR1_ACCESS | UART_ERRATA_i291_DMA_FORCEIDLE); + up->features |= OMAP_UART_WER_HAS_TX_WAKEUP; break; case OMAP_UART_REV_63: up->errata |= UART_ERRATA_i202_MDR1_ACCESS; + up->features |= OMAP_UART_WER_HAS_TX_WAKEUP; break; default: break; @@ -1375,15 +1530,64 @@ static struct omap_uart_port_info *of_get_uart_port_info(struct device *dev) return omap_up_info; } +static int serial_omap_probe_rs485(struct uart_omap_port *up, + struct device_node *np) +{ + struct serial_rs485 *rs485conf = &up->rs485; + u32 rs485_delay[2]; + enum of_gpio_flags flags; + int ret; + + rs485conf->flags = 0; + up->rts_gpio = -EINVAL; + + if (!np) + return 0; + + if (of_property_read_bool(np, "rs485-rts-active-high")) + rs485conf->flags |= SER_RS485_RTS_ON_SEND; + else + rs485conf->flags |= SER_RS485_RTS_AFTER_SEND; + + /* check for tx enable gpio */ + up->rts_gpio = of_get_named_gpio_flags(np, "rts-gpio", 0, &flags); + if (gpio_is_valid(up->rts_gpio)) { + ret = gpio_request(up->rts_gpio, "omap-serial"); + if (ret < 0) + return ret; + ret = gpio_direction_output(up->rts_gpio, + flags & SER_RS485_RTS_AFTER_SEND); + if (ret < 0) + return ret; + } else + up->rts_gpio = -EINVAL; + + if (of_property_read_u32_array(np, "rs485-rts-delay", + rs485_delay, 2) == 0) { + rs485conf->delay_rts_before_send = rs485_delay[0]; + rs485conf->delay_rts_after_send = rs485_delay[1]; + } + + if (of_property_read_bool(np, "rs485-rx-during-tx")) + rs485conf->flags |= SER_RS485_RX_DURING_TX; + + if (of_property_read_bool(np, "linux,rs485-enabled-at-boot-time")) + rs485conf->flags |= SER_RS485_ENABLED; + + return 0; +} + static int serial_omap_probe(struct platform_device *pdev) { struct uart_omap_port *up; struct resource *mem, *irq; - struct omap_uart_port_info *omap_up_info = pdev->dev.platform_data; + struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev); int ret; - if (pdev->dev.of_node) + if (pdev->dev.of_node) { omap_up_info = of_get_uart_port_info(&pdev->dev); + pdev->dev.platform_data = omap_up_info; + } mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { @@ -1448,12 +1652,9 @@ static int serial_omap_probe(struct platform_device *pdev) goto err_port_line; } - up->pins = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(up->pins)) { - dev_warn(&pdev->dev, "did not get pins for uart%i error: %li\n", - up->port.line, PTR_ERR(up->pins)); - up->pins = NULL; - } + ret = serial_omap_probe_rs485(up, pdev->dev.of_node); + if (ret < 0) + goto err_rs485; sprintf(up->name, "OMAP UART%d", up->port.line); up->port.mapbase = mem->start; @@ -1481,12 +1682,16 @@ static int serial_omap_probe(struct platform_device *pdev) INIT_WORK(&up->qos_work, serial_omap_uart_qos_work); platform_set_drvdata(pdev, up); - pm_runtime_enable(&pdev->dev); + if (omap_up_info->autosuspend_timeout == 0) + omap_up_info->autosuspend_timeout = -1; + device_init_wakeup(up->dev, true); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, omap_up_info->autosuspend_timeout); pm_runtime_irq_safe(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); omap_serial_fill_features_erratas(up); @@ -1506,6 +1711,7 @@ err_add_port: pm_runtime_put(&pdev->dev); pm_runtime_disable(&pdev->dev); err_ioremap: +err_rs485: err_port_line: dev_err(&pdev->dev, "[UART%d]: failure [%s]: %d\n", pdev->id, __func__, ret); @@ -1586,18 +1792,25 @@ static void serial_omap_restore_context(struct uart_omap_port *up) serial_omap_mdr1_errataset(up, up->mdr1); else serial_out(up, UART_OMAP_MDR1, up->mdr1); + serial_out(up, UART_OMAP_WER, up->wer); } static int serial_omap_runtime_suspend(struct device *dev) { struct uart_omap_port *up = dev_get_drvdata(dev); - struct omap_uart_port_info *pdata = dev->platform_data; if (!up) return -EINVAL; - if (!pdata) - return 0; + /* + * When using 'no_console_suspend', the console UART must not be + * suspended. Since driver suspend is managed by runtime suspend, + * preventing runtime suspend (by returning error) will keep device + * active during suspend. + */ + if (up->is_suspending && !console_suspend_enabled && + uart_console(&up->port)) + return -EBUSY; up->context_loss_cnt = serial_omap_get_context_loss_count(up); @@ -1626,7 +1839,7 @@ static int serial_omap_runtime_resume(struct device *dev) int loss_cnt = serial_omap_get_context_loss_count(up); if (loss_cnt < 0) { - dev_err(dev, "serial_omap_get_context_loss_count failed : %d\n", + dev_dbg(dev, "serial_omap_get_context_loss_count failed : %d\n", loss_cnt); serial_omap_restore_context(up); } else if (up->context_loss_cnt != loss_cnt) { @@ -1643,6 +1856,8 @@ static const struct dev_pm_ops serial_omap_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume) SET_RUNTIME_PM_OPS(serial_omap_runtime_suspend, serial_omap_runtime_resume, NULL) + .prepare = serial_omap_prepare, + .complete = serial_omap_complete, }; #if defined(CONFIG_OF) |
