aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/omap-wakeupgen.c
diff options
context:
space:
mode:
authorSantosh Shilimkar <santosh.shilimkar@ti.com>2010-06-16 12:49:47 -0400
committerKevin Hilman <khilman@ti.com>2011-12-08 14:29:00 -0500
commitfcf6efa3ffbc3cc19e7abe39e0b90f497df2fc42 (patch)
tree4b53e5380e09bbfd94370db8fc1d66e49ce6c7c3 /arch/arm/mach-omap2/omap-wakeupgen.c
parent259ee57a8cda5760dd3e803c5271a6327e1f38ac (diff)
ARM: OMAP4: PM: Add WakeupGen module as OMAP gic_arch_extn
OMAP WakeupGen is the interrupt controller extension used along with ARM GIC to wake the CPU out from low power states on external interrupts. The WakeupGen unit is responsible for generating the wakeup event from the incoming interrupts and enable bits. It is implemented in the MPU always ON power domain. During normal operation, WakeupGen delivers the external interrupts directly to the GIC. WakeupGen specification has one restriction as per Veyron version 1.6. It is SW responsibility to program interrupt enabling/disabling coherently in the GIC and in the WakeupGen enable registers. That is, a given interrupt for a given CPU is either enable at both GIC and WakeupGen, or disable at both, but no mix. That's the reason the WakeupGen is implemented as an extension of GIC. Signed-off-by: Santosh Shilimkar <santosh.shilimkar@ti.com> Acked-by: Jean Pihet <j-pihet@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Tested-by: Vishwanath BS <vishwanath.bs@ti.com> Signed-off-by: Kevin Hilman <khilman@ti.com>
Diffstat (limited to 'arch/arm/mach-omap2/omap-wakeupgen.c')
-rw-r--r--arch/arm/mach-omap2/omap-wakeupgen.c226
1 files changed, 226 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c
new file mode 100644
index 000000000000..a8a8d0efe350
--- /dev/null
+++ b/arch/arm/mach-omap2/omap-wakeupgen.c
@@ -0,0 +1,226 @@
1/*
2 * OMAP WakeupGen Source file
3 *
4 * OMAP WakeupGen is the interrupt controller extension used along
5 * with ARM GIC to wake the CPU out from low power states on
6 * external interrupts. It is responsible for generating wakeup
7 * event from the incoming interrupts and enable bits. It is
8 * implemented in MPU always ON power domain. During normal operation,
9 * WakeupGen delivers external interrupts directly to the GIC.
10 *
11 * Copyright (C) 2011 Texas Instruments, Inc.
12 * Santosh Shilimkar <santosh.shilimkar@ti.com>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License version 2 as
16 * published by the Free Software Foundation.
17 */
18
19#include <linux/kernel.h>
20#include <linux/init.h>
21#include <linux/io.h>
22#include <linux/irq.h>
23#include <linux/platform_device.h>
24#include <linux/cpu.h>
25
26#include <asm/hardware/gic.h>
27
28#include <mach/omap-wakeupgen.h>
29
30#define NR_REG_BANKS 4
31#define MAX_IRQS 128
32#define WKG_MASK_ALL 0x00000000
33#define WKG_UNMASK_ALL 0xffffffff
34#define CPU_ENA_OFFSET 0x400
35#define CPU0_ID 0x0
36#define CPU1_ID 0x1
37
38static void __iomem *wakeupgen_base;
39static DEFINE_PER_CPU(u32 [NR_REG_BANKS], irqmasks);
40static DEFINE_SPINLOCK(wakeupgen_lock);
41static unsigned int irq_target_cpu[NR_IRQS];
42
43/*
44 * Static helper functions.
45 */
46static inline u32 wakeupgen_readl(u8 idx, u32 cpu)
47{
48 return __raw_readl(wakeupgen_base + OMAP_WKG_ENB_A_0 +
49 (cpu * CPU_ENA_OFFSET) + (idx * 4));
50}
51
52static inline void wakeupgen_writel(u32 val, u8 idx, u32 cpu)
53{
54 __raw_writel(val, wakeupgen_base + OMAP_WKG_ENB_A_0 +
55 (cpu * CPU_ENA_OFFSET) + (idx * 4));
56}
57
58static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg)
59{
60 u8 i;
61
62 for (i = 0; i < NR_REG_BANKS; i++)
63 wakeupgen_writel(reg, i, cpu);
64}
65
66static inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index)
67{
68 unsigned int spi_irq;
69
70 /*
71 * PPIs and SGIs are not supported.
72 */
73 if (irq < OMAP44XX_IRQ_GIC_START)
74 return -EINVAL;
75
76 /*
77 * Subtract the GIC offset.
78 */
79 spi_irq = irq - OMAP44XX_IRQ_GIC_START;
80 if (spi_irq > MAX_IRQS) {
81 pr_err("omap wakeupGen: Invalid IRQ%d\n", irq);
82 return -EINVAL;
83 }
84
85 /*
86 * Each WakeupGen register controls 32 interrupt.
87 * i.e. 1 bit per SPI IRQ
88 */
89 *reg_index = spi_irq >> 5;
90 *bit_posn = spi_irq %= 32;
91
92 return 0;
93}
94
95static void _wakeupgen_clear(unsigned int irq, unsigned int cpu)
96{
97 u32 val, bit_number;
98 u8 i;
99
100 if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
101 return;
102
103 val = wakeupgen_readl(i, cpu);
104 val &= ~BIT(bit_number);
105 wakeupgen_writel(val, i, cpu);
106}
107
108static void _wakeupgen_set(unsigned int irq, unsigned int cpu)
109{
110 u32 val, bit_number;
111 u8 i;
112
113 if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
114 return;
115
116 val = wakeupgen_readl(i, cpu);
117 val |= BIT(bit_number);
118 wakeupgen_writel(val, i, cpu);
119}
120
121static void _wakeupgen_save_masks(unsigned int cpu)
122{
123 u8 i;
124
125 for (i = 0; i < NR_REG_BANKS; i++)
126 per_cpu(irqmasks, cpu)[i] = wakeupgen_readl(i, cpu);
127}
128
129static void _wakeupgen_restore_masks(unsigned int cpu)
130{
131 u8 i;
132
133 for (i = 0; i < NR_REG_BANKS; i++)
134 wakeupgen_writel(per_cpu(irqmasks, cpu)[i], i, cpu);
135}
136
137/*
138 * Architecture specific Mask extension
139 */
140static void wakeupgen_mask(struct irq_data *d)
141{
142 unsigned long flags;
143
144 spin_lock_irqsave(&wakeupgen_lock, flags);
145 _wakeupgen_clear(d->irq, irq_target_cpu[d->irq]);
146 spin_unlock_irqrestore(&wakeupgen_lock, flags);
147}
148
149/*
150 * Architecture specific Unmask extension
151 */
152static void wakeupgen_unmask(struct irq_data *d)
153{
154 unsigned long flags;
155
156 spin_lock_irqsave(&wakeupgen_lock, flags);
157 _wakeupgen_set(d->irq, irq_target_cpu[d->irq]);
158 spin_unlock_irqrestore(&wakeupgen_lock, flags);
159}
160
161/*
162 * Mask or unmask all interrupts on given CPU.
163 * 0 = Mask all interrupts on the 'cpu'
164 * 1 = Unmask all interrupts on the 'cpu'
165 * Ensure that the initial mask is maintained. This is faster than
166 * iterating through GIC registers to arrive at the correct masks.
167 */
168static void wakeupgen_irqmask_all(unsigned int cpu, unsigned int set)
169{
170 unsigned long flags;
171
172 spin_lock_irqsave(&wakeupgen_lock, flags);
173 if (set) {
174 _wakeupgen_save_masks(cpu);
175 _wakeupgen_set_all(cpu, WKG_MASK_ALL);
176 } else {
177 _wakeupgen_set_all(cpu, WKG_UNMASK_ALL);
178 _wakeupgen_restore_masks(cpu);
179 }
180 spin_unlock_irqrestore(&wakeupgen_lock, flags);
181}
182
183/*
184 * Initialise the wakeupgen module.
185 */
186int __init omap_wakeupgen_init(void)
187{
188 int i;
189 unsigned int boot_cpu = smp_processor_id();
190
191 /* Not supported on OMAP4 ES1.0 silicon */
192 if (omap_rev() == OMAP4430_REV_ES1_0) {
193 WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n");
194 return -EPERM;
195 }
196
197 /* Static mapping, never released */
198 wakeupgen_base = ioremap(OMAP44XX_WKUPGEN_BASE, SZ_4K);
199 if (WARN_ON(!wakeupgen_base))
200 return -ENOMEM;
201
202 /* Clear all IRQ bitmasks at wakeupGen level */
203 for (i = 0; i < NR_REG_BANKS; i++) {
204 wakeupgen_writel(0, i, CPU0_ID);
205 wakeupgen_writel(0, i, CPU1_ID);
206 }
207
208 /*
209 * Override GIC architecture specific functions to add
210 * OMAP WakeupGen interrupt controller along with GIC
211 */
212 gic_arch_extn.irq_mask = wakeupgen_mask;
213 gic_arch_extn.irq_unmask = wakeupgen_unmask;
214 gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE;
215
216 /*
217 * FIXME: Add support to set_smp_affinity() once the core
218 * GIC code has necessary hooks in place.
219 */
220
221 /* Associate all the IRQs to boot CPU like GIC init does. */
222 for (i = 0; i < NR_IRQS; i++)
223 irq_target_cpu[i] = boot_cpu;
224
225 return 0;
226}