xref: /linux/arch/arm/mach-omap2/omap-wakeupgen.c (revision b43ab901d671e3e3cad425ea5e9a3c74e266dcdd)
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 #include <linux/notifier.h>
26 #include <linux/cpu_pm.h>
27 
28 #include <asm/hardware/gic.h>
29 
30 #include <mach/omap-wakeupgen.h>
31 #include <mach/omap-secure.h>
32 
33 #include "omap4-sar-layout.h"
34 #include "common.h"
35 
36 #define NR_REG_BANKS		4
37 #define MAX_IRQS		128
38 #define WKG_MASK_ALL		0x00000000
39 #define WKG_UNMASK_ALL		0xffffffff
40 #define CPU_ENA_OFFSET		0x400
41 #define CPU0_ID			0x0
42 #define CPU1_ID			0x1
43 
44 static void __iomem *wakeupgen_base;
45 static void __iomem *sar_base;
46 static DEFINE_PER_CPU(u32 [NR_REG_BANKS], irqmasks);
47 static DEFINE_SPINLOCK(wakeupgen_lock);
48 static unsigned int irq_target_cpu[NR_IRQS];
49 
50 /*
51  * Static helper functions.
52  */
53 static inline u32 wakeupgen_readl(u8 idx, u32 cpu)
54 {
55 	return __raw_readl(wakeupgen_base + OMAP_WKG_ENB_A_0 +
56 				(cpu * CPU_ENA_OFFSET) + (idx * 4));
57 }
58 
59 static inline void wakeupgen_writel(u32 val, u8 idx, u32 cpu)
60 {
61 	__raw_writel(val, wakeupgen_base + OMAP_WKG_ENB_A_0 +
62 				(cpu * CPU_ENA_OFFSET) + (idx * 4));
63 }
64 
65 static inline void sar_writel(u32 val, u32 offset, u8 idx)
66 {
67 	__raw_writel(val, sar_base + offset + (idx * 4));
68 }
69 
70 static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg)
71 {
72 	u8 i;
73 
74 	for (i = 0; i < NR_REG_BANKS; i++)
75 		wakeupgen_writel(reg, i, cpu);
76 }
77 
78 static inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index)
79 {
80 	unsigned int spi_irq;
81 
82 	/*
83 	 * PPIs and SGIs are not supported.
84 	 */
85 	if (irq < OMAP44XX_IRQ_GIC_START)
86 		return -EINVAL;
87 
88 	/*
89 	 * Subtract the GIC offset.
90 	 */
91 	spi_irq = irq - OMAP44XX_IRQ_GIC_START;
92 	if (spi_irq > MAX_IRQS) {
93 		pr_err("omap wakeupGen: Invalid IRQ%d\n", irq);
94 		return -EINVAL;
95 	}
96 
97 	/*
98 	 * Each WakeupGen register controls 32 interrupt.
99 	 * i.e. 1 bit per SPI IRQ
100 	 */
101 	*reg_index = spi_irq >> 5;
102 	*bit_posn = spi_irq %= 32;
103 
104 	return 0;
105 }
106 
107 static void _wakeupgen_clear(unsigned int irq, unsigned int cpu)
108 {
109 	u32 val, bit_number;
110 	u8 i;
111 
112 	if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
113 		return;
114 
115 	val = wakeupgen_readl(i, cpu);
116 	val &= ~BIT(bit_number);
117 	wakeupgen_writel(val, i, cpu);
118 }
119 
120 static void _wakeupgen_set(unsigned int irq, unsigned int cpu)
121 {
122 	u32 val, bit_number;
123 	u8 i;
124 
125 	if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
126 		return;
127 
128 	val = wakeupgen_readl(i, cpu);
129 	val |= BIT(bit_number);
130 	wakeupgen_writel(val, i, cpu);
131 }
132 
133 static void _wakeupgen_save_masks(unsigned int cpu)
134 {
135 	u8 i;
136 
137 	for (i = 0; i < NR_REG_BANKS; i++)
138 		per_cpu(irqmasks, cpu)[i] = wakeupgen_readl(i, cpu);
139 }
140 
141 static void _wakeupgen_restore_masks(unsigned int cpu)
142 {
143 	u8 i;
144 
145 	for (i = 0; i < NR_REG_BANKS; i++)
146 		wakeupgen_writel(per_cpu(irqmasks, cpu)[i], i, cpu);
147 }
148 
149 /*
150  * Architecture specific Mask extension
151  */
152 static void wakeupgen_mask(struct irq_data *d)
153 {
154 	unsigned long flags;
155 
156 	spin_lock_irqsave(&wakeupgen_lock, flags);
157 	_wakeupgen_clear(d->irq, irq_target_cpu[d->irq]);
158 	spin_unlock_irqrestore(&wakeupgen_lock, flags);
159 }
160 
161 /*
162  * Architecture specific Unmask extension
163  */
164 static void wakeupgen_unmask(struct irq_data *d)
165 {
166 	unsigned long flags;
167 
168 	spin_lock_irqsave(&wakeupgen_lock, flags);
169 	_wakeupgen_set(d->irq, irq_target_cpu[d->irq]);
170 	spin_unlock_irqrestore(&wakeupgen_lock, flags);
171 }
172 
173 /*
174  * Mask or unmask all interrupts on given CPU.
175  *	0 = Mask all interrupts on the 'cpu'
176  *	1 = Unmask all interrupts on the 'cpu'
177  * Ensure that the initial mask is maintained. This is faster than
178  * iterating through GIC registers to arrive at the correct masks.
179  */
180 static void wakeupgen_irqmask_all(unsigned int cpu, unsigned int set)
181 {
182 	unsigned long flags;
183 
184 	spin_lock_irqsave(&wakeupgen_lock, flags);
185 	if (set) {
186 		_wakeupgen_save_masks(cpu);
187 		_wakeupgen_set_all(cpu, WKG_MASK_ALL);
188 	} else {
189 		_wakeupgen_set_all(cpu, WKG_UNMASK_ALL);
190 		_wakeupgen_restore_masks(cpu);
191 	}
192 	spin_unlock_irqrestore(&wakeupgen_lock, flags);
193 }
194 
195 #ifdef CONFIG_CPU_PM
196 /*
197  * Save WakeupGen interrupt context in SAR BANK3. Restore is done by
198  * ROM code. WakeupGen IP is integrated along with GIC to manage the
199  * interrupt wakeups from CPU low power states. It manages
200  * masking/unmasking of Shared peripheral interrupts(SPI). So the
201  * interrupt enable/disable control should be in sync and consistent
202  * at WakeupGen and GIC so that interrupts are not lost.
203  */
204 static void irq_save_context(void)
205 {
206 	u32 i, val;
207 
208 	if (omap_rev() == OMAP4430_REV_ES1_0)
209 		return;
210 
211 	if (!sar_base)
212 		sar_base = omap4_get_sar_ram_base();
213 
214 	for (i = 0; i < NR_REG_BANKS; i++) {
215 		/* Save the CPUx interrupt mask for IRQ 0 to 127 */
216 		val = wakeupgen_readl(i, 0);
217 		sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i);
218 		val = wakeupgen_readl(i, 1);
219 		sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i);
220 
221 		/*
222 		 * Disable the secure interrupts for CPUx. The restore
223 		 * code blindly restores secure and non-secure interrupt
224 		 * masks from SAR RAM. Secure interrupts are not suppose
225 		 * to be enabled from HLOS. So overwrite the SAR location
226 		 * so that the secure interrupt remains disabled.
227 		 */
228 		sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i);
229 		sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i);
230 	}
231 
232 	/* Save AuxBoot* registers */
233 	val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
234 	__raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET);
235 	val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
236 	__raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET);
237 
238 	/* Save SyncReq generation logic */
239 	val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
240 	__raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET);
241 	val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
242 	__raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET);
243 
244 	/* Save SyncReq generation logic */
245 	val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_MASK);
246 	__raw_writel(val, sar_base + PTMSYNCREQ_MASK_OFFSET);
247 	val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_EN);
248 	__raw_writel(val, sar_base + PTMSYNCREQ_EN_OFFSET);
249 
250 	/* Set the Backup Bit Mask status */
251 	val = __raw_readl(sar_base + SAR_BACKUP_STATUS_OFFSET);
252 	val |= SAR_BACKUP_STATUS_WAKEUPGEN;
253 	__raw_writel(val, sar_base + SAR_BACKUP_STATUS_OFFSET);
254 }
255 
256 /*
257  * Clear WakeupGen SAR backup status.
258  */
259 void irq_sar_clear(void)
260 {
261 	u32 val;
262 	val = __raw_readl(sar_base + SAR_BACKUP_STATUS_OFFSET);
263 	val &= ~SAR_BACKUP_STATUS_WAKEUPGEN;
264 	__raw_writel(val, sar_base + SAR_BACKUP_STATUS_OFFSET);
265 }
266 
267 /*
268  * Save GIC and Wakeupgen interrupt context using secure API
269  * for HS/EMU devices.
270  */
271 static void irq_save_secure_context(void)
272 {
273 	u32 ret;
274 	ret = omap_secure_dispatcher(OMAP4_HAL_SAVEGIC_INDEX,
275 				FLAG_START_CRITICAL,
276 				0, 0, 0, 0, 0);
277 	if (ret != API_HAL_RET_VALUE_OK)
278 		pr_err("GIC and Wakeupgen context save failed\n");
279 }
280 #endif
281 
282 #ifdef CONFIG_HOTPLUG_CPU
283 static int __cpuinit irq_cpu_hotplug_notify(struct notifier_block *self,
284 					 unsigned long action, void *hcpu)
285 {
286 	unsigned int cpu = (unsigned int)hcpu;
287 
288 	switch (action) {
289 	case CPU_ONLINE:
290 		wakeupgen_irqmask_all(cpu, 0);
291 		break;
292 	case CPU_DEAD:
293 		wakeupgen_irqmask_all(cpu, 1);
294 		break;
295 	}
296 	return NOTIFY_OK;
297 }
298 
299 static struct notifier_block __refdata irq_hotplug_notifier = {
300 	.notifier_call = irq_cpu_hotplug_notify,
301 };
302 
303 static void __init irq_hotplug_init(void)
304 {
305 	register_hotcpu_notifier(&irq_hotplug_notifier);
306 }
307 #else
308 static void __init irq_hotplug_init(void)
309 {}
310 #endif
311 
312 #ifdef CONFIG_CPU_PM
313 static int irq_notifier(struct notifier_block *self, unsigned long cmd,	void *v)
314 {
315 	switch (cmd) {
316 	case CPU_CLUSTER_PM_ENTER:
317 		if (omap_type() == OMAP2_DEVICE_TYPE_GP)
318 			irq_save_context();
319 		else
320 			irq_save_secure_context();
321 		break;
322 	case CPU_CLUSTER_PM_EXIT:
323 		if (omap_type() == OMAP2_DEVICE_TYPE_GP)
324 			irq_sar_clear();
325 		break;
326 	}
327 	return NOTIFY_OK;
328 }
329 
330 static struct notifier_block irq_notifier_block = {
331 	.notifier_call = irq_notifier,
332 };
333 
334 static void __init irq_pm_init(void)
335 {
336 	cpu_pm_register_notifier(&irq_notifier_block);
337 }
338 #else
339 static void __init irq_pm_init(void)
340 {}
341 #endif
342 
343 /*
344  * Initialise the wakeupgen module.
345  */
346 int __init omap_wakeupgen_init(void)
347 {
348 	int i;
349 	unsigned int boot_cpu = smp_processor_id();
350 
351 	/* Not supported on OMAP4 ES1.0 silicon */
352 	if (omap_rev() == OMAP4430_REV_ES1_0) {
353 		WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n");
354 		return -EPERM;
355 	}
356 
357 	/* Static mapping, never released */
358 	wakeupgen_base = ioremap(OMAP44XX_WKUPGEN_BASE, SZ_4K);
359 	if (WARN_ON(!wakeupgen_base))
360 		return -ENOMEM;
361 
362 	/* Clear all IRQ bitmasks at wakeupGen level */
363 	for (i = 0; i < NR_REG_BANKS; i++) {
364 		wakeupgen_writel(0, i, CPU0_ID);
365 		wakeupgen_writel(0, i, CPU1_ID);
366 	}
367 
368 	/*
369 	 * Override GIC architecture specific functions to add
370 	 * OMAP WakeupGen interrupt controller along with GIC
371 	 */
372 	gic_arch_extn.irq_mask = wakeupgen_mask;
373 	gic_arch_extn.irq_unmask = wakeupgen_unmask;
374 	gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE;
375 
376 	/*
377 	 * FIXME: Add support to set_smp_affinity() once the core
378 	 * GIC code has necessary hooks in place.
379 	 */
380 
381 	/* Associate all the IRQs to boot CPU like GIC init does. */
382 	for (i = 0; i < NR_IRQS; i++)
383 		irq_target_cpu[i] = boot_cpu;
384 
385 	irq_hotplug_init();
386 	irq_pm_init();
387 
388 	return 0;
389 }
390