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 #include <linux/irqchip/arm-gic.h> 28 29 #include "omap-wakeupgen.h" 30 #include "omap-secure.h" 31 32 #include "soc.h" 33 #include "omap4-sar-layout.h" 34 #include "common.h" 35 36 #define AM43XX_NR_REG_BANKS 7 37 #define AM43XX_IRQS 224 38 #define MAX_NR_REG_BANKS AM43XX_NR_REG_BANKS 39 #define MAX_IRQS AM43XX_IRQS 40 #define DEFAULT_NR_REG_BANKS 5 41 #define DEFAULT_IRQS 160 42 #define WKG_MASK_ALL 0x00000000 43 #define WKG_UNMASK_ALL 0xffffffff 44 #define CPU_ENA_OFFSET 0x400 45 #define CPU0_ID 0x0 46 #define CPU1_ID 0x1 47 #define OMAP4_NR_BANKS 4 48 #define OMAP4_NR_IRQS 128 49 50 static void __iomem *wakeupgen_base; 51 static void __iomem *sar_base; 52 static DEFINE_RAW_SPINLOCK(wakeupgen_lock); 53 static unsigned int irq_target_cpu[MAX_IRQS]; 54 static unsigned int irq_banks = DEFAULT_NR_REG_BANKS; 55 static unsigned int max_irqs = DEFAULT_IRQS; 56 static unsigned int omap_secure_apis; 57 58 /* 59 * Static helper functions. 60 */ 61 static inline u32 wakeupgen_readl(u8 idx, u32 cpu) 62 { 63 return readl_relaxed(wakeupgen_base + OMAP_WKG_ENB_A_0 + 64 (cpu * CPU_ENA_OFFSET) + (idx * 4)); 65 } 66 67 static inline void wakeupgen_writel(u32 val, u8 idx, u32 cpu) 68 { 69 writel_relaxed(val, wakeupgen_base + OMAP_WKG_ENB_A_0 + 70 (cpu * CPU_ENA_OFFSET) + (idx * 4)); 71 } 72 73 static inline void sar_writel(u32 val, u32 offset, u8 idx) 74 { 75 writel_relaxed(val, sar_base + offset + (idx * 4)); 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 /* 134 * Architecture specific Mask extension 135 */ 136 static void wakeupgen_mask(struct irq_data *d) 137 { 138 unsigned long flags; 139 140 raw_spin_lock_irqsave(&wakeupgen_lock, flags); 141 _wakeupgen_clear(d->hwirq, irq_target_cpu[d->hwirq]); 142 raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); 143 } 144 145 /* 146 * Architecture specific Unmask extension 147 */ 148 static void wakeupgen_unmask(struct irq_data *d) 149 { 150 unsigned long flags; 151 152 raw_spin_lock_irqsave(&wakeupgen_lock, flags); 153 _wakeupgen_set(d->hwirq, irq_target_cpu[d->hwirq]); 154 raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); 155 } 156 157 #ifdef CONFIG_HOTPLUG_CPU 158 static DEFINE_PER_CPU(u32 [MAX_NR_REG_BANKS], irqmasks); 159 160 static void _wakeupgen_save_masks(unsigned int cpu) 161 { 162 u8 i; 163 164 for (i = 0; i < irq_banks; i++) 165 per_cpu(irqmasks, cpu)[i] = wakeupgen_readl(i, cpu); 166 } 167 168 static void _wakeupgen_restore_masks(unsigned int cpu) 169 { 170 u8 i; 171 172 for (i = 0; i < irq_banks; i++) 173 wakeupgen_writel(per_cpu(irqmasks, cpu)[i], i, cpu); 174 } 175 176 static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg) 177 { 178 u8 i; 179 180 for (i = 0; i < irq_banks; i++) 181 wakeupgen_writel(reg, i, cpu); 182 } 183 184 /* 185 * Mask or unmask all interrupts on given CPU. 186 * 0 = Mask all interrupts on the 'cpu' 187 * 1 = Unmask all interrupts on the 'cpu' 188 * Ensure that the initial mask is maintained. This is faster than 189 * iterating through GIC registers to arrive at the correct masks. 190 */ 191 static void wakeupgen_irqmask_all(unsigned int cpu, unsigned int set) 192 { 193 unsigned long flags; 194 195 raw_spin_lock_irqsave(&wakeupgen_lock, flags); 196 if (set) { 197 _wakeupgen_save_masks(cpu); 198 _wakeupgen_set_all(cpu, WKG_MASK_ALL); 199 } else { 200 _wakeupgen_set_all(cpu, WKG_UNMASK_ALL); 201 _wakeupgen_restore_masks(cpu); 202 } 203 raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); 204 } 205 #endif 206 207 #ifdef CONFIG_CPU_PM 208 static inline void omap4_irq_save_context(void) 209 { 210 u32 i, val; 211 212 if (omap_rev() == OMAP4430_REV_ES1_0) 213 return; 214 215 for (i = 0; i < irq_banks; i++) { 216 /* Save the CPUx interrupt mask for IRQ 0 to 127 */ 217 val = wakeupgen_readl(i, 0); 218 sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i); 219 val = wakeupgen_readl(i, 1); 220 sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i); 221 222 /* 223 * Disable the secure interrupts for CPUx. The restore 224 * code blindly restores secure and non-secure interrupt 225 * masks from SAR RAM. Secure interrupts are not suppose 226 * to be enabled from HLOS. So overwrite the SAR location 227 * so that the secure interrupt remains disabled. 228 */ 229 sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i); 230 sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i); 231 } 232 233 /* Save AuxBoot* registers */ 234 val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); 235 writel_relaxed(val, sar_base + AUXCOREBOOT0_OFFSET); 236 val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_1); 237 writel_relaxed(val, sar_base + AUXCOREBOOT1_OFFSET); 238 239 /* Save SyncReq generation logic */ 240 val = readl_relaxed(wakeupgen_base + OMAP_PTMSYNCREQ_MASK); 241 writel_relaxed(val, sar_base + PTMSYNCREQ_MASK_OFFSET); 242 val = readl_relaxed(wakeupgen_base + OMAP_PTMSYNCREQ_EN); 243 writel_relaxed(val, sar_base + PTMSYNCREQ_EN_OFFSET); 244 245 /* Set the Backup Bit Mask status */ 246 val = readl_relaxed(sar_base + SAR_BACKUP_STATUS_OFFSET); 247 val |= SAR_BACKUP_STATUS_WAKEUPGEN; 248 writel_relaxed(val, sar_base + SAR_BACKUP_STATUS_OFFSET); 249 250 } 251 252 static inline void omap5_irq_save_context(void) 253 { 254 u32 i, val; 255 256 for (i = 0; i < irq_banks; i++) { 257 /* Save the CPUx interrupt mask for IRQ 0 to 159 */ 258 val = wakeupgen_readl(i, 0); 259 sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU0, i); 260 val = wakeupgen_readl(i, 1); 261 sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU1, i); 262 sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU0, i); 263 sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU1, i); 264 } 265 266 /* Save AuxBoot* registers */ 267 val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); 268 writel_relaxed(val, sar_base + OMAP5_AUXCOREBOOT0_OFFSET); 269 val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); 270 writel_relaxed(val, sar_base + OMAP5_AUXCOREBOOT1_OFFSET); 271 272 /* Set the Backup Bit Mask status */ 273 val = readl_relaxed(sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET); 274 val |= SAR_BACKUP_STATUS_WAKEUPGEN; 275 writel_relaxed(val, sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET); 276 277 } 278 279 /* 280 * Save WakeupGen interrupt context in SAR BANK3. Restore is done by 281 * ROM code. WakeupGen IP is integrated along with GIC to manage the 282 * interrupt wakeups from CPU low power states. It manages 283 * masking/unmasking of Shared peripheral interrupts(SPI). So the 284 * interrupt enable/disable control should be in sync and consistent 285 * at WakeupGen and GIC so that interrupts are not lost. 286 */ 287 static void irq_save_context(void) 288 { 289 if (!sar_base) 290 sar_base = omap4_get_sar_ram_base(); 291 292 if (soc_is_omap54xx()) 293 omap5_irq_save_context(); 294 else 295 omap4_irq_save_context(); 296 } 297 298 /* 299 * Clear WakeupGen SAR backup status. 300 */ 301 static void irq_sar_clear(void) 302 { 303 u32 val; 304 u32 offset = SAR_BACKUP_STATUS_OFFSET; 305 306 if (soc_is_omap54xx()) 307 offset = OMAP5_SAR_BACKUP_STATUS_OFFSET; 308 309 val = readl_relaxed(sar_base + offset); 310 val &= ~SAR_BACKUP_STATUS_WAKEUPGEN; 311 writel_relaxed(val, sar_base + offset); 312 } 313 314 /* 315 * Save GIC and Wakeupgen interrupt context using secure API 316 * for HS/EMU devices. 317 */ 318 static void irq_save_secure_context(void) 319 { 320 u32 ret; 321 ret = omap_secure_dispatcher(OMAP4_HAL_SAVEGIC_INDEX, 322 FLAG_START_CRITICAL, 323 0, 0, 0, 0, 0); 324 if (ret != API_HAL_RET_VALUE_OK) 325 pr_err("GIC and Wakeupgen context save failed\n"); 326 } 327 #endif 328 329 #ifdef CONFIG_HOTPLUG_CPU 330 static int irq_cpu_hotplug_notify(struct notifier_block *self, 331 unsigned long action, void *hcpu) 332 { 333 unsigned int cpu = (unsigned int)hcpu; 334 335 switch (action) { 336 case CPU_ONLINE: 337 wakeupgen_irqmask_all(cpu, 0); 338 break; 339 case CPU_DEAD: 340 wakeupgen_irqmask_all(cpu, 1); 341 break; 342 } 343 return NOTIFY_OK; 344 } 345 346 static struct notifier_block __refdata irq_hotplug_notifier = { 347 .notifier_call = irq_cpu_hotplug_notify, 348 }; 349 350 static void __init irq_hotplug_init(void) 351 { 352 register_hotcpu_notifier(&irq_hotplug_notifier); 353 } 354 #else 355 static void __init irq_hotplug_init(void) 356 {} 357 #endif 358 359 #ifdef CONFIG_CPU_PM 360 static int irq_notifier(struct notifier_block *self, unsigned long cmd, void *v) 361 { 362 switch (cmd) { 363 case CPU_CLUSTER_PM_ENTER: 364 if (omap_type() == OMAP2_DEVICE_TYPE_GP) 365 irq_save_context(); 366 else 367 irq_save_secure_context(); 368 break; 369 case CPU_CLUSTER_PM_EXIT: 370 if (omap_type() == OMAP2_DEVICE_TYPE_GP) 371 irq_sar_clear(); 372 break; 373 } 374 return NOTIFY_OK; 375 } 376 377 static struct notifier_block irq_notifier_block = { 378 .notifier_call = irq_notifier, 379 }; 380 381 static void __init irq_pm_init(void) 382 { 383 /* FIXME: Remove this when MPU OSWR support is added */ 384 if (!soc_is_omap54xx()) 385 cpu_pm_register_notifier(&irq_notifier_block); 386 } 387 #else 388 static void __init irq_pm_init(void) 389 {} 390 #endif 391 392 void __iomem *omap_get_wakeupgen_base(void) 393 { 394 return wakeupgen_base; 395 } 396 397 int omap_secure_apis_support(void) 398 { 399 return omap_secure_apis; 400 } 401 402 /* 403 * Initialise the wakeupgen module. 404 */ 405 int __init omap_wakeupgen_init(void) 406 { 407 int i; 408 unsigned int boot_cpu = smp_processor_id(); 409 410 /* Not supported on OMAP4 ES1.0 silicon */ 411 if (omap_rev() == OMAP4430_REV_ES1_0) { 412 WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n"); 413 return -EPERM; 414 } 415 416 /* Static mapping, never released */ 417 wakeupgen_base = ioremap(OMAP_WKUPGEN_BASE, SZ_4K); 418 if (WARN_ON(!wakeupgen_base)) 419 return -ENOMEM; 420 421 if (cpu_is_omap44xx()) { 422 irq_banks = OMAP4_NR_BANKS; 423 max_irqs = OMAP4_NR_IRQS; 424 omap_secure_apis = 1; 425 } else if (soc_is_am43xx()) { 426 irq_banks = AM43XX_NR_REG_BANKS; 427 max_irqs = AM43XX_IRQS; 428 } 429 430 /* Clear all IRQ bitmasks at wakeupGen level */ 431 for (i = 0; i < irq_banks; i++) { 432 wakeupgen_writel(0, i, CPU0_ID); 433 if (!soc_is_am43xx()) 434 wakeupgen_writel(0, i, CPU1_ID); 435 } 436 437 /* 438 * Override GIC architecture specific functions to add 439 * OMAP WakeupGen interrupt controller along with GIC 440 */ 441 gic_arch_extn.irq_mask = wakeupgen_mask; 442 gic_arch_extn.irq_unmask = wakeupgen_unmask; 443 gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE; 444 445 /* 446 * FIXME: Add support to set_smp_affinity() once the core 447 * GIC code has necessary hooks in place. 448 */ 449 450 /* Associate all the IRQs to boot CPU like GIC init does. */ 451 for (i = 0; i < max_irqs; i++) 452 irq_target_cpu[i] = boot_cpu; 453 454 irq_hotplug_init(); 455 irq_pm_init(); 456 457 return 0; 458 } 459