diff options
author | Kevin Hilman <khilman@ti.com> | 2011-05-20 11:53:37 +0200 |
---|---|---|
committer | Kevin Hilman <khilman@ti.com> | 2011-05-27 08:43:34 -0700 |
commit | e5cdb13ff95e1400bc94d3e6610fc5e95be3b5b1 (patch) | |
tree | 0421e5fc4819966c366fd68a6cdfc5f89422c430 /drivers/gpio | |
parent | 5ee1a81b84ee8b0bd0b4f8cdbac48b472863c1f2 (diff) |
GPIO: OMAP: move to drivers/gpio
Move OMAP GPIO driver to drivers/gpio. Builds whenever
CONFIG_ARCH_OMAP=y.
Signed-off-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-omap.c | 2007 |
2 files changed, 2008 insertions, 0 deletions
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index becef595435..a6f1d42f265 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_GPIO_MAX732X) += max732x.o obj-$(CONFIG_GPIO_MC33880) += mc33880.o obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_74X164) += 74x164.o +obj-$(CONFIG_ARCH_OMAP) += gpio-omap.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_PCH) += pch_gpio.o diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c new file mode 100644 index 00000000000..6c51191da56 --- /dev/null +++ b/drivers/gpio/gpio-omap.c @@ -0,0 +1,2007 @@ +/* + * Support functions for OMAP GPIO + * + * Copyright (C) 2003-2005 Nokia Corporation + * Written by Juha Yrjölä <juha.yrjola@nokia.com> + * + * Copyright (C) 2009 Texas Instruments + * Added OMAP4 support - Santosh Shilimkar <santosh.shilimkar@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/syscore_ops.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/pm_runtime.h> + +#include <mach/hardware.h> +#include <asm/irq.h> +#include <mach/irqs.h> +#include <mach/gpio.h> +#include <asm/mach/irq.h> + +struct gpio_bank { + unsigned long pbase; + void __iomem *base; + u16 irq; + u16 virtual_irq_start; + int method; +#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS) + u32 suspend_wakeup; + u32 saved_wakeup; +#endif + u32 non_wakeup_gpios; + u32 enabled_non_wakeup_gpios; + + u32 saved_datain; + u32 saved_fallingdetect; + u32 saved_risingdetect; + u32 level_mask; + u32 toggle_mask; + spinlock_t lock; + struct gpio_chip chip; + struct clk *dbck; + u32 mod_usage; + u32 dbck_enable_mask; + struct device *dev; + bool dbck_flag; + int stride; +}; + +#ifdef CONFIG_ARCH_OMAP3 +struct omap3_gpio_regs { + u32 irqenable1; + u32 irqenable2; + u32 wake_en; + u32 ctrl; + u32 oe; + u32 leveldetect0; + u32 leveldetect1; + u32 risingdetect; + u32 fallingdetect; + u32 dataout; +}; + +static struct omap3_gpio_regs gpio_context[OMAP34XX_NR_GPIOS]; +#endif + +/* + * TODO: Cleanup gpio_bank usage as it is having information + * related to all instances of the device + */ +static struct gpio_bank *gpio_bank; + +static int bank_width; + +/* TODO: Analyze removing gpio_bank_count usage from driver code */ +int gpio_bank_count; + +static inline struct gpio_bank *get_gpio_bank(int gpio) +{ + if (cpu_is_omap15xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1]; + } + if (cpu_is_omap16xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1 + (gpio >> 4)]; + } + if (cpu_is_omap7xx()) { + if (OMAP_GPIO_IS_MPUIO(gpio)) + return &gpio_bank[0]; + return &gpio_bank[1 + (gpio >> 5)]; + } + if (cpu_is_omap24xx()) + return &gpio_bank[gpio >> 5]; + if (cpu_is_omap34xx() || cpu_is_omap44xx()) + return &gpio_bank[gpio >> 5]; + BUG(); + return NULL; +} + +static inline int get_gpio_index(int gpio) +{ + if (cpu_is_omap7xx()) + return gpio & 0x1f; + if (cpu_is_omap24xx()) + return gpio & 0x1f; + if (cpu_is_omap34xx() || cpu_is_omap44xx()) + return gpio & 0x1f; + return gpio & 0x0f; +} + +static inline int gpio_valid(int gpio) +{ + if (gpio < 0) + return -1; + if (cpu_class_is_omap1() && OMAP_GPIO_IS_MPUIO(gpio)) { + if (gpio >= OMAP_MAX_GPIO_LINES + 16) + return -1; + return 0; + } + if (cpu_is_omap15xx() && gpio < 16) + return 0; + if ((cpu_is_omap16xx()) && gpio < 64) + return 0; + if (cpu_is_omap7xx() && gpio < 192) + return 0; + if (cpu_is_omap2420() && gpio < 128) + return 0; + if (cpu_is_omap2430() && gpio < 160) + return 0; + if ((cpu_is_omap34xx() || cpu_is_omap44xx()) && gpio < 192) + return 0; + return -1; +} + +static int check_gpio(int gpio) +{ + if (unlikely(gpio_valid(gpio) < 0)) { + printk(KERN_ERR "omap-gpio: invalid GPIO %d\n", gpio); + dump_stack(); + return -1; + } + return 0; +} + +static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input) +{ + void __iomem *reg = bank->base; + u32 l; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_IO_CNTL / bank->stride; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DIR_CONTROL; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_DIRECTION; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_DIR_CONTROL; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_OE; + break; +#endif +#if defined(CONFIG_ARCH_OMAP4) + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_OE; + break; +#endif + default: + WARN_ON(1); + return; + } + l = __raw_readl(reg); + if (is_input) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + __raw_writel(l, reg); +} + +static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable) +{ + void __iomem *reg = bank->base; + u32 l = 0; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_OUTPUT / bank->stride; + l = __raw_readl(reg); + if (enable) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DATA_OUTPUT; + l = __raw_readl(reg); + if (enable) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + if (enable) + reg += OMAP1610_GPIO_SET_DATAOUT; + else + reg += OMAP1610_GPIO_CLEAR_DATAOUT; + l = 1 << gpio; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_DATA_OUTPUT; + l = __raw_readl(reg); + if (enable) + l |= 1 << gpio; + else + l &= ~(1 << gpio); + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + if (enable) + reg += OMAP24XX_GPIO_SETDATAOUT; + else + reg += OMAP24XX_GPIO_CLEARDATAOUT; + l = 1 << gpio; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + if (enable) + reg += OMAP4_GPIO_SETDATAOUT; + else + reg += OMAP4_GPIO_CLEARDATAOUT; + l = 1 << gpio; + break; +#endif + default: + WARN_ON(1); + return; + } + __raw_writel(l, reg); +} + +static int _get_gpio_datain(struct gpio_bank *bank, int gpio) +{ + void __iomem *reg; + + if (check_gpio(gpio) < 0) + return -EINVAL; + reg = bank->base; + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_INPUT_LATCH / bank->stride; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DATA_INPUT; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_DATAIN; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_DATA_INPUT; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_DATAIN; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_DATAIN; + break; +#endif + default: + return -EINVAL; + } + return (__raw_readl(reg) + & (1 << get_gpio_index(gpio))) != 0; +} + +static int _get_gpio_dataout(struct gpio_bank *bank, int gpio) +{ + void __iomem *reg; + + if (check_gpio(gpio) < 0) + return -EINVAL; + reg = bank->base; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_OUTPUT / bank->stride; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_DATA_OUTPUT; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_DATAOUT; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_DATA_OUTPUT; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_DATAOUT; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_DATAOUT; + break; +#endif + default: + return -EINVAL; + } + + return (__raw_readl(reg) & (1 << get_gpio_index(gpio))) != 0; +} + +#define MOD_REG_BIT(reg, bit_mask, set) \ +do { \ + int l = __raw_readl(base + reg); \ + if (set) l |= bit_mask; \ + else l &= ~bit_mask; \ + __raw_writel(l, base + reg); \ +} while(0) + +/** + * _set_gpio_debounce - low level gpio debounce time + * @bank: the gpio bank we're acting upon + * @gpio: the gpio number on this @gpio + * @debounce: debounce time to use + * + * OMAP's debounce time is in 31us steps so we need + * to convert and round up to the closest unit. + */ +static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio, + unsigned debounce) +{ + void __iomem *reg = bank->base; + u32 val; + u32 l; + + if (!bank->dbck_flag) + return; + + if (debounce < 32) + debounce = 0x01; + else if (debounce > 7936) + debounce = 0xff; + else + debounce = (debounce / 0x1f) - 1; + + l = 1 << get_gpio_index(gpio); + + if (bank->method == METHOD_GPIO_44XX) + reg += OMAP4_GPIO_DEBOUNCINGTIME; + else + reg += OMAP24XX_GPIO_DEBOUNCE_VAL; + + __raw_writel(debounce, reg); + + reg = bank->base; + if (bank->method == METHOD_GPIO_44XX) + reg += OMAP4_GPIO_DEBOUNCENABLE; + else + reg += OMAP24XX_GPIO_DEBOUNCE_EN; + + val = __raw_readl(reg); + + if (debounce) { + val |= l; + clk_enable(bank->dbck); + } else { + val &= ~l; + clk_disable(bank->dbck); + } + bank->dbck_enable_mask = val; + + __raw_writel(val, reg); +} + +#ifdef CONFIG_ARCH_OMAP2PLUS +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; + u32 val; + + if (cpu_is_omap44xx()) { + MOD_REG_BIT(OMAP4_GPIO_LEVELDETECT0, gpio_bit, + trigger & IRQ_TYPE_LEVEL_LOW); + MOD_REG_BIT(OMAP4_GPIO_LEVELDETECT1, gpio_bit, + trigger & IRQ_TYPE_LEVEL_HIGH); + MOD_REG_BIT(OMAP4_GPIO_RISINGDETECT, gpio_bit, + trigger & IRQ_TYPE_EDGE_RISING); + MOD_REG_BIT(OMAP4_GPIO_FALLINGDETECT, gpio_bit, + trigger & IRQ_TYPE_EDGE_FALLING); + } else { + MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT0, gpio_bit, + trigger & IRQ_TYPE_LEVEL_LOW); + MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT1, gpio_bit, + trigger & IRQ_TYPE_LEVEL_HIGH); + MOD_REG_BIT(OMAP24XX_GPIO_RISINGDETECT, gpio_bit, + trigger & IRQ_TYPE_EDGE_RISING); + MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit, + trigger & IRQ_TYPE_EDGE_FALLING); + } + if (likely(!(bank->non_wakeup_gpios & gpio_bit))) { + if (cpu_is_omap44xx()) { + if (trigger != 0) + __raw_writel(1 << gpio, bank->base+ + OMAP4_GPIO_IRQWAKEN0); + else { + val = __raw_readl(bank->base + + OMAP4_GPIO_IRQWAKEN0); + __raw_writel(val & (~(1 << gpio)), bank->base + + OMAP4_GPIO_IRQWAKEN0); + } + } else { + /* + * GPIO wakeup request can only be generated on edge + * transitions + */ + if (trigger & IRQ_TYPE_EDGE_BOTH) + __raw_writel(1 << gpio, bank->base + + OMAP24XX_GPIO_SETWKUENA); + else + __raw_writel(1 << gpio, bank->base + + OMAP24XX_GPIO_CLEARWKUENA); + } + } + /* This part needs to be executed always for OMAP34xx */ + if (cpu_is_omap34xx() || (bank->non_wakeup_gpios & gpio_bit)) { + /* + * Log the edge gpio and manually trigger the IRQ + * after resume if the input level changes + * to avoid irq lost during PER RET/OFF mode + * Applies for omap2 non-wakeup gpio and all omap3 gpios + */ + if (trigger & IRQ_TYPE_EDGE_BOTH) + bank->enabled_non_wakeup_gpios |= gpio_bit; + else + bank->enabled_non_wakeup_gpios &= ~gpio_bit; + } + + if (cpu_is_omap44xx()) { + bank->level_mask = + __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT0) | + __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT1); + } else { + bank->level_mask = + __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0) | + __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1); + } +} +#endif + +#ifdef CONFIG_ARCH_OMAP1 +/* + * This only applies to chips that can't do both rising and falling edge + * detection at once. For all other chips, this function is a noop. + */ +static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio) +{ + void __iomem *reg = bank->base; + u32 l = 0; + + switch (bank->method) { + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride; + break; +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_CONTROL; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_CONTROL; + break; +#endif + default: + return; + } + + l = __raw_readl(reg); + if ((l >> gpio) & 1) + l &= ~(1 << gpio); + else + l |= 1 << gpio; + + __raw_writel(l, reg); +} +#endif + +static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger) +{ + void __iomem *reg = bank->base; + u32 l = 0; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride; + l = __raw_readl(reg); + if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 1 << gpio; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + l &= ~(1 << gpio); + else + goto bad; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_CONTROL; + l = __raw_readl(reg); + if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 1 << gpio; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + l &= ~(1 << gpio); + else + goto bad; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + if (gpio & 0x08) + reg += OMAP1610_GPIO_EDGE_CTRL2; + else + reg += OMAP1610_GPIO_EDGE_CTRL1; + gpio &= 0x07; + l = __raw_readl(reg); + l &= ~(3 << (gpio << 1)); + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 2 << (gpio << 1); + if (trigger & IRQ_TYPE_EDGE_FALLING) + 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 +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_CONTROL; + l = __raw_readl(reg); + if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) + bank->toggle_mask |= 1 << gpio; + if (trigger & IRQ_TYPE_EDGE_RISING) + l |= 1 << gpio; + else if (trigger & IRQ_TYPE_EDGE_FALLING) + l &= ~(1 << gpio); + else + goto bad; + break; +#endif +#ifdef CONFIG_ARCH_OMAP2PLUS + case METHOD_GPIO_24XX: + case METHOD_GPIO_44XX: + set_24xx_gpio_triggering(bank, gpio, trigger); + return 0; +#endif + default: + goto bad; + } + __raw_writel(l, reg); + return 0; +bad: + return -EINVAL; +} + +static int gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct gpio_bank *bank; + unsigned gpio; + int retval; + unsigned long flags; + + if (!cpu_class_is_omap2() && d->irq > IH_MPUIO_BASE) + gpio = OMAP_MPUIO(d->irq - IH_MPUIO_BASE); + else + gpio = d->irq - IH_GPIO_BASE; + + if (check_gpio(gpio) < 0) + return -EINVAL; + + if (type & ~IRQ_TYPE_SENSE_MASK) + return -EINVAL; + + /* OMAP1 allows only only edge triggering */ + if (!cpu_class_is_omap2() + && (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH))) + return -EINVAL; + + bank = irq_data_get_irq_chip_data(d); + spin_lock_irqsave(&bank->lock, flags); + retval = _set_gpio_triggering(bank, get_gpio_index(gpio), type); + spin_unlock_irqrestore(&bank->lock, flags); + + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) + __irq_set_handler_locked(d->irq, handle_level_irq); + else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) + __irq_set_handler_locked(d->irq, handle_edge_irq); + + return retval; +} + +static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask) +{ + void __iomem *reg = bank->base; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + /* MPUIO irqstatus is reset by reading the status register, + * so do nothing here */ + return; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_STATUS; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_IRQSTATUS1; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_STATUS; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_IRQSTATUS1; + break; +#endif +#if defined(CONFIG_ARCH_OMAP4) + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_IRQSTATUS0; + break; +#endif + default: + WARN_ON(1); + return; + } + __raw_writel(gpio_mask, reg); + + /* Workaround for clearing DSP GPIO interrupts to allow retention */ + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + reg = bank->base + OMAP24XX_GPIO_IRQSTATUS2; + else if (cpu_is_omap44xx()) + reg = bank->base + OMAP4_GPIO_IRQSTATUS1; + + if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap44xx()) { + __raw_writel(gpio_mask, reg); + + /* Flush posted write for the irq status to avoid spurious interrupts */ + __raw_readl(reg); + } +} + +static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio) +{ + _clear_gpio_irqbank(bank, 1 << get_gpio_index(gpio)); +} + +static u32 _get_gpio_irqbank_mask(struct gpio_bank *bank) +{ + void __iomem *reg = bank->base; + int inv = 0; + u32 l; + u32 mask; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_MASKIT / bank->stride; + mask = 0xffff; + inv = 1; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_MASK; + mask = 0xffff; + inv = 1; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + reg += OMAP1610_GPIO_IRQENABLE1; + mask = 0xffff; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_MASK; + mask = 0xffffffff; + inv = 1; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + reg += OMAP24XX_GPIO_IRQENABLE1; + mask = 0xffffffff; + break; +#endif +#if defined(CONFIG_ARCH_OMAP4) + case METHOD_GPIO_44XX: + reg += OMAP4_GPIO_IRQSTATUSSET0; + mask = 0xffffffff; + break; +#endif + default: + WARN_ON(1); + return 0; + } + + l = __raw_readl(reg); + if (inv) + l = ~l; + l &= mask; + return l; +} + +static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enable) +{ + void __iomem *reg = bank->base; + u32 l; + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP1 + case METHOD_MPUIO: + reg += OMAP_MPUIO_GPIO_MASKIT / bank->stride; + l = __raw_readl(reg); + if (enable) + l &= ~(gpio_mask); + else + l |= gpio_mask; + break; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + case METHOD_GPIO_1510: + reg += OMAP1510_GPIO_INT_MASK; + l = __raw_readl(reg); + if (enable) + l &= ~(gpio_mask); + else + l |= gpio_mask; + break; +#endif +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_GPIO_1610: + if (enable) + reg += OMAP1610_GPIO_SET_IRQENABLE1; + else + reg += OMAP1610_GPIO_CLEAR_IRQENABLE1; + l = gpio_mask; + break; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + case METHOD_GPIO_7XX: + reg += OMAP7XX_GPIO_INT_MASK; + l = __raw_readl(reg); + if (enable) + l &= ~(gpio_mask); + else + l |= gpio_mask; + break; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + case METHOD_GPIO_24XX: + if (enable) + reg += OMAP24XX_GPIO_SETIRQENABLE1; + else + reg += OMAP24XX_GPIO_CLEARIRQENABLE1; + l = gpio_mask; + break; +#endif +#ifdef CONFIG_ARCH_OMAP4 + case METHOD_GPIO_44XX: + if (enable) + reg += OMAP4_GPIO_IRQSTATUSSET0; + else + reg += OMAP4_GPIO_IRQSTATUSCLR0; + l = gpio_mask; + break; +#endif + default: + WARN_ON(1); + return; + } + __raw_writel(l, reg); +} + +static inline void _set_gpio_irqenable(struct gpio_bank *bank, int gpio, int enable) +{ + _enable_gpio_irqbank(bank, 1 << get_gpio_index(gpio), enable); +} + +/* + * Note that ENAWAKEUP needs to be enabled in GPIO_SYSCONFIG register. + * 1510 does not seem to have a wake-up register. If JTAG is connected + * to the target, system will wake up always on GPIO events. While + * system is running all registered GPIO interrupts need to have wake-up + * enabled. When system is suspended, only selected GPIO interrupts need + * to have wake-up enabled. + */ +static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable) +{ + unsigned long uninitialized_var(flags); + + switch (bank->method) { +#ifdef CONFIG_ARCH_OMAP16XX + case METHOD_MPUIO: + case METHOD_GPIO_1610: + spin_lock_irqsave(&bank->lock, flags); + if (enable) + bank->suspend_wakeup |= (1 << gpio); + else + bank->suspend_wakeup &= ~(1 << gpio); + spin_unlock_irqrestore(&bank->lock, flags); + return 0; +#endif +#ifdef CONFIG_ARCH_OMAP2PLUS + case METHOD_GPIO_24XX: + case METHOD_GPIO_44XX: + if (bank->non_wakeup_gpios & (1 << gpio)) { + printk(KERN_ERR "Unable to modify wakeup on " + "non-wakeup GPIO%d\n", + (bank - gpio_bank) * 32 + gpio); + return -EINVAL; + } + spin_lock_irqsave(&bank->lock, flags); + if (enable) + bank->suspend_wakeup |= (1 << gpio); + else + bank->suspend_wakeup &= ~(1 << gpio); + spin_unlock_irqrestore(&bank->lock, flags); + return 0; +#endif + default: + printk(KERN_ERR "Can't enable GPIO wakeup for method %i\n", + bank->method); + return -EINVAL; + } +} + +static void _reset_gpio(struct gpio_bank *bank, int gpio) +{ + _set_gpio_direction(bank, get_gpio_index(gpio), 1); + _set_gpio_irqenable(bank, gpio, 0); + _clear_gpio_irqstatus(bank, gpio); + _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE); +} + +/* Use disable_irq_wake() and enable_irq_wake() functions from drivers */ +static int gpio_wake_enable(struct irq_data *d, unsigned int enable) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank; + int retval; + + if (check_gpio(gpio) < 0) + return -ENODEV; + bank = irq_data_get_irq_chip_data(d); + retval = _set_gpio_wakeup(bank, get_gpio_index(gpio), enable); + + return retval; +} + +static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + /* Set trigger to none. You need to enable the desired trigger with + * request_irq() or set_irq_type(). + */ + _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); + +#ifdef CONFIG_ARCH_OMAP15XX + if (bank->method == METHOD_GPIO_1510) { + void __iomem *reg; + + /* Claim the pin for MPU */ + reg = bank->base + OMAP1510_GPIO_PIN_CONTROL; + __raw_writel(__raw_readl(reg) | (1 << offset), reg); + } +#endif + if (!cpu_class_is_omap1()) { + if (!bank->mod_usage) { + void __iomem *reg = bank->base; + u32 ctrl; + + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + reg += OMAP24XX_GPIO_CTRL; + else if (cpu_is_omap44xx()) + reg += OMAP4_GPIO_CTRL; + ctrl = __raw_readl(reg); + /* Module is enabled, clocks are not gated */ + ctrl &= 0xFFFFFFFE; + __raw_writel(ctrl, reg); + } + bank->mod_usage |= 1 << offset; + } + spin_unlock_irqrestore(&bank->lock, flags); + + return 0; +} + +static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); +#ifdef CONFIG_ARCH_OMAP16XX + if (bank->method == METHOD_GPIO_1610) { + /* Disable wake-up during idle for dynamic tick */ + void __iomem *reg = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA; + __raw_writel(1 << offset, reg); + } +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + if (bank->method == METHOD_GPIO_24XX) { + /* Disable wake-up during idle for dynamic tick */ + void __iomem *reg = bank->base + OMAP24XX_GPIO_CLEARWKUENA; + __raw_writel(1 << offset, reg); + } +#endif +#ifdef CONFIG_ARCH_OMAP4 + if (bank->method == METHOD_GPIO_44XX) { + /* Disable wake-up during idle for dynamic tick */ + void __iomem *reg = bank->base + OMAP4_GPIO_IRQWAKEN0; + __raw_writel(1 << offset, reg); + } +#endif + if (!cpu_class_is_omap1()) { + bank->mod_usage &= ~(1 << offset); + if (!bank->mod_usage) { + void __iomem *reg = bank->base; + u32 ctrl; + + if (cpu_is_omap24xx() || cpu_is_omap34xx()) + reg += OMAP24XX_GPIO_CTRL; + else if (cpu_is_omap44xx()) + reg += OMAP4_GPIO_CTRL; + ctrl = __raw_readl(reg); + /* Module is disabled, clocks are gated */ + ctrl |= 1; + __raw_writel(ctrl, reg); + } + } + _reset_gpio(bank, bank->chip.base + offset); + spin_unlock_irqrestore(&bank->lock, flags); +} + +/* + * We need to unmask the GPIO bank interrupt as soon as possible to + * avoid missing GPIO interrupts for other lines in the bank. + * Then we need to mask-read-clear-unmask the triggered GPIO lines + * in the bank to avoid missing nested interrupts for a GPIO line. + * If we wait to unmask individual GPIO lines in the bank after the + * line's interrupt handler has been run, we may miss some nested + * interrupts. + */ +static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +{ + void __iomem *isr_reg = NULL; + u32 isr; + unsigned int gpio_irq, gpio_index; + struct gpio_bank *bank; + u32 retrigger = 0; + int unmasked = 0; + struct irq_chip *chip = irq_desc_get_chip(desc); + + chained_irq_enter(chip, desc); + + bank = irq_get_handler_data(irq); +#ifdef CONFIG_ARCH_OMAP1 + if (bank->method == METHOD_MPUIO) + isr_reg = bank->base + + OMAP_MPUIO_GPIO_INT / bank->stride; +#endif +#ifdef CONFIG_ARCH_OMAP15XX + if (bank->method == METHOD_GPIO_1510) + isr_reg = bank->base + OMAP1510_GPIO_INT_STATUS; +#endif +#if defined(CONFIG_ARCH_OMAP16XX) + if (bank->method == METHOD_GPIO_1610) + isr_reg = bank->base + OMAP1610_GPIO_IRQSTATUS1; +#endif +#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850) + if (bank->method == METHOD_GPIO_7XX) + isr_reg = bank->base + OMAP7XX_GPIO_INT_STATUS; +#endif +#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) + if (bank->method == METHOD_GPIO_24XX) + isr_reg = bank->base + OMAP24XX_GPIO_IRQSTATUS1; +#endif +#if defined(CONFIG_ARCH_OMAP4) + if (bank->method == METHOD_GPIO_44XX) + isr_reg = bank->base + OMAP4_GPIO_IRQSTATUS0; +#endif + + if (WARN_ON(!isr_reg)) + goto exit; + + while(1) { + u32 isr_saved, level_mask = 0; + u32 enabled; + + enabled = _get_gpio_irqbank_mask(bank); + isr_saved = isr = __raw_readl(isr_reg) & enabled; + + if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO)) + isr &= 0x0000ffff; + + if (cpu_class_is_omap2()) { + level_mask = bank->level_mask & enabled; + } + + /* clear edge sensitive interrupts before handler(s) are + called so that we don't miss any interrupt occurred while + executing them */ + _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 0); + _clear_gpio_irqbank(bank, isr_saved & ~level_mask); + _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 1); + + /* if there is only edge sensitive GPIO pin interrupts + configured, we could unmask GPIO bank interrupt immediately */ + if (!level_mask && !unmasked) { + unmasked = 1; + chained_irq_exit(chip, desc); + } + + isr |= retrigger; + retrigger = 0; + if (!isr) + break; + + gpio_irq = bank->virtual_irq_start; + for (; isr != 0; isr >>= 1, gpio_irq++) { + gpio_index = get_gpio_index(irq_to_gpio(gpio_irq)); + + if (!(isr & 1)) + continue; + +#ifdef CONFIG_ARCH_OMAP1 + /* + * Some chips can't respond to both rising and falling + * at the same time. If this irq was requested with + * both flags, we need to flip the ICR data for the IRQ + * to respond to the IRQ for the opposite direction. + * This will be indicated in the bank toggle_mask. + */ + if (bank->toggle_mask & (1 << gpio_index)) + _toggle_gpio_edge_triggering(bank, gpio_index); +#endif + + generic_handle_irq(gpio_irq); + } + } + /* if bank has any level sensitive GPIO pin interrupt + configured, we must unmask the bank interrupt only after + handler(s) are executed in order to avoid spurious bank + interrupt */ +exit: + if (!unmasked) + chained_irq_exit(chip, desc); +} + +static void gpio_irq_shutdown(struct irq_data *d) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + + _reset_gpio(bank, gpio); +} + +static void gpio_ack_irq(struct irq_data *d) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + + _clear_gpio_irqstatus(bank, gpio); +} + +static void gpio_mask_irq(struct irq_data *d) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + + _set_gpio_irqenable(bank, gpio, 0); + _set_gpio_triggering(bank, get_gpio_index(gpio), IRQ_TYPE_NONE); +} + +static void gpio_unmask_irq(struct irq_data *d) +{ + unsigned int gpio = d->irq - IH_GPIO_BASE; + struct gpio_bank *bank = irq_data_get_irq_chip_data(d); + unsigned int irq_mask = 1 << get_gpio_index(gpio); + u32 trigger = irqd_get_trigger_type(d); + + if (trigger) + _set_gpio_triggering(bank, get_gpio_index(gpio), trigger); + + /* For level-triggered GPIOs, the clearing must be done after + * the HW source is cleared, thus after the handler has run */ + if (bank->level_mask & irq_mask) { + _set_gpio_irqenable(bank, gpio, 0); + _clear_gpio_irqstatus(bank, gpio); + } + + _set_gpio_irqenable(bank, gpio, 1); +} + +static struct irq_chip gpio_irq_chip = { + .name = "GPIO", + .irq_shutdown = gpio_irq_shutdown, + .irq_ack = gpio_ack_irq, + .irq_mask = gpio_mask_irq, + .irq_unmask = gpio_unmask_irq, + .irq_set_type = gpio_irq_type, + .irq_set_wake = gpio_wake_enable, +}; + +/*---------------------------------------------------------------------*/ |