1 | /* |
2 | * Interrupt handling for GE FPGA based PIC |
3 | * |
4 | * Author: Martyn Welch <martyn.welch@ge.com> |
5 | * |
6 | * 2008 (c) GE Intelligent Platforms Embedded Systems, Inc. |
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> |
17 | #include <linux/irqdomain.h> |
18 | #include <linux/interrupt.h> |
19 | #include <linux/of_address.h> |
20 | #include <linux/of_irq.h> |
21 | #include <linux/spinlock.h> |
22 | |
23 | #include <asm/byteorder.h> |
24 | #include <asm/io.h> |
25 | #include <asm/irq.h> |
26 | |
27 | #include "ge_pic.h" |
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 | |
51 | |
52 | static DEFINE_RAW_SPINLOCK(gef_pic_lock); |
53 | |
54 | static void __iomem *gef_pic_irq_reg_base; |
55 | static struct irq_domain *gef_pic_irq_host; |
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 | |
96 | static void gef_pic_cascade(struct irq_desc *desc) |
97 | { |
98 | struct irq_chip *chip = irq_desc_get_chip(desc); |
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 | |
107 | if (cascade_irq) |
108 | generic_handle_irq(irq: cascade_irq); |
109 | |
110 | chip->irq_eoi(&desc->irq_data); |
111 | } |
112 | |
113 | static void gef_pic_mask(struct irq_data *d) |
114 | { |
115 | unsigned long flags; |
116 | unsigned int hwirq = irqd_to_hwirq(d); |
117 | u32 mask; |
118 | |
119 | raw_spin_lock_irqsave(&gef_pic_lock, flags); |
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); |
123 | raw_spin_unlock_irqrestore(&gef_pic_lock, flags); |
124 | } |
125 | |
126 | static void gef_pic_mask_ack(struct irq_data *d) |
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 | */ |
131 | gef_pic_mask(d); |
132 | } |
133 | |
134 | static void gef_pic_unmask(struct irq_data *d) |
135 | { |
136 | unsigned long flags; |
137 | unsigned int hwirq = irqd_to_hwirq(d); |
138 | u32 mask; |
139 | |
140 | raw_spin_lock_irqsave(&gef_pic_lock, flags); |
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); |
144 | raw_spin_unlock_irqrestore(&gef_pic_lock, flags); |
145 | } |
146 | |
147 | static struct irq_chip gef_pic_chip = { |
148 | .name = "gefp" , |
149 | .irq_mask = gef_pic_mask, |
150 | .irq_mask_ack = gef_pic_mask_ack, |
151 | .irq_unmask = gef_pic_unmask, |
152 | }; |
153 | |
154 | |
155 | /* When an interrupt is being configured, this call allows some flexibility |
156 | * in deciding which irq_chip structure is used |
157 | */ |
158 | static int gef_pic_host_map(struct irq_domain *h, unsigned int virq, |
159 | irq_hw_number_t hwirq) |
160 | { |
161 | /* All interrupts are LEVEL sensitive */ |
162 | irq_set_status_flags(irq: virq, set: IRQ_LEVEL); |
163 | irq_set_chip_and_handler(irq: virq, chip: &gef_pic_chip, handle: handle_level_irq); |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | static int gef_pic_host_xlate(struct irq_domain *h, struct device_node *ct, |
169 | const u32 *intspec, unsigned int intsize, |
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 | |
182 | static const struct irq_domain_ops gef_pic_host_ops = { |
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(node: np, index: 0); |
197 | |
198 | raw_spin_lock_irqsave(&gef_pic_lock, flags); |
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 | |
207 | raw_spin_unlock_irqrestore(&gef_pic_lock, flags); |
208 | |
209 | /* Map controller */ |
210 | gef_pic_cascade_irq = irq_of_parse_and_map(node: np, index: 0); |
211 | if (!gef_pic_cascade_irq) { |
212 | printk(KERN_ERR "SBC610: failed to map cascade interrupt" ); |
213 | return; |
214 | } |
215 | |
216 | /* Setup an irq_domain structure */ |
217 | gef_pic_irq_host = irq_domain_add_linear(of_node: np, GEF_PIC_NUM_IRQS, |
218 | ops: &gef_pic_host_ops, NULL); |
219 | if (gef_pic_irq_host == NULL) |
220 | return; |
221 | |
222 | /* Chain with parent controller */ |
223 | irq_set_chained_handler(irq: gef_pic_cascade_irq, handle: gef_pic_cascade); |
224 | } |
225 | |
226 | /* |
227 | * This is called when we receive an interrupt with apparently comes from this |
228 | * chip - check, returning the highest interrupt generated or return 0. |
229 | */ |
230 | unsigned int gef_pic_get_irq(void) |
231 | { |
232 | u32 cause, mask, active; |
233 | unsigned int virq = 0; |
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(domain: gef_pic_irq_host, |
248 | hwirq: (irq_hw_number_t)hwirq); |
249 | } |
250 | |
251 | return virq; |
252 | } |
253 | |
254 | |