Skip to content

Commit

Permalink
ARM: OMAP: Implement workaround for GPIO wakeup bug in OMAP2420 silicon
Browse files Browse the repository at this point in the history
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: Juha Yrjola <juha.yrjola@solidboot.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
  • Loading branch information
Juha Yrjola authored and Russell King committed May 5, 2007
1 parent 14f1c3b commit 3ac4fa9
Showing 1 changed file with 136 additions and 18 deletions.
154 changes: 136 additions & 18 deletions arch/arm/plat-omap/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -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,
Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -651,15 +686,33 @@ 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);
else
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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

/*
Expand Down

0 comments on commit 3ac4fa9

Please sign in to comment.