diff options
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r-- | arch/arm/mach-omap2/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/include/mach/omap-wakeupgen.h | 39 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap-wakeupgen.c | 226 | ||||
-rw-r--r-- | arch/arm/mach-omap2/omap4-common.c | 3 |
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 | |||
25 | obj-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o | 25 | obj-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o |
26 | obj-$(CONFIG_LOCAL_TIMERS) += timer-mpu.o | 26 | obj-$(CONFIG_LOCAL_TIMERS) += timer-mpu.o |
27 | obj-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o | 27 | obj-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o |
28 | obj-$(CONFIG_ARCH_OMAP4) += omap4-common.o | 28 | obj-$(CONFIG_ARCH_OMAP4) += omap4-common.o omap-wakeupgen.o |
29 | 29 | ||
30 | plus_sec := $(call as-instr,.arch_extension sec,+sec) | 30 | plus_sec := $(call as-instr,.arch_extension sec,+sec) |
31 | AFLAGS_omap-headsmp.o :=-Wa,-march=armv7-a$(plus_sec) | 31 | AFLAGS_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 | |||
38 | extern 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 | |||
38 | static void __iomem *wakeupgen_base; | ||
39 | static DEFINE_PER_CPU(u32 [NR_REG_BANKS], irqmasks); | ||
40 | static DEFINE_SPINLOCK(wakeupgen_lock); | ||
41 | static unsigned int irq_target_cpu[NR_IRQS]; | ||
42 | |||
43 | /* | ||
44 | * Static helper functions. | ||
45 | */ | ||
46 | static 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 | |||
52 | static 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 | |||
58 | static 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 | |||
66 | static 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 | |||
95 | static 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 | |||
108 | static 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 | |||
121 | static 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 | |||
129 | static 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 | */ | ||
140 | static 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 | */ | ||
152 | static 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 | */ | ||
168 | static 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 | */ | ||
186 | int __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 | ||