1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
|
/*
* OMAP WakeupGen Source file
*
* The WakeupGen unit is responsible for generating wakeup event from the
* incoming interrupts and enable bits. The WakeupGen is implemented in MPU
* always-On power domain. The WakeupGen consists of two sub-units, one for
* each CPU and manages only SPI interrupts. Hardware requirements is that
* the GIC and WakeupGen should be kept in sync for proper operation.
*
* Copyright (C) 2011 Texas Instruments, Inc.
* Written by Santosh Shilimkar <santosh.shilimkar@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <asm/hardware/gic.h>
#include <mach/omap-wakeupgen.h>
#include <mach/omap4-common.h>
#include "omap4-sar-layout.h"
#define NR_BANKS 4
#define MAX_IRQS 128
#define WKG_MASK_ALL 0x00000000
#define WKG_UNMASK_ALL 0xffffffff
#define CPU_ENA_OFFSET 0x400
#define CPU0_ID 0x0
#define CPU1_ID 0x1
/* WakeupGen Base addres */
static void __iomem *wakeupgen_base;
static void __iomem *sar_base;
static DEFINE_PER_CPU(u32 [NR_BANKS], irqmasks);
static DEFINE_SPINLOCK(wakeupgen_lock);
/*
* Static helper functions
*/
static inline u32 wakeupgen_readl(u8 idx, u32 cpu)
{
return __raw_readl(wakeupgen_base + OMAP_WKG_ENB_A_0 +
(cpu * CPU_ENA_OFFSET) + (idx * 4));
}
static inline void wakeupgen_writel(u32 val, u8 idx, u32 cpu)
{
__raw_writel(val, wakeupgen_base + OMAP_WKG_ENB_A_0 +
(cpu * CPU_ENA_OFFSET) + (idx * 4));
}
static inline void sar_writel(u32 val, u32 offset, u8 idx)
{
__raw_writel(val, sar_base + offset + (idx * 4));
}
static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg)
{
u8 i;
for (i = 0; i < NR_BANKS; i++)
wakeupgen_writel(reg, i, cpu);
}
static inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index)
{
unsigned int spi_irq;
/*
* PPIs and SGIs are not supported
*/
if (irq < OMAP44XX_IRQ_GIC_START)
return -EINVAL;
/*
* Subtract the GIC offset
*/
spi_irq = irq - OMAP44XX_IRQ_GIC_START;
if (spi_irq > MAX_IRQS) {
pr_err("omap wakeupGen: Invalid IRQ%d\n", irq);
return -EINVAL;
}
/*
* Each wakeup gen register controls 32
* interrupts. i.e 1 bit per SPI IRQ
*/
*reg_index = spi_irq >> 5;
*bit_posn = spi_irq %= 32;
return 0;
}
static void _wakeupgen_clear(unsigned int irq, unsigned int cpu)
{
u32 val, bit_number;
u8 i;
if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
return;
val = wakeupgen_readl(i, cpu);
val &= ~BIT(bit_number);
wakeupgen_writel(val, i, cpu);
}
static void _wakeupgen_set(unsigned int irq, unsigned int cpu)
{
u32 val, bit_number;
u8 i;
if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
return;
val = wakeupgen_readl(i, cpu);
val |= BIT(bit_number);
wakeupgen_writel(val, i, cpu);
}
static void _wakeupgen_save_masks(unsigned int cpu)
{
u8 i;
for (i = 0; i < NR_BANKS; i++)
per_cpu(irqmasks, cpu)[i] = wakeupgen_readl(i, cpu);
}
static void _wakeupgen_restore_masks(unsigned int cpu)
{
u8 i;
for (i = 0; i < NR_BANKS; i++)
wakeupgen_writel(per_cpu(irqmasks, cpu)[i], i, cpu);
}
/*
* Architecture specific Mask extensiom
*/
static void wakeupgen_mask(struct irq_data *d)
{
spin_lock(&wakeupgen_lock);
_wakeupgen_clear(d->irq, d->node);
spin_unlock(&wakeupgen_lock);
}
/*
* Architecture specific Unmask extensiom
*/
static void wakeupgen_unmask(struct irq_data *d)
{
spin_lock(&wakeupgen_lock);
_wakeupgen_set(d->irq, d->node);
spin_unlock(&wakeupgen_lock);
}
/**
* omap_wakeupgen_irqmask_all() - Mask or unmask interrupts
* @cpu - CPU ID
* @set - The IRQ register mask.
* 0 = Mask all interrupts on the 'cpu'
* 1 = Unmask all interrupts on the 'cpu'
*
* Ensure that the initial mask is maintained. This is faster than
* iterating through GIC rgeisters to arrive at the correct masks
*/
void omap_wakeupgen_irqmask_all(unsigned int cpu, unsigned int set)
{
if (omap_rev() == OMAP4430_REV_ES1_0)
return;
spin_lock(&wakeupgen_lock);
if (set) {
_wakeupgen_save_masks(cpu);
_wakeupgen_set_all(cpu, WKG_MASK_ALL);
} else {
_wakeupgen_set_all(cpu, WKG_UNMASK_ALL);
_wakeupgen_restore_masks(cpu);
}
spin_unlock(&wakeupgen_lock);
}
#ifdef CONFIG_PM
/*
* Masking wakeup irqs is handled by the IRQCHIP_MASK_ON_SUSPEND flag,
* so no action is necessary in set_wake, but implement an empty handler
* here to prevent enable_irq_wake() returning an error.
*/
static int wakeupgen_set_wake(struct irq_data *d, unsigned int on)
{
return 0;
}
#else
#define wakeupgen_set_wake NULL
#endif
/*
* Initialse the wakeupgen module
*/
int __init omap_wakeupgen_init(void)
{
u8 i;
/* Not supported on on OMAP4 ES1.0 silicon */
if (omap_rev() == OMAP4430_REV_ES1_0) {
WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n");
return -EPERM;
}
/* Static mapping, never released */
wakeupgen_base = ioremap(OMAP44XX_WKUPGEN_BASE, SZ_4K);
if (WARN_ON(!wakeupgen_base))
return -ENODEV;
/* Clear all IRQ bitmasks at wakeupGen level */
for (i = 0; i < NR_BANKS; i++) {
wakeupgen_writel(0, i, CPU0_ID);
wakeupgen_writel(0, i, CPU1_ID);
}
/*
* Override gic architecture specific fucntioms to add
* OMAP WakeupGen interrupt controller along with GIC
*/
gic_arch_extn.irq_mask = wakeupgen_mask;
gic_arch_extn.irq_unmask = wakeupgen_unmask;
gic_arch_extn.irq_set_wake = wakeupgen_set_wake;
gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND;
return 0;
}
/**
* omap_wakeupgen_save() - WakeupGen context save function
*
* Save WakewupGen context in SAR BANK3. Restore is done by ROM code.
* WakeupGen IP is integrated along with GIC to manage the
* interrupt wakeups from CPU low power states. It's located in
* always ON power domain. It manages masking/unmasking of
* Shared peripheral interrupts(SPI).So the interrupt enable/disable
* control should be in sync and consistent at WakeupGen and GIC so
* that interrupts are not lost. Hence GIC and WakeupGen are saved
* and restored together.
* During normal operation, WakeupGen delivers external interrupts
* directly to the GIC. When the CPU asserts StandbyWFI, indicating
* it wants to enter lowpower state, the Standby Controller checks
* with the WakeupGen unit using the idlereq/idleack handshake to make
* sure there is no incoming interrupts.
*/
void omap_wakeupgen_save(void)
{
u8 i;
u32 val;
if (omap_rev() == OMAP4430_REV_ES1_0)
return;
if (!sar_base)
sar_base = omap4_get_sar_ram_base();
for (i = 0; i < NR_BANKS; i++) {
/* Save the CPUx interrupt mask for IRQ 0 to 127 */
val = wakeupgen_readl(i, 0);
sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i);
val = wakeupgen_readl(i, 1);
sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i);
/*
* Disable the secure interrupts for CPUx. The restore
* code blindly restores secure and non-secure interrupt
* masks from SAR RAM. Secure interrupts are not suppose
* to be enabled from HLOS. So overwrite the SAR location
* so that the secure interrupt remains disabled.
*/
sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i);
sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i);
}
/* Save AuxBoot* registers */
val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
__raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET);
val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
__raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET);
/* Save SyncReq generation logic */
val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
__raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET);
val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
__raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET);
/* Save SyncReq generation logic */
val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_MASK);
__raw_writel(val, sar_base + PTMSYNCREQ_MASK_OFFSET);
val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_EN);
__raw_writel(val, sar_base + PTMSYNCREQ_EN_OFFSET);
/* Set the Backup Bit Mask status */
val = __raw_readl(sar_base + SAR_BACKUP_STATUS_OFFSET);
val |= SAR_BACKUP_STATUS_WAKEUPGEN;
__raw_writel(val, sar_base + SAR_BACKUP_STATUS_OFFSET);
}
|