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