Skip to content

Commit

Permalink
gpio/omap: fix wakeups on level-triggered GPIOs
Browse files Browse the repository at this point in the history
While both level- and edge-triggered GPIOs are capable of generating
interrupts, only edge-triggered GPIOs are capable of generating a
module-level wakeup to the PRCM (c.f. 34xx NDA TRM section 25.5.3.2.)

In order to ensure that devices using level-triggered GPIOs as
interrupts can also cause wakeups (e.g. from idle), this patch enables
edge-triggering for wakeup-enabled, level-triggered GPIOs when a GPIO
bank is runtime-suspended (which also happens during idle.)

This fixes a problem found in GPMC-connected network cards with GPIO
interrupts (e.g. smsc911x on Zoom3, Overo, ...) where network booting
with NFSroot was very slow since the GPIO IRQs used by the NIC were
not generating PRCM wakeups, and thus not waking the system from idle.
NOTE: until v3.3, this boot-time problem was somewhat masked because
the UART init prevented WFI during boot until the full serial driver
was available.  Preventing WFI allowed regular GPIO interrupts to fire
and this problem was not seen.  After the UART runtime PM cleanups, we
no longer avoid WFI during boot, so GPIO IRQs that were not causing
wakeups resulted in very slow IRQ response times.

Tested on platforms using level-triggered GPIOs for network IRQs using
the SMSC911x NIC: 3530/Overo and 3630/Zoom3.

Reported-by: Tony Lindgren <tony@atomide.com>
Tested-by: Tarun Kanti DebBarma <tarun.kanti@ti.com>
Tested-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Kevin Hilman <khilman@ti.com>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
  • Loading branch information
Kevin Hilman authored and Grant Likely committed Mar 12, 2012
1 parent e2aa417 commit 68942ed
Showing 1 changed file with 34 additions and 0 deletions.
34 changes: 34 additions & 0 deletions drivers/gpio/gpio-omap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1206,8 +1206,30 @@ static int omap_gpio_runtime_suspend(struct device *dev)
struct gpio_bank *bank = platform_get_drvdata(pdev);
u32 l1 = 0, l2 = 0;
unsigned long flags;
u32 wake_low, wake_hi;

spin_lock_irqsave(&bank->lock, flags);

/*
* Only edges can generate a wakeup event to the PRCM.
*
* Therefore, ensure any wake-up capable GPIOs have
* edge-detection enabled before going idle to ensure a wakeup
* to the PRCM is generated on a GPIO transition. (c.f. 34xx
* NDA TRM 25.5.3.1)
*
* The normal values will be restored upon ->runtime_resume()
* by writing back the values saved in bank->context.
*/
wake_low = bank->context.leveldetect0 & bank->context.wake_en;
if (wake_low)
__raw_writel(wake_low | bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
wake_hi = bank->context.leveldetect1 & bank->context.wake_en;
if (wake_hi)
__raw_writel(wake_hi | bank->context.risingdetect,
bank->base + bank->regs->risingdetect);

if (bank->power_mode != OFF_MODE) {
bank->power_mode = 0;
goto update_gpio_context_count;
Expand Down Expand Up @@ -1256,6 +1278,18 @@ static int omap_gpio_runtime_resume(struct device *dev)

spin_lock_irqsave(&bank->lock, flags);
_gpio_dbck_enable(bank);

/*
* In ->runtime_suspend(), level-triggered, wakeup-enabled
* GPIOs were set to edge trigger also in order to be able to
* generate a PRCM wakeup. Here we restore the
* pre-runtime_suspend() values for edge triggering.
*/
__raw_writel(bank->context.fallingdetect,
bank->base + bank->regs->fallingdetect);
__raw_writel(bank->context.risingdetect,
bank->base + bank->regs->risingdetect);

if (!bank->enabled_non_wakeup_gpios || !bank->workaround_enabled) {
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
Expand Down

0 comments on commit 68942ed

Please sign in to comment.