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