18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * OMAP WakeupGen Source file 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * OMAP WakeupGen is the interrupt controller extension used along 68c2ecf20Sopenharmony_ci * with ARM GIC to wake the CPU out from low power states on 78c2ecf20Sopenharmony_ci * external interrupts. It is responsible for generating wakeup 88c2ecf20Sopenharmony_ci * event from the incoming interrupts and enable bits. It is 98c2ecf20Sopenharmony_ci * implemented in MPU always ON power domain. During normal operation, 108c2ecf20Sopenharmony_ci * WakeupGen delivers external interrupts directly to the GIC. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc. 138c2ecf20Sopenharmony_ci * Santosh Shilimkar <santosh.shilimkar@ti.com> 148c2ecf20Sopenharmony_ci */ 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/init.h> 188c2ecf20Sopenharmony_ci#include <linux/io.h> 198c2ecf20Sopenharmony_ci#include <linux/irq.h> 208c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 218c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 228c2ecf20Sopenharmony_ci#include <linux/of_address.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/cpu.h> 258c2ecf20Sopenharmony_ci#include <linux/notifier.h> 268c2ecf20Sopenharmony_ci#include <linux/cpu_pm.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "omap-wakeupgen.h" 298c2ecf20Sopenharmony_ci#include "omap-secure.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "soc.h" 328c2ecf20Sopenharmony_ci#include "omap4-sar-layout.h" 338c2ecf20Sopenharmony_ci#include "common.h" 348c2ecf20Sopenharmony_ci#include "pm.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define AM43XX_NR_REG_BANKS 7 378c2ecf20Sopenharmony_ci#define AM43XX_IRQS 224 388c2ecf20Sopenharmony_ci#define MAX_NR_REG_BANKS AM43XX_NR_REG_BANKS 398c2ecf20Sopenharmony_ci#define MAX_IRQS AM43XX_IRQS 408c2ecf20Sopenharmony_ci#define DEFAULT_NR_REG_BANKS 5 418c2ecf20Sopenharmony_ci#define DEFAULT_IRQS 160 428c2ecf20Sopenharmony_ci#define WKG_MASK_ALL 0x00000000 438c2ecf20Sopenharmony_ci#define WKG_UNMASK_ALL 0xffffffff 448c2ecf20Sopenharmony_ci#define CPU_ENA_OFFSET 0x400 458c2ecf20Sopenharmony_ci#define CPU0_ID 0x0 468c2ecf20Sopenharmony_ci#define CPU1_ID 0x1 478c2ecf20Sopenharmony_ci#define OMAP4_NR_BANKS 4 488c2ecf20Sopenharmony_ci#define OMAP4_NR_IRQS 128 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define SYS_NIRQ1_EXT_SYS_IRQ_1 7 518c2ecf20Sopenharmony_ci#define SYS_NIRQ2_EXT_SYS_IRQ_2 119 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void __iomem *wakeupgen_base; 548c2ecf20Sopenharmony_cistatic void __iomem *sar_base; 558c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(wakeupgen_lock); 568c2ecf20Sopenharmony_cistatic unsigned int irq_target_cpu[MAX_IRQS]; 578c2ecf20Sopenharmony_cistatic unsigned int irq_banks = DEFAULT_NR_REG_BANKS; 588c2ecf20Sopenharmony_cistatic unsigned int max_irqs = DEFAULT_IRQS; 598c2ecf20Sopenharmony_cistatic unsigned int omap_secure_apis; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_PM 628c2ecf20Sopenharmony_cistatic unsigned int wakeupgen_context[MAX_NR_REG_BANKS]; 638c2ecf20Sopenharmony_ci#endif 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistruct omap_wakeupgen_ops { 668c2ecf20Sopenharmony_ci void (*save_context)(void); 678c2ecf20Sopenharmony_ci void (*restore_context)(void); 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic struct omap_wakeupgen_ops *wakeupgen_ops; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* 738c2ecf20Sopenharmony_ci * Static helper functions. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic inline u32 wakeupgen_readl(u8 idx, u32 cpu) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci return readl_relaxed(wakeupgen_base + OMAP_WKG_ENB_A_0 + 788c2ecf20Sopenharmony_ci (cpu * CPU_ENA_OFFSET) + (idx * 4)); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic inline void wakeupgen_writel(u32 val, u8 idx, u32 cpu) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci writel_relaxed(val, wakeupgen_base + OMAP_WKG_ENB_A_0 + 848c2ecf20Sopenharmony_ci (cpu * CPU_ENA_OFFSET) + (idx * 4)); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic inline void sar_writel(u32 val, u32 offset, u8 idx) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + offset + (idx * 4)); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci /* 958c2ecf20Sopenharmony_ci * Each WakeupGen register controls 32 interrupt. 968c2ecf20Sopenharmony_ci * i.e. 1 bit per SPI IRQ 978c2ecf20Sopenharmony_ci */ 988c2ecf20Sopenharmony_ci *reg_index = irq >> 5; 998c2ecf20Sopenharmony_ci *bit_posn = irq %= 32; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void _wakeupgen_clear(unsigned int irq, unsigned int cpu) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci u32 val, bit_number; 1078c2ecf20Sopenharmony_ci u8 i; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (_wakeupgen_get_irq_info(irq, &bit_number, &i)) 1108c2ecf20Sopenharmony_ci return; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci val = wakeupgen_readl(i, cpu); 1138c2ecf20Sopenharmony_ci val &= ~BIT(bit_number); 1148c2ecf20Sopenharmony_ci wakeupgen_writel(val, i, cpu); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic void _wakeupgen_set(unsigned int irq, unsigned int cpu) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci u32 val, bit_number; 1208c2ecf20Sopenharmony_ci u8 i; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci if (_wakeupgen_get_irq_info(irq, &bit_number, &i)) 1238c2ecf20Sopenharmony_ci return; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci val = wakeupgen_readl(i, cpu); 1268c2ecf20Sopenharmony_ci val |= BIT(bit_number); 1278c2ecf20Sopenharmony_ci wakeupgen_writel(val, i, cpu); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* 1318c2ecf20Sopenharmony_ci * Architecture specific Mask extension 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_cistatic void wakeupgen_mask(struct irq_data *d) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned long flags; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&wakeupgen_lock, flags); 1388c2ecf20Sopenharmony_ci _wakeupgen_clear(d->hwirq, irq_target_cpu[d->hwirq]); 1398c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); 1408c2ecf20Sopenharmony_ci irq_chip_mask_parent(d); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* 1448c2ecf20Sopenharmony_ci * Architecture specific Unmask extension 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic void wakeupgen_unmask(struct irq_data *d) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci unsigned long flags; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&wakeupgen_lock, flags); 1518c2ecf20Sopenharmony_ci _wakeupgen_set(d->hwirq, irq_target_cpu[d->hwirq]); 1528c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); 1538c2ecf20Sopenharmony_ci irq_chip_unmask_parent(d); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* 1578c2ecf20Sopenharmony_ci * The sys_nirq pins bypass peripheral modules and are wired directly 1588c2ecf20Sopenharmony_ci * to MPUSS wakeupgen. They get automatically inverted for GIC. 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_cistatic int wakeupgen_irq_set_type(struct irq_data *d, unsigned int type) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci bool inverted = false; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci switch (type) { 1658c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 1668c2ecf20Sopenharmony_ci type &= ~IRQ_TYPE_LEVEL_MASK; 1678c2ecf20Sopenharmony_ci type |= IRQ_TYPE_LEVEL_HIGH; 1688c2ecf20Sopenharmony_ci inverted = true; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 1718c2ecf20Sopenharmony_ci type &= ~IRQ_TYPE_EDGE_BOTH; 1728c2ecf20Sopenharmony_ci type |= IRQ_TYPE_EDGE_RISING; 1738c2ecf20Sopenharmony_ci inverted = true; 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci default: 1768c2ecf20Sopenharmony_ci break; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci if (inverted && d->hwirq != SYS_NIRQ1_EXT_SYS_IRQ_1 && 1808c2ecf20Sopenharmony_ci d->hwirq != SYS_NIRQ2_EXT_SYS_IRQ_2) 1818c2ecf20Sopenharmony_ci pr_warn("wakeupgen: irq%li polarity inverted in dts\n", 1828c2ecf20Sopenharmony_ci d->hwirq); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return irq_chip_set_type_parent(d, type); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 1888c2ecf20Sopenharmony_cistatic DEFINE_PER_CPU(u32 [MAX_NR_REG_BANKS], irqmasks); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void _wakeupgen_save_masks(unsigned int cpu) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci u8 i; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci for (i = 0; i < irq_banks; i++) 1958c2ecf20Sopenharmony_ci per_cpu(irqmasks, cpu)[i] = wakeupgen_readl(i, cpu); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void _wakeupgen_restore_masks(unsigned int cpu) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci u8 i; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci for (i = 0; i < irq_banks; i++) 2038c2ecf20Sopenharmony_ci wakeupgen_writel(per_cpu(irqmasks, cpu)[i], i, cpu); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void _wakeupgen_set_all(unsigned int cpu, unsigned int reg) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci u8 i; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci for (i = 0; i < irq_banks; i++) 2118c2ecf20Sopenharmony_ci wakeupgen_writel(reg, i, cpu); 2128c2ecf20Sopenharmony_ci} 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci/* 2158c2ecf20Sopenharmony_ci * Mask or unmask all interrupts on given CPU. 2168c2ecf20Sopenharmony_ci * 0 = Mask all interrupts on the 'cpu' 2178c2ecf20Sopenharmony_ci * 1 = Unmask all interrupts on the 'cpu' 2188c2ecf20Sopenharmony_ci * Ensure that the initial mask is maintained. This is faster than 2198c2ecf20Sopenharmony_ci * iterating through GIC registers to arrive at the correct masks. 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_cistatic void wakeupgen_irqmask_all(unsigned int cpu, unsigned int set) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci unsigned long flags; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&wakeupgen_lock, flags); 2268c2ecf20Sopenharmony_ci if (set) { 2278c2ecf20Sopenharmony_ci _wakeupgen_save_masks(cpu); 2288c2ecf20Sopenharmony_ci _wakeupgen_set_all(cpu, WKG_MASK_ALL); 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci _wakeupgen_set_all(cpu, WKG_UNMASK_ALL); 2318c2ecf20Sopenharmony_ci _wakeupgen_restore_masks(cpu); 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&wakeupgen_lock, flags); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci#endif 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_PM 2388c2ecf20Sopenharmony_cistatic inline void omap4_irq_save_context(void) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci u32 i, val; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (omap_rev() == OMAP4430_REV_ES1_0) 2438c2ecf20Sopenharmony_ci return; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci for (i = 0; i < irq_banks; i++) { 2468c2ecf20Sopenharmony_ci /* Save the CPUx interrupt mask for IRQ 0 to 127 */ 2478c2ecf20Sopenharmony_ci val = wakeupgen_readl(i, 0); 2488c2ecf20Sopenharmony_ci sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i); 2498c2ecf20Sopenharmony_ci val = wakeupgen_readl(i, 1); 2508c2ecf20Sopenharmony_ci sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* 2538c2ecf20Sopenharmony_ci * Disable the secure interrupts for CPUx. The restore 2548c2ecf20Sopenharmony_ci * code blindly restores secure and non-secure interrupt 2558c2ecf20Sopenharmony_ci * masks from SAR RAM. Secure interrupts are not suppose 2568c2ecf20Sopenharmony_ci * to be enabled from HLOS. So overwrite the SAR location 2578c2ecf20Sopenharmony_ci * so that the secure interrupt remains disabled. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i); 2608c2ecf20Sopenharmony_ci sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* Save AuxBoot* registers */ 2648c2ecf20Sopenharmony_ci val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); 2658c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + AUXCOREBOOT0_OFFSET); 2668c2ecf20Sopenharmony_ci val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_1); 2678c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + AUXCOREBOOT1_OFFSET); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Save SyncReq generation logic */ 2708c2ecf20Sopenharmony_ci val = readl_relaxed(wakeupgen_base + OMAP_PTMSYNCREQ_MASK); 2718c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + PTMSYNCREQ_MASK_OFFSET); 2728c2ecf20Sopenharmony_ci val = readl_relaxed(wakeupgen_base + OMAP_PTMSYNCREQ_EN); 2738c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + PTMSYNCREQ_EN_OFFSET); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* Set the Backup Bit Mask status */ 2768c2ecf20Sopenharmony_ci val = readl_relaxed(sar_base + SAR_BACKUP_STATUS_OFFSET); 2778c2ecf20Sopenharmony_ci val |= SAR_BACKUP_STATUS_WAKEUPGEN; 2788c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + SAR_BACKUP_STATUS_OFFSET); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic inline void omap5_irq_save_context(void) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci u32 i, val; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci for (i = 0; i < irq_banks; i++) { 2878c2ecf20Sopenharmony_ci /* Save the CPUx interrupt mask for IRQ 0 to 159 */ 2888c2ecf20Sopenharmony_ci val = wakeupgen_readl(i, 0); 2898c2ecf20Sopenharmony_ci sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU0, i); 2908c2ecf20Sopenharmony_ci val = wakeupgen_readl(i, 1); 2918c2ecf20Sopenharmony_ci sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU1, i); 2928c2ecf20Sopenharmony_ci sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU0, i); 2938c2ecf20Sopenharmony_ci sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU1, i); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Save AuxBoot* registers */ 2978c2ecf20Sopenharmony_ci val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); 2988c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + OMAP5_AUXCOREBOOT0_OFFSET); 2998c2ecf20Sopenharmony_ci val = readl_relaxed(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); 3008c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + OMAP5_AUXCOREBOOT1_OFFSET); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* Set the Backup Bit Mask status */ 3038c2ecf20Sopenharmony_ci val = readl_relaxed(sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET); 3048c2ecf20Sopenharmony_ci val |= SAR_BACKUP_STATUS_WAKEUPGEN; 3058c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic inline void am43xx_irq_save_context(void) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci u32 i; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci for (i = 0; i < irq_banks; i++) { 3148c2ecf20Sopenharmony_ci wakeupgen_context[i] = wakeupgen_readl(i, 0); 3158c2ecf20Sopenharmony_ci wakeupgen_writel(0, i, CPU0_ID); 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* 3208c2ecf20Sopenharmony_ci * Save WakeupGen interrupt context in SAR BANK3. Restore is done by 3218c2ecf20Sopenharmony_ci * ROM code. WakeupGen IP is integrated along with GIC to manage the 3228c2ecf20Sopenharmony_ci * interrupt wakeups from CPU low power states. It manages 3238c2ecf20Sopenharmony_ci * masking/unmasking of Shared peripheral interrupts(SPI). So the 3248c2ecf20Sopenharmony_ci * interrupt enable/disable control should be in sync and consistent 3258c2ecf20Sopenharmony_ci * at WakeupGen and GIC so that interrupts are not lost. 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_cistatic void irq_save_context(void) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci /* DRA7 has no SAR to save */ 3308c2ecf20Sopenharmony_ci if (soc_is_dra7xx()) 3318c2ecf20Sopenharmony_ci return; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci if (wakeupgen_ops && wakeupgen_ops->save_context) 3348c2ecf20Sopenharmony_ci wakeupgen_ops->save_context(); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* 3388c2ecf20Sopenharmony_ci * Clear WakeupGen SAR backup status. 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_cistatic void irq_sar_clear(void) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci u32 val; 3438c2ecf20Sopenharmony_ci u32 offset = SAR_BACKUP_STATUS_OFFSET; 3448c2ecf20Sopenharmony_ci /* DRA7 has no SAR to save */ 3458c2ecf20Sopenharmony_ci if (soc_is_dra7xx()) 3468c2ecf20Sopenharmony_ci return; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (soc_is_omap54xx()) 3498c2ecf20Sopenharmony_ci offset = OMAP5_SAR_BACKUP_STATUS_OFFSET; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci val = readl_relaxed(sar_base + offset); 3528c2ecf20Sopenharmony_ci val &= ~SAR_BACKUP_STATUS_WAKEUPGEN; 3538c2ecf20Sopenharmony_ci writel_relaxed(val, sar_base + offset); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void am43xx_irq_restore_context(void) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci u32 i; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci for (i = 0; i < irq_banks; i++) 3618c2ecf20Sopenharmony_ci wakeupgen_writel(wakeupgen_context[i], i, CPU0_ID); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic void irq_restore_context(void) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci if (wakeupgen_ops && wakeupgen_ops->restore_context) 3678c2ecf20Sopenharmony_ci wakeupgen_ops->restore_context(); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/* 3718c2ecf20Sopenharmony_ci * Save GIC and Wakeupgen interrupt context using secure API 3728c2ecf20Sopenharmony_ci * for HS/EMU devices. 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_cistatic void irq_save_secure_context(void) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci u32 ret; 3778c2ecf20Sopenharmony_ci ret = omap_secure_dispatcher(OMAP4_HAL_SAVEGIC_INDEX, 3788c2ecf20Sopenharmony_ci FLAG_START_CRITICAL, 3798c2ecf20Sopenharmony_ci 0, 0, 0, 0, 0); 3808c2ecf20Sopenharmony_ci if (ret != API_HAL_RET_VALUE_OK) 3818c2ecf20Sopenharmony_ci pr_err("GIC and Wakeupgen context save failed\n"); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/* Define ops for context save and restore for each SoC */ 3858c2ecf20Sopenharmony_cistatic struct omap_wakeupgen_ops omap4_wakeupgen_ops = { 3868c2ecf20Sopenharmony_ci .save_context = omap4_irq_save_context, 3878c2ecf20Sopenharmony_ci .restore_context = irq_sar_clear, 3888c2ecf20Sopenharmony_ci}; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic struct omap_wakeupgen_ops omap5_wakeupgen_ops = { 3918c2ecf20Sopenharmony_ci .save_context = omap5_irq_save_context, 3928c2ecf20Sopenharmony_ci .restore_context = irq_sar_clear, 3938c2ecf20Sopenharmony_ci}; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic struct omap_wakeupgen_ops am43xx_wakeupgen_ops = { 3968c2ecf20Sopenharmony_ci .save_context = am43xx_irq_save_context, 3978c2ecf20Sopenharmony_ci .restore_context = am43xx_irq_restore_context, 3988c2ecf20Sopenharmony_ci}; 3998c2ecf20Sopenharmony_ci#else 4008c2ecf20Sopenharmony_cistatic struct omap_wakeupgen_ops omap4_wakeupgen_ops = {}; 4018c2ecf20Sopenharmony_cistatic struct omap_wakeupgen_ops omap5_wakeupgen_ops = {}; 4028c2ecf20Sopenharmony_cistatic struct omap_wakeupgen_ops am43xx_wakeupgen_ops = {}; 4038c2ecf20Sopenharmony_ci#endif 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU 4068c2ecf20Sopenharmony_cistatic int omap_wakeupgen_cpu_online(unsigned int cpu) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci wakeupgen_irqmask_all(cpu, 0); 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_cistatic int omap_wakeupgen_cpu_dead(unsigned int cpu) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci wakeupgen_irqmask_all(cpu, 1); 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic void __init irq_hotplug_init(void) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "arm/omap-wake:online", 4218c2ecf20Sopenharmony_ci omap_wakeupgen_cpu_online, NULL); 4228c2ecf20Sopenharmony_ci cpuhp_setup_state_nocalls(CPUHP_ARM_OMAP_WAKE_DEAD, 4238c2ecf20Sopenharmony_ci "arm/omap-wake:dead", NULL, 4248c2ecf20Sopenharmony_ci omap_wakeupgen_cpu_dead); 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci#else 4278c2ecf20Sopenharmony_cistatic void __init irq_hotplug_init(void) 4288c2ecf20Sopenharmony_ci{} 4298c2ecf20Sopenharmony_ci#endif 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_PM 4328c2ecf20Sopenharmony_cistatic int irq_notifier(struct notifier_block *self, unsigned long cmd, void *v) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci switch (cmd) { 4358c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_ENTER: 4368c2ecf20Sopenharmony_ci if (omap_type() == OMAP2_DEVICE_TYPE_GP || soc_is_am43xx()) 4378c2ecf20Sopenharmony_ci irq_save_context(); 4388c2ecf20Sopenharmony_ci else 4398c2ecf20Sopenharmony_ci irq_save_secure_context(); 4408c2ecf20Sopenharmony_ci break; 4418c2ecf20Sopenharmony_ci case CPU_CLUSTER_PM_EXIT: 4428c2ecf20Sopenharmony_ci if (omap_type() == OMAP2_DEVICE_TYPE_GP || soc_is_am43xx()) 4438c2ecf20Sopenharmony_ci irq_restore_context(); 4448c2ecf20Sopenharmony_ci break; 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci return NOTIFY_OK; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic struct notifier_block irq_notifier_block = { 4508c2ecf20Sopenharmony_ci .notifier_call = irq_notifier, 4518c2ecf20Sopenharmony_ci}; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic void __init irq_pm_init(void) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci /* FIXME: Remove this when MPU OSWR support is added */ 4568c2ecf20Sopenharmony_ci if (!IS_PM44XX_ERRATUM(PM_OMAP4_CPU_OSWR_DISABLE)) 4578c2ecf20Sopenharmony_ci cpu_pm_register_notifier(&irq_notifier_block); 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci#else 4608c2ecf20Sopenharmony_cistatic void __init irq_pm_init(void) 4618c2ecf20Sopenharmony_ci{} 4628c2ecf20Sopenharmony_ci#endif 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_civoid __iomem *omap_get_wakeupgen_base(void) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci return wakeupgen_base; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ciint omap_secure_apis_support(void) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci return omap_secure_apis; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic struct irq_chip wakeupgen_chip = { 4758c2ecf20Sopenharmony_ci .name = "WUGEN", 4768c2ecf20Sopenharmony_ci .irq_eoi = irq_chip_eoi_parent, 4778c2ecf20Sopenharmony_ci .irq_mask = wakeupgen_mask, 4788c2ecf20Sopenharmony_ci .irq_unmask = wakeupgen_unmask, 4798c2ecf20Sopenharmony_ci .irq_retrigger = irq_chip_retrigger_hierarchy, 4808c2ecf20Sopenharmony_ci .irq_set_type = wakeupgen_irq_set_type, 4818c2ecf20Sopenharmony_ci .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, 4828c2ecf20Sopenharmony_ci#ifdef CONFIG_SMP 4838c2ecf20Sopenharmony_ci .irq_set_affinity = irq_chip_set_affinity_parent, 4848c2ecf20Sopenharmony_ci#endif 4858c2ecf20Sopenharmony_ci}; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic int wakeupgen_domain_translate(struct irq_domain *d, 4888c2ecf20Sopenharmony_ci struct irq_fwspec *fwspec, 4898c2ecf20Sopenharmony_ci unsigned long *hwirq, 4908c2ecf20Sopenharmony_ci unsigned int *type) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci if (is_of_node(fwspec->fwnode)) { 4938c2ecf20Sopenharmony_ci if (fwspec->param_count != 3) 4948c2ecf20Sopenharmony_ci return -EINVAL; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* No PPI should point to this domain */ 4978c2ecf20Sopenharmony_ci if (fwspec->param[0] != 0) 4988c2ecf20Sopenharmony_ci return -EINVAL; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci *hwirq = fwspec->param[1]; 5018c2ecf20Sopenharmony_ci *type = fwspec->param[2]; 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci return -EINVAL; 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_cistatic int wakeupgen_domain_alloc(struct irq_domain *domain, 5098c2ecf20Sopenharmony_ci unsigned int virq, 5108c2ecf20Sopenharmony_ci unsigned int nr_irqs, void *data) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct irq_fwspec *fwspec = data; 5138c2ecf20Sopenharmony_ci struct irq_fwspec parent_fwspec; 5148c2ecf20Sopenharmony_ci irq_hw_number_t hwirq; 5158c2ecf20Sopenharmony_ci int i; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (fwspec->param_count != 3) 5188c2ecf20Sopenharmony_ci return -EINVAL; /* Not GIC compliant */ 5198c2ecf20Sopenharmony_ci if (fwspec->param[0] != 0) 5208c2ecf20Sopenharmony_ci return -EINVAL; /* No PPI should point to this domain */ 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci hwirq = fwspec->param[1]; 5238c2ecf20Sopenharmony_ci if (hwirq >= MAX_IRQS) 5248c2ecf20Sopenharmony_ci return -EINVAL; /* Can't deal with this */ 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci for (i = 0; i < nr_irqs; i++) 5278c2ecf20Sopenharmony_ci irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i, 5288c2ecf20Sopenharmony_ci &wakeupgen_chip, NULL); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci parent_fwspec = *fwspec; 5318c2ecf20Sopenharmony_ci parent_fwspec.fwnode = domain->parent->fwnode; 5328c2ecf20Sopenharmony_ci return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, 5338c2ecf20Sopenharmony_ci &parent_fwspec); 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic const struct irq_domain_ops wakeupgen_domain_ops = { 5378c2ecf20Sopenharmony_ci .translate = wakeupgen_domain_translate, 5388c2ecf20Sopenharmony_ci .alloc = wakeupgen_domain_alloc, 5398c2ecf20Sopenharmony_ci .free = irq_domain_free_irqs_common, 5408c2ecf20Sopenharmony_ci}; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci/* 5438c2ecf20Sopenharmony_ci * Initialise the wakeupgen module. 5448c2ecf20Sopenharmony_ci */ 5458c2ecf20Sopenharmony_cistatic int __init wakeupgen_init(struct device_node *node, 5468c2ecf20Sopenharmony_ci struct device_node *parent) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci struct irq_domain *parent_domain, *domain; 5498c2ecf20Sopenharmony_ci int i; 5508c2ecf20Sopenharmony_ci unsigned int boot_cpu = smp_processor_id(); 5518c2ecf20Sopenharmony_ci u32 val; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci if (!parent) { 5548c2ecf20Sopenharmony_ci pr_err("%pOF: no parent, giving up\n", node); 5558c2ecf20Sopenharmony_ci return -ENODEV; 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci parent_domain = irq_find_host(parent); 5598c2ecf20Sopenharmony_ci if (!parent_domain) { 5608c2ecf20Sopenharmony_ci pr_err("%pOF: unable to obtain parent domain\n", node); 5618c2ecf20Sopenharmony_ci return -ENXIO; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci /* Not supported on OMAP4 ES1.0 silicon */ 5648c2ecf20Sopenharmony_ci if (omap_rev() == OMAP4430_REV_ES1_0) { 5658c2ecf20Sopenharmony_ci WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n"); 5668c2ecf20Sopenharmony_ci return -EPERM; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Static mapping, never released */ 5708c2ecf20Sopenharmony_ci wakeupgen_base = of_iomap(node, 0); 5718c2ecf20Sopenharmony_ci if (WARN_ON(!wakeupgen_base)) 5728c2ecf20Sopenharmony_ci return -ENOMEM; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (cpu_is_omap44xx()) { 5758c2ecf20Sopenharmony_ci irq_banks = OMAP4_NR_BANKS; 5768c2ecf20Sopenharmony_ci max_irqs = OMAP4_NR_IRQS; 5778c2ecf20Sopenharmony_ci omap_secure_apis = 1; 5788c2ecf20Sopenharmony_ci wakeupgen_ops = &omap4_wakeupgen_ops; 5798c2ecf20Sopenharmony_ci } else if (soc_is_omap54xx()) { 5808c2ecf20Sopenharmony_ci wakeupgen_ops = &omap5_wakeupgen_ops; 5818c2ecf20Sopenharmony_ci } else if (soc_is_am43xx()) { 5828c2ecf20Sopenharmony_ci irq_banks = AM43XX_NR_REG_BANKS; 5838c2ecf20Sopenharmony_ci max_irqs = AM43XX_IRQS; 5848c2ecf20Sopenharmony_ci wakeupgen_ops = &am43xx_wakeupgen_ops; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci domain = irq_domain_add_hierarchy(parent_domain, 0, max_irqs, 5888c2ecf20Sopenharmony_ci node, &wakeupgen_domain_ops, 5898c2ecf20Sopenharmony_ci NULL); 5908c2ecf20Sopenharmony_ci if (!domain) { 5918c2ecf20Sopenharmony_ci iounmap(wakeupgen_base); 5928c2ecf20Sopenharmony_ci return -ENOMEM; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci /* Clear all IRQ bitmasks at wakeupGen level */ 5968c2ecf20Sopenharmony_ci for (i = 0; i < irq_banks; i++) { 5978c2ecf20Sopenharmony_ci wakeupgen_writel(0, i, CPU0_ID); 5988c2ecf20Sopenharmony_ci if (!soc_is_am43xx()) 5998c2ecf20Sopenharmony_ci wakeupgen_writel(0, i, CPU1_ID); 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* 6038c2ecf20Sopenharmony_ci * FIXME: Add support to set_smp_affinity() once the core 6048c2ecf20Sopenharmony_ci * GIC code has necessary hooks in place. 6058c2ecf20Sopenharmony_ci */ 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Associate all the IRQs to boot CPU like GIC init does. */ 6088c2ecf20Sopenharmony_ci for (i = 0; i < max_irqs; i++) 6098c2ecf20Sopenharmony_ci irq_target_cpu[i] = boot_cpu; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* 6128c2ecf20Sopenharmony_ci * Enables OMAP5 ES2 PM Mode using ES2_PM_MODE in AMBA_IF_MODE 6138c2ecf20Sopenharmony_ci * 0x0: ES1 behavior, CPU cores would enter and exit OFF mode together. 6148c2ecf20Sopenharmony_ci * 0x1: ES2 behavior, CPU cores are allowed to enter/exit OFF mode 6158c2ecf20Sopenharmony_ci * independently. 6168c2ecf20Sopenharmony_ci * This needs to be set one time thanks to always ON domain. 6178c2ecf20Sopenharmony_ci * 6188c2ecf20Sopenharmony_ci * We do not support ES1 behavior anymore. OMAP5 is assumed to be 6198c2ecf20Sopenharmony_ci * ES2.0, and the same is applicable for DRA7. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci if (soc_is_omap54xx() || soc_is_dra7xx()) { 6228c2ecf20Sopenharmony_ci val = __raw_readl(wakeupgen_base + OMAP_AMBA_IF_MODE); 6238c2ecf20Sopenharmony_ci val |= BIT(5); 6248c2ecf20Sopenharmony_ci omap_smc1(OMAP5_MON_AMBA_IF_INDEX, val); 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci irq_hotplug_init(); 6288c2ecf20Sopenharmony_ci irq_pm_init(); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci sar_base = omap4_get_sar_ram_base(); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci return 0; 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(ti_wakeupgen, "ti,omap4-wugen-mpu", wakeupgen_init); 635