Blame view
drivers/irqchip/irq-omap-intc.c
9.81 KB
1dbae815a [ARM] 3145/1: OMA... |
1 |
/* |
f30c22695 fix file specific... |
2 |
* linux/arch/arm/mach-omap2/irq.c |
1dbae815a [ARM] 3145/1: OMA... |
3 4 5 6 7 8 9 10 11 12 13 |
* * Interrupt handler for OMAP2 boards. * * Copyright (C) 2005 Nokia Corporation * Author: Paul Mundt <paul.mundt@nokia.com> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include <linux/kernel.h> |
52fa21208 ARM: OMAP2/3: int... |
14 |
#include <linux/module.h> |
1dbae815a [ARM] 3145/1: OMA... |
15 |
#include <linux/init.h> |
1dbae815a [ARM] 3145/1: OMA... |
16 |
#include <linux/interrupt.h> |
2e7509e5b ARM: OMAP2: Fix s... |
17 |
#include <linux/io.h> |
ee0839c22 ARM: OMAP2+: Move... |
18 |
|
2db149978 ARM: omap2/3: Add... |
19 |
#include <asm/exception.h> |
41a83e06e irqchip: Prepare ... |
20 |
#include <linux/irqchip.h> |
52fa21208 ARM: OMAP2/3: int... |
21 22 23 |
#include <linux/irqdomain.h> #include <linux/of.h> #include <linux/of_address.h> |
c4082d499 ARM: omap2+: boar... |
24 |
#include <linux/of_irq.h> |
1dbae815a [ARM] 3145/1: OMA... |
25 |
|
f3142635d irqchip/omap-intc... |
26 |
#include <linux/irqchip/irq-omap-intc.h> |
8598066cd arm: omap: irq: m... |
27 28 29 |
/* Define these here for now until we drop all board-files */ #define OMAP24XX_IC_BASE 0x480fe000 #define OMAP34XX_IC_BASE 0x48200000 |
2e7509e5b ARM: OMAP2: Fix s... |
30 31 32 33 34 35 |
/* selected INTC register offsets */ #define INTC_REVISION 0x0000 #define INTC_SYSCONFIG 0x0010 #define INTC_SYSSTATUS 0x0014 |
6ccc4c0de ARM: OMAP3: Warn ... |
36 |
#define INTC_SIR 0x0040 |
2e7509e5b ARM: OMAP2: Fix s... |
37 |
#define INTC_CONTROL 0x0048 |
0addd61bc OMAP3: PM: INTC c... |
38 39 40 41 |
#define INTC_PROTECTION 0x004C #define INTC_IDLE 0x0050 #define INTC_THRESHOLD 0x0068 #define INTC_MIR0 0x0084 |
2e7509e5b ARM: OMAP2: Fix s... |
42 43 44 |
#define INTC_MIR_CLEAR0 0x0088 #define INTC_MIR_SET0 0x008c #define INTC_PENDING_IRQ0 0x0098 |
119836562 arm: omap: irq: s... |
45 46 47 |
#define INTC_PENDING_IRQ1 0x00b8 #define INTC_PENDING_IRQ2 0x00d8 #define INTC_PENDING_IRQ3 0x00f8 |
33c7c7b7f arm: omap: irq: d... |
48 |
#define INTC_ILR0 0x0100 |
1dbae815a [ARM] 3145/1: OMA... |
49 |
|
2db149978 ARM: omap2/3: Add... |
50 |
#define ACTIVEIRQ_MASK 0x7f /* omap2/3 active interrupt bits */ |
d3b421cd0 irqchip/omap-intc... |
51 |
#define SPURIOUSIRQ_MASK (0x1ffffff << 7) |
a88ab4308 arm: omap: irq: r... |
52 |
#define INTCPS_NR_ILR_REGS 128 |
74b6c8ef5 irqchip: omap-int... |
53 |
#define INTCPS_NR_MIR_REGS 4 |
2db149978 ARM: omap2/3: Add... |
54 |
|
b30791498 irqchip: omap-int... |
55 56 |
#define INTC_IDLE_FUNCIDLE (1 << 0) #define INTC_IDLE_TURBO (1 << 1) |
9836ee9f8 irqchip: omap-int... |
57 |
#define INTC_PROTECTION_ENABLE (1 << 0) |
272a8b04a arm: omap: irq: r... |
58 |
struct omap_intc_regs { |
0addd61bc OMAP3: PM: INTC c... |
59 60 61 62 |
u32 sysconfig; u32 protection; u32 idle; u32 threshold; |
a88ab4308 arm: omap: irq: r... |
63 |
u32 ilr[INTCPS_NR_ILR_REGS]; |
0addd61bc OMAP3: PM: INTC c... |
64 65 |
u32 mir[INTCPS_NR_MIR_REGS]; }; |
131b48c06 arm: omap: irq: r... |
66 67 68 69 |
static struct omap_intc_regs intc_context; static struct irq_domain *domain; static void __iomem *omap_irq_base; |
52b1e1291 arm: omap: irq: i... |
70 |
static int omap_nr_pending = 3; |
131b48c06 arm: omap: irq: r... |
71 |
static int omap_nr_irqs = 96; |
0addd61bc OMAP3: PM: INTC c... |
72 |
|
71be00c90 arm: omap: irq: s... |
73 |
static void intc_writel(u32 reg, u32 val) |
2e7509e5b ARM: OMAP2: Fix s... |
74 |
{ |
71be00c90 arm: omap: irq: s... |
75 |
writel_relaxed(val, omap_irq_base + reg); |
2e7509e5b ARM: OMAP2: Fix s... |
76 |
} |
71be00c90 arm: omap: irq: s... |
77 |
static u32 intc_readl(u32 reg) |
2e7509e5b ARM: OMAP2: Fix s... |
78 |
{ |
71be00c90 arm: omap: irq: s... |
79 |
return readl_relaxed(omap_irq_base + reg); |
2e7509e5b ARM: OMAP2: Fix s... |
80 |
} |
131b48c06 arm: omap: irq: r... |
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
void omap_intc_save_context(void) { int i; intc_context.sysconfig = intc_readl(INTC_SYSCONFIG); intc_context.protection = intc_readl(INTC_PROTECTION); intc_context.idle = intc_readl(INTC_IDLE); intc_context.threshold = intc_readl(INTC_THRESHOLD); for (i = 0; i < omap_nr_irqs; i++) intc_context.ilr[i] = intc_readl((INTC_ILR0 + 0x4 * i)); for (i = 0; i < INTCPS_NR_MIR_REGS; i++) intc_context.mir[i] = intc_readl(INTC_MIR0 + (0x20 * i)); } void omap_intc_restore_context(void) { int i; intc_writel(INTC_SYSCONFIG, intc_context.sysconfig); intc_writel(INTC_PROTECTION, intc_context.protection); intc_writel(INTC_IDLE, intc_context.idle); intc_writel(INTC_THRESHOLD, intc_context.threshold); for (i = 0; i < omap_nr_irqs; i++) intc_writel(INTC_ILR0 + 0x4 * i, intc_context.ilr[i]); for (i = 0; i < INTCPS_NR_MIR_REGS; i++) intc_writel(INTC_MIR0 + 0x20 * i, intc_context.mir[i]); /* MIRs are saved and restore with other PRCM registers */ } void omap3_intc_prepare_idle(void) { /* * Disable autoidle as it can stall interrupt controller, * cf. errata ID i540 for 3430 (all revisions up to 3.1.x) */ intc_writel(INTC_SYSCONFIG, 0); |
b30791498 irqchip: omap-int... |
128 |
intc_writel(INTC_IDLE, INTC_IDLE_TURBO); |
131b48c06 arm: omap: irq: r... |
129 130 131 132 133 134 |
} void omap3_intc_resume_idle(void) { /* Re-enable autoidle */ intc_writel(INTC_SYSCONFIG, 1); |
b30791498 irqchip: omap-int... |
135 |
intc_writel(INTC_IDLE, 0); |
131b48c06 arm: omap: irq: r... |
136 |
} |
1dbae815a [ARM] 3145/1: OMA... |
137 |
/* XXX: FIQ and additional INTC support (only MPU at the moment) */ |
df303477b ARM: omap2: irq_d... |
138 |
static void omap_ack_irq(struct irq_data *d) |
1dbae815a [ARM] 3145/1: OMA... |
139 |
{ |
71be00c90 arm: omap: irq: s... |
140 |
intc_writel(INTC_CONTROL, 0x1); |
1dbae815a [ARM] 3145/1: OMA... |
141 |
} |
df303477b ARM: omap2: irq_d... |
142 |
static void omap_mask_ack_irq(struct irq_data *d) |
1dbae815a [ARM] 3145/1: OMA... |
143 |
{ |
667a11fac arm: omap2/3: Use... |
144 |
irq_gc_mask_disable_reg(d); |
df303477b ARM: omap2: irq_d... |
145 |
omap_ack_irq(d); |
1dbae815a [ARM] 3145/1: OMA... |
146 |
} |
a88ab4308 arm: omap: irq: r... |
147 |
static void __init omap_irq_soft_reset(void) |
1dbae815a [ARM] 3145/1: OMA... |
148 149 |
{ unsigned long tmp; |
71be00c90 arm: omap: irq: s... |
150 |
tmp = intc_readl(INTC_REVISION) & 0xff; |
a88ab4308 arm: omap: irq: r... |
151 |
|
7852ec053 ARM: OMAP: unwrap... |
152 153 |
pr_info("IRQ: Found an INTC at 0x%p (revision %ld.%ld) with %d interrupts ", |
a88ab4308 arm: omap: irq: r... |
154 |
omap_irq_base, tmp >> 4, tmp & 0xf, omap_nr_irqs); |
1dbae815a [ARM] 3145/1: OMA... |
155 |
|
71be00c90 arm: omap: irq: s... |
156 |
tmp = intc_readl(INTC_SYSCONFIG); |
1dbae815a [ARM] 3145/1: OMA... |
157 |
tmp |= 1 << 1; /* soft reset */ |
71be00c90 arm: omap: irq: s... |
158 |
intc_writel(INTC_SYSCONFIG, tmp); |
1dbae815a [ARM] 3145/1: OMA... |
159 |
|
71be00c90 arm: omap: irq: s... |
160 |
while (!(intc_readl(INTC_SYSSTATUS) & 0x1)) |
1dbae815a [ARM] 3145/1: OMA... |
161 |
/* Wait for reset to complete */; |
375e12abc ARM: OMAP: Optimi... |
162 163 |
/* Enable autoidle */ |
71be00c90 arm: omap: irq: s... |
164 |
intc_writel(INTC_SYSCONFIG, 1 << 0); |
1dbae815a [ARM] 3145/1: OMA... |
165 |
} |
94434535b OMAP: Add new fun... |
166 167 |
int omap_irq_pending(void) { |
6bd0f16e3 irqchip: omap-int... |
168 |
int i; |
94434535b OMAP: Add new fun... |
169 |
|
6bd0f16e3 irqchip: omap-int... |
170 171 |
for (i = 0; i < omap_nr_pending; i++) if (intc_readl(INTC_PENDING_IRQ0 + (0x20 * i))) |
a88ab4308 arm: omap: irq: r... |
172 |
return 1; |
94434535b OMAP: Add new fun... |
173 174 |
return 0; } |
131b48c06 arm: omap: irq: r... |
175 176 177 178 179 |
void omap3_intc_suspend(void) { /* A pending interrupt would prevent OMAP from entering suspend */ omap_ack_irq(NULL); } |
55601c9f2 arm: omap: intc: ... |
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
static int __init omap_alloc_gc_of(struct irq_domain *d, void __iomem *base) { int ret; int i; ret = irq_alloc_domain_generic_chips(d, 32, 1, "INTC", handle_level_irq, IRQ_NOREQUEST | IRQ_NOPROBE, IRQ_LEVEL, 0); if (ret) { pr_warn("Failed to allocate irq chips "); return ret; } for (i = 0; i < omap_nr_pending; i++) { struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_get_domain_generic_chip(d, 32 * i); gc->reg_base = base; ct = gc->chip_types; ct->type = IRQ_TYPE_LEVEL_MASK; |
55601c9f2 arm: omap: intc: ... |
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
ct->chip.irq_ack = omap_mask_ack_irq; ct->chip.irq_mask = irq_gc_mask_disable_reg; ct->chip.irq_unmask = irq_gc_unmask_enable_reg; ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE; ct->regs.enable = INTC_MIR_CLEAR0 + 32 * i; ct->regs.disable = INTC_MIR_SET0 + 32 * i; } return 0; } static void __init omap_alloc_gc_legacy(void __iomem *base, unsigned int irq_start, unsigned int num) |
667a11fac arm: omap2/3: Use... |
219 220 221 222 223 |
{ struct irq_chip_generic *gc; struct irq_chip_type *ct; gc = irq_alloc_generic_chip("INTC", 1, irq_start, base, |
55601c9f2 arm: omap: intc: ... |
224 |
handle_level_irq); |
667a11fac arm: omap2/3: Use... |
225 226 227 228 |
ct = gc->chip_types; ct->chip.irq_ack = omap_mask_ack_irq; ct->chip.irq_mask = irq_gc_mask_disable_reg; ct->chip.irq_unmask = irq_gc_unmask_enable_reg; |
e3c83c2db ARM: OMAP2+: INTC... |
229 |
ct->chip.flags |= IRQCHIP_SKIP_SET_WAKE; |
667a11fac arm: omap2/3: Use... |
230 |
|
667a11fac arm: omap2/3: Use... |
231 232 233 |
ct->regs.enable = INTC_MIR_CLEAR0; ct->regs.disable = INTC_MIR_SET0; irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, |
55601c9f2 arm: omap: intc: ... |
234 |
IRQ_NOREQUEST | IRQ_NOPROBE, 0); |
667a11fac arm: omap2/3: Use... |
235 |
} |
55601c9f2 arm: omap: intc: ... |
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
static int __init omap_init_irq_of(struct device_node *node) { int ret; omap_irq_base = of_iomap(node, 0); if (WARN_ON(!omap_irq_base)) return -ENOMEM; domain = irq_domain_add_linear(node, omap_nr_irqs, &irq_generic_chip_ops, NULL); omap_irq_soft_reset(); ret = omap_alloc_gc_of(domain, omap_irq_base); if (ret < 0) irq_domain_remove(domain); return ret; } |
4b149e417 irqchip: omap-int... |
255 |
static int __init omap_init_irq_legacy(u32 base, struct device_node *node) |
1dbae815a [ARM] 3145/1: OMA... |
256 |
{ |
a88ab4308 arm: omap: irq: r... |
257 |
int j, irq_base; |
1dbae815a [ARM] 3145/1: OMA... |
258 |
|
741e3a89d omap: Use separat... |
259 260 |
omap_irq_base = ioremap(base, SZ_4K); if (WARN_ON(!omap_irq_base)) |
55601c9f2 arm: omap: intc: ... |
261 |
return -ENOMEM; |
741e3a89d omap: Use separat... |
262 |
|
a74f0a176 arm: omap: irq: r... |
263 |
irq_base = irq_alloc_descs(-1, 0, omap_nr_irqs, 0); |
52fa21208 ARM: OMAP2/3: int... |
264 265 266 267 268 |
if (irq_base < 0) { pr_warn("Couldn't allocate IRQ numbers "); irq_base = 0; } |
4b149e417 irqchip: omap-int... |
269 |
domain = irq_domain_add_legacy(node, omap_nr_irqs, irq_base, 0, |
a88ab4308 arm: omap: irq: r... |
270 |
&irq_domain_simple_ops, NULL); |
1dbae815a [ARM] 3145/1: OMA... |
271 |
|
a88ab4308 arm: omap: irq: r... |
272 |
omap_irq_soft_reset(); |
667a11fac arm: omap2/3: Use... |
273 |
|
a88ab4308 arm: omap: irq: r... |
274 |
for (j = 0; j < omap_nr_irqs; j += 32) |
55601c9f2 arm: omap: intc: ... |
275 276 277 278 |
omap_alloc_gc_legacy(omap_irq_base + j, j + irq_base, 32); return 0; } |
9836ee9f8 irqchip: omap-int... |
279 280 281 282 283 284 285 286 |
static void __init omap_irq_enable_protection(void) { u32 reg; reg = intc_readl(INTC_PROTECTION); reg |= INTC_PROTECTION_ENABLE; intc_writel(INTC_PROTECTION, reg); } |
55601c9f2 arm: omap: intc: ... |
287 288 |
static int __init omap_init_irq(u32 base, struct device_node *node) { |
9836ee9f8 irqchip: omap-int... |
289 |
int ret; |
4b149e417 irqchip: omap-int... |
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
/* * FIXME legacy OMAP DMA driver sitting under arch/arm/plat-omap/dma.c * depends is still not ready for linear IRQ domains; because of that * we need to temporarily "blacklist" OMAP2 and OMAP3 devices from using * linear IRQ Domain until that driver is finally fixed. */ if (of_device_is_compatible(node, "ti,omap2-intc") || of_device_is_compatible(node, "ti,omap3-intc")) { struct resource res; if (of_address_to_resource(node, 0, &res)) return -ENOMEM; base = res.start; ret = omap_init_irq_legacy(base, node); } else if (node) { |
9836ee9f8 irqchip: omap-int... |
306 |
ret = omap_init_irq_of(node); |
4b149e417 irqchip: omap-int... |
307 308 309 |
} else { ret = omap_init_irq_legacy(base, NULL); } |
9836ee9f8 irqchip: omap-int... |
310 311 312 313 314 |
if (ret == 0) omap_irq_enable_protection(); return ret; |
1dbae815a [ARM] 3145/1: OMA... |
315 |
} |
2aced8924 arm: omap: irq: d... |
316 317 |
static asmlinkage void __exception_irq_entry omap_intc_handle_irq(struct pt_regs *regs) |
2db149978 ARM: omap2/3: Add... |
318 |
{ |
d3b421cd0 irqchip/omap-intc... |
319 |
extern unsigned long irq_err_count; |
6ed346489 irqchip: omap-int... |
320 |
u32 irqnr; |
2db149978 ARM: omap2/3: Add... |
321 |
|
6ed346489 irqchip: omap-int... |
322 |
irqnr = intc_readl(INTC_SIR); |
d3b421cd0 irqchip/omap-intc... |
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 |
/* * A spurious IRQ can result if interrupt that triggered the * sorting is no longer active during the sorting (10 INTC * functional clock cycles after interrupt assertion). Or a * change in interrupt mask affected the result during sorting * time. There is no special handling required except ignoring * the SIR register value just read and retrying. * See section 6.2.5 of AM335x TRM Literature Number: SPRUH73K * * Many a times, a spurious interrupt situation has been fixed * by adding a flush for the posted write acking the IRQ in * the device driver. Typically, this is going be the device * driver whose interrupt was handled just before the spurious * IRQ occurred. Pay attention to those device drivers if you * run into hitting the spurious IRQ condition below. */ if (unlikely((irqnr & SPURIOUSIRQ_MASK) == SPURIOUSIRQ_MASK)) { pr_err_once("%s: spurious irq! ", __func__); irq_err_count++; omap_ack_irq(NULL); return; } |
6ed346489 irqchip: omap-int... |
347 |
irqnr &= ACTIVEIRQ_MASK; |
6ed346489 irqchip: omap-int... |
348 |
handle_domain_irq(domain, irqnr, regs); |
2db149978 ARM: omap2/3: Add... |
349 |
} |
a4d3c5d91 arm: omap: irq: m... |
350 351 |
void __init omap3_init_irq(void) { |
a74f0a176 arm: omap: irq: r... |
352 |
omap_nr_irqs = 96; |
52b1e1291 arm: omap: irq: i... |
353 |
omap_nr_pending = 3; |
a74f0a176 arm: omap: irq: r... |
354 |
omap_init_irq(OMAP34XX_IC_BASE, NULL); |
2aced8924 arm: omap: irq: d... |
355 |
set_handle_irq(omap_intc_handle_irq); |
a4d3c5d91 arm: omap: irq: m... |
356 |
} |
00b6b031a arm: omap: irq: m... |
357 |
static int __init intc_of_init(struct device_node *node, |
52fa21208 ARM: OMAP2/3: int... |
358 359 |
struct device_node *parent) { |
55601c9f2 arm: omap: intc: ... |
360 |
int ret; |
a74f0a176 arm: omap: irq: r... |
361 |
|
52b1e1291 arm: omap: irq: i... |
362 |
omap_nr_pending = 3; |
a74f0a176 arm: omap: irq: r... |
363 |
omap_nr_irqs = 96; |
52fa21208 ARM: OMAP2/3: int... |
364 365 366 |
if (WARN_ON(!node)) return -ENODEV; |
19f92b237 irqchip: omap-int... |
367 368 369 |
if (of_device_is_compatible(node, "ti,dm814-intc") || of_device_is_compatible(node, "ti,dm816-intc") || of_device_is_compatible(node, "ti,am33xx-intc")) { |
a74f0a176 arm: omap: irq: r... |
370 |
omap_nr_irqs = 128; |
52b1e1291 arm: omap: irq: i... |
371 372 |
omap_nr_pending = 4; } |
470f30dea arm: omap: irq: u... |
373 |
|
55601c9f2 arm: omap: intc: ... |
374 375 376 |
ret = omap_init_irq(-1, of_node_get(node)); if (ret < 0) return ret; |
52fa21208 ARM: OMAP2/3: int... |
377 |
|
2aced8924 arm: omap: irq: d... |
378 |
set_handle_irq(omap_intc_handle_irq); |
b15c76b74 arm: omap: irq: c... |
379 |
|
52fa21208 ARM: OMAP2/3: int... |
380 381 |
return 0; } |
a35db9a4c arm: omap: irq: a... |
382 383 |
IRQCHIP_DECLARE(omap2_intc, "ti,omap2-intc", intc_of_init); IRQCHIP_DECLARE(omap3_intc, "ti,omap3-intc", intc_of_init); |
19f92b237 irqchip: omap-int... |
384 385 |
IRQCHIP_DECLARE(dm814x_intc, "ti,dm814-intc", intc_of_init); IRQCHIP_DECLARE(dm816x_intc, "ti,dm816-intc", intc_of_init); |
a35db9a4c arm: omap: irq: a... |
386 |
IRQCHIP_DECLARE(am33xx_intc, "ti,am33xx-intc", intc_of_init); |