]>
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 | */ | |
5c159422 JL |
91 | if (!cpumask_test_cpu(smp_processor_id(), |
92 | irq_data_get_affinity_mask(d))) | |
92592c9c A |
93 | return; |
94 | #endif | |
95 | ||
96 | vpe = get_current_vpe(); | |
97 | LOCK_VPE(flags, mtflags); | |
d7881fbd | 98 | cic_msk_reg[vpe] |= (1 << (d->irq - MSP_CIC_INTBASE)); |
92592c9c A |
99 | UNLOCK_VPE(flags, mtflags); |
100 | cic_wmb(); | |
35832e26 MSJ |
101 | } |
102 | ||
d7881fbd | 103 | static void mask_cic_irq(struct irq_data *d) |
35832e26 | 104 | { |
92592c9c A |
105 | volatile u32 *cic_msk_reg = CIC_VPE0_MSK_REG; |
106 | int vpe = get_current_vpe(); | |
107 | #ifdef CONFIG_SMP | |
108 | unsigned long flags, mtflags; | |
109 | #endif | |
110 | LOCK_VPE(flags, mtflags); | |
d7881fbd | 111 | cic_msk_reg[vpe] &= ~(1 << (d->irq - MSP_CIC_INTBASE)); |
92592c9c A |
112 | UNLOCK_VPE(flags, mtflags); |
113 | cic_wmb(); | |
114 | } | |
d7881fbd | 115 | static void msp_cic_irq_ack(struct irq_data *d) |
92592c9c | 116 | { |
d7881fbd | 117 | mask_cic_irq(d); |
35832e26 | 118 | /* |
92592c9c A |
119 | * Only really necessary for 18, 16-14 and sometimes 3:0 |
120 | * (since these can be edge sensitive) but it doesn't | |
121 | * hurt for the others | |
122 | */ | |
d7881fbd | 123 | *CIC_STS_REG = (1 << (d->irq - MSP_CIC_INTBASE)); |
92592c9c A |
124 | } |
125 | ||
b633648c | 126 | /* Note: Limiting to VSMP. */ |
92592c9c A |
127 | |
128 | #ifdef CONFIG_MIPS_MT_SMP | |
d7881fbd TG |
129 | static int msp_cic_irq_set_affinity(struct irq_data *d, |
130 | const struct cpumask *cpumask, bool force) | |
92592c9c A |
131 | { |
132 | int cpu; | |
133 | unsigned long flags; | |
134 | unsigned int mtflags; | |
6fa88d9e | 135 | unsigned long imask = (1 << (d->irq - MSP_CIC_INTBASE)); |
92592c9c A |
136 | volatile u32 *cic_mask = (volatile u32 *)CIC_VPE0_MSK_REG; |
137 | ||
138 | /* timer balancing should be disabled in kernel code */ | |
6fa88d9e | 139 | BUG_ON(d->irq == MSP_INT_VPE0_TIMER || d->irq == MSP_INT_VPE1_TIMER); |
92592c9c A |
140 | |
141 | LOCK_CORE(flags, mtflags); | |
142 | /* enable if any of each VPE's TCs require this IRQ */ | |
143 | for_each_online_cpu(cpu) { | |
144 | if (cpumask_test_cpu(cpu, cpumask)) | |
145 | cic_mask[cpu] |= imask; | |
146 | else | |
147 | cic_mask[cpu] &= ~imask; | |
148 | ||
149 | } | |
150 | ||
151 | UNLOCK_CORE(flags, mtflags); | |
152 | return 0; | |
153 | ||
35832e26 | 154 | } |
92592c9c | 155 | #endif |
35832e26 MSJ |
156 | |
157 | static struct irq_chip msp_cic_irq_controller = { | |
158 | .name = "MSP_CIC", | |
d7881fbd TG |
159 | .irq_mask = mask_cic_irq, |
160 | .irq_mask_ack = msp_cic_irq_ack, | |
161 | .irq_unmask = unmask_cic_irq, | |
162 | .irq_ack = msp_cic_irq_ack, | |
92592c9c | 163 | #ifdef CONFIG_MIPS_MT_SMP |
d7881fbd | 164 | .irq_set_affinity = msp_cic_irq_set_affinity, |
92592c9c | 165 | #endif |
35832e26 MSJ |
166 | }; |
167 | ||
35832e26 MSJ |
168 | void __init msp_cic_irq_init(void) |
169 | { | |
170 | int i; | |
35832e26 MSJ |
171 | /* Mask/clear interrupts. */ |
172 | *CIC_VPE0_MSK_REG = 0x00000000; | |
92592c9c | 173 | *CIC_VPE1_MSK_REG = 0x00000000; |
70342287 | 174 | *CIC_STS_REG = 0xFFFFFFFF; |
35832e26 | 175 | /* |
92592c9c A |
176 | * The MSP7120 RG and EVBD boards use IRQ[6:4] for PCI. |
177 | * These inputs map to EXT_INT_POL[6:4] inside the CIC. | |
178 | * They are to be active low, level sensitive. | |
179 | */ | |
35832e26 | 180 | *CIC_EXT_CFG_REG &= 0xFFFF8F8F; |
35832e26 MSJ |
181 | |
182 | /* initialize all the IRQ descriptors */ | |
92592c9c | 183 | for (i = MSP_CIC_INTBASE ; i < MSP_CIC_INTBASE + 32 ; i++) { |
e4ec7989 | 184 | irq_set_chip_and_handler(i, &msp_cic_irq_controller, |
35832e26 | 185 | handle_level_irq); |
92592c9c A |
186 | } |
187 | ||
188 | /* Initialize the PER interrupt sub-system */ | |
189 | msp_per_irq_init(); | |
35832e26 MSJ |
190 | } |
191 | ||
92592c9c | 192 | /* CIC masked by CIC vector processing before dispatch called */ |
35832e26 MSJ |
193 | void msp_cic_irq_dispatch(void) |
194 | { | |
92592c9c A |
195 | volatile u32 *cic_msk_reg = (volatile u32 *)CIC_VPE0_MSK_REG; |
196 | u32 cic_mask; | |
197 | u32 pending; | |
198 | int cic_status = *CIC_STS_REG; | |
199 | cic_mask = cic_msk_reg[get_current_vpe()]; | |
200 | pending = cic_status & cic_mask; | |
201 | if (pending & (1 << (MSP_INT_VPE0_TIMER - MSP_CIC_INTBASE))) { | |
35832e26 | 202 | do_IRQ(MSP_INT_VPE0_TIMER); |
92592c9c A |
203 | } else if (pending & (1 << (MSP_INT_VPE1_TIMER - MSP_CIC_INTBASE))) { |
204 | do_IRQ(MSP_INT_VPE1_TIMER); | |
205 | } else if (pending & (1 << (MSP_INT_PER - MSP_CIC_INTBASE))) { | |
206 | msp_per_irq_dispatch(); | |
207 | } else if (pending) { | |
208 | do_IRQ(ffs(pending) + MSP_CIC_INTBASE - 1); | |
209 | } else{ | |
210 | spurious_interrupt(); | |
92592c9c | 211 | } |
35832e26 | 212 | } |