Commit | Line | Data |
---|---|---|
35832e26 | 1 | /* |
92592c9c | 2 | * Copyright 2010 PMC-Sierra, Inc, derived from irq_cpu.c |
35832e26 | 3 | * |
92592c9c | 4 | * This file define the irq handler for MSP CIC subsystem interrupts. |
35832e26 | 5 | * |
70342287 RB |
6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the | |
35832e26 MSJ |
8 | * Free Software Foundation; either version 2 of the License, or (at your |
9 | * option) any later version. | |
10 | */ | |
11 | ||
12 | #include <linux/init.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/bitops.h> | |
ca4d3e67 | 16 | #include <linux/irq.h> |
35832e26 | 17 | |
92592c9c | 18 | #include <asm/mipsregs.h> |
35832e26 MSJ |
19 | |
20 | #include <msp_cic_int.h> | |
21 | #include <msp_regs.h> | |
22 | ||
23 | /* | |
92592c9c | 24 | * External API |
35832e26 | 25 | */ |
92592c9c A |
26 | extern void msp_per_irq_init(void); |
27 | extern void msp_per_irq_dispatch(void); | |
35832e26 | 28 | |
92592c9c A |
29 | |
30 | /* | |
31 | * Convenience Macro. Should be somewhere generic. | |
32 | */ | |
33 | #define get_current_vpe() \ | |
34 | ((read_c0_tcbind() >> TCBIND_CURVPE_SHIFT) & TCBIND_CURVPE) | |
35 | ||
36 | #ifdef CONFIG_SMP | |
37 | ||
38 | #define LOCK_VPE(flags, mtflags) \ | |
39 | do { \ | |
40 | local_irq_save(flags); \ | |
41 | mtflags = dmt(); \ | |
42 | } while (0) | |
43 | ||
44 | #define UNLOCK_VPE(flags, mtflags) \ | |
45 | do { \ | |
46 | emt(mtflags); \ | |
47 | local_irq_restore(flags);\ | |
48 | } while (0) | |
49 | ||
50 | #define LOCK_CORE(flags, mtflags) \ | |
51 | do { \ | |
52 | local_irq_save(flags); \ | |
53 | mtflags = dvpe(); \ | |
54 | } while (0) | |
55 | ||
56 | #define UNLOCK_CORE(flags, mtflags) \ | |
57 | do { \ | |
58 | evpe(mtflags); \ | |
59 | local_irq_restore(flags);\ | |
60 | } while (0) | |
61 | ||
62 | #else | |
63 | ||
64 | #define LOCK_VPE(flags, mtflags) | |
65 | #define UNLOCK_VPE(flags, mtflags) | |
66 | #endif | |
67 | ||
68 | /* ensure writes to cic are completed */ | |
69 | static inline void cic_wmb(void) | |
35832e26 | 70 | { |
92592c9c A |
71 | const volatile void __iomem *cic_mem = CIC_VPE0_MSK_REG; |
72 | volatile u32 dummy_read; | |
35832e26 | 73 | |
92592c9c A |
74 | wmb(); |
75 | dummy_read = __raw_readl(cic_mem); | |
76 | dummy_read++; | |
35832e26 MSJ |
77 | } |
78 | ||
d7881fbd | 79 | static void unmask_cic_irq(struct irq_data *d) |
35832e26 | 80 | { |
92592c9c A |
81 | volatile u32 *cic_msk_reg = CIC_VPE0_MSK_REG; |
82 | int vpe; | |
83 | #ifdef CONFIG_SMP | |
84 | unsigned int mtflags; | |
85 | unsigned long flags; | |
86 | ||
87 | /* | |
88 | * Make sure we have IRQ affinity. It may have changed while | |
89 | * we were processing the IRQ. | |
90 | */ | |
d7881fbd | 91 | if (!cpumask_test_cpu(smp_processor_id(), d->affinity)) |
92592c9c A |
92 | return; |
93 | #endif | |
94 | ||
95 | vpe = get_current_vpe(); | |
96 | LOCK_VPE(flags, mtflags); | |
d7881fbd | 97 | cic_msk_reg[vpe] |= (1 << (d->irq - MSP_CIC_INTBASE)); |
92592c9c A |
98 | UNLOCK_VPE(flags, mtflags); |
99 | cic_wmb(); | |
35832e26 MSJ |
100 | } |
101 | ||
d7881fbd | 102 | static void mask_cic_irq(struct irq_data *d) |
35832e26 | 103 | { |
92592c9c A |
104 | volatile u32 *cic_msk_reg = CIC_VPE0_MSK_REG; |
105 | int vpe = get_current_vpe(); | |
106 | #ifdef CONFIG_SMP | |
107 | unsigned long flags, mtflags; | |
108 | #endif | |
109 | LOCK_VPE(flags, mtflags); | |
d7881fbd | 110 | cic_msk_reg[vpe] &= ~(1 << (d->irq - MSP_CIC_INTBASE)); |
92592c9c A |
111 | UNLOCK_VPE(flags, mtflags); |
112 | cic_wmb(); | |
113 | } | |
d7881fbd | 114 | static void msp_cic_irq_ack(struct irq_data *d) |
92592c9c | 115 | { |
d7881fbd | 116 | mask_cic_irq(d); |
35832e26 | 117 | /* |
92592c9c A |
118 | * Only really necessary for 18, 16-14 and sometimes 3:0 |
119 | * (since these can be edge sensitive) but it doesn't | |
120 | * hurt for the others | |
121 | */ | |
d7881fbd | 122 | *CIC_STS_REG = (1 << (d->irq - MSP_CIC_INTBASE)); |
92592c9c A |
123 | } |
124 | ||
b633648c | 125 | /* Note: Limiting to VSMP. */ |
92592c9c A |
126 | |
127 | #ifdef CONFIG_MIPS_MT_SMP | |
d7881fbd TG |
128 | static int msp_cic_irq_set_affinity(struct irq_data *d, |
129 | const struct cpumask *cpumask, bool force) | |
92592c9c A |
130 | { |
131 | int cpu; | |
132 | unsigned long flags; | |
133 | unsigned int mtflags; | |
134 | unsigned long imask = (1 << (irq - MSP_CIC_INTBASE)); | |
135 | volatile u32 *cic_mask = (volatile u32 *)CIC_VPE0_MSK_REG; | |
136 | ||
137 | /* timer balancing should be disabled in kernel code */ | |
138 | BUG_ON(irq == MSP_INT_VPE0_TIMER || irq == MSP_INT_VPE1_TIMER); | |
139 | ||
140 | LOCK_CORE(flags, mtflags); | |
141 | /* enable if any of each VPE's TCs require this IRQ */ | |
142 | for_each_online_cpu(cpu) { | |
143 | if (cpumask_test_cpu(cpu, cpumask)) | |
144 | cic_mask[cpu] |= imask; | |
145 | else | |
146 | cic_mask[cpu] &= ~imask; | |
147 | ||
148 | } | |
149 | ||
150 | UNLOCK_CORE(flags, mtflags); | |
151 | return 0; | |
152 | ||
35832e26 | 153 | } |
92592c9c | 154 | #endif |
35832e26 MSJ |
155 | |
156 | static struct irq_chip msp_cic_irq_controller = { | |
157 | .name = "MSP_CIC", | |
d7881fbd TG |
158 | .irq_mask = mask_cic_irq, |
159 | .irq_mask_ack = msp_cic_irq_ack, | |
160 | .irq_unmask = unmask_cic_irq, | |
161 | .irq_ack = msp_cic_irq_ack, | |
92592c9c | 162 | #ifdef CONFIG_MIPS_MT_SMP |
d7881fbd | 163 | .irq_set_affinity = msp_cic_irq_set_affinity, |
92592c9c | 164 | #endif |
35832e26 MSJ |
165 | }; |
166 | ||
35832e26 MSJ |
167 | void __init msp_cic_irq_init(void) |
168 | { | |
169 | int i; | |
35832e26 MSJ |
170 | /* Mask/clear interrupts. */ |
171 | *CIC_VPE0_MSK_REG = 0x00000000; | |
92592c9c | 172 | *CIC_VPE1_MSK_REG = 0x00000000; |
70342287 | 173 | *CIC_STS_REG = 0xFFFFFFFF; |
35832e26 | 174 | /* |
92592c9c A |
175 | * The MSP7120 RG and EVBD boards use IRQ[6:4] for PCI. |
176 | * These inputs map to EXT_INT_POL[6:4] inside the CIC. | |
177 | * They are to be active low, level sensitive. | |
178 | */ | |
35832e26 | 179 | *CIC_EXT_CFG_REG &= 0xFFFF8F8F; |
35832e26 MSJ |
180 | |
181 | /* initialize all the IRQ descriptors */ | |
92592c9c | 182 | for (i = MSP_CIC_INTBASE ; i < MSP_CIC_INTBASE + 32 ; i++) { |
e4ec7989 | 183 | irq_set_chip_and_handler(i, &msp_cic_irq_controller, |
35832e26 | 184 | handle_level_irq); |
92592c9c A |
185 | } |
186 | ||
187 | /* Initialize the PER interrupt sub-system */ | |
188 | msp_per_irq_init(); | |
35832e26 MSJ |
189 | } |
190 | ||
92592c9c | 191 | /* CIC masked by CIC vector processing before dispatch called */ |
35832e26 MSJ |
192 | void msp_cic_irq_dispatch(void) |
193 | { | |
92592c9c A |
194 | volatile u32 *cic_msk_reg = (volatile u32 *)CIC_VPE0_MSK_REG; |
195 | u32 cic_mask; | |
196 | u32 pending; | |
197 | int cic_status = *CIC_STS_REG; | |
198 | cic_mask = cic_msk_reg[get_current_vpe()]; | |
199 | pending = cic_status & cic_mask; | |
200 | if (pending & (1 << (MSP_INT_VPE0_TIMER - MSP_CIC_INTBASE))) { | |
35832e26 | 201 | do_IRQ(MSP_INT_VPE0_TIMER); |
92592c9c A |
202 | } else if (pending & (1 << (MSP_INT_VPE1_TIMER - MSP_CIC_INTBASE))) { |
203 | do_IRQ(MSP_INT_VPE1_TIMER); | |
204 | } else if (pending & (1 << (MSP_INT_PER - MSP_CIC_INTBASE))) { | |
205 | msp_per_irq_dispatch(); | |
206 | } else if (pending) { | |
207 | do_IRQ(ffs(pending) + MSP_CIC_INTBASE - 1); | |
208 | } else{ | |
209 | spurious_interrupt(); | |
92592c9c | 210 | } |
35832e26 | 211 | } |