summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-omap.txt11
-rw-r--r--arch/arm/mach-omap2/irq.c25
-rw-r--r--arch/arm/mach-omap2/pm34xx.c2
-rw-r--r--drivers/gpio/gpio-omap.c73
-rw-r--r--include/linux/platform_data/gpio-omap.h5
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