Commit 3ac4fa99 authored by Juha Yrjola's avatar Juha Yrjola Committed by Russell King

ARM: OMAP: Implement workaround for GPIO wakeup bug in OMAP2420 silicon

Some GPIOs on OMAP2420 do not have wakeup capabilities. If these GPIOs
are configured as IRQ sources, spurious interrupts will be generated
each time the core domain enters retention.
Signed-off-by: default avatarJuha Yrjola <juha.yrjola@solidboot.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 14f1c3bf
......@@ -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);
......@@ -720,20 +773,6 @@ int omap_request_gpio(int gpio)
reg = bank->base + OMAP1510_GPIO_PIN_CONTROL;
__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);
......@@ -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
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment