15ad36c5fSErik Gilling /* 2938fa349SColin Cross * Copyright (C) 2011 Google, Inc. 35ad36c5fSErik Gilling * 45ad36c5fSErik Gilling * Author: 5938fa349SColin Cross * Colin Cross <ccross@android.com> 65ad36c5fSErik Gilling * 7e307cc89SJoseph Lo * Copyright (C) 2010,2013, NVIDIA Corporation 8460907bcSGary King * 95ad36c5fSErik Gilling * This software is licensed under the terms of the GNU General Public 105ad36c5fSErik Gilling * License version 2, as published by the Free Software Foundation, and 115ad36c5fSErik Gilling * may be copied, distributed, and modified under those terms. 125ad36c5fSErik Gilling * 135ad36c5fSErik Gilling * This program is distributed in the hope that it will be useful, 145ad36c5fSErik Gilling * but WITHOUT ANY WARRANTY; without even the implied warranty of 155ad36c5fSErik Gilling * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 165ad36c5fSErik Gilling * GNU General Public License for more details. 175ad36c5fSErik Gilling * 185ad36c5fSErik Gilling */ 195ad36c5fSErik Gilling 207e8b15dbSJoseph Lo #include <linux/cpu_pm.h> 215ad36c5fSErik Gilling #include <linux/interrupt.h> 225ad36c5fSErik Gilling #include <linux/io.h> 23520f7bd7SRob Herring #include <linux/irqchip/arm-gic.h> 24*a0524accSThierry Reding #include <linux/irq.h> 25*a0524accSThierry Reding #include <linux/kernel.h> 26*a0524accSThierry Reding #include <linux/of_address.h> 27*a0524accSThierry Reding #include <linux/of.h> 28e307cc89SJoseph Lo #include <linux/syscore_ops.h> 295ad36c5fSErik Gilling 305ad36c5fSErik Gilling #include "board.h" 312be39c07SStephen Warren #include "iomap.h" 325ad36c5fSErik Gilling 33d1d8c666SColin Cross #define ICTLR_CPU_IEP_VFIQ 0x08 34d1d8c666SColin Cross #define ICTLR_CPU_IEP_FIR 0x14 35d1d8c666SColin Cross #define ICTLR_CPU_IEP_FIR_SET 0x18 36d1d8c666SColin Cross #define ICTLR_CPU_IEP_FIR_CLR 0x1c 37d1d8c666SColin Cross 38d1d8c666SColin Cross #define ICTLR_CPU_IER 0x20 39d1d8c666SColin Cross #define ICTLR_CPU_IER_SET 0x24 40d1d8c666SColin Cross #define ICTLR_CPU_IER_CLR 0x28 41d1d8c666SColin Cross #define ICTLR_CPU_IEP_CLASS 0x2C 42d1d8c666SColin Cross 43d1d8c666SColin Cross #define ICTLR_COP_IER 0x30 44d1d8c666SColin Cross #define ICTLR_COP_IER_SET 0x34 45d1d8c666SColin Cross #define ICTLR_COP_IER_CLR 0x38 46d1d8c666SColin Cross #define ICTLR_COP_IEP_CLASS 0x3c 47d1d8c666SColin Cross 48d1d8c666SColin Cross #define FIRST_LEGACY_IRQ 32 49e307cc89SJoseph Lo #define TEGRA_MAX_NUM_ICTLRS 5 50d1d8c666SColin Cross 51d4b92fb2SJoseph Lo #define SGI_MASK 0xFFFF 52d4b92fb2SJoseph Lo 53caa4868eSPeter De Schrijver static int num_ictlrs; 54caa4868eSPeter De Schrijver 55d1d8c666SColin Cross static void __iomem *ictlr_reg_base[] = { 56d1d8c666SColin Cross IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE), 57d1d8c666SColin Cross IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE), 58d1d8c666SColin Cross IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE), 59d1d8c666SColin Cross IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE), 60caa4868eSPeter De Schrijver IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE), 61d1d8c666SColin Cross }; 62d1d8c666SColin Cross 63e307cc89SJoseph Lo #ifdef CONFIG_PM_SLEEP 64e307cc89SJoseph Lo static u32 cop_ier[TEGRA_MAX_NUM_ICTLRS]; 65e307cc89SJoseph Lo static u32 cop_iep[TEGRA_MAX_NUM_ICTLRS]; 66e307cc89SJoseph Lo static u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS]; 67e307cc89SJoseph Lo static u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS]; 68e307cc89SJoseph Lo 69e307cc89SJoseph Lo static u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS]; 707e8b15dbSJoseph Lo static void __iomem *tegra_gic_cpu_base; 71e307cc89SJoseph Lo #endif 72e307cc89SJoseph Lo 73d4b92fb2SJoseph Lo bool tegra_pending_sgi(void) 74d4b92fb2SJoseph Lo { 75d4b92fb2SJoseph Lo u32 pending_set; 76d4b92fb2SJoseph Lo void __iomem *distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); 77d4b92fb2SJoseph Lo 78d4b92fb2SJoseph Lo pending_set = readl_relaxed(distbase + GIC_DIST_PENDING_SET); 79d4b92fb2SJoseph Lo 80d4b92fb2SJoseph Lo if (pending_set & SGI_MASK) 81d4b92fb2SJoseph Lo return true; 82d4b92fb2SJoseph Lo 83d4b92fb2SJoseph Lo return false; 84d4b92fb2SJoseph Lo } 85d4b92fb2SJoseph Lo 86d1d8c666SColin Cross static inline void tegra_irq_write_mask(unsigned int irq, unsigned long reg) 87d1d8c666SColin Cross { 88d1d8c666SColin Cross void __iomem *base; 89d1d8c666SColin Cross u32 mask; 90d1d8c666SColin Cross 91d1d8c666SColin Cross BUG_ON(irq < FIRST_LEGACY_IRQ || 92caa4868eSPeter De Schrijver irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32); 93d1d8c666SColin Cross 94d1d8c666SColin Cross base = ictlr_reg_base[(irq - FIRST_LEGACY_IRQ) / 32]; 95d1d8c666SColin Cross mask = BIT((irq - FIRST_LEGACY_IRQ) % 32); 96d1d8c666SColin Cross 97d1d8c666SColin Cross __raw_writel(mask, base + reg); 98d1d8c666SColin Cross } 99d1d8c666SColin Cross 10037337a8dSLennert Buytenhek static void tegra_mask(struct irq_data *d) 101460907bcSGary King { 102d1d8c666SColin Cross if (d->irq < FIRST_LEGACY_IRQ) 103d1d8c666SColin Cross return; 104d1d8c666SColin Cross 105d1d8c666SColin Cross tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_CLR); 106460907bcSGary King } 107460907bcSGary King 10837337a8dSLennert Buytenhek static void tegra_unmask(struct irq_data *d) 109460907bcSGary King { 110d1d8c666SColin Cross if (d->irq < FIRST_LEGACY_IRQ) 111d1d8c666SColin Cross return; 112d1d8c666SColin Cross 113d1d8c666SColin Cross tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_SET); 114460907bcSGary King } 115460907bcSGary King 11626d902c0SColin Cross static void tegra_ack(struct irq_data *d) 11726d902c0SColin Cross { 118d1d8c666SColin Cross if (d->irq < FIRST_LEGACY_IRQ) 119d1d8c666SColin Cross return; 120d1d8c666SColin Cross 121d1d8c666SColin Cross tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR); 12226d902c0SColin Cross } 12326d902c0SColin Cross 1244bd66cfdSColin Cross static void tegra_eoi(struct irq_data *d) 1254bd66cfdSColin Cross { 1264bd66cfdSColin Cross if (d->irq < FIRST_LEGACY_IRQ) 1274bd66cfdSColin Cross return; 1284bd66cfdSColin Cross 1294bd66cfdSColin Cross tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR); 1304bd66cfdSColin Cross } 1314bd66cfdSColin Cross 13226d902c0SColin Cross static int tegra_retrigger(struct irq_data *d) 13326d902c0SColin Cross { 134d1d8c666SColin Cross if (d->irq < FIRST_LEGACY_IRQ) 135938fa349SColin Cross return 0; 136938fa349SColin Cross 137d1d8c666SColin Cross tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_SET); 138d1d8c666SColin Cross 13926d902c0SColin Cross return 1; 14026d902c0SColin Cross } 14126d902c0SColin Cross 142e307cc89SJoseph Lo #ifdef CONFIG_PM_SLEEP 143e307cc89SJoseph Lo static int tegra_set_wake(struct irq_data *d, unsigned int enable) 144e307cc89SJoseph Lo { 145e307cc89SJoseph Lo u32 irq = d->irq; 146e307cc89SJoseph Lo u32 index, mask; 147e307cc89SJoseph Lo 148e307cc89SJoseph Lo if (irq < FIRST_LEGACY_IRQ || 149e307cc89SJoseph Lo irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32) 150e307cc89SJoseph Lo return -EINVAL; 151e307cc89SJoseph Lo 152e307cc89SJoseph Lo index = ((irq - FIRST_LEGACY_IRQ) / 32); 153e307cc89SJoseph Lo mask = BIT((irq - FIRST_LEGACY_IRQ) % 32); 154e307cc89SJoseph Lo if (enable) 155e307cc89SJoseph Lo ictlr_wake_mask[index] |= mask; 156e307cc89SJoseph Lo else 157e307cc89SJoseph Lo ictlr_wake_mask[index] &= ~mask; 158e307cc89SJoseph Lo 159e307cc89SJoseph Lo return 0; 160e307cc89SJoseph Lo } 161e307cc89SJoseph Lo 162e307cc89SJoseph Lo static int tegra_legacy_irq_suspend(void) 163e307cc89SJoseph Lo { 164e307cc89SJoseph Lo unsigned long flags; 165e307cc89SJoseph Lo int i; 166e307cc89SJoseph Lo 167e307cc89SJoseph Lo local_irq_save(flags); 168e307cc89SJoseph Lo for (i = 0; i < num_ictlrs; i++) { 169e307cc89SJoseph Lo void __iomem *ictlr = ictlr_reg_base[i]; 170e307cc89SJoseph Lo /* Save interrupt state */ 171e307cc89SJoseph Lo cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER); 172e307cc89SJoseph Lo cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS); 173e307cc89SJoseph Lo cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER); 174e307cc89SJoseph Lo cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS); 175e307cc89SJoseph Lo 176e307cc89SJoseph Lo /* Disable COP interrupts */ 177e307cc89SJoseph Lo writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); 178e307cc89SJoseph Lo 179e307cc89SJoseph Lo /* Disable CPU interrupts */ 180e307cc89SJoseph Lo writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); 181e307cc89SJoseph Lo 182e307cc89SJoseph Lo /* Enable the wakeup sources of ictlr */ 183e307cc89SJoseph Lo writel_relaxed(ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET); 184e307cc89SJoseph Lo } 185e307cc89SJoseph Lo local_irq_restore(flags); 186e307cc89SJoseph Lo 187e307cc89SJoseph Lo return 0; 188e307cc89SJoseph Lo } 189e307cc89SJoseph Lo 190e307cc89SJoseph Lo static void tegra_legacy_irq_resume(void) 191e307cc89SJoseph Lo { 192e307cc89SJoseph Lo unsigned long flags; 193e307cc89SJoseph Lo int i; 194e307cc89SJoseph Lo 195e307cc89SJoseph Lo local_irq_save(flags); 196e307cc89SJoseph Lo for (i = 0; i < num_ictlrs; i++) { 197e307cc89SJoseph Lo void __iomem *ictlr = ictlr_reg_base[i]; 198e307cc89SJoseph Lo writel_relaxed(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS); 199e307cc89SJoseph Lo writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); 200e307cc89SJoseph Lo writel_relaxed(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET); 201e307cc89SJoseph Lo writel_relaxed(cop_iep[i], ictlr + ICTLR_COP_IEP_CLASS); 202e307cc89SJoseph Lo writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); 203e307cc89SJoseph Lo writel_relaxed(cop_ier[i], ictlr + ICTLR_COP_IER_SET); 204e307cc89SJoseph Lo } 205e307cc89SJoseph Lo local_irq_restore(flags); 206e307cc89SJoseph Lo } 207e307cc89SJoseph Lo 208e307cc89SJoseph Lo static struct syscore_ops tegra_legacy_irq_syscore_ops = { 209e307cc89SJoseph Lo .suspend = tegra_legacy_irq_suspend, 210e307cc89SJoseph Lo .resume = tegra_legacy_irq_resume, 211e307cc89SJoseph Lo }; 212e307cc89SJoseph Lo 213e307cc89SJoseph Lo int tegra_legacy_irq_syscore_init(void) 214e307cc89SJoseph Lo { 215e307cc89SJoseph Lo register_syscore_ops(&tegra_legacy_irq_syscore_ops); 216e307cc89SJoseph Lo 217e307cc89SJoseph Lo return 0; 218e307cc89SJoseph Lo } 2197e8b15dbSJoseph Lo 2207e8b15dbSJoseph Lo static int tegra_gic_notifier(struct notifier_block *self, 2217e8b15dbSJoseph Lo unsigned long cmd, void *v) 2227e8b15dbSJoseph Lo { 2237e8b15dbSJoseph Lo switch (cmd) { 2247e8b15dbSJoseph Lo case CPU_PM_ENTER: 2257e8b15dbSJoseph Lo writel_relaxed(0x1E0, tegra_gic_cpu_base + GIC_CPU_CTRL); 2267e8b15dbSJoseph Lo break; 2277e8b15dbSJoseph Lo } 2287e8b15dbSJoseph Lo 2297e8b15dbSJoseph Lo return NOTIFY_OK; 2307e8b15dbSJoseph Lo } 2317e8b15dbSJoseph Lo 2327e8b15dbSJoseph Lo static struct notifier_block tegra_gic_notifier_block = { 2337e8b15dbSJoseph Lo .notifier_call = tegra_gic_notifier, 2347e8b15dbSJoseph Lo }; 2357e8b15dbSJoseph Lo 2367e8b15dbSJoseph Lo static const struct of_device_id tegra114_dt_gic_match[] __initconst = { 2377e8b15dbSJoseph Lo { .compatible = "arm,cortex-a15-gic" }, 2387e8b15dbSJoseph Lo { } 2397e8b15dbSJoseph Lo }; 2407e8b15dbSJoseph Lo 2417e8b15dbSJoseph Lo static void tegra114_gic_cpu_pm_registration(void) 2427e8b15dbSJoseph Lo { 2437e8b15dbSJoseph Lo struct device_node *dn; 2447e8b15dbSJoseph Lo 2457e8b15dbSJoseph Lo dn = of_find_matching_node(NULL, tegra114_dt_gic_match); 2467e8b15dbSJoseph Lo if (!dn) 2477e8b15dbSJoseph Lo return; 2487e8b15dbSJoseph Lo 2497e8b15dbSJoseph Lo tegra_gic_cpu_base = of_iomap(dn, 1); 2507e8b15dbSJoseph Lo 2517e8b15dbSJoseph Lo cpu_pm_register_notifier(&tegra_gic_notifier_block); 2527e8b15dbSJoseph Lo } 253e307cc89SJoseph Lo #else 254e307cc89SJoseph Lo #define tegra_set_wake NULL 2557e8b15dbSJoseph Lo static void tegra114_gic_cpu_pm_registration(void) { } 256e307cc89SJoseph Lo #endif 257e307cc89SJoseph Lo 2585ad36c5fSErik Gilling void __init tegra_init_irq(void) 2595ad36c5fSErik Gilling { 260d1d8c666SColin Cross int i; 261caa4868eSPeter De Schrijver void __iomem *distbase; 262d1d8c666SColin Cross 263caa4868eSPeter De Schrijver distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); 264caa4868eSPeter De Schrijver num_ictlrs = readl_relaxed(distbase + GIC_DIST_CTR) & 0x1f; 265caa4868eSPeter De Schrijver 266caa4868eSPeter De Schrijver if (num_ictlrs > ARRAY_SIZE(ictlr_reg_base)) { 267caa4868eSPeter De Schrijver WARN(1, "Too many (%d) interrupt controllers found. Maximum is %d.", 268caa4868eSPeter De Schrijver num_ictlrs, ARRAY_SIZE(ictlr_reg_base)); 269caa4868eSPeter De Schrijver num_ictlrs = ARRAY_SIZE(ictlr_reg_base); 270caa4868eSPeter De Schrijver } 271caa4868eSPeter De Schrijver 272caa4868eSPeter De Schrijver for (i = 0; i < num_ictlrs; i++) { 273d1d8c666SColin Cross void __iomem *ictlr = ictlr_reg_base[i]; 274d1d8c666SColin Cross writel(~0, ictlr + ICTLR_CPU_IER_CLR); 275d1d8c666SColin Cross writel(0, ictlr + ICTLR_CPU_IEP_CLASS); 276d1d8c666SColin Cross } 277460907bcSGary King 278938fa349SColin Cross gic_arch_extn.irq_ack = tegra_ack; 2794bd66cfdSColin Cross gic_arch_extn.irq_eoi = tegra_eoi; 280938fa349SColin Cross gic_arch_extn.irq_mask = tegra_mask; 281938fa349SColin Cross gic_arch_extn.irq_unmask = tegra_unmask; 282938fa349SColin Cross gic_arch_extn.irq_retrigger = tegra_retrigger; 283e307cc89SJoseph Lo gic_arch_extn.irq_set_wake = tegra_set_wake; 284e307cc89SJoseph Lo gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND; 285938fa349SColin Cross 2860d4f7479Spdeschrijver@nvidia.com /* 2870d4f7479Spdeschrijver@nvidia.com * Check if there is a devicetree present, since the GIC will be 2880d4f7479Spdeschrijver@nvidia.com * initialized elsewhere under DT. 2890d4f7479Spdeschrijver@nvidia.com */ 2900d4f7479Spdeschrijver@nvidia.com if (!of_have_populated_dt()) 291caa4868eSPeter De Schrijver gic_init(0, 29, distbase, 292b580b899SRussell King IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); 2937e8b15dbSJoseph Lo 2947e8b15dbSJoseph Lo tegra114_gic_cpu_pm_registration(); 295460907bcSGary King } 296