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