/* * OMAP WakeupGen Source file * * OMAP WakeupGen is the interrupt controller extension used along * with ARM GIC to wake the CPU out from low power states on * external interrupts. It is responsible for generating wakeup * event from the incoming interrupts and enable bits. It is * implemented in MPU always ON power domain. During normal operation, * WakeupGen delivers external interrupts directly to the GIC. * * Copyright (C) 2011 Texas Instruments, Inc. * Santosh Shilimkar * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "omap4-sar-layout.h" #include "common.h" #include "pm.h" #include "clockdomain.h" #define MAX_NR_BANKS 5 #define MAX_IRQS 160 #define WKG_MASK_ALL 0x00000000 #define WKG_UNMASK_ALL 0xffffffff #define CPU_ENA_OFFSET 0x400 #define CPU0_ID 0x0 #define CPU1_ID 0x1 #define OMAP4_NR_BANKS 4 #define OMAP4_NR_IRQS 128 #define GIC_MASK_ALL 0x0 #define GIC_ISR_NON_SECURE 0xffffffff #define SPI_ENABLE_SET_OFFSET 0x04 #define PPI_PRI_OFFSET 0x1c #define SPI_PRI_OFFSET 0x20 #define SPI_TARGET_OFFSET 0x20 #define SPI_CONFIG_OFFSET 0x20 static void __iomem *wakeupgen_base; static void __iomem *sar_base; static DEFINE_PER_CPU(u32 [MAX_NR_BANKS], irqmasks); static DEFINE_SPINLOCK(wakeupgen_lock); static unsigned int irq_target_cpu[NR_IRQS]; static unsigned int irq_banks = MAX_NR_BANKS; static unsigned int max_irqs = MAX_IRQS; static unsigned int secure_api_index; static struct powerdomain *mpuss_pd; static struct clockdomain *l4_secure_clkdm; static struct omap_hwmod *l3_main_3_oh; /* * Static helper functions. */ static inline u32 wakeupgen_readl(u8 idx, u32 cpu) { return __raw_readl(wakeupgen_base + OMAP_WKG_ENB_A_0 + (cpu * CPU_ENA_OFFSET) + (idx * 4)); } static inline void wakeupgen_writel(u32 val, u8 idx, u32 cpu) { __raw_writel(val, wakeupgen_base + OMAP_WKG_ENB_A_0 + (cpu * CPU_ENA_OFFSET) + (idx * 4)); } static inline void sar_writel(u32 val, u32 offset, u8 idx) { __raw_writel(val, sar_base + offset + (idx * 4)); } static void _wakeupgen_set_all(unsigned int cpu, unsigned int reg) { u8 i; for (i = 0; i < irq_banks; i++) wakeupgen_writel(reg, i, cpu); } static inline int _wakeupgen_get_irq_info(u32 irq, u32 *bit_posn, u8 *reg_index) { unsigned int spi_irq; /* * PPIs and SGIs are not supported. */ if (irq < OMAP44XX_IRQ_GIC_START) return -EINVAL; /* * Subtract the GIC offset. */ spi_irq = irq - OMAP44XX_IRQ_GIC_START; if (spi_irq > max_irqs) { pr_err("omap wakeupGen: Invalid IRQ%d\n", irq); return -EINVAL; } /* * Each WakeupGen register controls 32 interrupt. * i.e. 1 bit per SPI IRQ */ *reg_index = spi_irq >> 5; *bit_posn = spi_irq %= 32; return 0; } static void _wakeupgen_clear(unsigned int irq, unsigned int cpu) { u32 val, bit_number; u8 i; if (_wakeupgen_get_irq_info(irq, &bit_number, &i)) return; val = wakeupgen_readl(i, cpu); val &= ~BIT(bit_number); wakeupgen_writel(val, i, cpu); } static void _wakeupgen_set(unsigned int irq, unsigned int cpu) { u32 val, bit_number; u8 i; if (_wakeupgen_get_irq_info(irq, &bit_number, &i)) return; val = wakeupgen_readl(i, cpu); val |= BIT(bit_number); wakeupgen_writel(val, i, cpu); } static void _wakeupgen_save_masks(unsigned int cpu) { u8 i; for (i = 0; i < irq_banks; i++) per_cpu(irqmasks, cpu)[i] = wakeupgen_readl(i, cpu); } static void _wakeupgen_restore_masks(unsigned int cpu) { u8 i; for (i = 0; i < irq_banks; i++) wakeupgen_writel(per_cpu(irqmasks, cpu)[i], i, cpu); } /* * Architecture specific Mask extension */ static void wakeupgen_mask(struct irq_data *d) { unsigned long flags; spin_lock_irqsave(&wakeupgen_lock, flags); _wakeupgen_clear(d->irq, irq_target_cpu[d->irq]); spin_unlock_irqrestore(&wakeupgen_lock, flags); } /* * Architecture specific Unmask extension */ static void wakeupgen_unmask(struct irq_data *d) { unsigned long flags; spin_lock_irqsave(&wakeupgen_lock, flags); _wakeupgen_set(d->irq, irq_target_cpu[d->irq]); spin_unlock_irqrestore(&wakeupgen_lock, flags); } /* * Mask or unmask all interrupts on given CPU. * 0 = Mask all interrupts on the 'cpu' * 1 = Unmask all interrupts on the 'cpu' * Ensure that the initial mask is maintained. This is faster than * iterating through GIC registers to arrive at the correct masks. */ static void wakeupgen_irqmask_all(unsigned int cpu, unsigned int set) { unsigned long flags; spin_lock_irqsave(&wakeupgen_lock, flags); if (set) { _wakeupgen_save_masks(cpu); _wakeupgen_set_all(cpu, WKG_MASK_ALL); } else { _wakeupgen_set_all(cpu, WKG_UNMASK_ALL); _wakeupgen_restore_masks(cpu); } spin_unlock_irqrestore(&wakeupgen_lock, flags); } #ifdef CONFIG_CPU_PM #if 0 /* currently unused, avoid warnings */ /* * Save GIC context in SAR RAM. Restore is done by ROM code * GIC is lost only when MPU hits OSWR or OFF. It consists * of a distributor and a per-CPU interface module. The GIC * save restore is optimised to save only necessary registers. */ static void gic_save_context(void) { u8 i; u32 val; /* * Interrupt Clear Enable registers are inverse of set enable * and hence not needed to be saved. ROM code programs it * based on Set Enable register values. */ /* Save CPU 0 Interrupt Set Enable register */ val = gic_readl(GIC_DIST_ENABLE_SET, 0); sar_writel(val, ICDISER_CPU0_OFFSET, 0); /* Disable interrupts on CPU1 */ sar_writel(GIC_MASK_ALL, ICDISER_CPU1_OFFSET, 0); /* Save all SPI Set Enable register */ for (i = 0; i < max_spi_reg; i++) { val = gic_readl(GIC_DIST_ENABLE_SET + SPI_ENABLE_SET_OFFSET, i); sar_writel(val, ICDISER_SPI_OFFSET, i); } /* * Interrupt Priority Registers * Secure sw accesses, last 5 bits of the 8 bits (bit[7:3] are used) * Non-Secure sw accesses, last 4 bits (i.e. bits[7:4] are used) * But the Secure Bits[7:3] are shifted by 1 in Non-Secure access. * Secure (bits[7:3] << 1)== Non Secure bits[7:4] * Hence right shift the value by 1 while saving the priority */ /* Save SGI priority registers (Software Generated Interrupt) */ for (i = 0; i < 4; i++) { val = gic_readl(GIC_DIST_PRI, i); /* Save the priority bits of the Interrupts */ sar_writel(val >> 0x1, ICDIPR_SFI_CPU0_OFFSET, i); /* Disable the interrupts on CPU1 */ sar_writel(GIC_MASK_ALL, ICDIPR_SFI_CPU1_OFFSET, i); } /* Save PPI priority registers (Private Peripheral Intterupts) */ val = gic_readl(GIC_DIST_PRI + PPI_PRI_OFFSET, 0); sar_writel(val >> 0x1, ICDIPR_PPI_CPU0_OFFSET, 0); sar_writel(GIC_MASK_ALL, ICDIPR_PPI_CPU1_OFFSET, 0); /* SPI priority registers - 4 interrupts/register */ for (i = 0; i < (max_spi_irq / 4); i++) { val = gic_readl((GIC_DIST_PRI + SPI_PRI_OFFSET), i); sar_writel(val >> 0x1, ICDIPR_SPI_OFFSET, i); } /* SPI Interrupt Target registers - 4 interrupts/register */ for (i = 0; i < (max_spi_irq / 4); i++) { val = gic_readl((GIC_DIST_TARGET + SPI_TARGET_OFFSET), i); sar_writel(val, ICDIPTR_SPI_OFFSET, i); } /* SPI Interrupt Congigeration eegisters- 16 interrupts/register */ for (i = 0; i < (max_spi_irq / 16); i++) { val = gic_readl((GIC_DIST_CONFIG + SPI_CONFIG_OFFSET), i); sar_writel(val, ICDICFR_OFFSET, i); } /* Set the Backup Bit Mask status for GIC */ val = __raw_readl(sar_base + SAR_BACKUP_STATUS_OFFSET); val |= (SAR_BACKUP_STATUS_GIC_CPU0 | SAR_BACKUP_STATUS_GIC_CPU1); __raw_writel(val, sar_base + SAR_BACKUP_STATUS_OFFSET); } #endif static inline void omap4_wakeupgen_save_context(void) { u32 i, val; for (i = 0; i < irq_banks; i++) { /* Save the CPUx interrupt mask for IRQ 0 to 127 */ val = wakeupgen_readl(i, 0); sar_writel(val, WAKEUPGENENB_OFFSET_CPU0, i); val = wakeupgen_readl(i, 1); sar_writel(val, WAKEUPGENENB_OFFSET_CPU1, i); /* * Disable the secure interrupts for CPUx. The restore * code blindly restores secure and non-secure interrupt * masks from SAR RAM. Secure interrupts are not suppose * to be enabled from HLOS. So overwrite the SAR location * so that the secure interrupt remains disabled. */ sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU0, i); sar_writel(0x0, WAKEUPGENENB_SECURE_OFFSET_CPU1, i); } /* Save AuxBoot* registers */ val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); __raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET); val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); __raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET); /* Save SyncReq generation logic */ val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); __raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET); val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); __raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET); /* Save SyncReq generation logic */ val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_MASK); __raw_writel(val, sar_base + PTMSYNCREQ_MASK_OFFSET); val = __raw_readl(wakeupgen_base + OMAP_PTMSYNCREQ_EN); __raw_writel(val, sar_base + PTMSYNCREQ_EN_OFFSET); /* Set the Backup Bit Mask status */ val = __raw_readl(sar_base + SAR_BACKUP_STATUS_OFFSET); val |= SAR_BACKUP_STATUS_WAKEUPGEN; __raw_writel(val, sar_base + SAR_BACKUP_STATUS_OFFSET); } static inline void omap5_wakeupgen_save_context(void) { u32 i, val; for (i = 0; i < irq_banks; i++) { /* Save the CPUx interrupt mask for IRQ 0 to 159 */ val = wakeupgen_readl(i, 0); sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU0, i); val = wakeupgen_readl(i, 1); sar_writel(val, OMAP5_WAKEUPGENENB_OFFSET_CPU1, i); sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU0, i); sar_writel(0x0, OMAP5_WAKEUPGENENB_SECURE_OFFSET_CPU1, i); } /* Save AuxBoot* registers */ val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); __raw_writel(val, sar_base + OMAP5_AUXCOREBOOT0_OFFSET); val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0); __raw_writel(val, sar_base + OMAP5_AUXCOREBOOT1_OFFSET); /* Set the Backup Bit Mask status */ val = __raw_readl(sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET); val |= SAR_BACKUP_STATUS_WAKEUPGEN; __raw_writel(val, sar_base + OMAP5_SAR_BACKUP_STATUS_OFFSET); } /* * Save WakeupGen interrupt context in SAR BANK3. Restore is done by * ROM code. WakeupGen IP is integrated along with GIC to manage the * interrupt wakeups from CPU low power states. It manages * masking/unmasking of Shared peripheral interrupts(SPI). So the * interrupt enable/disable control should be in sync and consistent * at WakeupGen and GIC so that interrupts are not lost. */ static void irq_save_context(void) { if (!sar_base) sar_base = omap4_get_sar_ram_base(); if (cpu_is_omap54xx()) omap5_wakeupgen_save_context(); else omap4_wakeupgen_save_context(); } /* * Clear WakeupGen SAR backup status. */ static void irq_sar_clear(void) { u32 val; u32 offset = SAR_BACKUP_STATUS_OFFSET; if (!sar_base) sar_base = omap4_get_sar_ram_base(); if (cpu_is_omap54xx()) offset = OMAP5_SAR_BACKUP_STATUS_OFFSET; val = __raw_readl(sar_base + offset); val &= ~SAR_BACKUP_STATUS_WAKEUPGEN; __raw_writel(val, sar_base + offset); } /* * Save GIC and Wakeupgen interrupt context using secure API * for HS/EMU devices. */ static void irq_save_secure_gic(void) { u32 ret; ret = omap_secure_dispatcher(secure_api_index, FLAG_START_CRITICAL, 0, 0, 0, 0, 0); if (ret != API_HAL_RET_VALUE_OK) pr_err("GIC and Wakeupgen context save failed\n"); } #endif static void save_secure_ram(void) { u32 ret; ret = omap_secure_dispatcher(OMAP4_HAL_SAVESECURERAM_INDEX, FLAG_START_CRITICAL, 1, omap_secure_ram_mempool_base(), 0, 0, 0); if (ret != API_HAL_RET_VALUE_OK) pr_err("Secure ram context save failed\n"); } static void save_secure_all(void) { u32 ret; omap_hwmod_enable(l3_main_3_oh); clkdm_wakeup(l4_secure_clkdm); ret = omap_secure_dispatcher(OMAP4_HAL_SAVEALL_INDEX, FLAG_START_CRITICAL, 1, omap_secure_ram_mempool_base(), 0, 0, 0); clkdm_allow_idle(l4_secure_clkdm); omap_hwmod_idle(l3_main_3_oh); if (ret != API_HAL_RET_VALUE_OK) pr_err("Secure all context save failed\n"); } static void irq_save_secure_context(void) { if (omap4_device_next_state_off()) { save_secure_all(); } else if (pwrdm_read_next_pwrst(mpuss_pd) == PWRDM_POWER_OFF) { irq_save_secure_gic(); save_secure_ram(); } else { irq_save_secure_gic(); } } #ifdef CONFIG_HOTPLUG_CPU static int __cpuinit irq_cpu_hotplug_notify(struct notifier_block *self, unsigned long action, void *hcpu) { unsigned int cpu = (unsigned int)hcpu; switch (action) { case CPU_ONLINE: wakeupgen_irqmask_all(cpu, 0); break; case CPU_DEAD: wakeupgen_irqmask_all(cpu, 1); break; } return NOTIFY_OK; } static struct notifier_block __refdata irq_hotplug_notifier = { .notifier_call = irq_cpu_hotplug_notify, }; static void __init irq_hotplug_init(void) { register_hotcpu_notifier(&irq_hotplug_notifier); } #else static void __init irq_hotplug_init(void) {} #endif #ifdef CONFIG_CPU_PM static int irq_notifier(struct notifier_block *self, unsigned long cmd, void *v) { switch (cmd) { case CPU_CLUSTER_PM_ENTER: if (omap_type() == OMAP2_DEVICE_TYPE_GP) irq_save_context(); else irq_save_secure_context(); break; case CPU_CLUSTER_PM_EXIT: if (omap_type() == OMAP2_DEVICE_TYPE_GP) irq_sar_clear(); break; } return NOTIFY_OK; } static struct notifier_block irq_notifier_block = { .notifier_call = irq_notifier, }; static void __init irq_pm_init(void) { cpu_pm_register_notifier(&irq_notifier_block); } #else static void __init irq_pm_init(void) {} #endif void __iomem *omap_get_wakeupgen_base(void) { return wakeupgen_base; } /* * Initialise the wakeupgen module. */ int __init omap_wakeupgen_init(void) { int i; unsigned int boot_cpu = smp_processor_id(); int max_spi_reg; /* Not supported on OMAP4 ES1.0 silicon */ if (omap_rev() == OMAP4430_REV_ES1_0) { WARN(1, "WakeupGen: Not supported on OMAP4430 ES1.0\n"); return -EPERM; } /* Static mapping, never released */ wakeupgen_base = ioremap(OMAP_WKUPGEN_BASE, SZ_4K); if (WARN_ON(!wakeupgen_base)) return -ENOMEM; if (cpu_is_omap44xx()) { irq_banks = OMAP4_NR_BANKS; max_irqs = OMAP4_NR_IRQS; secure_api_index = OMAP4_HAL_SAVEGIC_INDEX; } else if (cpu_is_omap54xx()) { secure_api_index = OMAP5_HAL_SAVEGIC_INDEX; } /* Clear all IRQ bitmasks at wakeupGen level */ for (i = 0; i < irq_banks; i++) { wakeupgen_writel(0, i, CPU0_ID); wakeupgen_writel(0, i, CPU1_ID); } /* * Override GIC architecture specific functions to add * OMAP WakeupGen interrupt controller along with GIC */ gic_arch_extn.irq_mask = wakeupgen_mask; gic_arch_extn.irq_unmask = wakeupgen_unmask; gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE; /* * FIXME: Add support to set_smp_affinity() once the core * GIC code has necessary hooks in place. */ /* Associate all the IRQs to boot CPU like GIC init does. */ for (i = 0; i < max_irqs; i++) irq_target_cpu[i] = boot_cpu; /* * Find out how many interrupts are supported. * OMAP4 supports max of 128 SPIs where as GIC can support * up to 1020 interrupt sources. On OMAP4, maximum SPIs are * fused in DIST_CTR bit-fields as 128. Hence the code is safe * from reserved register writes since its well within 1020. */ max_spi_reg = gic_readl(GIC_DIST_CTR, 0) & 0x1f; if (omap_type() == OMAP2_DEVICE_TYPE_GP) { sar_base = ioremap(OMAP44XX_SAR_RAM_BASE, SZ_16K); /* Clean GIC SAR area */ for (i = SAR_BACKUP_STATUS_OFFSET; i < WAKEUPGENENB_OFFSET_CPU0; i += 4) sar_writel(0, i, 0); sar_writel(GIC_ISR_NON_SECURE, ICDISR_CPU0_OFFSET, 0); sar_writel(GIC_ISR_NON_SECURE, ICDISR_CPU1_OFFSET, 0); for (i = 0; i < max_spi_reg; i++) sar_writel(GIC_ISR_NON_SECURE, ICDISR_SPI_OFFSET, i); /* * Set CPU0 GIC backup flag permanently for omap4460, * this is needed because of the ROM code bug that breaks * GIC during wakeup from device off */ if (cpu_is_omap446x()) __raw_writel(SAR_BACKUP_STATUS_GIC_CPU0, sar_base + SAR_BACKUP_STATUS_OFFSET); iounmap(sar_base); sar_base = NULL; } else { l3_main_3_oh = omap_hwmod_lookup("l3_main_3"); if (!l3_main_3_oh) pr_err("%s: failed to get l3_main_3_oh\n", __func__); l4_secure_clkdm = clkdm_lookup("l4_secure_clkdm"); if (!l4_secure_clkdm) pr_err("%s: failed to get l4_secure_clkdm\n", __func__); } irq_hotplug_init(); irq_pm_init(); mpuss_pd = pwrdm_lookup("mpu_pwrdm"); if (!mpuss_pd) { pr_err("wakeupgen: unable to get mpu_pwrdm\n"); return -EINVAL; } return 0; }