Commit | Line | Data |
---|---|---|
98658538 LY |
1 | /* |
2 | * arch/powerpc/sysdev/qe_lib/qe_ic.c | |
3 | * | |
8a56e1ee | 4 | * Copyright (C) 2006 Freescale Semiconductor, Inc. All rights reserved. |
98658538 LY |
5 | * |
6 | * Author: Li Yang <leoli@freescale.com> | |
7 | * Based on code from Shlomi Gridish <gridish@freescale.com> | |
8 | * | |
9 | * QUICC ENGINE Interrupt Controller | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify it | |
12 | * under the terms of the GNU General Public License as published by the | |
13 | * Free Software Foundation; either version 2 of the License, or (at your | |
14 | * option) any later version. | |
15 | */ | |
16 | ||
17 | #include <linux/kernel.h> | |
18 | #include <linux/init.h> | |
19 | #include <linux/errno.h> | |
20 | #include <linux/reboot.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/stddef.h> | |
23 | #include <linux/sched.h> | |
24 | #include <linux/signal.h> | |
98658538 | 25 | #include <linux/device.h> |
98658538 LY |
26 | #include <linux/spinlock.h> |
27 | #include <asm/irq.h> | |
28 | #include <asm/io.h> | |
29 | #include <asm/prom.h> | |
30 | #include <asm/qe_ic.h> | |
31 | ||
32 | #include "qe_ic.h" | |
33 | ||
43a5a01b | 34 | static DEFINE_RAW_SPINLOCK(qe_ic_lock); |
98658538 LY |
35 | |
36 | static struct qe_ic_info qe_ic_info[] = { | |
37 | [1] = { | |
38 | .mask = 0x00008000, | |
39 | .mask_reg = QEIC_CIMR, | |
40 | .pri_code = 0, | |
41 | .pri_reg = QEIC_CIPWCC, | |
42 | }, | |
43 | [2] = { | |
44 | .mask = 0x00004000, | |
45 | .mask_reg = QEIC_CIMR, | |
46 | .pri_code = 1, | |
47 | .pri_reg = QEIC_CIPWCC, | |
48 | }, | |
49 | [3] = { | |
50 | .mask = 0x00002000, | |
51 | .mask_reg = QEIC_CIMR, | |
52 | .pri_code = 2, | |
53 | .pri_reg = QEIC_CIPWCC, | |
54 | }, | |
55 | [10] = { | |
56 | .mask = 0x00000040, | |
57 | .mask_reg = QEIC_CIMR, | |
58 | .pri_code = 1, | |
59 | .pri_reg = QEIC_CIPZCC, | |
60 | }, | |
61 | [11] = { | |
62 | .mask = 0x00000020, | |
63 | .mask_reg = QEIC_CIMR, | |
64 | .pri_code = 2, | |
65 | .pri_reg = QEIC_CIPZCC, | |
66 | }, | |
67 | [12] = { | |
68 | .mask = 0x00000010, | |
69 | .mask_reg = QEIC_CIMR, | |
70 | .pri_code = 3, | |
71 | .pri_reg = QEIC_CIPZCC, | |
72 | }, | |
73 | [13] = { | |
74 | .mask = 0x00000008, | |
75 | .mask_reg = QEIC_CIMR, | |
76 | .pri_code = 4, | |
77 | .pri_reg = QEIC_CIPZCC, | |
78 | }, | |
79 | [14] = { | |
80 | .mask = 0x00000004, | |
81 | .mask_reg = QEIC_CIMR, | |
82 | .pri_code = 5, | |
83 | .pri_reg = QEIC_CIPZCC, | |
84 | }, | |
85 | [15] = { | |
86 | .mask = 0x00000002, | |
87 | .mask_reg = QEIC_CIMR, | |
88 | .pri_code = 6, | |
89 | .pri_reg = QEIC_CIPZCC, | |
90 | }, | |
91 | [20] = { | |
92 | .mask = 0x10000000, | |
93 | .mask_reg = QEIC_CRIMR, | |
94 | .pri_code = 3, | |
95 | .pri_reg = QEIC_CIPRTA, | |
96 | }, | |
97 | [25] = { | |
98 | .mask = 0x00800000, | |
99 | .mask_reg = QEIC_CRIMR, | |
100 | .pri_code = 0, | |
101 | .pri_reg = QEIC_CIPRTB, | |
102 | }, | |
103 | [26] = { | |
104 | .mask = 0x00400000, | |
105 | .mask_reg = QEIC_CRIMR, | |
106 | .pri_code = 1, | |
107 | .pri_reg = QEIC_CIPRTB, | |
108 | }, | |
109 | [27] = { | |
110 | .mask = 0x00200000, | |
111 | .mask_reg = QEIC_CRIMR, | |
112 | .pri_code = 2, | |
113 | .pri_reg = QEIC_CIPRTB, | |
114 | }, | |
115 | [28] = { | |
116 | .mask = 0x00100000, | |
117 | .mask_reg = QEIC_CRIMR, | |
118 | .pri_code = 3, | |
119 | .pri_reg = QEIC_CIPRTB, | |
120 | }, | |
121 | [32] = { | |
122 | .mask = 0x80000000, | |
123 | .mask_reg = QEIC_CIMR, | |
124 | .pri_code = 0, | |
125 | .pri_reg = QEIC_CIPXCC, | |
126 | }, | |
127 | [33] = { | |
128 | .mask = 0x40000000, | |
129 | .mask_reg = QEIC_CIMR, | |
130 | .pri_code = 1, | |
131 | .pri_reg = QEIC_CIPXCC, | |
132 | }, | |
133 | [34] = { | |
134 | .mask = 0x20000000, | |
135 | .mask_reg = QEIC_CIMR, | |
136 | .pri_code = 2, | |
137 | .pri_reg = QEIC_CIPXCC, | |
138 | }, | |
139 | [35] = { | |
140 | .mask = 0x10000000, | |
141 | .mask_reg = QEIC_CIMR, | |
142 | .pri_code = 3, | |
143 | .pri_reg = QEIC_CIPXCC, | |
144 | }, | |
145 | [36] = { | |
146 | .mask = 0x08000000, | |
147 | .mask_reg = QEIC_CIMR, | |
148 | .pri_code = 4, | |
149 | .pri_reg = QEIC_CIPXCC, | |
150 | }, | |
151 | [40] = { | |
152 | .mask = 0x00800000, | |
153 | .mask_reg = QEIC_CIMR, | |
154 | .pri_code = 0, | |
155 | .pri_reg = QEIC_CIPYCC, | |
156 | }, | |
157 | [41] = { | |
158 | .mask = 0x00400000, | |
159 | .mask_reg = QEIC_CIMR, | |
160 | .pri_code = 1, | |
161 | .pri_reg = QEIC_CIPYCC, | |
162 | }, | |
163 | [42] = { | |
164 | .mask = 0x00200000, | |
165 | .mask_reg = QEIC_CIMR, | |
166 | .pri_code = 2, | |
167 | .pri_reg = QEIC_CIPYCC, | |
168 | }, | |
169 | [43] = { | |
170 | .mask = 0x00100000, | |
171 | .mask_reg = QEIC_CIMR, | |
172 | .pri_code = 3, | |
173 | .pri_reg = QEIC_CIPYCC, | |
174 | }, | |
175 | }; | |
176 | ||
177 | static inline u32 qe_ic_read(volatile __be32 __iomem * base, unsigned int reg) | |
178 | { | |
179 | return in_be32(base + (reg >> 2)); | |
180 | } | |
181 | ||
182 | static inline void qe_ic_write(volatile __be32 __iomem * base, unsigned int reg, | |
183 | u32 value) | |
184 | { | |
185 | out_be32(base + (reg >> 2), value); | |
186 | } | |
187 | ||
188 | static inline struct qe_ic *qe_ic_from_irq(unsigned int virq) | |
189 | { | |
ec775d0e | 190 | return irq_get_chip_data(virq); |
3a0adfab LB |
191 | } |
192 | ||
193 | static inline struct qe_ic *qe_ic_from_irq_data(struct irq_data *d) | |
194 | { | |
195 | return irq_data_get_irq_chip_data(d); | |
98658538 LY |
196 | } |
197 | ||
3a0adfab | 198 | static void qe_ic_unmask_irq(struct irq_data *d) |
98658538 | 199 | { |
3a0adfab | 200 | struct qe_ic *qe_ic = qe_ic_from_irq_data(d); |
476eb491 | 201 | unsigned int src = irqd_to_hwirq(d); |
98658538 LY |
202 | unsigned long flags; |
203 | u32 temp; | |
204 | ||
43a5a01b | 205 | raw_spin_lock_irqsave(&qe_ic_lock, flags); |
98658538 LY |
206 | |
207 | temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); | |
208 | qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, | |
209 | temp | qe_ic_info[src].mask); | |
210 | ||
43a5a01b | 211 | raw_spin_unlock_irqrestore(&qe_ic_lock, flags); |
98658538 LY |
212 | } |
213 | ||
3a0adfab | 214 | static void qe_ic_mask_irq(struct irq_data *d) |
98658538 | 215 | { |
3a0adfab | 216 | struct qe_ic *qe_ic = qe_ic_from_irq_data(d); |
476eb491 | 217 | unsigned int src = irqd_to_hwirq(d); |
98658538 LY |
218 | unsigned long flags; |
219 | u32 temp; | |
220 | ||
43a5a01b | 221 | raw_spin_lock_irqsave(&qe_ic_lock, flags); |
98658538 LY |
222 | |
223 | temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].mask_reg); | |
224 | qe_ic_write(qe_ic->regs, qe_ic_info[src].mask_reg, | |
225 | temp & ~qe_ic_info[src].mask); | |
226 | ||
2c1d2f34 SW |
227 | /* Flush the above write before enabling interrupts; otherwise, |
228 | * spurious interrupts will sometimes happen. To be 100% sure | |
229 | * that the write has reached the device before interrupts are | |
230 | * enabled, the mask register would have to be read back; however, | |
231 | * this is not required for correctness, only to avoid wasting | |
232 | * time on a large number of spurious interrupts. In testing, | |
233 | * a sync reduced the observed spurious interrupts to zero. | |
234 | */ | |
235 | mb(); | |
98658538 | 236 | |
43a5a01b | 237 | raw_spin_unlock_irqrestore(&qe_ic_lock, flags); |
98658538 LY |
238 | } |
239 | ||
240 | static struct irq_chip qe_ic_irq_chip = { | |
fc380c0c | 241 | .name = "QEIC", |
3a0adfab LB |
242 | .irq_unmask = qe_ic_unmask_irq, |
243 | .irq_mask = qe_ic_mask_irq, | |
244 | .irq_mask_ack = qe_ic_mask_irq, | |
98658538 LY |
245 | }; |
246 | ||
ad3aedfb MZ |
247 | static int qe_ic_host_match(struct irq_domain *h, struct device_node *node, |
248 | enum irq_domain_bus_token bus_token) | |
98658538 | 249 | { |
98658538 | 250 | /* Exact match, unless qe_ic node is NULL */ |
5d4c9bc7 MZ |
251 | struct device_node *of_node = irq_domain_get_of_node(h); |
252 | return of_node == NULL || of_node == node; | |
98658538 LY |
253 | } |
254 | ||
bae1d8f1 | 255 | static int qe_ic_host_map(struct irq_domain *h, unsigned int virq, |
98658538 LY |
256 | irq_hw_number_t hw) |
257 | { | |
258 | struct qe_ic *qe_ic = h->host_data; | |
259 | struct irq_chip *chip; | |
260 | ||
261 | if (qe_ic_info[hw].mask == 0) { | |
8354be9c | 262 | printk(KERN_ERR "Can't map reserved IRQ\n"); |
98658538 LY |
263 | return -EINVAL; |
264 | } | |
265 | /* Default chip */ | |
266 | chip = &qe_ic->hc_irq; | |
267 | ||
ec775d0e | 268 | irq_set_chip_data(virq, qe_ic); |
98488db9 | 269 | irq_set_status_flags(virq, IRQ_LEVEL); |
98658538 | 270 | |
ec775d0e | 271 | irq_set_chip_and_handler(virq, chip, handle_level_irq); |
98658538 LY |
272 | |
273 | return 0; | |
274 | } | |
275 | ||
202648a6 | 276 | static const struct irq_domain_ops qe_ic_host_ops = { |
98658538 LY |
277 | .match = qe_ic_host_match, |
278 | .map = qe_ic_host_map, | |
ff8c3ab8 | 279 | .xlate = irq_domain_xlate_onetwocell, |
98658538 LY |
280 | }; |
281 | ||
282 | /* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ | |
35a84c2f | 283 | unsigned int qe_ic_get_low_irq(struct qe_ic *qe_ic) |
98658538 LY |
284 | { |
285 | int irq; | |
286 | ||
287 | BUG_ON(qe_ic == NULL); | |
288 | ||
289 | /* get the interrupt source vector. */ | |
290 | irq = qe_ic_read(qe_ic->regs, QEIC_CIVEC) >> 26; | |
291 | ||
292 | if (irq == 0) | |
293 | return NO_IRQ; | |
294 | ||
295 | return irq_linear_revmap(qe_ic->irqhost, irq); | |
296 | } | |
297 | ||
298 | /* Return an interrupt vector or NO_IRQ if no interrupt is pending. */ | |
35a84c2f | 299 | unsigned int qe_ic_get_high_irq(struct qe_ic *qe_ic) |
98658538 LY |
300 | { |
301 | int irq; | |
302 | ||
303 | BUG_ON(qe_ic == NULL); | |
304 | ||
305 | /* get the interrupt source vector. */ | |
306 | irq = qe_ic_read(qe_ic->regs, QEIC_CHIVEC) >> 26; | |
307 | ||
308 | if (irq == 0) | |
309 | return NO_IRQ; | |
310 | ||
311 | return irq_linear_revmap(qe_ic->irqhost, irq); | |
312 | } | |
313 | ||
cccd2102 | 314 | void __init qe_ic_init(struct device_node *node, unsigned int flags, |
bd0b9ac4 TG |
315 | void (*low_handler)(struct irq_desc *desc), |
316 | void (*high_handler)(struct irq_desc *desc)) | |
98658538 LY |
317 | { |
318 | struct qe_ic *qe_ic; | |
319 | struct resource res; | |
320 | u32 temp = 0, ret, high_active = 0; | |
321 | ||
2272a55f ME |
322 | ret = of_address_to_resource(node, 0, &res); |
323 | if (ret) | |
324 | return; | |
325 | ||
ea96025a | 326 | qe_ic = kzalloc(sizeof(*qe_ic), GFP_KERNEL); |
98658538 LY |
327 | if (qe_ic == NULL) |
328 | return; | |
329 | ||
a8db8cf0 GL |
330 | qe_ic->irqhost = irq_domain_add_linear(node, NR_QE_IC_INTS, |
331 | &qe_ic_host_ops, qe_ic); | |
3475dd8a JL |
332 | if (qe_ic->irqhost == NULL) { |
333 | kfree(qe_ic); | |
98658538 | 334 | return; |
3475dd8a | 335 | } |
98658538 | 336 | |
28f65c11 | 337 | qe_ic->regs = ioremap(res.start, resource_size(&res)); |
98658538 | 338 | |
98658538 LY |
339 | qe_ic->hc_irq = qe_ic_irq_chip; |
340 | ||
341 | qe_ic->virq_high = irq_of_parse_and_map(node, 0); | |
342 | qe_ic->virq_low = irq_of_parse_and_map(node, 1); | |
343 | ||
344 | if (qe_ic->virq_low == NO_IRQ) { | |
345 | printk(KERN_ERR "Failed to map QE_IC low IRQ\n"); | |
3475dd8a | 346 | kfree(qe_ic); |
98658538 LY |
347 | return; |
348 | } | |
349 | ||
350 | /* default priority scheme is grouped. If spread mode is */ | |
351 | /* required, configure cicr accordingly. */ | |
352 | if (flags & QE_IC_SPREADMODE_GRP_W) | |
353 | temp |= CICR_GWCC; | |
354 | if (flags & QE_IC_SPREADMODE_GRP_X) | |
355 | temp |= CICR_GXCC; | |
356 | if (flags & QE_IC_SPREADMODE_GRP_Y) | |
357 | temp |= CICR_GYCC; | |
358 | if (flags & QE_IC_SPREADMODE_GRP_Z) | |
359 | temp |= CICR_GZCC; | |
360 | if (flags & QE_IC_SPREADMODE_GRP_RISCA) | |
361 | temp |= CICR_GRTA; | |
362 | if (flags & QE_IC_SPREADMODE_GRP_RISCB) | |
363 | temp |= CICR_GRTB; | |
364 | ||
365 | /* choose destination signal for highest priority interrupt */ | |
366 | if (flags & QE_IC_HIGH_SIGNAL) { | |
367 | temp |= (SIGNAL_HIGH << CICR_HPIT_SHIFT); | |
368 | high_active = 1; | |
369 | } | |
370 | ||
371 | qe_ic_write(qe_ic->regs, QEIC_CICR, temp); | |
372 | ||
ec775d0e TG |
373 | irq_set_handler_data(qe_ic->virq_low, qe_ic); |
374 | irq_set_chained_handler(qe_ic->virq_low, low_handler); | |
98658538 | 375 | |
cccd2102 AV |
376 | if (qe_ic->virq_high != NO_IRQ && |
377 | qe_ic->virq_high != qe_ic->virq_low) { | |
ec775d0e TG |
378 | irq_set_handler_data(qe_ic->virq_high, qe_ic); |
379 | irq_set_chained_handler(qe_ic->virq_high, high_handler); | |
98658538 | 380 | } |
98658538 LY |
381 | } |
382 | ||
383 | void qe_ic_set_highest_priority(unsigned int virq, int high) | |
384 | { | |
385 | struct qe_ic *qe_ic = qe_ic_from_irq(virq); | |
386 | unsigned int src = virq_to_hw(virq); | |
387 | u32 temp = 0; | |
388 | ||
389 | temp = qe_ic_read(qe_ic->regs, QEIC_CICR); | |
390 | ||
391 | temp &= ~CICR_HP_MASK; | |
392 | temp |= src << CICR_HP_SHIFT; | |
393 | ||
394 | temp &= ~CICR_HPIT_MASK; | |
395 | temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << CICR_HPIT_SHIFT; | |
396 | ||
397 | qe_ic_write(qe_ic->regs, QEIC_CICR, temp); | |
398 | } | |
399 | ||
400 | /* Set Priority level within its group, from 1 to 8 */ | |
401 | int qe_ic_set_priority(unsigned int virq, unsigned int priority) | |
402 | { | |
403 | struct qe_ic *qe_ic = qe_ic_from_irq(virq); | |
404 | unsigned int src = virq_to_hw(virq); | |
405 | u32 temp; | |
406 | ||
407 | if (priority > 8 || priority == 0) | |
408 | return -EINVAL; | |
409 | if (src > 127) | |
410 | return -EINVAL; | |
411 | if (qe_ic_info[src].pri_reg == 0) | |
412 | return -EINVAL; | |
413 | ||
414 | temp = qe_ic_read(qe_ic->regs, qe_ic_info[src].pri_reg); | |
415 | ||
416 | if (priority < 4) { | |
417 | temp &= ~(0x7 << (32 - priority * 3)); | |
418 | temp |= qe_ic_info[src].pri_code << (32 - priority * 3); | |
419 | } else { | |
420 | temp &= ~(0x7 << (24 - priority * 3)); | |
421 | temp |= qe_ic_info[src].pri_code << (24 - priority * 3); | |
422 | } | |
423 | ||
424 | qe_ic_write(qe_ic->regs, qe_ic_info[src].pri_reg, temp); | |
425 | ||
426 | return 0; | |
427 | } | |
428 | ||
429 | /* Set a QE priority to use high irq, only priority 1~2 can use high irq */ | |
430 | int qe_ic_set_high_priority(unsigned int virq, unsigned int priority, int high) | |
431 | { | |
432 | struct qe_ic *qe_ic = qe_ic_from_irq(virq); | |
433 | unsigned int src = virq_to_hw(virq); | |
434 | u32 temp, control_reg = QEIC_CICNR, shift = 0; | |
435 | ||
436 | if (priority > 2 || priority == 0) | |
437 | return -EINVAL; | |
438 | ||
439 | switch (qe_ic_info[src].pri_reg) { | |
440 | case QEIC_CIPZCC: | |
441 | shift = CICNR_ZCC1T_SHIFT; | |
442 | break; | |
443 | case QEIC_CIPWCC: | |
444 | shift = CICNR_WCC1T_SHIFT; | |
445 | break; | |
446 | case QEIC_CIPYCC: | |
447 | shift = CICNR_YCC1T_SHIFT; | |
448 | break; | |
449 | case QEIC_CIPXCC: | |
450 | shift = CICNR_XCC1T_SHIFT; | |
451 | break; | |
452 | case QEIC_CIPRTA: | |
453 | shift = CRICR_RTA1T_SHIFT; | |
454 | control_reg = QEIC_CRICR; | |
455 | break; | |
456 | case QEIC_CIPRTB: | |
457 | shift = CRICR_RTB1T_SHIFT; | |
458 | control_reg = QEIC_CRICR; | |
459 | break; | |
460 | default: | |
461 | return -EINVAL; | |
462 | } | |
463 | ||
464 | shift += (2 - priority) * 2; | |
465 | temp = qe_ic_read(qe_ic->regs, control_reg); | |
466 | temp &= ~(SIGNAL_MASK << shift); | |
467 | temp |= (high ? SIGNAL_HIGH : SIGNAL_LOW) << shift; | |
468 | qe_ic_write(qe_ic->regs, control_reg, temp); | |
469 | ||
470 | return 0; | |
471 | } | |
472 | ||
cfde779f | 473 | static struct bus_type qe_ic_subsys = { |
af5ca3f4 | 474 | .name = "qe_ic", |
cfde779f | 475 | .dev_name = "qe_ic", |
98658538 LY |
476 | }; |
477 | ||
cfde779f | 478 | static struct device device_qe_ic = { |
98658538 | 479 | .id = 0, |
cfde779f | 480 | .bus = &qe_ic_subsys, |
98658538 LY |
481 | }; |
482 | ||
483 | static int __init init_qe_ic_sysfs(void) | |
484 | { | |
485 | int rc; | |
486 | ||
487 | printk(KERN_DEBUG "Registering qe_ic with sysfs...\n"); | |
488 | ||
cfde779f | 489 | rc = subsys_system_register(&qe_ic_subsys, NULL); |
98658538 LY |
490 | if (rc) { |
491 | printk(KERN_ERR "Failed registering qe_ic sys class\n"); | |
492 | return -ENODEV; | |
493 | } | |
cfde779f | 494 | rc = device_register(&device_qe_ic); |
98658538 LY |
495 | if (rc) { |
496 | printk(KERN_ERR "Failed registering qe_ic sys device\n"); | |
497 | return -ENODEV; | |
498 | } | |
499 | return 0; | |
500 | } | |
501 | ||
502 | subsys_initcall(init_qe_ic_sysfs); |