diff options
-rw-r--r-- | arch/arm/plat-omap/include/plat/omap-serial.h | 1 | ||||
-rw-r--r-- | drivers/tty/serial/omap-serial.c | 51 |
2 files changed, 51 insertions, 1 deletions
diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h index 9ff444469f3..12a64eb8c62 100644 --- a/arch/arm/plat-omap/include/plat/omap-serial.h +++ b/arch/arm/plat-omap/include/plat/omap-serial.h @@ -131,6 +131,7 @@ struct uart_omap_port { u32 context_loss_cnt; u32 errata; u8 wakeups_enabled; + u8 max_tx_count; struct pm_qos_request pm_qos_request; u32 latency; diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c index ca54f038ab4..e00ac05cfdb 100644 --- a/drivers/tty/serial/omap-serial.c +++ b/drivers/tty/serial/omap-serial.c @@ -88,6 +88,49 @@ static inline void serial_omap_clear_fifos(struct uart_omap_port *up) serial_out(up, UART_FCR, 0); } +/** + * serial_omap_block_cpu_low_power_state - prevent MPU pwrdm from leaving ON + * @up: struct uart_omap_port * + * + * Prevent the MPU powerdomain from entering a power state lower than + * ON. (It should be sufficient to prevent it from entering INACTIVE, + * but there is presently no easy way to do this.) This works around + * a suspected silicon bug in the OMAP UART IP blocks. The UARTs should + * wake the PRCM when the transmit FIFO threshold interrupt is raised, but + * they do not. See also serial_omap_allow_cpu_low_power_state(). No + * return value. + */ +static void serial_omap_block_cpu_low_power_state(struct uart_omap_port *up) +{ +#ifdef CONFIG_CPU_IDLE + up->latency = 1; + schedule_work(&up->qos_work); +#else + up->max_tx_count = 1; +#endif +} + +/** + * serial_omap_allow_cpu_low_power_state - remove power state restriction on MPU + * @up: struct uart_omap_port * + * + * Cancel the effects of serial_omap_block_cpu_low_power_state(). + * This should allow the MPU powerdomain to enter a power state lower + * than ON, assuming the rest of the kernel is not restricting it. + * This works around a suspected silicon bug in the OMAP UART IP + * blocks. The UARTs should wake the PRCM when the transmit FIFO + * threshold interrupt is raised, but they do not. No return value. + */ +static void serial_omap_allow_cpu_low_power_state(struct uart_omap_port *up) +{ +#ifdef CONFIG_CPU_IDLE + up->latency = up->calc_latency; + schedule_work(&up->qos_work); +#else + up->max_tx_count = up->port.fifosize / 4; +#endif +} + /* * serial_omap_get_divisor - calculate divisor value * @port: uart port info @@ -163,6 +206,9 @@ static void serial_omap_stop_tx(struct uart_port *port) serial_out(up, UART_IER, up->ier); } + if (!up->use_dma) + serial_omap_allow_cpu_low_power_state(up); + pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); } @@ -264,7 +310,7 @@ static void transmit_chars(struct uart_omap_port *up) serial_omap_stop_tx(&up->port); return; } - count = up->port.fifosize / 4; + count = up->max_tx_count; do { serial_out(up, UART_TX, xmit->buf[xmit->tail]); xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); @@ -297,6 +343,7 @@ static void serial_omap_start_tx(struct uart_port *port) if (!up->use_dma) { pm_runtime_get_sync(&up->pdev->dev); + serial_omap_block_cpu_low_power_state(up); serial_omap_enable_ier_thri(up); pm_runtime_mark_last_busy(&up->pdev->dev); pm_runtime_put_autosuspend(&up->pdev->dev); @@ -1421,6 +1468,8 @@ static int serial_omap_probe(struct platform_device *pdev) up->port.fifosize = 64; up->port.ops = &serial_omap_pops; + up->max_tx_count = up->port.fifosize / 4; + if (pdev->dev.of_node) up->port.line = of_alias_get_id(pdev->dev.of_node, "serial"); else |