diff options
Diffstat (limited to 'arch/arm/plat-omap/gpio.c')
-rw-r--r-- | arch/arm/plat-omap/gpio.c | 154 |
1 files changed, 136 insertions, 18 deletions
diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c index 037a4930ec61..ec0e2f18fdea 100644 --- a/arch/arm/plat-omap/gpio.c +++ b/arch/arm/plat-omap/gpio.c @@ -117,8 +117,18 @@ struct gpio_bank { u16 virtual_irq_start; int method; u32 reserved_map; +#if defined (CONFIG_ARCH_OMAP16XX) || defined (CONFIG_ARCH_OMAP24XX) u32 suspend_wakeup; u32 saved_wakeup; +#endif +#ifdef CONFIG_ARCH_OMAP24XX + u32 non_wakeup_gpios; + u32 enabled_non_wakeup_gpios; + + u32 saved_datain; + u32 saved_fallingdetect; + u32 saved_risingdetect; +#endif spinlock_t lock; }; @@ -397,8 +407,10 @@ do { \ __raw_writel(l, base + reg); \ } while(0) -static inline void set_24xx_gpio_triggering(void __iomem *base, int gpio, int trigger) +#ifdef CONFIG_ARCH_OMAP24XX +static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) { + void __iomem *base = bank->base; u32 gpio_bit = 1 << gpio; MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT0, gpio_bit, @@ -409,9 +421,21 @@ static inline void set_24xx_gpio_triggering(void __iomem *base, int gpio, int tr trigger & __IRQT_RISEDGE); MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit, trigger & __IRQT_FALEDGE); + if (likely(!(bank->non_wakeup_gpios & gpio_bit))) { + if (trigger != 0) + __raw_writel(1 << gpio, bank->base + OMAP24XX_GPIO_SETWKUENA); + else + __raw_writel(1 << gpio, bank->base + OMAP24XX_GPIO_CLEARWKUENA); + } else { + if (trigger != 0) + bank->enabled_non_wakeup_gpios |= gpio_bit; + else + bank->enabled_non_wakeup_gpios &= ~gpio_bit; + } /* FIXME: Possibly do 'set_irq_handler(j, handle_level_irq)' if only level * triggering requested. */ } +#endif static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) { @@ -439,6 +463,7 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) else goto bad; break; +#ifdef CONFIG_ARCH_OMAP16XX case METHOD_GPIO_1610: if (gpio & 0x08) reg += OMAP1610_GPIO_EDGE_CTRL2; @@ -454,7 +479,14 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) l |= 2 << (gpio << 1); if (trigger & __IRQT_FALEDGE) l |= 1 << (gpio << 1); + if (trigger) + /* Enable wake-up during idle for dynamic tick */ + __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_SET_WAKEUPENA); + else + __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA); break; +#endif +#ifdef CONFIG_ARCH_OMAP730 case METHOD_GPIO_730: reg += OMAP730_GPIO_INT_CONTROL; l = __raw_readl(reg); @@ -465,9 +497,12 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) else goto bad; break; +#endif +#ifdef CONFIG_ARCH_OMAP24XX case METHOD_GPIO_24XX: - set_24xx_gpio_triggering(reg, gpio, trigger); + set_24xx_gpio_triggering(bank, gpio, trigger); break; +#endif default: BUG(); goto bad; @@ -651,8 +686,8 @@ static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int ena static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable) { switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP16XX case METHOD_GPIO_1610: - case METHOD_GPIO_24XX: spin_lock(&bank->lock); if (enable) bank->suspend_wakeup |= (1 << gpio); @@ -660,6 +695,24 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable) bank->suspend_wakeup &= ~(1 << gpio); spin_unlock(&bank->lock); return 0; +#endif +#ifdef CONFIG_ARCH_OMAP24XX + case METHOD_GPIO_24XX: + spin_lock(&bank->lock); + if (enable) { + if (bank->non_wakeup_gpios & (1 << gpio)) { + printk(KERN_ERR "Unable to enable wakeup on" + "non-wakeup GPIO%d\n", + (bank - gpio_bank) * 32 + gpio); + spin_unlock(&bank->lock); + return -EINVAL; + } + bank->suspend_wakeup |= (1 << gpio); + } else + bank->suspend_wakeup &= ~(1 << gpio); + spin_unlock(&bank->lock); + return 0; +#endif default: printk(KERN_ERR "Can't enable GPIO wakeup for method %i\n", bank->method); @@ -721,20 +774,6 @@ int omap_request_gpio(int gpio) __raw_writel(__raw_readl(reg) | (1 << get_gpio_index(gpio)), reg); } #endif -#ifdef CONFIG_ARCH_OMAP16XX - if (bank->method == METHOD_GPIO_1610) { - /* Enable wake-up during idle for dynamic tick */ - void __iomem *reg = bank->base + OMAP1610_GPIO_SET_WAKEUPENA; - __raw_writel(1 << get_gpio_index(gpio), reg); - } -#endif -#ifdef CONFIG_ARCH_OMAP24XX - if (bank->method == METHOD_GPIO_24XX) { - /* Enable wake-up during idle for dynamic tick */ - void __iomem *reg = bank->base + OMAP24XX_GPIO_SETWKUENA; - __raw_writel(1 << get_gpio_index(gpio), reg); - } -#endif spin_unlock(&bank->lock); return 0; @@ -1080,13 +1119,18 @@ static int __init _omap_gpio_init(void) #endif #ifdef CONFIG_ARCH_OMAP24XX if (bank->method == METHOD_GPIO_24XX) { + static const u32 non_wakeup_gpios[] = { + 0xe203ffc0, 0x08700040 + }; + __raw_writel(0x00000000, bank->base + OMAP24XX_GPIO_IRQENABLE1); __raw_writel(0xffffffff, bank->base + OMAP24XX_GPIO_IRQSTATUS1); __raw_writew(0x0015, bank->base + OMAP24XX_GPIO_SYSCONFIG); /* Initialize interface clock ungated, module enabled */ __raw_writel(0, bank->base + OMAP24XX_GPIO_CTRL); - + if (i < ARRAY_SIZE(non_wakeup_gpios)) + bank->non_wakeup_gpios = non_wakeup_gpios[i]; gpio_count = 32; } #endif @@ -1200,6 +1244,80 @@ static struct sys_device omap_gpio_device = { .id = 0, .cls = &omap_gpio_sysclass, }; + +#endif + +#ifdef CONFIG_ARCH_OMAP24XX + +static int workaround_enabled; + +void omap2_gpio_prepare_for_retention(void) +{ + int i, c = 0; + + /* Remove triggering for all non-wakeup GPIOs. Otherwise spurious + * IRQs will be generated. See OMAP2420 Errata item 1.101. */ + for (i = 0; i < gpio_bank_count; i++) { + struct gpio_bank *bank = &gpio_bank[i]; + u32 l1, l2; + + if (!(bank->enabled_non_wakeup_gpios)) + continue; + bank->saved_datain = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN); + l1 = __raw_readl(bank->base + OMAP24XX_GPIO_FALLINGDETECT); + l2 = __raw_readl(bank->base + OMAP24XX_GPIO_RISINGDETECT); + bank->saved_fallingdetect = l1; + bank->saved_risingdetect = l2; + l1 &= ~bank->enabled_non_wakeup_gpios; + l2 &= ~bank->enabled_non_wakeup_gpios; + __raw_writel(l1, bank->base + OMAP24XX_GPIO_FALLINGDETECT); + __raw_writel(l2, bank->base + OMAP24XX_GPIO_RISINGDETECT); + c++; + } + if (!c) { + workaround_enabled = 0; + return; + } + workaround_enabled = 1; +} + +void omap2_gpio_resume_after_retention(void) +{ + int i; + + if (!workaround_enabled) + return; + for (i = 0; i < gpio_bank_count; i++) { + struct gpio_bank *bank = &gpio_bank[i]; + u32 l; + + if (!(bank->enabled_non_wakeup_gpios)) + continue; + __raw_writel(bank->saved_fallingdetect, + bank->base + OMAP24XX_GPIO_FALLINGDETECT); + __raw_writel(bank->saved_risingdetect, + bank->base + OMAP24XX_GPIO_RISINGDETECT); + /* Check if any of the non-wakeup interrupt GPIOs have changed + * state. If so, generate an IRQ by software. This is + * horribly racy, but it's the best we can do to work around + * this silicon bug. */ + l = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN); + l ^= bank->saved_datain; + l &= bank->non_wakeup_gpios; + if (l) { + u32 old0, old1; + + old0 = __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0); + old1 = __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1); + __raw_writel(old0 | l, bank->base + OMAP24XX_GPIO_LEVELDETECT0); + __raw_writel(old1 | l, bank->base + OMAP24XX_GPIO_LEVELDETECT1); + __raw_writel(old0, bank->base + OMAP24XX_GPIO_LEVELDETECT0); + __raw_writel(old1, bank->base + OMAP24XX_GPIO_LEVELDETECT1); + } + } + +} + #endif /* |