Commit | Line | Data |
---|---|---|
5ad36c5f | 1 | /* |
938fa349 | 2 | * Copyright (C) 2011 Google, Inc. |
5ad36c5f EG |
3 | * |
4 | * Author: | |
938fa349 | 5 | * Colin Cross <ccross@android.com> |
5ad36c5f | 6 | * |
e307cc89 | 7 | * Copyright (C) 2010,2013, NVIDIA Corporation |
460907bc | 8 | * |
5ad36c5f EG |
9 | * This software is licensed under the terms of the GNU General Public |
10 | * License version 2, as published by the Free Software Foundation, and | |
11 | * may be copied, distributed, and modified under those terms. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | */ | |
19 | ||
20 | #include <linux/kernel.h> | |
7e8b15db | 21 | #include <linux/cpu_pm.h> |
5ad36c5f EG |
22 | #include <linux/interrupt.h> |
23 | #include <linux/irq.h> | |
24 | #include <linux/io.h> | |
0d4f7479 | 25 | #include <linux/of.h> |
7e8b15db | 26 | #include <linux/of_address.h> |
520f7bd7 | 27 | #include <linux/irqchip/arm-gic.h> |
e307cc89 | 28 | #include <linux/syscore_ops.h> |
5ad36c5f | 29 | |
5ad36c5f | 30 | #include "board.h" |
2be39c07 | 31 | #include "iomap.h" |
5ad36c5f | 32 | |
d1d8c666 CC |
33 | #define ICTLR_CPU_IEP_VFIQ 0x08 |
34 | #define ICTLR_CPU_IEP_FIR 0x14 | |
35 | #define ICTLR_CPU_IEP_FIR_SET 0x18 | |
36 | #define ICTLR_CPU_IEP_FIR_CLR 0x1c | |
37 | ||
38 | #define ICTLR_CPU_IER 0x20 | |
39 | #define ICTLR_CPU_IER_SET 0x24 | |
40 | #define ICTLR_CPU_IER_CLR 0x28 | |
41 | #define ICTLR_CPU_IEP_CLASS 0x2C | |
42 | ||
43 | #define ICTLR_COP_IER 0x30 | |
44 | #define ICTLR_COP_IER_SET 0x34 | |
45 | #define ICTLR_COP_IER_CLR 0x38 | |
46 | #define ICTLR_COP_IEP_CLASS 0x3c | |
47 | ||
d1d8c666 | 48 | #define FIRST_LEGACY_IRQ 32 |
e307cc89 | 49 | #define TEGRA_MAX_NUM_ICTLRS 5 |
d1d8c666 | 50 | |
d4b92fb2 JL |
51 | #define SGI_MASK 0xFFFF |
52 | ||
caa4868e PDS |
53 | static int num_ictlrs; |
54 | ||
d1d8c666 CC |
55 | static void __iomem *ictlr_reg_base[] = { |
56 | IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE), | |
57 | IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE), | |
58 | IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE), | |
59 | IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE), | |
caa4868e | 60 | IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE), |
d1d8c666 CC |
61 | }; |
62 | ||
e307cc89 JL |
63 | #ifdef CONFIG_PM_SLEEP |
64 | static u32 cop_ier[TEGRA_MAX_NUM_ICTLRS]; | |
65 | static u32 cop_iep[TEGRA_MAX_NUM_ICTLRS]; | |
66 | static u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS]; | |
67 | static u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS]; | |
68 | ||
69 | static u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS]; | |
7e8b15db | 70 | static void __iomem *tegra_gic_cpu_base; |
e307cc89 JL |
71 | #endif |
72 | ||
d4b92fb2 JL |
73 | bool tegra_pending_sgi(void) |
74 | { | |
75 | u32 pending_set; | |
76 | void __iomem *distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); | |
77 | ||
78 | pending_set = readl_relaxed(distbase + GIC_DIST_PENDING_SET); | |
79 | ||
80 | if (pending_set & SGI_MASK) | |
81 | return true; | |
82 | ||
83 | return false; | |
84 | } | |
85 | ||
d1d8c666 CC |
86 | static inline void tegra_irq_write_mask(unsigned int irq, unsigned long reg) |
87 | { | |
88 | void __iomem *base; | |
89 | u32 mask; | |
90 | ||
91 | BUG_ON(irq < FIRST_LEGACY_IRQ || | |
caa4868e | 92 | irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32); |
d1d8c666 CC |
93 | |
94 | base = ictlr_reg_base[(irq - FIRST_LEGACY_IRQ) / 32]; | |
95 | mask = BIT((irq - FIRST_LEGACY_IRQ) % 32); | |
96 | ||
97 | __raw_writel(mask, base + reg); | |
98 | } | |
99 | ||
3524b70e CC |
100 | static void tegra_mask(struct irq_data *d) |
101 | { | |
d1d8c666 CC |
102 | if (d->irq < FIRST_LEGACY_IRQ) |
103 | return; | |
104 | ||
105 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_CLR); | |
3524b70e | 106 | } |
460907bc | 107 | |
3524b70e | 108 | static void tegra_unmask(struct irq_data *d) |
460907bc | 109 | { |
d1d8c666 CC |
110 | if (d->irq < FIRST_LEGACY_IRQ) |
111 | return; | |
112 | ||
113 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IER_SET); | |
460907bc | 114 | } |
460907bc | 115 | |
26d902c0 CC |
116 | static void tegra_ack(struct irq_data *d) |
117 | { | |
d1d8c666 CC |
118 | if (d->irq < FIRST_LEGACY_IRQ) |
119 | return; | |
120 | ||
121 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR); | |
26d902c0 CC |
122 | } |
123 | ||
4bd66cfd CC |
124 | static void tegra_eoi(struct irq_data *d) |
125 | { | |
126 | if (d->irq < FIRST_LEGACY_IRQ) | |
127 | return; | |
128 | ||
129 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_CLR); | |
130 | } | |
131 | ||
26d902c0 CC |
132 | static int tegra_retrigger(struct irq_data *d) |
133 | { | |
d1d8c666 | 134 | if (d->irq < FIRST_LEGACY_IRQ) |
938fa349 CC |
135 | return 0; |
136 | ||
d1d8c666 CC |
137 | tegra_irq_write_mask(d->irq, ICTLR_CPU_IEP_FIR_SET); |
138 | ||
26d902c0 CC |
139 | return 1; |
140 | } | |
141 | ||
e307cc89 JL |
142 | #ifdef CONFIG_PM_SLEEP |
143 | static int tegra_set_wake(struct irq_data *d, unsigned int enable) | |
144 | { | |
145 | u32 irq = d->irq; | |
146 | u32 index, mask; | |
147 | ||
148 | if (irq < FIRST_LEGACY_IRQ || | |
149 | irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32) | |
150 | return -EINVAL; | |
151 | ||
152 | index = ((irq - FIRST_LEGACY_IRQ) / 32); | |
153 | mask = BIT((irq - FIRST_LEGACY_IRQ) % 32); | |
154 | if (enable) | |
155 | ictlr_wake_mask[index] |= mask; | |
156 | else | |
157 | ictlr_wake_mask[index] &= ~mask; | |
158 | ||
159 | return 0; | |
160 | } | |
161 | ||
162 | static int tegra_legacy_irq_suspend(void) | |
163 | { | |
164 | unsigned long flags; | |
165 | int i; | |
166 | ||
167 | local_irq_save(flags); | |
168 | for (i = 0; i < num_ictlrs; i++) { | |
169 | void __iomem *ictlr = ictlr_reg_base[i]; | |
170 | /* Save interrupt state */ | |
171 | cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER); | |
172 | cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS); | |
173 | cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER); | |
174 | cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS); | |
175 | ||
176 | /* Disable COP interrupts */ | |
177 | writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); | |
178 | ||
179 | /* Disable CPU interrupts */ | |
180 | writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); | |
181 | ||
182 | /* Enable the wakeup sources of ictlr */ | |
183 | writel_relaxed(ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET); | |
184 | } | |
185 | local_irq_restore(flags); | |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
190 | static void tegra_legacy_irq_resume(void) | |
191 | { | |
192 | unsigned long flags; | |
193 | int i; | |
194 | ||
195 | local_irq_save(flags); | |
196 | for (i = 0; i < num_ictlrs; i++) { | |
197 | void __iomem *ictlr = ictlr_reg_base[i]; | |
198 | writel_relaxed(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS); | |
199 | writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); | |
200 | writel_relaxed(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET); | |
201 | writel_relaxed(cop_iep[i], ictlr + ICTLR_COP_IEP_CLASS); | |
202 | writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); | |
203 | writel_relaxed(cop_ier[i], ictlr + ICTLR_COP_IER_SET); | |
204 | } | |
205 | local_irq_restore(flags); | |
206 | } | |
207 | ||
208 | static struct syscore_ops tegra_legacy_irq_syscore_ops = { | |
209 | .suspend = tegra_legacy_irq_suspend, | |
210 | .resume = tegra_legacy_irq_resume, | |
211 | }; | |
212 | ||
213 | int tegra_legacy_irq_syscore_init(void) | |
214 | { | |
215 | register_syscore_ops(&tegra_legacy_irq_syscore_ops); | |
216 | ||
217 | return 0; | |
218 | } | |
7e8b15db JL |
219 | |
220 | static int tegra_gic_notifier(struct notifier_block *self, | |
221 | unsigned long cmd, void *v) | |
222 | { | |
223 | switch (cmd) { | |
224 | case CPU_PM_ENTER: | |
225 | writel_relaxed(0x1E0, tegra_gic_cpu_base + GIC_CPU_CTRL); | |
226 | break; | |
227 | } | |
228 | ||
229 | return NOTIFY_OK; | |
230 | } | |
231 | ||
232 | static struct notifier_block tegra_gic_notifier_block = { | |
233 | .notifier_call = tegra_gic_notifier, | |
234 | }; | |
235 | ||
236 | static const struct of_device_id tegra114_dt_gic_match[] __initconst = { | |
237 | { .compatible = "arm,cortex-a15-gic" }, | |
238 | { } | |
239 | }; | |
240 | ||
241 | static void tegra114_gic_cpu_pm_registration(void) | |
242 | { | |
243 | struct device_node *dn; | |
244 | ||
245 | dn = of_find_matching_node(NULL, tegra114_dt_gic_match); | |
246 | if (!dn) | |
247 | return; | |
248 | ||
249 | tegra_gic_cpu_base = of_iomap(dn, 1); | |
250 | ||
251 | cpu_pm_register_notifier(&tegra_gic_notifier_block); | |
252 | } | |
e307cc89 JL |
253 | #else |
254 | #define tegra_set_wake NULL | |
7e8b15db | 255 | static void tegra114_gic_cpu_pm_registration(void) { } |
e307cc89 JL |
256 | #endif |
257 | ||
5ad36c5f EG |
258 | void __init tegra_init_irq(void) |
259 | { | |
d1d8c666 | 260 | int i; |
caa4868e PDS |
261 | void __iomem *distbase; |
262 | ||
263 | distbase = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE); | |
264 | num_ictlrs = readl_relaxed(distbase + GIC_DIST_CTR) & 0x1f; | |
265 | ||
266 | if (num_ictlrs > ARRAY_SIZE(ictlr_reg_base)) { | |
267 | WARN(1, "Too many (%d) interrupt controllers found. Maximum is %d.", | |
268 | num_ictlrs, ARRAY_SIZE(ictlr_reg_base)); | |
269 | num_ictlrs = ARRAY_SIZE(ictlr_reg_base); | |
270 | } | |
d1d8c666 | 271 | |
caa4868e | 272 | for (i = 0; i < num_ictlrs; i++) { |
d1d8c666 CC |
273 | void __iomem *ictlr = ictlr_reg_base[i]; |
274 | writel(~0, ictlr + ICTLR_CPU_IER_CLR); | |
275 | writel(0, ictlr + ICTLR_CPU_IEP_CLASS); | |
276 | } | |
460907bc | 277 | |
938fa349 | 278 | gic_arch_extn.irq_ack = tegra_ack; |
4bd66cfd | 279 | gic_arch_extn.irq_eoi = tegra_eoi; |
938fa349 CC |
280 | gic_arch_extn.irq_mask = tegra_mask; |
281 | gic_arch_extn.irq_unmask = tegra_unmask; | |
282 | gic_arch_extn.irq_retrigger = tegra_retrigger; | |
e307cc89 JL |
283 | gic_arch_extn.irq_set_wake = tegra_set_wake; |
284 | gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND; | |
938fa349 | 285 | |
0d4f7479 | 286 | /* |
287 | * Check if there is a devicetree present, since the GIC will be | |
288 | * initialized elsewhere under DT. | |
289 | */ | |
290 | if (!of_have_populated_dt()) | |
caa4868e | 291 | gic_init(0, 29, distbase, |
0d4f7479 | 292 | IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); |
7e8b15db JL |
293 | |
294 | tegra114_gic_cpu_pm_registration(); | |
460907bc | 295 | } |