Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+136 −18
Original line number Diff line number Diff line
@@ -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

/*