Commit | Line | Data |
---|---|---|
35832e26 MSJ |
1 | /* |
2 | * This file define the irq handler for MSP SLM subsystem interrupts. | |
3 | * | |
4 | * Copyright 2005-2006 PMC-Sierra, Inc, derived from irq_cpu.c | |
5 | * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com | |
6 | * | |
70342287 RB |
7 | * This program is free software; you can redistribute it and/or modify it |
8 | * under the terms of the GNU General Public License as published by the | |
35832e26 MSJ |
9 | * Free Software Foundation; either version 2 of the License, or (at your |
10 | * option) any later version. | |
11 | */ | |
12 | ||
13 | #include <linux/init.h> | |
14 | #include <linux/interrupt.h> | |
15 | #include <linux/kernel.h> | |
16 | #include <linux/bitops.h> | |
17 | ||
18 | #include <asm/mipsregs.h> | |
35832e26 MSJ |
19 | |
20 | #include <msp_slp_int.h> | |
21 | #include <msp_regs.h> | |
22 | ||
d7881fbd | 23 | static inline void unmask_msp_slp_irq(struct irq_data *d) |
35832e26 | 24 | { |
d7881fbd TG |
25 | unsigned int irq = d->irq; |
26 | ||
35832e26 MSJ |
27 | /* check for PER interrupt range */ |
28 | if (irq < MSP_PER_INTBASE) | |
29 | *SLP_INT_MSK_REG |= (1 << (irq - MSP_SLP_INTBASE)); | |
30 | else | |
31 | *PER_INT_MSK_REG |= (1 << (irq - MSP_PER_INTBASE)); | |
32 | } | |
33 | ||
d7881fbd | 34 | static inline void mask_msp_slp_irq(struct irq_data *d) |
35832e26 | 35 | { |
d7881fbd TG |
36 | unsigned int irq = d->irq; |
37 | ||
35832e26 MSJ |
38 | /* check for PER interrupt range */ |
39 | if (irq < MSP_PER_INTBASE) | |
40 | *SLP_INT_MSK_REG &= ~(1 << (irq - MSP_SLP_INTBASE)); | |
41 | else | |
42 | *PER_INT_MSK_REG &= ~(1 << (irq - MSP_PER_INTBASE)); | |
43 | } | |
44 | ||
45 | /* | |
46 | * While we ack the interrupt interrupts are disabled and thus we don't need | |
47 | * to deal with concurrency issues. Same for msp_slp_irq_end. | |
48 | */ | |
d7881fbd | 49 | static inline void ack_msp_slp_irq(struct irq_data *d) |
35832e26 | 50 | { |
d7881fbd TG |
51 | unsigned int irq = d->irq; |
52 | ||
35832e26 MSJ |
53 | /* check for PER interrupt range */ |
54 | if (irq < MSP_PER_INTBASE) | |
55 | *SLP_INT_STS_REG = (1 << (irq - MSP_SLP_INTBASE)); | |
56 | else | |
57 | *PER_INT_STS_REG = (1 << (irq - MSP_PER_INTBASE)); | |
58 | } | |
59 | ||
60 | static struct irq_chip msp_slp_irq_controller = { | |
61 | .name = "MSP_SLP", | |
d7881fbd TG |
62 | .irq_ack = ack_msp_slp_irq, |
63 | .irq_mask = mask_msp_slp_irq, | |
64 | .irq_unmask = unmask_msp_slp_irq, | |
35832e26 MSJ |
65 | }; |
66 | ||
67 | void __init msp_slp_irq_init(void) | |
68 | { | |
69 | int i; | |
70 | ||
71 | /* Mask/clear interrupts. */ | |
72 | *SLP_INT_MSK_REG = 0x00000000; | |
73 | *PER_INT_MSK_REG = 0x00000000; | |
74 | *SLP_INT_STS_REG = 0xFFFFFFFF; | |
75 | *PER_INT_STS_REG = 0xFFFFFFFF; | |
76 | ||
77 | /* initialize all the IRQ descriptors */ | |
78 | for (i = MSP_SLP_INTBASE; i < MSP_PER_INTBASE + 32; i++) | |
e4ec7989 | 79 | irq_set_chip_and_handler(i, &msp_slp_irq_controller, |
35832e26 MSJ |
80 | handle_level_irq); |
81 | } | |
82 | ||
83 | void msp_slp_irq_dispatch(void) | |
84 | { | |
85 | u32 pending; | |
86 | int intbase; | |
87 | ||
88 | intbase = MSP_SLP_INTBASE; | |
89 | pending = *SLP_INT_STS_REG & *SLP_INT_MSK_REG; | |
90 | ||
91 | /* check for PER interrupt */ | |
92 | if (pending == (1 << (MSP_INT_PER - MSP_SLP_INTBASE))) { | |
93 | intbase = MSP_PER_INTBASE; | |
94 | pending = *PER_INT_STS_REG & *PER_INT_MSK_REG; | |
95 | } | |
96 | ||
97 | /* check for spurious interrupt */ | |
98 | if (pending == 0x00000000) { | |
99 | printk(KERN_ERR "Spurious %s interrupt?\n", | |
100 | (intbase == MSP_SLP_INTBASE) ? "SLP" : "PER"); | |
101 | return; | |
102 | } | |
103 | ||
104 | /* dispatch the irq */ | |
105 | do_IRQ(ffs(pending) + intbase - 1); | |
106 | } |