Commit | Line | Data |
---|---|---|
5f97f7f9 | 1 | /* |
aa8e87ca | 2 | * Copyright (C) 2006, 2008 Atmel Corporation |
5f97f7f9 HS |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/clk.h> | |
10 | #include <linux/err.h> | |
11 | #include <linux/init.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/irq.h> | |
14 | #include <linux/platform_device.h> | |
f25f4f52 | 15 | #include <linux/syscore_ops.h> |
09cf6a29 | 16 | #include <linux/export.h> |
5f97f7f9 HS |
17 | |
18 | #include <asm/io.h> | |
19 | ||
20 | #include "intc.h" | |
21 | ||
22 | struct intc { | |
aa8e87ca HS |
23 | void __iomem *regs; |
24 | struct irq_chip chip; | |
02a00cf6 HS |
25 | #ifdef CONFIG_PM |
26 | unsigned long suspend_ipr; | |
27 | unsigned long saved_ipr[64]; | |
28 | #endif | |
5f97f7f9 HS |
29 | }; |
30 | ||
31 | extern struct platform_device at32_intc0_device; | |
32 | ||
33 | /* | |
34 | * TODO: We may be able to implement mask/unmask by setting IxM flags | |
35 | * in the status register. | |
36 | */ | |
3972f691 | 37 | static void intc_mask_irq(struct irq_data *d) |
5f97f7f9 HS |
38 | { |
39 | ||
40 | } | |
41 | ||
3972f691 | 42 | static void intc_unmask_irq(struct irq_data *d) |
5f97f7f9 HS |
43 | { |
44 | ||
45 | } | |
46 | ||
47 | static struct intc intc0 = { | |
48 | .chip = { | |
49 | .name = "intc", | |
3972f691 TG |
50 | .irq_mask = intc_mask_irq, |
51 | .irq_unmask = intc_unmask_irq, | |
5f97f7f9 HS |
52 | }, |
53 | }; | |
54 | ||
55 | /* | |
56 | * All interrupts go via intc at some point. | |
57 | */ | |
58 | asmlinkage void do_IRQ(int level, struct pt_regs *regs) | |
59 | { | |
4e0fadfc | 60 | struct pt_regs *old_regs; |
5f97f7f9 HS |
61 | unsigned int irq; |
62 | unsigned long status_reg; | |
63 | ||
64 | local_irq_disable(); | |
65 | ||
4e0fadfc HS |
66 | old_regs = set_irq_regs(regs); |
67 | ||
5f97f7f9 HS |
68 | irq_enter(); |
69 | ||
70 | irq = intc_readl(&intc0, INTCAUSE0 - 4 * level); | |
3972f691 | 71 | generic_handle_irq(irq); |
5f97f7f9 HS |
72 | |
73 | /* | |
74 | * Clear all interrupt level masks so that we may handle | |
75 | * interrupts during softirq processing. If this is a nested | |
76 | * interrupt, interrupts must stay globally disabled until we | |
77 | * return. | |
78 | */ | |
79 | status_reg = sysreg_read(SR); | |
80 | status_reg &= ~(SYSREG_BIT(I0M) | SYSREG_BIT(I1M) | |
81 | | SYSREG_BIT(I2M) | SYSREG_BIT(I3M)); | |
82 | sysreg_write(SR, status_reg); | |
83 | ||
84 | irq_exit(); | |
4e0fadfc HS |
85 | |
86 | set_irq_regs(old_regs); | |
5f97f7f9 HS |
87 | } |
88 | ||
89 | void __init init_IRQ(void) | |
90 | { | |
91 | extern void _evba(void); | |
92 | extern void irq_level0(void); | |
93 | struct resource *regs; | |
94 | struct clk *pclk; | |
95 | unsigned int i; | |
96 | u32 offset, readback; | |
97 | ||
98 | regs = platform_get_resource(&at32_intc0_device, IORESOURCE_MEM, 0); | |
99 | if (!regs) { | |
100 | printk(KERN_EMERG "intc: no mmio resource defined\n"); | |
101 | goto fail; | |
102 | } | |
103 | pclk = clk_get(&at32_intc0_device.dev, "pclk"); | |
104 | if (IS_ERR(pclk)) { | |
105 | printk(KERN_EMERG "intc: no clock defined\n"); | |
106 | goto fail; | |
107 | } | |
108 | ||
109 | clk_enable(pclk); | |
110 | ||
28f65c11 | 111 | intc0.regs = ioremap(regs->start, resource_size(regs)); |
5f97f7f9 HS |
112 | if (!intc0.regs) { |
113 | printk(KERN_EMERG "intc: failed to map registers (0x%08lx)\n", | |
114 | (unsigned long)regs->start); | |
115 | goto fail; | |
116 | } | |
117 | ||
118 | /* | |
119 | * Initialize all interrupts to level 0 (lowest priority). The | |
120 | * priority level may be changed by calling | |
121 | * irq_set_priority(). | |
122 | * | |
123 | */ | |
124 | offset = (unsigned long)&irq_level0 - (unsigned long)&_evba; | |
125 | for (i = 0; i < NR_INTERNAL_IRQS; i++) { | |
126 | intc_writel(&intc0, INTPR0 + 4 * i, offset); | |
127 | readback = intc_readl(&intc0, INTPR0 + 4 * i); | |
128 | if (readback == offset) | |
d75f1bfd | 129 | irq_set_chip_and_handler(i, &intc0.chip, |
5f97f7f9 HS |
130 | handle_simple_irq); |
131 | } | |
132 | ||
133 | /* Unmask all interrupt levels */ | |
134 | sysreg_write(SR, (sysreg_read(SR) | |
135 | & ~(SR_I3M | SR_I2M | SR_I1M | SR_I0M))); | |
136 | ||
137 | return; | |
138 | ||
139 | fail: | |
140 | panic("Interrupt controller initialization failed!\n"); | |
141 | } | |
142 | ||
02a00cf6 HS |
143 | #ifdef CONFIG_PM |
144 | void intc_set_suspend_handler(unsigned long offset) | |
145 | { | |
146 | intc0.suspend_ipr = offset; | |
147 | } | |
148 | ||
f25f4f52 | 149 | static int intc_suspend(void) |
02a00cf6 | 150 | { |
02a00cf6 HS |
151 | int i; |
152 | ||
153 | if (unlikely(!irqs_disabled())) { | |
154 | pr_err("intc_suspend: called with interrupts enabled\n"); | |
155 | return -EINVAL; | |
156 | } | |
157 | ||
f25f4f52 | 158 | if (unlikely(!intc0.suspend_ipr)) { |
02a00cf6 HS |
159 | pr_err("intc_suspend: suspend_ipr not initialized\n"); |
160 | return -EINVAL; | |
161 | } | |
162 | ||
163 | for (i = 0; i < 64; i++) { | |
f25f4f52 RW |
164 | intc0.saved_ipr[i] = intc_readl(&intc0, INTPR0 + 4 * i); |
165 | intc_writel(&intc0, INTPR0 + 4 * i, intc0.suspend_ipr); | |
02a00cf6 HS |
166 | } |
167 | ||
168 | return 0; | |
169 | } | |
170 | ||
c1627554 | 171 | static void intc_resume(void) |
02a00cf6 | 172 | { |
02a00cf6 HS |
173 | int i; |
174 | ||
02a00cf6 | 175 | for (i = 0; i < 64; i++) |
f25f4f52 | 176 | intc_writel(&intc0, INTPR0 + 4 * i, intc0.saved_ipr[i]); |
02a00cf6 HS |
177 | } |
178 | #else | |
179 | #define intc_suspend NULL | |
180 | #define intc_resume NULL | |
181 | #endif | |
182 | ||
f25f4f52 | 183 | static struct syscore_ops intc_syscore_ops = { |
02a00cf6 HS |
184 | .suspend = intc_suspend, |
185 | .resume = intc_resume, | |
aa8e87ca HS |
186 | }; |
187 | ||
f25f4f52 | 188 | static int __init intc_init_syscore(void) |
aa8e87ca | 189 | { |
f25f4f52 | 190 | register_syscore_ops(&intc_syscore_ops); |
aa8e87ca | 191 | |
f25f4f52 | 192 | return 0; |
aa8e87ca | 193 | } |
f25f4f52 | 194 | device_initcall(intc_init_syscore); |
aa8e87ca | 195 | |
597702ae | 196 | unsigned long intc_get_pending(unsigned int group) |
69562118 HS |
197 | { |
198 | return intc_readl(&intc0, INTREQ0 + 4 * group); | |
199 | } | |
597702ae | 200 | EXPORT_SYMBOL_GPL(intc_get_pending); |