diff options
Diffstat (limited to 'arch/arm/common/vic.c')
-rw-r--r-- | arch/arm/common/vic.c | 265 |
1 files changed, 132 insertions, 133 deletions
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c index f232941de8ab..1cf999ade4bc 100644 --- a/arch/arm/common/vic.c +++ b/arch/arm/common/vic.c | |||
@@ -18,6 +18,7 @@ | |||
18 | * along with this program; if not, write to the Free Software | 18 | * along with this program; if not, write to the Free Software |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
20 | */ | 20 | */ |
21 | |||
21 | #include <linux/init.h> | 22 | #include <linux/init.h> |
22 | #include <linux/list.h> | 23 | #include <linux/list.h> |
23 | #include <linux/io.h> | 24 | #include <linux/io.h> |
@@ -28,48 +29,6 @@ | |||
28 | #include <asm/mach/irq.h> | 29 | #include <asm/mach/irq.h> |
29 | #include <asm/hardware/vic.h> | 30 | #include <asm/hardware/vic.h> |
30 | 31 | ||
31 | static void vic_ack_irq(unsigned int irq) | ||
32 | { | ||
33 | void __iomem *base = get_irq_chip_data(irq); | ||
34 | irq &= 31; | ||
35 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); | ||
36 | /* moreover, clear the soft-triggered, in case it was the reason */ | ||
37 | writel(1 << irq, base + VIC_INT_SOFT_CLEAR); | ||
38 | } | ||
39 | |||
40 | static void vic_mask_irq(unsigned int irq) | ||
41 | { | ||
42 | void __iomem *base = get_irq_chip_data(irq); | ||
43 | irq &= 31; | ||
44 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); | ||
45 | } | ||
46 | |||
47 | static void vic_unmask_irq(unsigned int irq) | ||
48 | { | ||
49 | void __iomem *base = get_irq_chip_data(irq); | ||
50 | irq &= 31; | ||
51 | writel(1 << irq, base + VIC_INT_ENABLE); | ||
52 | } | ||
53 | |||
54 | /** | ||
55 | * vic_init2 - common initialisation code | ||
56 | * @base: Base of the VIC. | ||
57 | * | ||
58 | * Common initialisation code for registeration | ||
59 | * and resume. | ||
60 | */ | ||
61 | static void vic_init2(void __iomem *base) | ||
62 | { | ||
63 | int i; | ||
64 | |||
65 | for (i = 0; i < 16; i++) { | ||
66 | void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); | ||
67 | writel(VIC_VECT_CNTL_ENABLE | i, reg); | ||
68 | } | ||
69 | |||
70 | writel(32, base + VIC_PL190_DEF_VECT_ADDR); | ||
71 | } | ||
72 | |||
73 | #if defined(CONFIG_PM) | 32 | #if defined(CONFIG_PM) |
74 | /** | 33 | /** |
75 | * struct vic_device - VIC PM device | 34 | * struct vic_device - VIC PM device |
@@ -99,13 +58,34 @@ struct vic_device { | |||
99 | /* we cannot allocate memory when VICs are initially registered */ | 58 | /* we cannot allocate memory when VICs are initially registered */ |
100 | static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; | 59 | static struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; |
101 | 60 | ||
61 | static int vic_id; | ||
62 | |||
102 | static inline struct vic_device *to_vic(struct sys_device *sys) | 63 | static inline struct vic_device *to_vic(struct sys_device *sys) |
103 | { | 64 | { |
104 | return container_of(sys, struct vic_device, sysdev); | 65 | return container_of(sys, struct vic_device, sysdev); |
105 | } | 66 | } |
67 | #endif /* CONFIG_PM */ | ||
106 | 68 | ||
107 | static int vic_id; | 69 | /** |
70 | * vic_init2 - common initialisation code | ||
71 | * @base: Base of the VIC. | ||
72 | * | ||
73 | * Common initialisation code for registeration | ||
74 | * and resume. | ||
75 | */ | ||
76 | static void vic_init2(void __iomem *base) | ||
77 | { | ||
78 | int i; | ||
79 | |||
80 | for (i = 0; i < 16; i++) { | ||
81 | void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); | ||
82 | writel(VIC_VECT_CNTL_ENABLE | i, reg); | ||
83 | } | ||
84 | |||
85 | writel(32, base + VIC_PL190_DEF_VECT_ADDR); | ||
86 | } | ||
108 | 87 | ||
88 | #if defined(CONFIG_PM) | ||
109 | static int vic_class_resume(struct sys_device *dev) | 89 | static int vic_class_resume(struct sys_device *dev) |
110 | { | 90 | { |
111 | struct vic_device *vic = to_vic(dev); | 91 | struct vic_device *vic = to_vic(dev); |
@@ -159,31 +139,6 @@ struct sysdev_class vic_class = { | |||
159 | }; | 139 | }; |
160 | 140 | ||
161 | /** | 141 | /** |
162 | * vic_pm_register - Register a VIC for later power management control | ||
163 | * @base: The base address of the VIC. | ||
164 | * @irq: The base IRQ for the VIC. | ||
165 | * @resume_sources: bitmask of interrupts allowed for resume sources. | ||
166 | * | ||
167 | * Register the VIC with the system device tree so that it can be notified | ||
168 | * of suspend and resume requests and ensure that the correct actions are | ||
169 | * taken to re-instate the settings on resume. | ||
170 | */ | ||
171 | static void __init vic_pm_register(void __iomem *base, unsigned int irq, u32 resume_sources) | ||
172 | { | ||
173 | struct vic_device *v; | ||
174 | |||
175 | if (vic_id >= ARRAY_SIZE(vic_devices)) | ||
176 | printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__); | ||
177 | else { | ||
178 | v = &vic_devices[vic_id]; | ||
179 | v->base = base; | ||
180 | v->resume_sources = resume_sources; | ||
181 | v->irq = irq; | ||
182 | vic_id++; | ||
183 | } | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * vic_pm_init - initicall to register VIC pm | 142 | * vic_pm_init - initicall to register VIC pm |
188 | * | 143 | * |
189 | * This is called via late_initcall() to register | 144 | * This is called via late_initcall() to register |
@@ -219,9 +174,60 @@ static int __init vic_pm_init(void) | |||
219 | 174 | ||
220 | return 0; | 175 | return 0; |
221 | } | 176 | } |
222 | |||
223 | late_initcall(vic_pm_init); | 177 | late_initcall(vic_pm_init); |
224 | 178 | ||
179 | /** | ||
180 | * vic_pm_register - Register a VIC for later power management control | ||
181 | * @base: The base address of the VIC. | ||
182 | * @irq: The base IRQ for the VIC. | ||
183 | * @resume_sources: bitmask of interrupts allowed for resume sources. | ||
184 | * | ||
185 | * Register the VIC with the system device tree so that it can be notified | ||
186 | * of suspend and resume requests and ensure that the correct actions are | ||
187 | * taken to re-instate the settings on resume. | ||
188 | */ | ||
189 | static void __init vic_pm_register(void __iomem *base, unsigned int irq, u32 resume_sources) | ||
190 | { | ||
191 | struct vic_device *v; | ||
192 | |||
193 | if (vic_id >= ARRAY_SIZE(vic_devices)) | ||
194 | printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__); | ||
195 | else { | ||
196 | v = &vic_devices[vic_id]; | ||
197 | v->base = base; | ||
198 | v->resume_sources = resume_sources; | ||
199 | v->irq = irq; | ||
200 | vic_id++; | ||
201 | } | ||
202 | } | ||
203 | #else | ||
204 | static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { } | ||
205 | #endif /* CONFIG_PM */ | ||
206 | |||
207 | static void vic_ack_irq(unsigned int irq) | ||
208 | { | ||
209 | void __iomem *base = get_irq_chip_data(irq); | ||
210 | irq &= 31; | ||
211 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); | ||
212 | /* moreover, clear the soft-triggered, in case it was the reason */ | ||
213 | writel(1 << irq, base + VIC_INT_SOFT_CLEAR); | ||
214 | } | ||
215 | |||
216 | static void vic_mask_irq(unsigned int irq) | ||
217 | { | ||
218 | void __iomem *base = get_irq_chip_data(irq); | ||
219 | irq &= 31; | ||
220 | writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); | ||
221 | } | ||
222 | |||
223 | static void vic_unmask_irq(unsigned int irq) | ||
224 | { | ||
225 | void __iomem *base = get_irq_chip_data(irq); | ||
226 | irq &= 31; | ||
227 | writel(1 << irq, base + VIC_INT_ENABLE); | ||
228 | } | ||
229 | |||
230 | #if defined(CONFIG_PM) | ||
225 | static struct vic_device *vic_from_irq(unsigned int irq) | 231 | static struct vic_device *vic_from_irq(unsigned int irq) |
226 | { | 232 | { |
227 | struct vic_device *v = vic_devices; | 233 | struct vic_device *v = vic_devices; |
@@ -255,10 +261,7 @@ static int vic_set_wake(unsigned int irq, unsigned int on) | |||
255 | 261 | ||
256 | return 0; | 262 | return 0; |
257 | } | 263 | } |
258 | |||
259 | #else | 264 | #else |
260 | static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg1) { } | ||
261 | |||
262 | #define vic_set_wake NULL | 265 | #define vic_set_wake NULL |
263 | #endif /* CONFIG_PM */ | 266 | #endif /* CONFIG_PM */ |
264 | 267 | ||
@@ -270,9 +273,62 @@ static struct irq_chip vic_chip = { | |||
270 | .set_wake = vic_set_wake, | 273 | .set_wake = vic_set_wake, |
271 | }; | 274 | }; |
272 | 275 | ||
273 | /* The PL190 cell from ARM has been modified by ST, so handle both here */ | 276 | /* |
274 | static void vik_init_st(void __iomem *base, unsigned int irq_start, | 277 | * The PL190 cell from ARM has been modified by ST to handle 64 interrupts. |
275 | u32 vic_sources); | 278 | * The original cell has 32 interrupts, while the modified one has 64, |
279 | * replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case | ||
280 | * the probe function is called twice, with base set to offset 000 | ||
281 | * and 020 within the page. We call this "second block". | ||
282 | */ | ||
283 | static void __init vic_init_st(void __iomem *base, unsigned int irq_start, | ||
284 | u32 vic_sources) | ||
285 | { | ||
286 | unsigned int i; | ||
287 | int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; | ||
288 | |||
289 | /* Disable all interrupts initially. */ | ||
290 | |||
291 | writel(0, base + VIC_INT_SELECT); | ||
292 | writel(0, base + VIC_INT_ENABLE); | ||
293 | writel(~0, base + VIC_INT_ENABLE_CLEAR); | ||
294 | writel(0, base + VIC_IRQ_STATUS); | ||
295 | writel(0, base + VIC_ITCR); | ||
296 | writel(~0, base + VIC_INT_SOFT_CLEAR); | ||
297 | |||
298 | /* | ||
299 | * Make sure we clear all existing interrupts. The vector registers | ||
300 | * in this cell are after the second block of general registers, | ||
301 | * so we can address them using standard offsets, but only from | ||
302 | * the second base address, which is 0x20 in the page | ||
303 | */ | ||
304 | if (vic_2nd_block) { | ||
305 | writel(0, base + VIC_PL190_VECT_ADDR); | ||
306 | for (i = 0; i < 19; i++) { | ||
307 | unsigned int value; | ||
308 | |||
309 | value = readl(base + VIC_PL190_VECT_ADDR); | ||
310 | writel(value, base + VIC_PL190_VECT_ADDR); | ||
311 | } | ||
312 | /* ST has 16 vectors as well, but we don't enable them by now */ | ||
313 | for (i = 0; i < 16; i++) { | ||
314 | void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); | ||
315 | writel(0, reg); | ||
316 | } | ||
317 | |||
318 | writel(32, base + VIC_PL190_DEF_VECT_ADDR); | ||
319 | } | ||
320 | |||
321 | for (i = 0; i < 32; i++) { | ||
322 | if (vic_sources & (1 << i)) { | ||
323 | unsigned int irq = irq_start + i; | ||
324 | |||
325 | set_irq_chip(irq, &vic_chip); | ||
326 | set_irq_chip_data(irq, base); | ||
327 | set_irq_handler(irq, handle_level_irq); | ||
328 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | ||
329 | } | ||
330 | } | ||
331 | } | ||
276 | 332 | ||
277 | /** | 333 | /** |
278 | * vic_init - initialise a vectored interrupt controller | 334 | * vic_init - initialise a vectored interrupt controller |
@@ -299,7 +355,7 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, | |||
299 | 355 | ||
300 | switch(vendor) { | 356 | switch(vendor) { |
301 | case AMBA_VENDOR_ST: | 357 | case AMBA_VENDOR_ST: |
302 | vik_init_st(base, irq_start, vic_sources); | 358 | vic_init_st(base, irq_start, vic_sources); |
303 | return; | 359 | return; |
304 | default: | 360 | default: |
305 | printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); | 361 | printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); |
@@ -343,60 +399,3 @@ void __init vic_init(void __iomem *base, unsigned int irq_start, | |||
343 | 399 | ||
344 | vic_pm_register(base, irq_start, resume_sources); | 400 | vic_pm_register(base, irq_start, resume_sources); |
345 | } | 401 | } |
346 | |||
347 | /* | ||
348 | * The PL190 cell from ARM has been modified by ST to handle 64 interrupts. | ||
349 | * The original cell has 32 interrupts, while the modified one has 64, | ||
350 | * replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case | ||
351 | * the probe function is called twice, with base set to offset 000 | ||
352 | * and 020 within the page. We call this "second block". | ||
353 | */ | ||
354 | static void __init vik_init_st(void __iomem *base, unsigned int irq_start, | ||
355 | u32 vic_sources) | ||
356 | { | ||
357 | unsigned int i; | ||
358 | int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; | ||
359 | |||
360 | /* Disable all interrupts initially. */ | ||
361 | |||
362 | writel(0, base + VIC_INT_SELECT); | ||
363 | writel(0, base + VIC_INT_ENABLE); | ||
364 | writel(~0, base + VIC_INT_ENABLE_CLEAR); | ||
365 | writel(0, base + VIC_IRQ_STATUS); | ||
366 | writel(0, base + VIC_ITCR); | ||
367 | writel(~0, base + VIC_INT_SOFT_CLEAR); | ||
368 | |||
369 | /* | ||
370 | * Make sure we clear all existing interrupts. The vector registers | ||
371 | * in this cell are after the second block of general registers, | ||
372 | * so we can address them using standard offsets, but only from | ||
373 | * the second base address, which is 0x20 in the page | ||
374 | */ | ||
375 | if (vic_2nd_block) { | ||
376 | writel(0, base + VIC_PL190_VECT_ADDR); | ||
377 | for (i = 0; i < 19; i++) { | ||
378 | unsigned int value; | ||
379 | |||
380 | value = readl(base + VIC_PL190_VECT_ADDR); | ||
381 | writel(value, base + VIC_PL190_VECT_ADDR); | ||
382 | } | ||
383 | /* ST has 16 vectors as well, but we don't enable them by now */ | ||
384 | for (i = 0; i < 16; i++) { | ||
385 | void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); | ||
386 | writel(0, reg); | ||
387 | } | ||
388 | |||
389 | writel(32, base + VIC_PL190_DEF_VECT_ADDR); | ||
390 | } | ||
391 | |||
392 | for (i = 0; i < 32; i++) { | ||
393 | if (vic_sources & (1 << i)) { | ||
394 | unsigned int irq = irq_start + i; | ||
395 | |||
396 | set_irq_chip(irq, &vic_chip); | ||
397 | set_irq_chip_data(irq, base); | ||
398 | set_irq_handler(irq, handle_level_irq); | ||
399 | set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); | ||
400 | } | ||
401 | } | ||
402 | } | ||