mirror of
https://github.com/joel16/android_kernel_sony_msm8994_rework.git
synced 2024-12-28 06:54:55 +00:00
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: 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>
This commit is contained in:
parent
14f1c3bf51
commit
3ac4fa9929
@ -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
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user