[PATCH v4 07/11] Serial: OMAP: Add runtime pm support for omap-serial driver

Govindraj govindraj.ti at gmail.com
Fri Sep 9 02:32:49 EDT 2011


On Fri, Sep 9, 2011 at 5:54 AM, Kevin Hilman <khilman at ti.com> wrote:
> "Govindraj.R" <govindraj.raja at ti.com> writes:
>
>> Adapts omap-serial driver to use pm_runtime API's.
>>
>> 1.) Moving context_restore func to driver from serial.c
>> 2.) Use runtime irq safe to use get_sync from irq context.
>> 3.) autosuspend for port specific activities and put for reg access.
>
> Re: autosuspend, it looks like the driver now has 2 mechanisms for
> tracking activity.  The runtime PM 'mark last busy' and the existing
> 'up->port_activity = jiffies' stuff.  Do you think those could be
> unified?
>

Is there a way where we can retrieve the last busy jiffie value
using runtime API? I don't find that available.


> At first glance, it looks like most places with a _mark_last_busy() also
> have a up->port_activity update.  I'm not familiar enough with the
> driver to make the call, but they look awfully similar.
>

Ok, I will check whether I can get rid if it.

>> 4.) for earlyprintk usage the debug macro will write to console uart
>>     so keep uart clocks active from boot, idle using hwmod API's re-enable back
>>     using runtime API's.
>
> /me doesn't understand

I have added this reason in early mail reply to 04/11 patch review
[needed for earlyprintk option from low level debug ]


>
>> 5.) Moving context restore to runtime suspend and using reg values from port
>>     structure to restore the uart port context based on context_loss_count.
>>     Maintain internal state machine for avoiding repeated enable/disable of
>>     uart port wakeup mechanism.
>> 6.) Add API to enable wakeup and check wakeup status.
>>
>> Acked-by: Alan Cox <alan at linux.intel.com>
>> Signed-off-by: Govindraj.R <govindraj.raja at ti.com>
>> ---
>>  arch/arm/mach-omap2/serial.c                  |   49 ++++++
>>  arch/arm/plat-omap/include/plat/omap-serial.h |   10 ++
>>  drivers/tty/serial/omap-serial.c              |  211 ++++++++++++++++++++++---
>>  3 files changed, 250 insertions(+), 20 deletions(-)
>>

[..]

>> +
>> +static void omap_uart_wakeup_enable(struct platform_device *pdev, bool enable)
>> +{
>> +     struct omap_device *od;
>> +     struct omap_uart_port_info *up = pdev->dev.platform_data;
>> +
>> +     /* Set or clear wake-enable bit */
>> +     if (up->wk_en && up->wk_mask) {
>> +             u32 v = __raw_readl(up->wk_en);
>> +             if (enable)
>> +                     v |= up->wk_mask;
>> +             else
>> +                     v &= ~up->wk_mask;
>> +             __raw_writel(v, up->wk_en);
>> +     }
>> +
>> +     od = to_omap_device(pdev);
>> +     if (enable)
>> +             omap_hwmod_enable_wakeup(od->hwmods[0]);
>> +     else
>> +             omap_hwmod_disable_wakeup(od->hwmods[0]);
>> +}
>> +
>
> Here again, this is difficult to review because you removed the code in
> [4/11] and add it back here, but with rather different functionality
> with no description as to why.
>

will move this change as part of 4/11 itself.

> The previous version enabled wakeups at the PRCM and at the IO ring.
> This version enables wakeups at the PRCM as well but instead of enabling
> them at the IO ring, enables them at the module.
>

Since we are gating using prepare idle call I think
we can use this enable wake-up call as part of prepare idle call itself,
as done earlier.

