1 /* 2 * Copyright 2011-2013 Freescale Semiconductor, Inc. 3 * Copyright 2011 Linaro Ltd. 4 * 5 * The code contained herein is licensed under the GNU General Public 6 * License. You may obtain a copy of the GNU General Public License 7 * Version 2 or later at the following locations: 8 * 9 * http://www.opensource.org/licenses/gpl-license.html 10 * http://www.gnu.org/copyleft/gpl.html 11 */ 12 13 #include <linux/io.h> 14 #include <linux/irq.h> 15 #include <linux/of.h> 16 #include <linux/of_address.h> 17 #include <linux/of_irq.h> 18 #include <linux/irqchip/arm-gic.h> 19 #include "common.h" 20 21 #define GPC_IMR1 0x008 22 #define GPC_PGC_CPU_PDN 0x2a0 23 #define GPC_PGC_CPU_PUPSCR 0x2a4 24 #define GPC_PGC_CPU_PDNSCR 0x2a8 25 #define GPC_PGC_SW2ISO_SHIFT 0x8 26 #define GPC_PGC_SW_SHIFT 0x0 27 28 #define IMR_NUM 4 29 30 static void __iomem *gpc_base; 31 static u32 gpc_wake_irqs[IMR_NUM]; 32 static u32 gpc_saved_imrs[IMR_NUM]; 33 34 void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw) 35 { 36 writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) | 37 (sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PUPSCR); 38 } 39 40 void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw) 41 { 42 writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) | 43 (sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PDNSCR); 44 } 45 46 void imx_gpc_set_arm_power_in_lpm(bool power_off) 47 { 48 writel_relaxed(power_off, gpc_base + GPC_PGC_CPU_PDN); 49 } 50 51 void imx_gpc_pre_suspend(bool arm_power_off) 52 { 53 void __iomem *reg_imr1 = gpc_base + GPC_IMR1; 54 int i; 55 56 /* Tell GPC to power off ARM core when suspend */ 57 if (arm_power_off) 58 imx_gpc_set_arm_power_in_lpm(arm_power_off); 59 60 for (i = 0; i < IMR_NUM; i++) { 61 gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4); 62 writel_relaxed(~gpc_wake_irqs[i], reg_imr1 + i * 4); 63 } 64 } 65 66 void imx_gpc_post_resume(void) 67 { 68 void __iomem *reg_imr1 = gpc_base + GPC_IMR1; 69 int i; 70 71 /* Keep ARM core powered on for other low-power modes */ 72 imx_gpc_set_arm_power_in_lpm(false); 73 74 for (i = 0; i < IMR_NUM; i++) 75 writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); 76 } 77 78 static int imx_gpc_irq_set_wake(struct irq_data *d, unsigned int on) 79 { 80 unsigned int idx = d->hwirq / 32 - 1; 81 u32 mask; 82 83 /* Sanity check for SPI irq */ 84 if (d->hwirq < 32) 85 return -EINVAL; 86 87 mask = 1 << d->hwirq % 32; 88 gpc_wake_irqs[idx] = on ? gpc_wake_irqs[idx] | mask : 89 gpc_wake_irqs[idx] & ~mask; 90 91 return 0; 92 } 93 94 void imx_gpc_mask_all(void) 95 { 96 void __iomem *reg_imr1 = gpc_base + GPC_IMR1; 97 int i; 98 99 for (i = 0; i < IMR_NUM; i++) { 100 gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4); 101 writel_relaxed(~0, reg_imr1 + i * 4); 102 } 103 104 } 105 106 void imx_gpc_restore_all(void) 107 { 108 void __iomem *reg_imr1 = gpc_base + GPC_IMR1; 109 int i; 110 111 for (i = 0; i < IMR_NUM; i++) 112 writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4); 113 } 114 115 void imx_gpc_hwirq_unmask(unsigned int hwirq) 116 { 117 void __iomem *reg; 118 u32 val; 119 120 reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4; 121 val = readl_relaxed(reg); 122 val &= ~(1 << hwirq % 32); 123 writel_relaxed(val, reg); 124 } 125 126 void imx_gpc_hwirq_mask(unsigned int hwirq) 127 { 128 void __iomem *reg; 129 u32 val; 130 131 reg = gpc_base + GPC_IMR1 + (hwirq / 32 - 1) * 4; 132 val = readl_relaxed(reg); 133 val |= 1 << (hwirq % 32); 134 writel_relaxed(val, reg); 135 } 136 137 static void imx_gpc_irq_unmask(struct irq_data *d) 138 { 139 /* Sanity check for SPI irq */ 140 if (d->hwirq < 32) 141 return; 142 143 imx_gpc_hwirq_unmask(d->hwirq); 144 } 145 146 static void imx_gpc_irq_mask(struct irq_data *d) 147 { 148 /* Sanity check for SPI irq */ 149 if (d->hwirq < 32) 150 return; 151 152 imx_gpc_hwirq_mask(d->hwirq); 153 } 154 155 void __init imx_gpc_init(void) 156 { 157 struct device_node *np; 158 int i; 159 160 np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc"); 161 gpc_base = of_iomap(np, 0); 162 WARN_ON(!gpc_base); 163 164 /* Initially mask all interrupts */ 165 for (i = 0; i < IMR_NUM; i++) 166 writel_relaxed(~0, gpc_base + GPC_IMR1 + i * 4); 167 168 /* Register GPC as the secondary interrupt controller behind GIC */ 169 gic_arch_extn.irq_mask = imx_gpc_irq_mask; 170 gic_arch_extn.irq_unmask = imx_gpc_irq_unmask; 171 gic_arch_extn.irq_set_wake = imx_gpc_irq_set_wake; 172 } 173