Commit | Line | Data |
---|---|---|
39b8d525 RB |
1 | #undef DEBUG |
2 | ||
3 | #include <linux/bitmap.h> | |
4 | #include <linux/init.h> | |
5 | ||
6 | #include <asm/io.h> | |
7 | #include <asm/gic.h> | |
8 | #include <asm/gcmpregs.h> | |
9 | #include <asm/mips-boards/maltaint.h> | |
10 | #include <asm/irq.h> | |
11 | #include <linux/hardirq.h> | |
12 | #include <asm-generic/bitops/find.h> | |
13 | ||
14 | ||
15 | static unsigned long _gic_base; | |
16 | static unsigned int _irqbase, _mapsize, numvpes, numintrs; | |
17 | static struct gic_intr_map *_intrmap; | |
18 | ||
19 | static struct gic_pcpu_mask pcpu_masks[NR_CPUS]; | |
20 | static struct gic_pending_regs pending_regs[NR_CPUS]; | |
21 | static struct gic_intrmask_regs intrmask_regs[NR_CPUS]; | |
22 | ||
23 | #define gic_wedgeb2bok 0 /* | |
24 | * Can GIC handle b2b writes to wedge register? | |
25 | */ | |
26 | #if gic_wedgeb2bok == 0 | |
27 | static DEFINE_SPINLOCK(gic_wedgeb2b_lock); | |
28 | #endif | |
29 | ||
30 | void gic_send_ipi(unsigned int intr) | |
31 | { | |
32 | #if gic_wedgeb2bok == 0 | |
33 | unsigned long flags; | |
34 | #endif | |
35 | pr_debug("CPU%d: %s status %08x\n", smp_processor_id(), __func__, | |
36 | read_c0_status()); | |
37 | if (!gic_wedgeb2bok) | |
38 | spin_lock_irqsave(&gic_wedgeb2b_lock, flags); | |
39 | GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), 0x80000000 | intr); | |
40 | if (!gic_wedgeb2bok) { | |
41 | (void) GIC_REG(SHARED, GIC_SH_CONFIG); | |
42 | spin_unlock_irqrestore(&gic_wedgeb2b_lock, flags); | |
43 | } | |
44 | } | |
45 | ||
46 | /* This is Malta specific and needs to be exported */ | |
47 | static void vpe_local_setup(unsigned int numvpes) | |
48 | { | |
49 | int i; | |
50 | unsigned long timer_interrupt = 5, perf_interrupt = 5; | |
51 | unsigned int vpe_ctl; | |
52 | ||
53 | /* | |
54 | * Setup the default performance counter timer interrupts | |
55 | * for all VPEs | |
56 | */ | |
57 | for (i = 0; i < numvpes; i++) { | |
58 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i); | |
59 | ||
60 | /* Are Interrupts locally routable? */ | |
61 | GICREAD(GIC_REG(VPE_OTHER, GIC_VPE_CTL), vpe_ctl); | |
62 | if (vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK) | |
63 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP), | |
64 | GIC_MAP_TO_PIN_MSK | timer_interrupt); | |
65 | ||
66 | if (vpe_ctl & GIC_VPE_CTL_PERFCNT_RTBL_MSK) | |
67 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_PERFCTR_MAP), | |
68 | GIC_MAP_TO_PIN_MSK | perf_interrupt); | |
69 | } | |
70 | } | |
71 | ||
72 | unsigned int gic_get_int(void) | |
73 | { | |
74 | unsigned int i; | |
75 | unsigned long *pending, *intrmask, *pcpu_mask; | |
76 | unsigned long *pending_abs, *intrmask_abs; | |
77 | ||
78 | /* Get per-cpu bitmaps */ | |
79 | pending = pending_regs[smp_processor_id()].pending; | |
80 | intrmask = intrmask_regs[smp_processor_id()].intrmask; | |
81 | pcpu_mask = pcpu_masks[smp_processor_id()].pcpu_mask; | |
82 | ||
83 | pending_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, | |
84 | GIC_SH_PEND_31_0_OFS); | |
85 | intrmask_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, | |
86 | GIC_SH_MASK_31_0_OFS); | |
87 | ||
88 | for (i = 0; i < BITS_TO_LONGS(GIC_NUM_INTRS); i++) { | |
89 | GICREAD(*pending_abs, pending[i]); | |
90 | GICREAD(*intrmask_abs, intrmask[i]); | |
91 | pending_abs++; | |
92 | intrmask_abs++; | |
93 | } | |
94 | ||
95 | bitmap_and(pending, pending, intrmask, GIC_NUM_INTRS); | |
96 | bitmap_and(pending, pending, pcpu_mask, GIC_NUM_INTRS); | |
97 | ||
98 | i = find_first_bit(pending, GIC_NUM_INTRS); | |
99 | ||
100 | pr_debug("CPU%d: %s pend=%d\n", smp_processor_id(), __func__, i); | |
101 | ||
102 | return i; | |
103 | } | |
104 | ||
105 | static unsigned int gic_irq_startup(unsigned int irq) | |
106 | { | |
107 | pr_debug("CPU%d: %s: irq%d\n", smp_processor_id(), __func__, irq); | |
108 | irq -= _irqbase; | |
109 | /* FIXME: this is wrong for !GICISWORDLITTLEENDIAN */ | |
110 | GICWRITE(GIC_REG_ADDR(SHARED, (GIC_SH_SMASK_31_0_OFS + (irq / 32))), | |
111 | 1 << (irq % 32)); | |
112 | return 0; | |
113 | } | |
114 | ||
115 | static void gic_irq_ack(unsigned int irq) | |
116 | { | |
117 | #if gic_wedgeb2bok == 0 | |
118 | unsigned long flags; | |
119 | #endif | |
120 | pr_debug("CPU%d: %s: irq%d\n", smp_processor_id(), __func__, irq); | |
121 | irq -= _irqbase; | |
122 | GICWRITE(GIC_REG_ADDR(SHARED, (GIC_SH_RMASK_31_0_OFS + (irq / 32))), | |
123 | 1 << (irq % 32)); | |
124 | ||
125 | if (_intrmap[irq].trigtype == GIC_TRIG_EDGE) { | |
126 | if (!gic_wedgeb2bok) | |
127 | spin_lock_irqsave(&gic_wedgeb2b_lock, flags); | |
128 | GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), irq); | |
129 | if (!gic_wedgeb2bok) { | |
130 | (void) GIC_REG(SHARED, GIC_SH_CONFIG); | |
131 | spin_unlock_irqrestore(&gic_wedgeb2b_lock, flags); | |
132 | } | |
133 | } | |
134 | } | |
135 | ||
136 | static void gic_mask_irq(unsigned int irq) | |
137 | { | |
138 | pr_debug("CPU%d: %s: irq%d\n", smp_processor_id(), __func__, irq); | |
139 | irq -= _irqbase; | |
140 | /* FIXME: this is wrong for !GICISWORDLITTLEENDIAN */ | |
141 | GICWRITE(GIC_REG_ADDR(SHARED, (GIC_SH_RMASK_31_0_OFS + (irq / 32))), | |
142 | 1 << (irq % 32)); | |
143 | } | |
144 | ||
145 | static void gic_unmask_irq(unsigned int irq) | |
146 | { | |
147 | pr_debug("CPU%d: %s: irq%d\n", smp_processor_id(), __func__, irq); | |
148 | irq -= _irqbase; | |
149 | /* FIXME: this is wrong for !GICISWORDLITTLEENDIAN */ | |
150 | GICWRITE(GIC_REG_ADDR(SHARED, (GIC_SH_SMASK_31_0_OFS + (irq / 32))), | |
151 | 1 << (irq % 32)); | |
152 | } | |
153 | ||
154 | #ifdef CONFIG_SMP | |
155 | ||
156 | static DEFINE_SPINLOCK(gic_lock); | |
157 | ||
d5dedd45 | 158 | static int gic_set_affinity(unsigned int irq, const struct cpumask *cpumask) |
39b8d525 RB |
159 | { |
160 | cpumask_t tmp = CPU_MASK_NONE; | |
161 | unsigned long flags; | |
162 | int i; | |
163 | ||
164 | pr_debug(KERN_DEBUG "%s called\n", __func__); | |
165 | irq -= _irqbase; | |
166 | ||
0de26520 | 167 | cpumask_and(&tmp, cpumask, cpu_online_mask); |
39b8d525 | 168 | if (cpus_empty(tmp)) |
d5dedd45 | 169 | return -1; |
39b8d525 RB |
170 | |
171 | /* Assumption : cpumask refers to a single CPU */ | |
172 | spin_lock_irqsave(&gic_lock, flags); | |
173 | for (;;) { | |
174 | /* Re-route this IRQ */ | |
175 | GIC_SH_MAP_TO_VPE_SMASK(irq, first_cpu(tmp)); | |
176 | ||
177 | /* | |
178 | * FIXME: assumption that _intrmap is ordered and has no holes | |
179 | */ | |
180 | ||
181 | /* Update the intr_map */ | |
182 | _intrmap[irq].cpunum = first_cpu(tmp); | |
183 | ||
184 | /* Update the pcpu_masks */ | |
185 | for (i = 0; i < NR_CPUS; i++) | |
186 | clear_bit(irq, pcpu_masks[i].pcpu_mask); | |
187 | set_bit(irq, pcpu_masks[first_cpu(tmp)].pcpu_mask); | |
188 | ||
189 | } | |
e65e49d0 | 190 | cpumask_copy(irq_desc[irq].affinity, cpumask); |
39b8d525 RB |
191 | spin_unlock_irqrestore(&gic_lock, flags); |
192 | ||
d5dedd45 | 193 | return 0; |
39b8d525 RB |
194 | } |
195 | #endif | |
196 | ||
197 | static struct irq_chip gic_irq_controller = { | |
198 | .name = "MIPS GIC", | |
199 | .startup = gic_irq_startup, | |
200 | .ack = gic_irq_ack, | |
201 | .mask = gic_mask_irq, | |
202 | .mask_ack = gic_mask_irq, | |
203 | .unmask = gic_unmask_irq, | |
204 | .eoi = gic_unmask_irq, | |
205 | #ifdef CONFIG_SMP | |
206 | .set_affinity = gic_set_affinity, | |
207 | #endif | |
208 | }; | |
209 | ||
210 | static void __init setup_intr(unsigned int intr, unsigned int cpu, | |
211 | unsigned int pin, unsigned int polarity, unsigned int trigtype) | |
212 | { | |
213 | /* Setup Intr to Pin mapping */ | |
214 | if (pin & GIC_MAP_TO_NMI_MSK) { | |
215 | GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), pin); | |
216 | /* FIXME: hack to route NMI to all cpu's */ | |
217 | for (cpu = 0; cpu < NR_CPUS; cpu += 32) { | |
218 | GICWRITE(GIC_REG_ADDR(SHARED, | |
219 | GIC_SH_MAP_TO_VPE_REG_OFF(intr, cpu)), | |
220 | 0xffffffff); | |
221 | } | |
222 | } else { | |
223 | GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), | |
224 | GIC_MAP_TO_PIN_MSK | pin); | |
225 | /* Setup Intr to CPU mapping */ | |
226 | GIC_SH_MAP_TO_VPE_SMASK(intr, cpu); | |
227 | } | |
228 | ||
229 | /* Setup Intr Polarity */ | |
230 | GIC_SET_POLARITY(intr, polarity); | |
231 | ||
232 | /* Setup Intr Trigger Type */ | |
233 | GIC_SET_TRIGGER(intr, trigtype); | |
234 | ||
235 | /* Init Intr Masks */ | |
236 | GIC_SET_INTR_MASK(intr, 0); | |
237 | } | |
238 | ||
239 | static void __init gic_basic_init(void) | |
240 | { | |
241 | unsigned int i, cpu; | |
242 | ||
243 | /* Setup defaults */ | |
244 | for (i = 0; i < GIC_NUM_INTRS; i++) { | |
245 | GIC_SET_POLARITY(i, GIC_POL_POS); | |
246 | GIC_SET_TRIGGER(i, GIC_TRIG_LEVEL); | |
247 | GIC_SET_INTR_MASK(i, 0); | |
248 | } | |
249 | ||
250 | /* Setup specifics */ | |
251 | for (i = 0; i < _mapsize; i++) { | |
252 | cpu = _intrmap[i].cpunum; | |
253 | if (cpu == X) | |
254 | continue; | |
255 | ||
256 | setup_intr(_intrmap[i].intrnum, | |
257 | _intrmap[i].cpunum, | |
258 | _intrmap[i].pin, | |
259 | _intrmap[i].polarity, | |
260 | _intrmap[i].trigtype); | |
261 | /* Initialise per-cpu Interrupt software masks */ | |
262 | if (_intrmap[i].ipiflag) | |
263 | set_bit(_intrmap[i].intrnum, pcpu_masks[cpu].pcpu_mask); | |
264 | } | |
265 | ||
266 | vpe_local_setup(numvpes); | |
267 | ||
268 | for (i = _irqbase; i < (_irqbase + numintrs); i++) | |
269 | set_irq_chip(i, &gic_irq_controller); | |
270 | } | |
271 | ||
272 | void __init gic_init(unsigned long gic_base_addr, | |
273 | unsigned long gic_addrspace_size, | |
274 | struct gic_intr_map *intr_map, unsigned int intr_map_size, | |
275 | unsigned int irqbase) | |
276 | { | |
277 | unsigned int gicconfig; | |
278 | ||
279 | _gic_base = (unsigned long) ioremap_nocache(gic_base_addr, | |
280 | gic_addrspace_size); | |
281 | _irqbase = irqbase; | |
282 | _intrmap = intr_map; | |
283 | _mapsize = intr_map_size; | |
284 | ||
285 | GICREAD(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); | |
286 | numintrs = (gicconfig & GIC_SH_CONFIG_NUMINTRS_MSK) >> | |
287 | GIC_SH_CONFIG_NUMINTRS_SHF; | |
288 | numintrs = ((numintrs + 1) * 8); | |
289 | ||
290 | numvpes = (gicconfig & GIC_SH_CONFIG_NUMVPES_MSK) >> | |
291 | GIC_SH_CONFIG_NUMVPES_SHF; | |
292 | ||
293 | pr_debug("%s called\n", __func__); | |
294 | ||
295 | gic_basic_init(); | |
296 | } |