>>  struct omap_hwmod *omap_uart_hwmod_lookup(int num)
>>  {
>>       struct omap_hwmod *oh;
>> @@ -317,6 +363,9 @@ void __init omap_serial_init_port(struct omap_board_data *bdata)
>>
>>       pdata->uartclk = OMAP24XX_BASE_BAUD * 16;
>>       pdata->flags = UPF_BOOT_AUTOCONF;
>> +     pdata->enable_wakeup = omap_uart_wakeup_enable;
>> +     pdata->chk_wakeup = omap_uart_chk_wakeup;
>> +     pdata->get_context_loss_count = omap_pm_get_dev_context_loss_count;
>>
>>       pdev = omap_device_build(name, bdata->id, oh, pdata,
>>                               sizeof(*pdata), omap_uart_latency,
>> diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
>> index 06e3aa2..7fc63b8 100644
>> --- a/arch/arm/plat-omap/include/plat/omap-serial.h
>> +++ b/arch/arm/plat-omap/include/plat/omap-serial.h
>> @@ -67,6 +67,9 @@ struct omap_uart_port_info {
>>       void __iomem *wk_st;
>>       void __iomem *wk_en;
>>       u32 wk_mask;
>> +     void (*enable_wakeup)(struct platform_device *, bool);
>> +     bool (*chk_wakeup)(struct platform_device *);
>> +     u32 (*get_context_loss_count)(struct device *);
>>  };
>>
>>  struct uart_omap_dma {
>> @@ -118,7 +121,14 @@ struct uart_omap_port {
>>       unsigned char           msr_saved_flags;
>>       char                    name[20];
>>       unsigned long           port_activity;
>> +     u32                     context_loss_cnt;
>> +     u8                      wake_status;
>> +
>>       unsigned int            errata;
>> +     unsigned int            clocked;
>
> This is a legacy from serial.c and should not be needed.  Checking
> pm_runtime_suspended() will provide the same info.
>

Yes will try to use that.

>> +     u8                      console_locked;
>> +
>> +     bool (*chk_wakeup)(struct platform_device *);
>
> s/chk/check/ please.  It's not that much longer.
>

will change.

>>  };
>>
>>  #endif /* __OMAP_SERIAL_H__ */
>> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
>> index 6ac0ade..30bdd05 100644
>> --- a/drivers/tty/serial/omap-serial.c
>> +++ b/drivers/tty/serial/omap-serial.c
>> @@ -37,10 +37,14 @@
>>  #include <linux/clk.h>
>>  #include <linux/serial_core.h>
>>  #include <linux/irq.h>
>> +#include <linux/pm_runtime.h>
>>
>>  #include <plat/dma.h>
>>  #include <plat/dmtimer.h>
>>  #include <plat/omap-serial.h>
>> +#include <plat/omap_device.h>
>> +
>> +#define OMAP_UART_AUTOSUSPEND_DELAY 3000     /* Value is msecs */
>
> Because of the character lost problem when UARTs are idled, Tony prefers
> that the default on this be no auto timeout, like the current mainline
> code does.
>

Yes fine, IIUC this value should be -1 by default and can be later set using
sysfs entry.


>>  static struct uart_omap_port *ui[OMAP_MAX_HSUART_PORTS];
>>
>> @@ -102,6 +106,8 @@ static void serial_omap_stop_rxdma(struct uart_omap_port *up)
>>               omap_free_dma(up->uart_dma.rx_dma_channel);
>>               up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
>>               up->uart_dma.rx_dma_used = false;
>> +             pm_runtime_mark_last_busy(&up->pdev->dev);
>> +             pm_runtime_put_autosuspend(&up->pdev->dev);
>>       }
>>  }
>>
>> @@ -110,8 +116,11 @@ static void serial_omap_enable_ms(struct uart_port *port)

[..]

>>
>>  #endif /* CONFIG_CONSOLE_POLL */
>>
>>  #ifdef CONFIG_SERIAL_OMAP_CONSOLE
>> -
>>  static struct uart_omap_port *serial_omap_console_ports[4];
>>
>>  static struct uart_driver serial_omap_reg;
>> @@ -951,6 +1004,8 @@ serial_omap_console_write(struct console *co, const char *s,
>>       unsigned int ier;
>>       int locked = 1;
>>
>> +     pm_runtime_get_sync(&up->pdev->dev);
>> +
>>       local_irq_save(flags);
>>       if (up->port.sysrq)
>>               locked = 0;
>> @@ -983,9 +1038,12 @@ serial_omap_console_write(struct console *co, const char *s,
>>       if (up->msr_saved_flags)
>>               check_modem_status(up);
>>
>> +     pm_runtime_mark_last_busy(&up->pdev->dev);
>> +     pm_runtime_put_autosuspend(&up->pdev->dev);
>>       if (locked)
>>               spin_unlock(&up->port.lock);
>>       local_irq_restore(flags);
>> +     up->port_activity = jiffies;
>
> hmm, why?  this change looks suspicious.
>

Yes will try to unify with mark last busy.
But as said earlier can we have option to retrieve
last busy jiffies value.


>>  }
>>
>>  static int __init
>> @@ -1065,22 +1123,33 @@ static struct uart_driver serial_omap_reg = {
>>       .cons           = OMAP_CONSOLE,
>>  };
>>
>> -static int
>> -serial_omap_suspend(struct platform_device *pdev, pm_message_t state)
>> +static int serial_omap_suspend(struct device *dev)
>>  {
>> -     struct uart_omap_port *up = platform_get_drvdata(pdev);
>> +     struct uart_omap_port *up = dev_get_drvdata(dev);
>>
>> -     if (up)
>> +     if (up) {
>>               uart_suspend_port(&serial_omap_reg, &up->port);
>> +             if (up->port.line == up->port.cons->index &&
>> +                             !is_console_locked())
>> +                     up->console_locked = console_trylock();
>> +     }
>> +
>
> Motiviation for console locking in this version of the series is not
> clear, and not described in the changelog.
>
> It's also present here in static suspend/resume but not in runtime
> suspend/resume, which also is suspicious.
>

Yes will add to change log,

We needed this for no console suspend use case
no console lock is taken in no_console_suspend is specified,

Since our pwr_dmn hooks disable clocks left enabled during suspend,
so any prints after pwr_dmn hooks can cause problems since clocks
are already cut from pwr_dmn hooks.

So buffer prints while entering suspend by taking console lock
if not taken already and print back after uart state machine restores
console uart.


>>       return 0;
>>  }
>>
>> -static int serial_omap_resume(struct platform_device *dev)
>> +static int serial_omap_resume(struct device *dev)
>>  {
>> -     struct uart_omap_port *up = platform_get_drvdata(dev);
>> +     struct uart_omap_port *up = dev_get_drvdata(dev);
>>
>> -     if (up)
>> +     if (up) {

[..]

>> +     up->port.mapbase = mem->start;
>> +     up->port.membase = ioremap(mem->start, mem->end - mem->start);
>
> The size is (end - start + 1), but please use the resource_size() helper
> for this to avoid this common problem.
>

will change that.

>> +     if (!up->port.membase) {
>> +             dev_err(&pdev->dev, "can't ioremap UART\n");
>> +             ret = -ENOMEM;
>> +             goto err1;
>> +     }
>> +
>>       up->port.flags = omap_up_info->flags;
>> -     up->port.irqflags = omap_up_info->irqflags;
>>       up->port.uartclk = omap_up_info->uartclk;
>>       up->uart_dma.uart_base = mem->start;
>> +     up->errata = omap_up_info->errata;
>> +     up->chk_wakeup = omap_up_info->chk_wakeup;
>>
>>       if (omap_up_info->dma_enabled) {
>>               up->uart_dma.uart_dma_tx = dma_tx->start;
>> @@ -1299,18 +1378,34 @@ static int serial_omap_probe(struct platform_device *pdev)
>>               up->uart_dma.rx_dma_channel = OMAP_UART_DMA_CH_FREE;
>>       }
>>
>> +     pm_runtime_use_autosuspend(&pdev->dev);
>> +     pm_runtime_set_autosuspend_delay(&pdev->dev,
>> +                     OMAP_UART_AUTOSUSPEND_DELAY);
>> +
>> +     pm_runtime_irq_safe(&pdev->dev);
>> +     if (device_may_wakeup(&pdev->dev)) {
>> +             pm_runtime_enable(&pdev->dev);
>> +             od = to_omap_device(up->pdev);
>> +             omap_hwmod_idle(od->hwmods[0]);
>
> No hwmod calls in drivers please.
>
> In this case, this is a legacy of using HWMOD_INIT_NO_IDLE and
> _NO_RESET.  That should be removed so this could be removed.
>

low level debug early printk use case as said earlier.


>> +             pm_runtime_get_sync(&up->pdev->dev);
>> +             up->clocked = 1;
>> +     }
>> +
>>       ui[pdev->id] = up;
>>       serial_omap_add_console_port(up);
>>

[..]

>> +     if (pdata->get_context_loss_count)
>> +             up->context_loss_cnt = pdata->get_context_loss_count(dev);
>
> You need
>
>        if (!pdata->enable_wakeup)
>                return 0;
>
> here in case there is no ->enable_wakeup provided.  Otherwise...
>
>> +     if (device_may_wakeup(dev)) {
>> +             if (!up->wake_status) {
>> +                     pdata->enable_wakeup(up->pdev, true);
>
> ...boom.
>

will correct it.

>> +                     up->wake_status = true;
>> +             }
>> +     } else {
>> +             if (up->wake_status) {
>> +                     pdata->enable_wakeup(up->pdev, false);
>> +                     up->wake_status = false;
>> +             }
>> +     }
>
> up->wake_status would be better named ->wakeups_enabled;

sure.

>
>> +     return 0;
>> +}
>> +
>> +static int omap_serial_runtime_resume(struct device *dev)
>> +{
>> +     struct uart_omap_port *up = dev_get_drvdata(dev);
>> +     struct omap_uart_port_info *pdata = dev->platform_data;
>> +
>> +     if (up) {
>> +             if (pdata->get_context_loss_count) {
>> +                     u32 loss_cnt = pdata->get_context_loss_count(dev);
>> +
>> +                     if (up->context_loss_cnt != loss_cnt)
>> +                             omap_uart_restore_context(up);
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_RUNTIME
>> +static const struct dev_pm_ops omap_serial_dev_pm_ops = {
>> +     .suspend = serial_omap_suspend,
>> +     .resume = serial_omap_resume,
>
> Static suspend operations should exists even when runtime PM is
> disabled.
>
>> +     .runtime_suspend = omap_serial_runtime_suspend,
>> +     .runtime_resume = omap_serial_runtime_resume,
>
> Note that some functions have serial_omap_ prefix and others have
> omap_serial_.  It looks like serial_omap_ is used througout the driver.
> Please unify.
>

yes sure. will change.

>> +};
>> +#define SERIAL_PM_OPS (&omap_serial_dev_pm_ops)
>> +#else
>> +#define SERIAL_PM_OPS NULL
>> +#endif
>
> Instead of this ifdef construct, please use SET_SYSTEM_SLEEP_PM_OPS()
> and SET_RUNTIME_PM_OPS() which take care of the right ifdefs for you,
> and also ensure that callbacks are available for suspend-to-disk (hibernate):
>
> static const struct dev_pm_ops omap_serial_dev_pm_ops = {
>       SET_SYSTEM_SLEEP_PM_OPS(serial_omap_suspend, serial_omap_resume),
>       SET_RUNTIME_PM_OPS(runtime_suspend, runtime_resume, NULL),
> };
>

yes will incorporate this.

Will give a quick respin and try to repost asap.

--
Thanks,
Govindraj.R


>>  static struct platform_driver serial_omap_driver = {
>>       .probe          = serial_omap_probe,
>>       .remove         = serial_omap_remove,
>> -
>> -     .suspend        = serial_omap_suspend,
>> -     .resume         = serial_omap_resume,
>>       .driver         = {
>>               .name   = DRIVER_NAME,
>> +             .pm = SERIAL_PM_OPS,
>>       },
>>  };
>
> Kevin
> --
> 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