Commit | Line | Data |
---|---|---|
3a470247 | 1 | /* |
948e78c3 | 2 | * Interrupt handling for GE FPGA based PIC |
3a470247 | 3 | * |
948e78c3 | 4 | * Author: Martyn Welch <martyn.welch@ge.com> |
3a470247 | 5 | * |
948e78c3 | 6 | * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. |
3a470247 MW |
7 | * |
8 | * This file is licensed under the terms of the GNU General Public License | |
9 | * version 2. This program is licensed "as is" without any warranty of any | |
10 | * kind, whether express or implied. | |
11 | */ | |
12 | ||
13 | #include <linux/stddef.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/init.h> | |
16 | #include <linux/irq.h> | |
e6f6390a | 17 | #include <linux/irqdomain.h> |
3a470247 | 18 | #include <linux/interrupt.h> |
e6f6390a CL |
19 | #include <linux/of_address.h> |
20 | #include <linux/of_irq.h> | |
3a470247 MW |
21 | #include <linux/spinlock.h> |
22 | ||
23 | #include <asm/byteorder.h> | |
24 | #include <asm/io.h> | |
3a470247 MW |
25 | #include <asm/irq.h> |
26 | ||
44b24b74 | 27 | #include "ge_pic.h" |
3a470247 MW |
28 | |
29 | #define DEBUG | |
30 | #undef DEBUG | |
31 | ||
32 | #ifdef DEBUG | |
33 | #define DBG(fmt...) do { printk(KERN_DEBUG "gef_pic: " fmt); } while (0) | |
34 | #else | |
35 | #define DBG(fmt...) do { } while (0) | |
36 | #endif | |
37 | ||
38 | #define GEF_PIC_NUM_IRQS 32 | |
39 | ||
40 | /* Interrupt Controller Interface Registers */ | |
41 | #define GEF_PIC_INTR_STATUS 0x0000 | |
42 | ||
43 | #define GEF_PIC_INTR_MASK(cpu) (0x0010 + (0x4 * cpu)) | |
44 | #define GEF_PIC_CPU0_INTR_MASK GEF_PIC_INTR_MASK(0) | |
45 | #define GEF_PIC_CPU1_INTR_MASK GEF_PIC_INTR_MASK(1) | |
46 | ||
47 | #define GEF_PIC_MCP_MASK(cpu) (0x0018 + (0x4 * cpu)) | |
48 | #define GEF_PIC_CPU0_MCP_MASK GEF_PIC_MCP_MASK(0) | |
49 | #define GEF_PIC_CPU1_MCP_MASK GEF_PIC_MCP_MASK(1) | |
50 | ||
3a470247 | 51 | |
6f3d395a | 52 | static DEFINE_RAW_SPINLOCK(gef_pic_lock); |
3a470247 MW |
53 | |
54 | static void __iomem *gef_pic_irq_reg_base; | |
bae1d8f1 | 55 | static struct irq_domain *gef_pic_irq_host; |
3a470247 MW |
56 | static int gef_pic_cascade_irq; |
57 | ||
58 | /* | |
59 | * Interrupt Controller Handling | |
60 | * | |
61 | * The interrupt controller handles interrupts for most on board interrupts, | |
62 | * apart from PCI interrupts. For example on SBC610: | |
63 | * | |
64 | * 17:31 RO Reserved | |
65 | * 16 RO PCI Express Doorbell 3 Status | |
66 | * 15 RO PCI Express Doorbell 2 Status | |
67 | * 14 RO PCI Express Doorbell 1 Status | |
68 | * 13 RO PCI Express Doorbell 0 Status | |
69 | * 12 RO Real Time Clock Interrupt Status | |
70 | * 11 RO Temperature Interrupt Status | |
71 | * 10 RO Temperature Critical Interrupt Status | |
72 | * 9 RO Ethernet PHY1 Interrupt Status | |
73 | * 8 RO Ethernet PHY3 Interrupt Status | |
74 | * 7 RO PEX8548 Interrupt Status | |
75 | * 6 RO Reserved | |
76 | * 5 RO Watchdog 0 Interrupt Status | |
77 | * 4 RO Watchdog 1 Interrupt Status | |
78 | * 3 RO AXIS Message FIFO A Interrupt Status | |
79 | * 2 RO AXIS Message FIFO B Interrupt Status | |
80 | * 1 RO AXIS Message FIFO C Interrupt Status | |
81 | * 0 RO AXIS Message FIFO D Interrupt Status | |
82 | * | |
83 | * Interrupts can be forwarded to one of two output lines. Nothing | |
84 | * clever is done, so if the masks are incorrectly set, a single input | |
85 | * interrupt could generate interrupts on both output lines! | |
86 | * | |
87 | * The dual lines are there to allow the chained interrupts to be easily | |
88 | * passed into two different cores. We currently do not use this functionality | |
89 | * in this driver. | |
90 | * | |
91 | * Controller can also be configured to generate Machine checks (MCP), again on | |
92 | * two lines, to be attached to two different cores. It is suggested that these | |
93 | * should be masked out. | |
94 | */ | |
95 | ||
bd0b9ac4 | 96 | static void gef_pic_cascade(struct irq_desc *desc) |
3a470247 | 97 | { |
ec775d0e | 98 | struct irq_chip *chip = irq_desc_get_chip(desc); |
3a470247 MW |
99 | unsigned int cascade_irq; |
100 | ||
101 | /* | |
102 | * See if we actually have an interrupt, call generic handling code if | |
103 | * we do. | |
104 | */ | |
105 | cascade_irq = gef_pic_get_irq(); | |
106 | ||
ef24ba70 | 107 | if (cascade_irq) |
3a470247 MW |
108 | generic_handle_irq(cascade_irq); |
109 | ||
5b250889 | 110 | chip->irq_eoi(&desc->irq_data); |
3a470247 MW |
111 | } |
112 | ||
5b250889 | 113 | static void gef_pic_mask(struct irq_data *d) |
3a470247 MW |
114 | { |
115 | unsigned long flags; | |
476eb491 | 116 | unsigned int hwirq = irqd_to_hwirq(d); |
3a470247 MW |
117 | u32 mask; |
118 | ||
6f3d395a | 119 | raw_spin_lock_irqsave(&gef_pic_lock, flags); |
3a470247 MW |
120 | mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); |
121 | mask &= ~(1 << hwirq); | |
122 | out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); | |
6f3d395a | 123 | raw_spin_unlock_irqrestore(&gef_pic_lock, flags); |
3a470247 MW |
124 | } |
125 | ||
5b250889 | 126 | static void gef_pic_mask_ack(struct irq_data *d) |
3a470247 MW |
127 | { |
128 | /* Don't think we actually have to do anything to ack an interrupt, | |
129 | * we just need to clear down the devices interrupt and it will go away | |
130 | */ | |
5b250889 | 131 | gef_pic_mask(d); |
3a470247 MW |
132 | } |
133 | ||
5b250889 | 134 | static void gef_pic_unmask(struct irq_data *d) |
3a470247 MW |
135 | { |
136 | unsigned long flags; | |
476eb491 | 137 | unsigned int hwirq = irqd_to_hwirq(d); |
3a470247 MW |
138 | u32 mask; |
139 | ||
6f3d395a | 140 | raw_spin_lock_irqsave(&gef_pic_lock, flags); |
3a470247 MW |
141 | mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); |
142 | mask |= (1 << hwirq); | |
143 | out_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0), mask); | |
6f3d395a | 144 | raw_spin_unlock_irqrestore(&gef_pic_lock, flags); |
3a470247 MW |
145 | } |
146 | ||
147 | static struct irq_chip gef_pic_chip = { | |
b27df672 | 148 | .name = "gefp", |
5b250889 LB |
149 | .irq_mask = gef_pic_mask, |
150 | .irq_mask_ack = gef_pic_mask_ack, | |
151 | .irq_unmask = gef_pic_unmask, | |
3a470247 MW |
152 | }; |
153 | ||
154 | ||
1fd02f66 | 155 | /* When an interrupt is being configured, this call allows some flexibility |
3a470247 MW |
156 | * in deciding which irq_chip structure is used |
157 | */ | |
bae1d8f1 | 158 | static int gef_pic_host_map(struct irq_domain *h, unsigned int virq, |
3a470247 MW |
159 | irq_hw_number_t hwirq) |
160 | { | |
161 | /* All interrupts are LEVEL sensitive */ | |
98488db9 | 162 | irq_set_status_flags(virq, IRQ_LEVEL); |
ec775d0e | 163 | irq_set_chip_and_handler(virq, &gef_pic_chip, handle_level_irq); |
3a470247 MW |
164 | |
165 | return 0; | |
166 | } | |
167 | ||
bae1d8f1 | 168 | static int gef_pic_host_xlate(struct irq_domain *h, struct device_node *ct, |
40d50cf7 | 169 | const u32 *intspec, unsigned int intsize, |
3a470247 MW |
170 | irq_hw_number_t *out_hwirq, unsigned int *out_flags) |
171 | { | |
172 | ||
173 | *out_hwirq = intspec[0]; | |
174 | if (intsize > 1) | |
175 | *out_flags = intspec[1]; | |
176 | else | |
177 | *out_flags = IRQ_TYPE_LEVEL_HIGH; | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
9f70b8eb | 182 | static const struct irq_domain_ops gef_pic_host_ops = { |
3a470247 MW |
183 | .map = gef_pic_host_map, |
184 | .xlate = gef_pic_host_xlate, | |
185 | }; | |
186 | ||
187 | ||
188 | /* | |
189 | * Initialisation of PIC, this should be called in BSP | |
190 | */ | |
191 | void __init gef_pic_init(struct device_node *np) | |
192 | { | |
193 | unsigned long flags; | |
194 | ||
195 | /* Map the devices registers into memory */ | |
196 | gef_pic_irq_reg_base = of_iomap(np, 0); | |
197 | ||
6f3d395a | 198 | raw_spin_lock_irqsave(&gef_pic_lock, flags); |
3a470247 MW |
199 | |
200 | /* Initialise everything as masked. */ | |
201 | out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_INTR_MASK, 0); | |
202 | out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_INTR_MASK, 0); | |
203 | ||
204 | out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU0_MCP_MASK, 0); | |
205 | out_be32(gef_pic_irq_reg_base + GEF_PIC_CPU1_MCP_MASK, 0); | |
206 | ||
6f3d395a | 207 | raw_spin_unlock_irqrestore(&gef_pic_lock, flags); |
3a470247 MW |
208 | |
209 | /* Map controller */ | |
210 | gef_pic_cascade_irq = irq_of_parse_and_map(np, 0); | |
ef24ba70 | 211 | if (!gef_pic_cascade_irq) { |
3a470247 MW |
212 | printk(KERN_ERR "SBC610: failed to map cascade interrupt"); |
213 | return; | |
214 | } | |
215 | ||
bae1d8f1 | 216 | /* Setup an irq_domain structure */ |
a8db8cf0 GL |
217 | gef_pic_irq_host = irq_domain_add_linear(np, GEF_PIC_NUM_IRQS, |
218 | &gef_pic_host_ops, NULL); | |
3a470247 MW |
219 | if (gef_pic_irq_host == NULL) |
220 | return; | |
221 | ||
222 | /* Chain with parent controller */ | |
ec775d0e | 223 | irq_set_chained_handler(gef_pic_cascade_irq, gef_pic_cascade); |
3a470247 MW |
224 | } |
225 | ||
226 | /* | |
227 | * This is called when we receive an interrupt with apparently comes from this | |
ef24ba70 | 228 | * chip - check, returning the highest interrupt generated or return 0. |
3a470247 MW |
229 | */ |
230 | unsigned int gef_pic_get_irq(void) | |
231 | { | |
232 | u32 cause, mask, active; | |
ef24ba70 | 233 | unsigned int virq = 0; |
3a470247 MW |
234 | int hwirq; |
235 | ||
236 | cause = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_STATUS); | |
237 | ||
238 | mask = in_be32(gef_pic_irq_reg_base + GEF_PIC_INTR_MASK(0)); | |
239 | ||
240 | active = cause & mask; | |
241 | ||
242 | if (active) { | |
243 | for (hwirq = GEF_PIC_NUM_IRQS - 1; hwirq > -1; hwirq--) { | |
244 | if (active & (0x1 << hwirq)) | |
245 | break; | |
246 | } | |
247 | virq = irq_linear_revmap(gef_pic_irq_host, | |
248 | (irq_hw_number_t)hwirq); | |
249 | } | |
250 | ||
251 | return virq; | |
252 | } | |
253 |