aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2012-01-25 21:50:52 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-02-09 13:48:34 -0500
commitbe4b0281956c5cae4f63f31f11d07625a6988766 (patch)
treec75f6d88a142889eec90c5ae2e023b31fb3445df
parent0ba5f66836c9253c67c469fb4016f94ea30ff2ce (diff)
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: Paul Walmsley <paul@pwsan.com> Cc: Alan Cox <alan@linux.intel.com> Cc: Tomi Valkeinen <tomi.valkeinen@ti.com> Acked-by: Govindraj.R <govindraj.raja@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Kevin Hilman <khilman@ti.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--arch/arm/mach-omap2/serial.c8
-rw-r--r--drivers/tty/serial/omap-serial.c7
2 files changed, 11 insertions, 4 deletions
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index 247d89478f24..f590afc1f673 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -107,18 +107,18 @@ static void omap_uart_set_noidle(struct platform_device *pdev)
107 omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO); 107 omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO);
108} 108}
109 109
110static void omap_uart_set_forceidle(struct platform_device *pdev) 110static void omap_uart_set_smartidle(struct platform_device *pdev)
111{ 111{
112 struct omap_device *od = to_omap_device(pdev); 112 struct omap_device *od = to_omap_device(pdev);
113 113
114 omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_FORCE); 114 omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_SMART);
115} 115}
116 116
117#else 117#else
118static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable) 118static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable)
119{} 119{}
120static void omap_uart_set_noidle(struct platform_device *pdev) {} 120static void omap_uart_set_noidle(struct platform_device *pdev) {}
121static void omap_uart_set_forceidle(struct platform_device *pdev) {} 121static void omap_uart_set_smartidle(struct platform_device *pdev) {}
122#endif /* CONFIG_PM */ 122#endif /* CONFIG_PM */
123 123
124#ifdef CONFIG_OMAP_MUX 124#ifdef CONFIG_OMAP_MUX
@@ -349,7 +349,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata,
349 omap_up.uartclk = OMAP24XX_BASE_BAUD * 16; 349 omap_up.uartclk = OMAP24XX_BASE_BAUD * 16;
350 omap_up.flags = UPF_BOOT_AUTOCONF; 350 omap_up.flags = UPF_BOOT_AUTOCONF;
351 omap_up.get_context_loss_count = omap_pm_get_dev_context_loss_count; 351 omap_up.get_context_loss_count = omap_pm_get_dev_context_loss_count;
352 omap_up.set_forceidle = omap_uart_set_forceidle; 352 omap_up.set_forceidle = omap_uart_set_smartidle;
353 omap_up.set_noidle = omap_uart_set_noidle; 353 omap_up.set_noidle = omap_uart_set_noidle;
354 omap_up.enable_wakeup = omap_uart_enable_wakeup; 354 omap_up.enable_wakeup = omap_uart_enable_wakeup;
355 omap_up.dma_rx_buf_size = info->dma_rx_buf_size; 355 omap_up.dma_rx_buf_size = info->dma_rx_buf_size;
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index a3f5ea46f345..18d13248d9ba 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -136,6 +136,7 @@ static void serial_omap_enable_ms(struct uart_port *port)
136static void serial_omap_stop_tx(struct uart_port *port) 136static void serial_omap_stop_tx(struct uart_port *port)
137{ 137{
138 struct uart_omap_port *up = (struct uart_omap_port *)port; 138 struct uart_omap_port *up = (struct uart_omap_port *)port;
139 struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
139 140
140 if (up->use_dma && 141 if (up->use_dma &&
141 up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) { 142 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)
158 serial_out(up, UART_IER, up->ier); 159 serial_out(up, UART_IER, up->ier);
159 } 160 }
160 161
162 if (!up->use_dma && pdata->set_forceidle)
163 pdata->set_forceidle(up->pdev);
164
161 pm_runtime_mark_last_busy(&up->pdev->dev); 165 pm_runtime_mark_last_busy(&up->pdev->dev);
162 pm_runtime_put_autosuspend(&up->pdev->dev); 166 pm_runtime_put_autosuspend(&up->pdev->dev);
163} 167}
@@ -286,6 +290,7 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
286static void serial_omap_start_tx(struct uart_port *port) 290static void serial_omap_start_tx(struct uart_port *port)
287{ 291{
288 struct uart_omap_port *up = (struct uart_omap_port *)port; 292 struct uart_omap_port *up = (struct uart_omap_port *)port;
293 struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
289 struct circ_buf *xmit; 294 struct circ_buf *xmit;
290 unsigned int start; 295 unsigned int start;
291 int ret = 0; 296 int ret = 0;
@@ -293,6 +298,8 @@ static void serial_omap_start_tx(struct uart_port *port)
293 if (!up->use_dma) { 298 if (!up->use_dma) {
294 pm_runtime_get_sync(&up->pdev->dev); 299 pm_runtime_get_sync(&up->pdev->dev);
295 serial_omap_enable_ier_thri(up); 300 serial_omap_enable_ier_thri(up);
301 if (pdata->set_noidle)
302 pdata->set_noidle(up->pdev);
296 pm_runtime_mark_last_busy(&up->pdev->dev); 303 pm_runtime_mark_last_busy(&up->pdev->dev);
297 pm_runtime_put_autosuspend(&up->pdev->dev); 304 pm_runtime_put_autosuspend(&up->pdev->dev);
298 return; 305 return;