[PATCH 2/3] tty: serial: OMAP: block idle while the UART is transferring data in PIO mode

Govindraj govindraj.ti at gmail.com
Fri Jan 27 02:23:04 EST 2012


On Thu, Jan 26, 2012 at 8:20 AM, Paul Walmsley <paul at pwsan.com> wrote:
> 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 at ti.com> for mentioning the erratum i291
> workaround, which led to the development of this approach.
>
> Signed-off-by: Paul Walmsley <paul at pwsan.com>
> Cc: Kevin Hilman <khilman at ti.com>
> Cc: Govindraj.R <govindraj.raja at ti.com>
> Cc: Greg Kroah-Hartman <gregkh at suse.de>
> Cc: Alan Cox <alan at linux.intel.com>
> Cc: Tomi Valkeinen <tomi.valkeinen at ti.com>

Acked-by: Govindraj.R <govindraj.raja at ti.com>

> ---
>  arch/arm/mach-omap2/serial.c     |    8 ++++----
>  drivers/tty/serial/omap-serial.c |    7 +++++++
>  2 files changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
> index 247d894..f590afc 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)
>        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;
> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
> index c9c9ba2..11fa156 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)
>  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;
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the linux-arm-kernel mailing list