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