From: "Varadarajan, Charulatha" <charu@ti.com>
To: Kevin Hilman <khilman@ti.com>
Cc: linux-omap@vger.kernel.org, tony@atomide.com, paul@pwsan.com,
Tarun Kanti DebBarma <tarun.kanti@ti.com>
Subject: Re: [PATCH 5/5] OMAP: GPIO: use PM runtime framework
Date: Sat, 5 Mar 2011 09:40:12 +0430 [thread overview]
Message-ID: <AANLkTi=Sd0zJwnx1DHCCOr9ncHv1ZP1EworoJex7NLEK@mail.gmail.com> (raw)
In-Reply-To: <87mxlabih5.fsf@ti.com>
Hi Kevin,
Thanks for the detailed review.
On Sat, Mar 5, 2011 at 03:29, Kevin Hilman <khilman@ti.com> wrote:
> Charulatha V <charu@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 <charu@ti.com>
>> Signed-off-by: Tarun Kanti DebBarma <tarun.kanti@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 majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2011-03-05 5:10 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-02-28 10:57 [PATCH 0/5] OMAP: GPIO: use PM runtime framework Charulatha V
2011-02-28 10:57 ` [PATCH 1/5] OMAP: GPIO: make gpio_context part of gpio_bank structure Charulatha V
2011-02-28 10:57 ` [PATCH 2/5] OMAP: GPIO: use pwrdmn name to find wkup dmn GPIO Charulatha V
2011-03-04 21:51 ` Kevin Hilman
2011-03-05 5:21 ` Varadarajan, Charulatha
2011-03-07 18:56 ` Kevin Hilman
2011-03-08 2:25 ` Paul Walmsley
2011-03-08 5:45 ` Varadarajan, Charulatha
2011-02-28 10:57 ` [PATCH 3/5] OMAP4: GPIO: save/restore context Charulatha V
2011-02-28 10:57 ` [PATCH 4/5] OMAP: GPIO: call save/restore ctxt from GPIO driver Charulatha V
2011-03-04 22:20 ` Kevin Hilman
2011-03-05 5:15 ` Varadarajan, Charulatha
2011-02-28 10:57 ` [PATCH 5/5] OMAP: GPIO: use PM runtime framework Charulatha V
2011-03-04 22:59 ` Kevin Hilman
2011-03-05 5:10 ` Varadarajan, Charulatha [this message]
2011-03-07 18:55 ` Kevin Hilman
2011-03-08 15:05 ` Varadarajan, Charulatha
2011-03-08 18:23 ` Kevin Hilman
2011-03-09 1:24 ` Varadarajan, Charulatha
2011-03-09 10:23 ` Varadarajan, Charulatha
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to='AANLkTi=Sd0zJwnx1DHCCOr9ncHv1ZP1EworoJex7NLEK@mail.gmail.com' \
--to=charu@ti.com \
--cc=khilman@ti.com \
--cc=linux-omap@vger.kernel.org \
--cc=paul@pwsan.com \
--cc=tarun.kanti@ti.com \
--cc=tony@atomide.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).