Commit be4b0281 authored by Paul Walmsley's avatar Paul Walmsley Committed by Greg Kroah-Hartman

tty: serial: OMAP: block idle while the UART is transferring data in PIO mode

Prevent OMAP UARTs from going idle while they are still transferring
data in PIO mode.  This works around an oversight in the OMAP UART
hardware present in OMAP34xx and earlier: an idle UART won't send a
wakeup when the TX FIFO threshold is reached.  This causes long delays
during data transmission when the MPU powerdomain enters a low-power
mode.  The MPU interrupt controller is not able to respond to
interrupts when it's in a low-power state, so the TX buffer is not
refilled until another wakeup event occurs.

This fix changes the erratum i291 DMA idle workaround.  Rather than
toggling between force-idle and no-idle, it will toggle between
smart-idle and no-idle.  The important part of the workaround is the
no-idle part, so this shouldn't result in any change in behavior.

This fix should work on all OMAP UARTs.  Future patches intended for
the 3.4 merge window will make this workaround conditional on a
"feature" flag, and will use the OMAP36xx+ TX event wakeup support.

Thanks to Kevin Hilman <khilman@ti.com> for mentioning the erratum i291
workaround, which led to the development of this approach.
Signed-off-by: default avatarPaul Walmsley <paul@pwsan.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Acked-by: default avatarGovindraj.R <govindraj.raja@ti.com>
Reviewed-by: default avatarKevin Hilman <khilman@ti.com>
Tested-by: default avatarKevin Hilman <khilman@ti.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0ba5f668
......@@ -107,18 +107,18 @@ static void omap_uart_set_noidle(struct platform_device *pdev)
omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO);
}
static void omap_uart_set_forceidle(struct platform_device *pdev)
static void omap_uart_set_smartidle(struct platform_device *pdev)
{
struct omap_device *od = to_omap_device(pdev);
omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_FORCE);
omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_SMART);
}
#else
static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable)
{}
static void omap_uart_set_noidle(struct platform_device *pdev) {}
static void omap_uart_set_forceidle(struct platform_device *pdev) {}
static void omap_uart_set_smartidle(struct platform_device *pdev) {}
#endif /* CONFIG_PM */
#ifdef CONFIG_OMAP_MUX
......@@ -349,7 +349,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata,
omap_up.uartclk = OMAP24XX_BASE_BAUD * 16;
omap_up.flags = UPF_BOOT_AUTOCONF;
omap_up.get_context_loss_count = omap_pm_get_dev_context_loss_count;
omap_up.set_forceidle = omap_uart_set_forceidle;
omap_up.set_forceidle = omap_uart_set_smartidle;
omap_up.set_noidle = omap_uart_set_noidle;
omap_up.enable_wakeup = omap_uart_enable_wakeup;
omap_up.dma_rx_buf_size = info->dma_rx_buf_size;
......
......@@ -136,6 +136,7 @@ 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 = (struct uart_omap_port *)port;
struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
if (up->use_dma &&
up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) {
......@@ -158,6 +159,9 @@ static void serial_omap_stop_tx(struct uart_port *port)
serial_out(up, UART_IER, up->ier);
}
if (!up->use_dma && pdata->set_forceidle)
pdata->set_forceidle(up->pdev);
pm_runtime_mark_last_busy(&up->pdev->dev);
pm_runtime_put_autosuspend(&up->pdev->dev);
}
......@@ -286,6 +290,7 @@ 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 = (struct uart_omap_port *)port;
struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
struct circ_buf *xmit;
unsigned int start;
int ret = 0;
......@@ -293,6 +298,8 @@ static void serial_omap_start_tx(struct uart_port *port)
if (!up->use_dma) {
pm_runtime_get_sync(&up->pdev->dev);
serial_omap_enable_ier_thri(up);
if (pdata->set_noidle)
pdata->set_noidle(up->pdev);
pm_runtime_mark_last_busy(&up->pdev->dev);
pm_runtime_put_autosuspend(&up->pdev->dev);
return;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment