aboutsummaryrefslogtreecommitdiffstats
path: root/arch
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
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')
-rw-r--r--arch/arm/mach-omap2/Makefile2
-rw-r--r--arch/arm/mach-omap2/include/mach/omap-wakeupgen.h39
-rw-r--r--arch/arm/mach-omap2/omap-wakeupgen.c226
-rw-r--r--arch/arm/mach-omap2/omap4-common.c3
4 files changed, 269 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index bd3a224d1678..19c29d569d82 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -25,7 +25,7 @@ obj-$(CONFIG_TWL4030_CORE) += omap_twl.o
25obj-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o 25obj-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o
26obj-$(CONFIG_LOCAL_TIMERS) += timer-mpu.o 26obj-$(CONFIG_LOCAL_TIMERS) += timer-mpu.o
27obj-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o 27obj-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o
28obj-$(CONFIG_ARCH_OMAP4) += omap4-common.o 28obj-$(CONFIG_ARCH_OMAP4) += omap4-common.o omap-wakeupgen.o
29 29
30plus_sec := $(call as-instr,.arch_extension sec,+sec) 30plus_sec := $(call as-instr,.arch_extension sec,+sec)
31AFLAGS_omap-headsmp.o :=-Wa,-march=armv7-a$(plus_sec) 31AFLAGS_omap-headsmp.o :=-Wa,-march=armv7-a$(plus_sec)
diff --git a/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
new file mode 100644
index 000000000000..d79321b0f2a2
--- /dev/null
+++ b/arch/arm/mach-omap2/include/mach/omap-wakeupgen.h
@@ -0,0 +1,39 @@
1/*
2 * OMAP WakeupGen header file
3 *
4 * Copyright (C) 2011 Texas Instruments, Inc.
5 * Santosh Shilimkar <santosh.shilimkar@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#ifndef OMAP_ARCH_WAKEUPGEN_H
12#define OMAP_ARCH_WAKEUPGEN_H
13
14#define OMAP_WKG_CONTROL_0 0x00
15#define OMAP_WKG_ENB_A_0 0x10
16#define OMAP_WKG_ENB_B_0 0x14
17#define OMAP_WKG_ENB_C_0 0x18
18#define OMAP_WKG_ENB_D_0 0x1c
19#define OMAP_WKG_ENB_SECURE_A_0 0x20
20#define OMAP_WKG_ENB_SECURE_B_0 0x24
21#define OMAP_WKG_ENB_SECURE_C_0 0x28
22#define OMAP_WKG_ENB_SECURE_D_0 0x2c
23#define OMAP_WKG_ENB_A_1 0x410
24#define OMAP_WKG_ENB_B_1 0x414
25#define OMAP_WKG_ENB_C_1 0x418
26#define OMAP_WKG_ENB_D_1 0x41c
27#define OMAP_WKG_ENB_SECURE_A_1 0x420
28#define OMAP_WKG_ENB_SECURE_B_1 0x424
29#define OMAP_WKG_ENB_SECURE_C_1 0x428
30#define OMAP_WKG_ENB_SECURE_D_1 0x42c
31#define OMAP_AUX_CORE_BOOT_0 0x800
32#define OMAP_AUX_CORE_BOOT_1 0x804
33#define OMAP_PTMSYNCREQ_MASK 0xc00
34#define OMAP_PTMSYNCREQ_EN 0xc04
35#define OMAP_TIMESTAMPCYCLELO 0xc08
36#define OMAP_TIMESTAMPCYCLEHI 0xc0c
37
38extern int __init omap_wakeupgen_init(void);
39#endif
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}
diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c
index 2489f5b8b983..1b93d31fe8e9 100644
--- a/arch/arm/mach-omap2/omap4-common.c
+++ b/arch/arm/mach-omap2/omap4-common.c
@@ -22,6 +22,7 @@
22#include <plat/irqs.h> 22#include <plat/irqs.h>
23 23
24#include <mach/hardware.h> 24#include <mach/hardware.h>
25#include <mach/omap-wakeupgen.h>
25 26
26#include "common.h" 27#include "common.h"
27#include "omap4-sar-layout.h" 28#include "omap4-sar-layout.h"
@@ -45,6 +46,8 @@ void __init gic_init_irq(void)
45 omap_irq_base = ioremap(OMAP44XX_GIC_CPU_BASE, SZ_512); 46 omap_irq_base = ioremap(OMAP44XX_GIC_CPU_BASE, SZ_512);
46 BUG_ON(!omap_irq_base); 47 BUG_ON(!omap_irq_base);
47 48
49 omap_wakeupgen_init();
50
48 gic_init(0, 29, gic_dist_base_addr, omap_irq_base); 51 gic_init(0, 29, gic_dist_base_addr, omap_irq_base);
49} 52}
50 53