]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - sitara-epos/sitara-epos-kernel.git/blob - arch/arm/mach-omap2/omap-wakeupgen.c
arm:omap:am33xx: register edma platform
[sitara-epos/sitara-epos-kernel.git] / arch / arm / mach-omap2 / omap-wakeupgen.c
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  */
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>
28 #include <asm/hardware/gic.h>
30 #include <mach/omap-wakeupgen.h>
31 #include <mach/omap-secure.h>
33 #include "omap4-sar-layout.h"
34 #include "common.h"
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
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];
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 }
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 }
65 static inline void sar_writel(u32 val, u32 offset, u8 idx)
66 {
67         __raw_writel(val, sar_base + offset + (idx * 4));
68 }
70 static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg)
71 {
72         u8 i;
74         for (i = 0; i < NR_REG_BANKS; i++)
75                 wakeupgen_writel(reg, i, cpu);
76 }
78 static inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index)
79 {
80         unsigned int spi_irq;
82         /*
83          * PPIs and SGIs are not supported.
84          */
85         if (irq < OMAP44XX_IRQ_GIC_START)
86                 return -EINVAL;
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         }
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;
104         return 0;
107 static void _wakeupgen_clear(unsigned int irq, unsigned int cpu)
109         u32 val, bit_number;
110         u8 i;
112         if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
113                 return;
115         val = wakeupgen_readl(i, cpu);
116         val &= ~BIT(bit_number);
117         wakeupgen_writel(val, i, cpu);
120 static void _wakeupgen_set(unsigned int irq, unsigned int cpu)
122         u32 val, bit_number;
123         u8 i;
125         if (_wakeupgen_get_irq_info(irq, &bit_number, &i))
126                 return;
128         val = wakeupgen_readl(i, cpu);
129         val |= BIT(bit_number);
130         wakeupgen_writel(val, i, cpu);
133 static void _wakeupgen_save_masks(unsigned int cpu)
135         u8 i;
137         for (i = 0; i < NR_REG_BANKS; i++)
138                 per_cpu(irqmasks, cpu)[i] = wakeupgen_readl(i, cpu);
141 static void _wakeupgen_restore_masks(unsigned int cpu)
143         u8 i;
145         for (i = 0; i < NR_REG_BANKS; i++)
146                 wakeupgen_writel(per_cpu(irqmasks, cpu)[i], i, cpu);
149 /*
150  * Architecture specific Mask extension
151  */
152 static void wakeupgen_mask(struct irq_data *d)
154         unsigned long flags;
156         spin_lock_irqsave(&wakeupgen_lock, flags);
157         _wakeupgen_clear(d->irq, irq_target_cpu[d->irq]);
158         spin_unlock_irqrestore(&wakeupgen_lock, flags);
161 /*
162  * Architecture specific Unmask extension
163  */
164 static void wakeupgen_unmask(struct irq_data *d)
166         unsigned long flags;
168         spin_lock_irqsave(&wakeupgen_lock, flags);
169         _wakeupgen_set(d->irq, irq_target_cpu[d->irq]);
170         spin_unlock_irqrestore(&wakeupgen_lock, flags);
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)
182         unsigned long flags;
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);
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)
206         u32 i, val;
208         if (omap_rev() == OMAP4430_REV_ES1_0)
209                 return;
211         if (!sar_base)
212                 sar_base = omap4_get_sar_ram_base();
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);
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         }
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);
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);
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);
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);
256 /*
257  * Clear WakeupGen SAR backup status.
258  */
259 void irq_sar_clear(void)
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);
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)
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");
280 #endif
282 #ifdef CONFIG_HOTPLUG_CPU
283 static int __cpuinit irq_cpu_hotplug_notify(struct notifier_block *self,
284                                          unsigned long action, void *hcpu)
286         unsigned int cpu = (unsigned int)hcpu;
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;
299 static struct notifier_block __refdata irq_hotplug_notifier = {
300         .notifier_call = irq_cpu_hotplug_notify,
301 };
303 static void __init irq_hotplug_init(void)
305         register_hotcpu_notifier(&irq_hotplug_notifier);
307 #else
308 static void __init irq_hotplug_init(void)
309 {}
310 #endif
312 #ifdef CONFIG_CPU_PM
313 static int irq_notifier(struct notifier_block *self, unsigned long cmd, void *v)
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;
330 static struct notifier_block irq_notifier_block = {
331         .notifier_call = irq_notifier,
332 };
334 static void __init irq_pm_init(void)
336         cpu_pm_register_notifier(&irq_notifier_block);
338 #else
339 static void __init irq_pm_init(void)
340 {}
341 #endif
343 /*
344  * Initialise the wakeupgen module.
345  */
346 int __init omap_wakeupgen_init(void)
348         int i;
349         unsigned int boot_cpu = smp_processor_id();
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         }
357         /* Static mapping, never released */
358         wakeupgen_base = ioremap(OMAP44XX_WKUPGEN_BASE, SZ_4K);
359         if (WARN_ON(!wakeupgen_base))
360                 return -ENOMEM;
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         }
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;
376         /*
377          * FIXME: Add support to set_smp_affinity() once the core
378          * GIC code has necessary hooks in place.
379          */
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;
385         irq_hotplug_init();
386         irq_pm_init();
388         return 0;