diff options
| -rw-r--r-- | Documentation/devicetree/bindings/gpio/gpio-omap.txt | 11 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/irq.c | 25 | ||||
| -rw-r--r-- | arch/arm/mach-omap2/pm34xx.c | 2 | ||||
| -rw-r--r-- | drivers/gpio/gpio-omap.c | 73 | ||||
| -rw-r--r-- | include/linux/platform_data/gpio-omap.h | 5 |
5 files changed, 111 insertions, 5 deletions
diff --git a/Documentation/devicetree/bindings/gpio/gpio-omap.txt b/Documentation/devicetree/bindings/gpio/gpio-omap.txt index 8d950522e7f..a320bfd862c 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-omap.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-omap.txt @@ -26,6 +26,14 @@ OMAP specific properties: - ti,gpio-always-on: Indicates if a GPIO bank is always powered and so will never lose its logic state. +OMAP3 specific properties +Offmode wkup allows translation of a wakeup event +from a single pad in a gpio bank to a wakeup + an interrupt. +This works around the loss of isr status during off mode for +gpio banks 2 through 6 + +- offmode-wkup-isr: The isr offset in the bank +- offmode-wkup-pad: The pad offset associated with this pin Example: @@ -36,4 +44,7 @@ gpio4: gpio4 { #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; + + offmode-wkup-isr = <0x00000008>; + offmode-wkup-pad = <0x008e>; }; diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c index 3926f370448..9a2bc4ba235 100644 --- a/arch/arm/mach-omap2/irq.c +++ b/arch/arm/mach-omap2/irq.c @@ -40,7 +40,10 @@ #define INTC_MIR0 0x0084 #define INTC_MIR_CLEAR0 0x0088 #define INTC_MIR_SET0 0x008c +#define INTC_ISR_SET0 0x0090 +#define INTC_ISR_CLEAR0 0x0094 #define INTC_PENDING_IRQ0 0x0098 + /* Number of IRQ state bits in each MIR register */ #define IRQ_BITS_PER_REG 32 @@ -51,6 +54,9 @@ #define INTCPS_NR_MIR_REGS 3 #define INTCPS_NR_IRQS 96 +#define INTCPS_ISR_SET(n) (INTC_ISR_SET0 + (0x20 * (n))) +#define INTCPS_ISR_CLR(n) (INTC_ISR_CLEAR0 + (0x20 * (n))) + /* * OMAP2 has a number of different interrupt controllers, each interrupt * controller is identified as its own "bank". Register definitions are @@ -122,6 +128,24 @@ static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank) intc_bank_write_reg(1 << 0, bank, INTC_SYSCONFIG); } +void omap_clr_soft_irq(int irq) +{ + u32 shift = (irq - domain->revmap_data.legacy.first_irq); + u32 offset = INTCPS_ISR_CLR(shift / (IRQ_BITS_PER_REG - 1)); + u32 isr = 1 << (shift % IRQ_BITS_PER_REG); + + __raw_writel(isr, OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + offset)); +} + +void omap_set_soft_irq(int irq) +{ + u32 shift = (irq - domain->revmap_data.legacy.first_irq); + u32 offset = INTCPS_ISR_SET(shift / (IRQ_BITS_PER_REG - 1)); + u32 isr = 1 << (shift % IRQ_BITS_PER_REG); + + __raw_writel(isr, OMAP2_L4_IO_ADDRESS(OMAP34XX_IC_BASE + offset)); +} + int omap_irq_pending(void) { int i; @@ -179,6 +203,7 @@ static void __init omap_init_irq(u32 base, int nr_irqs, domain = irq_domain_add_legacy(node, nr_irqs, irq_base, 0, &irq_domain_simple_ops, NULL); + for (i = 0; i < ARRAY_SIZE(irq_banks); i++) { struct omap_irq_bank *bank = irq_banks + i; diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c index 8b2281ec261..f56d1d678ad 100644 --- a/arch/arm/mach-omap2/pm34xx.c +++ b/arch/arm/mach-omap2/pm34xx.c @@ -369,7 +369,7 @@ void omap_sram_idle(bool in_suspend) /* PER */ if (per_next_state < PWRDM_POWER_ON) - omap2_gpio_resume_after_idle(in_suspend); + omap2_gpio_resume_after_idle(in_suspend, per_going_off); } static void omap3_pm_idle(void) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 378e3d8a418..7377ae4ee8d 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -82,12 +82,36 @@ struct gpio_bank { struct pad_context *pads_ctx; void __iomem *pads_base; + u32 offmode_handle_soft_isr; + u32 offmode_wkup_isr; + u32 offmode_wkup_pad; + u32 offmode_wkup_pad_shift; + void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable); int (*get_context_loss_count)(struct device *dev); struct omap_gpio_reg_offs *regs; }; +static inline void clr_soft_irq(struct gpio_bank *bank) +{ + omap_clr_soft_irq(bank->irq); +} + +static inline void set_soft_irq(struct gpio_bank *bank) +{ + omap_set_soft_irq(bank->irq); +} + +#define WAKEUPEVENT 0x8000 +static inline int is_wkup_pad(struct gpio_bank *bank) +{ + int pad = __raw_readl(bank->pads_base + bank->offmode_wkup_pad); + pad = pad >> bank->offmode_wkup_pad_shift; + + return (pad & WAKEUPEVENT) ? 1 : 0; +} + #define GPIO_INDEX(bank, gpio) (gpio % bank->width) #define GPIO_BIT(bank, gpio) (1 << GPIO_INDEX(bank, gpio)) #define GPIO_MOD_CTRL_BIT BIT(0) @@ -713,8 +737,18 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) u32 isr_saved, level_mask = 0; u32 enabled; - enabled = _get_gpio_irqbank_mask(bank); - isr_saved = isr = __raw_readl(isr_reg) & enabled; + if (unlikely(bank->offmode_handle_soft_isr)) { + clr_soft_irq(bank); + + enabled = bank->offmode_handle_soft_isr; + isr = bank->offmode_handle_soft_isr & enabled; + isr_saved = isr; + bank->offmode_handle_soft_isr = 0x0; + } else { + enabled = _get_gpio_irqbank_mask(bank); + isr = __raw_readl(isr_reg) & enabled; + isr_saved = isr; + } if (bank->level_mask) level_mask = bank->level_mask & enabled; @@ -758,6 +792,9 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc) handler(s) are executed in order to avoid spurious bank interrupt */ exit: + + bank->offmode_handle_soft_isr = 0x0; + if (!unmasked) chained_irq_exit(chip, desc); pm_runtime_put(bank->dev); @@ -1253,8 +1290,33 @@ static int omap_gpio_probe(struct platform_device *pdev) bank->chip.of_node = of_node_get(node); #endif if (node) { + int ret; + u32 offmode_wkup_isr; + u32 offmode_wkup_pad; + if (!of_property_read_bool(node, "ti,gpio-always-on")) bank->loses_context = true; + + /* offmode-wkup */ + ret = of_property_read_u32(node, "offmode-wkup-isr", + &offmode_wkup_isr); + ret |= of_property_read_u32(node, "offmode-wkup-pad", + &offmode_wkup_pad); + + if (!ret) { + bank->offmode_wkup_isr = offmode_wkup_isr; + bank->offmode_wkup_pad = + offmode_wkup_pad & 0xFFFFFFFC; + bank->offmode_wkup_pad_shift = + offmode_wkup_pad % 4 ? 16 : 0; + + dev_info(dev, "offmode_wkup_isr = 0x%08x\n", + bank->offmode_wkup_isr); + dev_info(dev, "offmode_wkup_pad = 0x%08x\n", + bank->offmode_wkup_pad); + dev_info(dev, "offmode_wkup_shift = %d\n", + bank->offmode_wkup_pad_shift); + } } else { bank->loses_context = pdata->loses_context; @@ -1541,7 +1603,7 @@ void omap2_gpio_prepare_for_idle(int pwr_mode) } } -void omap2_gpio_resume_after_idle(bool in_suspend) +void omap2_gpio_resume_after_idle(bool in_suspend, int pwr_mode) { struct gpio_bank *bank; @@ -1555,6 +1617,11 @@ void omap2_gpio_resume_after_idle(bool in_suspend) bank->power_mode = 0; omap_gpio_pad_ctx_restore(bank); } + if (bank->offmode_wkup_isr && (pwr_mode == OFF_MODE) && + is_wkup_pad(bank)) { + bank->offmode_handle_soft_isr = bank->offmode_wkup_isr; + set_soft_irq(bank); + } } } diff --git a/include/linux/platform_data/gpio-omap.h b/include/linux/platform_data/gpio-omap.h index 11f4bff9c7e..c62713acfc7 100644 --- a/include/linux/platform_data/gpio-omap.h +++ b/include/linux/platform_data/gpio-omap.h @@ -211,8 +211,11 @@ struct omap_gpio_platform_data { }; extern void omap2_gpio_prepare_for_idle(int off_mode); -extern void omap2_gpio_resume_after_idle(bool in_suspend); +extern void omap2_gpio_resume_after_idle(bool in_suspend, int off_mode); extern void omap_set_gpio_debounce(int gpio, int enable); extern void omap_set_gpio_debounce_time(int gpio, int enable); +extern void omap_clr_soft_irq(int irq); +extern void omap_set_soft_irq(int irq); + #endif |