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