Hi Kevin,

Thanks for the detailed review.

On Sat, Mar 5, 2011 at 03:29, Kevin Hilman <khil...@ti.com> wrote:
> Charulatha V <ch...@ti.com> writes:
>
>> Call runtime pm APIs pm_runtime_put_sync() and pm_runtime_get()
>
> Minor: I think you mean _get_sync() and _put()

Yes, thanks for catching it. It was a typo  :-(

>
>> for enabling/disabling the clocks, sysconfig settings instead of using
>> clock FW APIs.
>> Note: OMAP16xx & OMAP2 has interface and functional clocks whereas
>> OMAP3&4 has interface and debounce clocks.
>>
>> GPIO driver is modified to use dev_pm_ops instead of sysdev_class.
>> With this approach, gpio_bank_suspend() & gpio_bank_resume()
>> are not part of sys_dev_class.
>>
>> Usage of PM runtime get/put APIs in GPIO driver is as given below:
>> pm_runtime_get_sync():
>> * In the probe before accessing the GPIO registers
>> * at the beginning of omap_gpio_request()
>>       -only when one of the gpios is requested on a bank, in which,
>>        no other gpio is being used (when mod_usage becomes non-zero).
>
> When using runtime PM, bank->mod_usage acutally becomes redundant with
> usage counting already done at the runtime PM level.  IOW, checking
> bank->mod_usage is he equivalent of checking pm_runtime_suspended(), so
> I think you can totally get rid of bank->mod_usage.

I wish to differ here. bank->mod_usage is filled for each GPIO pin in a bank.
Hence during request/free if pm_runtime_get_sync() is called for each GPIO
pin, then the count gets increased for each GPIO pin in a bank. But
gpio_prepare_for_idle/suspend calls pm_runtime_put() only once for
each bank. Hence there will be a count mismatch and hence this will lead
to problems and never a bank will get suspended.

IMO it is required to have bank->mod_usage checks. Please correct
me if I am wrong.

>
> pm_runtime_get* on an already active device is harmless and will just
> increment the runtime PM internal use counting.  It does however have
> the additional benefit of taking advantage of the runtime PM statistics
> so using tools like powertop, we will be able to see stats for *all*
> GPIO users, not just the first (and last) ones to use a given bank.
> IMO, This is a big win for PM debug.
>
> More on the implementation of this below...
>
>> * at the beginning of gpio_resume_after_idle()
>>       - only if the GPIO bank is under use
>>       (and)
>>       - if the bank is in non-wkup power domain
>> * at the beginning of gpio_irq_handler()
>>       - only if the specific GPIO bank is pm_runtime_suspended()
>> * at the beginning of omap_gpio_resume()
>>       - only if the GPIO bank is under use
>>
>> pm_runtime_put():
>> * In the probe after completing GPIO register access
>> * at the end of omap_gpio_free()
>>       - only when the last used gpio in the gpio bank is
>>         freed (when mod_usage becomes 0).
>> * at the end of gpio_prepare_for_idle()
>>       - only if the GPIO bank is under use
>>       (and)
>>       - if the bank is in non-wkup power domain
>> * at the end of gpio_irq_handler()
>>       - only if a corresponding pm_runtime_get_sync() was done
>>         in gpio_irq_handler()
>> * at the end of omap_gpio_suspend()
>>       - only if the GPIO bank is under use
>>
>> OMAP GPIO Request/ Free:
>> *During a gpio_request when mod_usage becomes non-zero, the bank
>>  registers are configured with init time configurations inorder to
>>  make sure that the GPIO init time configurations are not lost if
>>  the context was earlier lost when the GPIO bank was not in use.
>>
>>  TODO:
>>  Cleanup GPIO driver to avoid usage of gpio_bank_count &
>>  cpu_is_* checks
>>
>> Signed-off-by: Charulatha V <ch...@ti.com>
>> Signed-off-by: Tarun Kanti DebBarma <tarun.ka...@ti.com>
>> ---
>>  arch/arm/plat-omap/gpio.c |  305 
>> +++++++++++++++++++++++++--------------------
>>  1 files changed, 167 insertions(+), 138 deletions(-)
>>
>> diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c
>> index 10792b6..908bad2 100644
>> --- a/arch/arm/plat-omap/gpio.c
>> +++ b/arch/arm/plat-omap/gpio.c
>> @@ -177,6 +177,7 @@ struct gpio_bank {
>>
>>  static void omap_gpio_save_context(struct gpio_bank *bank);
>>  static void omap_gpio_restore_context(struct gpio_bank *bank);
>> +static void omap_gpio_mod_init(struct gpio_bank *bank, int id);
>>
>>  /*
>>   * TODO: Cleanup gpio_bank usage as it is having information
>> @@ -1042,8 +1043,28 @@ static int omap_gpio_request(struct gpio_chip *chip, 
>> unsigned offset)
>>       struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
>>       unsigned long flags;
>>
>> +     /*
>> +      * If this is the first gpio_request for the bank,
>> +      * enable the bank module
>> +      */
>> +     if (!bank->mod_usage) {
>> +             struct platform_device *pdev = to_platform_device(bank->dev);
>> +
>> +             if (unlikely(pm_runtime_get_sync(bank->dev) < 0)) {
>> +                     dev_err(bank->dev, "%s: GPIO bank %d "
>> +                                     "pm_runtime_get_sync failed\n",
>> +                                     __func__, pdev->id);
>> +                     return -EINVAL;
>> +             }
>> +
>> +             /* Initialize the gpio bank registers to init time value */
>> +             omap_gpio_mod_init(bank, pdev->id);
>> +     }
>> +
>
> This could all be replaced by:
>        if (!pm_runtime_get_sync(bank->dev))
>                omap_gpio_mod_init(bank, pdev->id);
>
> since the first 'get' that actually resumes the device will return zero,
> all the others will return 1.
>
> Actually, even better (and my prefernce) would be to just do the
> _get_sync() for every request as above, but put the omap_gpio_mod_init()
> in the ->runtime_resume() hook so it gets called whenever the first GPIO
> in the bank is activated.

The problem is only about the count mismatch I mentioned above.

>
>
>>       spin_lock_irqsave(&bank->lock, flags);
>>
>> +     bank->mod_usage |= 1 << offset;
>> +
>
>>       /* Set trigger to none. You need to enable the desired trigger with
>>        * request_irq() or set_irq_type().
>>        */
>> @@ -1058,22 +1079,7 @@ static int omap_gpio_request(struct gpio_chip *chip, 
>> unsigned offset)
>>               __raw_writel(__raw_readl(reg) | (1 << offset), reg);
>>       }
>>  #endif
>> -     if (!cpu_class_is_omap1()) {
>> -             if (!bank->mod_usage) {
>> -                     void __iomem *reg = bank->base;
>> -                     u32 ctrl;
>> -
>> -                     if (cpu_is_omap24xx() || cpu_is_omap34xx())
>> -                             reg += OMAP24XX_GPIO_CTRL;
>> -                     else if (cpu_is_omap44xx())
>> -                             reg += OMAP4_GPIO_CTRL;
>> -                     ctrl = __raw_readl(reg);
>> -                     /* Module is enabled, clocks are not gated */
>> -                     ctrl &= 0xFFFFFFFE;
>> -                     __raw_writel(ctrl, reg);
>> -             }
>> -             bank->mod_usage |= 1 << offset;
>> -     }
>
> Where did this code go?  I expected it to be moved, but not removed 
> completely.

It is only moved and not removed.
bank->mod_usage |= 1 << offset; is done above and the rest is done below.

> This code also belongs in the ->runtime_resume() method so it happens
> when the first GPIO in a bank is activated.

Okay.

>
>> +
>>       spin_unlock_irqrestore(&bank->lock, flags);
>>
>>       return 0;
>> @@ -1106,24 +1112,39 @@ static void omap_gpio_free(struct gpio_chip *chip, 
>> unsigned offset)
>>               __raw_writel(1 << offset, reg);
>>       }
>>  #endif
>> -     if (!cpu_class_is_omap1()) {
>> -             bank->mod_usage &= ~(1 << offset);
>> -             if (!bank->mod_usage) {
>> -                     void __iomem *reg = bank->base;
>> -                     u32 ctrl;
>> -
>> -                     if (cpu_is_omap24xx() || cpu_is_omap34xx())
>> -                             reg += OMAP24XX_GPIO_CTRL;
>> -                     else if (cpu_is_omap44xx())
>> -                             reg += OMAP4_GPIO_CTRL;
>> -                     ctrl = __raw_readl(reg);
>> -                     /* Module is disabled, clocks are gated */
>> -                     ctrl |= 1;
>> -                     __raw_writel(ctrl, reg);
>> -             }
>> +     bank->mod_usage &= ~(1 << offset);
>> +     if (!bank->mod_usage) {
>> +             void __iomem *reg = bank->base;
>> +             u32 ctrl;
>> +
>> +             if (bank->method == METHOD_GPIO_24XX)
>> +                     reg += OMAP24XX_GPIO_CTRL;
>> +             else if (bank->method == METHOD_GPIO_44XX)
>> +                     reg += OMAP4_GPIO_CTRL;
>> +             else
>> +                     goto reset_gpio;
>> +
>> +             ctrl = __raw_readl(reg);
>> +             /* Module is disabled, clocks are gated */
>> +             ctrl |= 1;
>> +             __raw_writel(ctrl, reg);
>
> And here, rather than updating bank->mod_usage, just move this code
> into a ->runtime_suspend hook which will then be called whenever the
> bank is actually suspended.

Pls see above explanation for bank->mod_usage.

>
>>       }
>> +reset_gpio:
>>       _reset_gpio(bank, bank->chip.base + offset);
>>       spin_unlock_irqrestore(&bank->lock, flags);
>> +
>> +     /*
>> +      * If this is the last gpio to be freed in the bank,
>> +      * disable the bank module
>> +      */
>> +     if (!bank->mod_usage) {
>> +             if (unlikely(pm_runtime_put(bank->dev) < 0)) {
>> +                     struct platform_device *pdev =
>> +                                     to_platform_device(bank->dev);
>> +                     dev_err(bank->dev, "%s: GPIO bank %d pm_runtime_put "
>> +                                     "failed\n", __func__, pdev->id);
>> +             }
>> +     }
>
> and this can just be a pm_runtime_put(bank->dev)

ditto.

>
>>  }
>>
>>  /*
>> @@ -1143,10 +1164,17 @@ static void gpio_irq_handler(unsigned int irq, 
>> struct irq_desc *desc)
>>       struct gpio_bank *bank;
>>       u32 retrigger = 0;
>>       int unmasked = 0;
>> +     int enable_gpio = 0;
>>
>>       desc->irq_data.chip->irq_ack(&desc->irq_data);
>>
>>       bank = get_irq_data(irq);
>> +
>> +     if (pm_runtime_suspended(bank->dev)) {
>
> Why do you need the check here?
>
> If the device is already suspended, and you call _get_sync(), it
> just increments the usecount, and returns.

Yes, you are right. I will remove pm_runtime_suspended() checks
and also the enable_gpio flag.

>
>> +             if (unlikely(pm_runtime_get_sync(bank->dev) == 0))
>> +                     enable_gpio = 1;
>> +     }
>> +
>>  #ifdef CONFIG_ARCH_OMAP1
>>       if (bank->method == METHOD_MPUIO)
>>               isr_reg = bank->base +
>> @@ -1238,6 +1266,9 @@ static void gpio_irq_handler(unsigned int irq, struct 
>> irq_desc *desc)
>>  exit:
>>       if (!unmasked)
>>               desc->irq_data.chip->irq_unmask(&desc->irq_data);
>> +
>> +     if (enable_gpio)
>> +             pm_runtime_put(bank->dev);
>
> Likewise, I think you could just always do a 'put' here without having
> to have a flag.

Sure, will change this.

>
>>  }
>>
>>  static void gpio_irq_shutdown(struct irq_data *d)
>> @@ -1742,126 +1773,121 @@ static int __devinit omap_gpio_probe(struct 
>> platform_device *pdev)
>>       }
>>
>>       pm_runtime_enable(bank->dev);
>> -     pm_runtime_get_sync(bank->dev);
>> +     pm_runtime_irq_safe(bank->dev);
>> +
>> +     if (unlikely(pm_runtime_get_sync(bank->dev) < 0)) {
>> +             dev_err(bank->dev, "%s: GPIO bank %d pm_runtime_get_sync "
>> +                             "failed\n", __func__, id);
>> +             iounmap(bank->base);
>> +             return -EINVAL;
>> +     }
>>
>> -     omap_gpio_mod_init(bank, id);
>>       omap_gpio_chip_init(bank);
>>       omap_gpio_show_rev(bank);
>>
>> +     if (unlikely(pm_runtime_put(bank->dev) < 0)) {
>
> use IS_ERR_VALUE() instead of '< 0'

Okay.

>
>> +             dev_err(bank->dev, "%s: GPIO bank %d pm_runtime_put "
>> +                             "failed\n", __func__, id);
>> +             iounmap(bank->base);
>> +             return -EINVAL;
>> +     }
>> +
>>       if (!gpio_init_done)
>>               gpio_init_done = 1;
>>
>>       return 0;
>>  }
>>
>> -#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS)
>> -static int omap_gpio_suspend(struct sys_device *dev, pm_message_t mesg)
>> +static int omap_gpio_suspend(struct device *dev)
>>  {
>> -     int i;
>> +     struct platform_device *pdev = to_platform_device(dev);
>> +     void __iomem *wake_status;
>> +     void __iomem *wake_clear;
>> +     void __iomem *wake_set;
>> +     unsigned long flags;
>> +     struct gpio_bank *bank = &gpio_bank[pdev->id];
>>
>> -     if (!cpu_class_is_omap2() && !cpu_is_omap16xx())
>> +     /* If the gpio bank is not used, do nothing */
>> +     if (!bank->mod_usage)
>
>        if (pm_runtime_suspended(bank-dev))
>
>>               return 0;
>>
>> -     for (i = 0; i < gpio_bank_count; i++) {
>> -             struct gpio_bank *bank = &gpio_bank[i];
>> -             void __iomem *wake_status;
>> -             void __iomem *wake_clear;
>> -             void __iomem *wake_set;
>> -             unsigned long flags;
>> +     switch (bank->method) {
>> +     case METHOD_GPIO_1610:
>> +             wake_status = bank->base + OMAP1610_GPIO_WAKEUPENABLE;
>> +             wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA;
>> +             wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA;
>> +             break;
>> +     case METHOD_GPIO_24XX:
>> +             wake_status = bank->base + OMAP24XX_GPIO_WAKE_EN;
>> +             wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA;
>> +             wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA;
>> +             break;
>> +     case METHOD_GPIO_44XX:
>> +             wake_status = bank->base + OMAP4_GPIO_IRQWAKEN0;
>> +             wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0;
>> +             wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0;
>> +             break;
>> +     default:
>> +             return 0;
>> +     }
>>
>> -             switch (bank->method) {
>> -#ifdef CONFIG_ARCH_OMAP16XX
>> -             case METHOD_GPIO_1610:
>> -                     wake_status = bank->base + OMAP1610_GPIO_WAKEUPENABLE;
>> -                     wake_clear = bank->base + 
>> OMAP1610_GPIO_CLEAR_WAKEUPENA;
>> -                     wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA;
>> -                     break;
>> -#endif
>> -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
>> -             case METHOD_GPIO_24XX:
>> -                     wake_status = bank->base + OMAP24XX_GPIO_WAKE_EN;
>> -                     wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA;
>> -                     wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA;
>> -                     break;
>> -#endif
>> -#ifdef CONFIG_ARCH_OMAP4
>> -             case METHOD_GPIO_44XX:
>> -                     wake_status = bank->base + OMAP4_GPIO_IRQWAKEN0;
>> -                     wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0;
>> -                     wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0;
>> -                     break;
>> -#endif
>> -             default:
>> -                     continue;
>> -             }
>> +     if (strcmp(bank->pwrdm_name, "wkup_pwrdm"))
>> +             omap_gpio_save_context(bank);
>
> see comments on patch 2.

I agree.

>
>> -             spin_lock_irqsave(&bank->lock, flags);
>> -             bank->saved_wakeup = __raw_readl(wake_status);
>> -             __raw_writel(0xffffffff, wake_clear);
>> -             __raw_writel(bank->suspend_wakeup, wake_set);
>> -             spin_unlock_irqrestore(&bank->lock, flags);
>> -     }
>> +     spin_lock_irqsave(&bank->lock, flags);
>> +     bank->saved_wakeup = __raw_readl(wake_status);
>> +     __raw_writel(0xffffffff, wake_clear);
>> +     __raw_writel(bank->suspend_wakeup, wake_set);
>> +     spin_unlock_irqrestore(&bank->lock, flags);
>> +
>> +     if (unlikely(pm_runtime_put(bank->dev) < 0))
>> +             return -EINVAL;
>>
>>       return 0;
>>  }
>>

<<snip>>
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to