vserver 2.0 rc7
[linux-2.6.git] / arch / ia64 / sn / kernel / irq.c
1 /*
2  * Platform dependent support for SGI SN
3  *
4  * This file is subject to the terms and conditions of the GNU General Public
5  * License.  See the file "COPYING" in the main directory of this archive
6  * for more details.
7  *
8  * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
9  */
10
11 #include <linux/irq.h>
12 #include <asm/sn/intr.h>
13 #include <asm/sn/addrs.h>
14 #include <asm/sn/arch.h>
15 #include "xtalk/xwidgetdev.h"
16 #include <asm/sn/pcibus_provider_defs.h>
17 #include <asm/sn/pcidev.h>
18 #include "pci/pcibr_provider.h"
19 #include <asm/sn/shub_mmr.h>
20 #include <asm/sn/sn_sal.h>
21
22 static void force_interrupt(int irq);
23 static void register_intr_pda(struct sn_irq_info *sn_irq_info);
24 static void unregister_intr_pda(struct sn_irq_info *sn_irq_info);
25
26 extern int sn_force_interrupt_flag;
27 extern int sn_ioif_inited;
28 struct sn_irq_info **sn_irq;
29
30 static inline uint64_t sn_intr_alloc(nasid_t local_nasid, int local_widget,
31                                      u64 sn_irq_info,
32                                      int req_irq, nasid_t req_nasid,
33                                      int req_slice)
34 {
35         struct ia64_sal_retval ret_stuff;
36         ret_stuff.status = 0;
37         ret_stuff.v0 = 0;
38
39         SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT,
40                         (u64) SAL_INTR_ALLOC, (u64) local_nasid,
41                         (u64) local_widget, (u64) sn_irq_info, (u64) req_irq,
42                         (u64) req_nasid, (u64) req_slice);
43         return ret_stuff.status;
44 }
45
46 static inline void sn_intr_free(nasid_t local_nasid, int local_widget,
47                                 struct sn_irq_info *sn_irq_info)
48 {
49         struct ia64_sal_retval ret_stuff;
50         ret_stuff.status = 0;
51         ret_stuff.v0 = 0;
52
53         SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT,
54                         (u64) SAL_INTR_FREE, (u64) local_nasid,
55                         (u64) local_widget, (u64) sn_irq_info->irq_irq,
56                         (u64) sn_irq_info->irq_cookie, 0, 0);
57 }
58
59 static unsigned int sn_startup_irq(unsigned int irq)
60 {
61         return 0;
62 }
63
64 static void sn_shutdown_irq(unsigned int irq)
65 {
66 }
67
68 static void sn_disable_irq(unsigned int irq)
69 {
70 }
71
72 static void sn_enable_irq(unsigned int irq)
73 {
74 }
75
76 static void sn_ack_irq(unsigned int irq)
77 {
78         uint64_t event_occurred, mask = 0;
79         int nasid;
80
81         irq = irq & 0xff;
82         nasid = get_nasid();
83         event_occurred =
84             HUB_L((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED));
85         mask = event_occurred & SH_ALL_INT_MASK;
86         HUB_S((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED_ALIAS),
87                  mask);
88         __set_bit(irq, (volatile void *)pda->sn_in_service_ivecs);
89
90         move_irq(irq);
91 }
92
93 static void sn_end_irq(unsigned int irq)
94 {
95         int nasid;
96         int ivec;
97         uint64_t event_occurred;
98
99         ivec = irq & 0xff;
100         if (ivec == SGI_UART_VECTOR) {
101                 nasid = get_nasid();
102                 event_occurred = HUB_L((uint64_t *) GLOBAL_MMR_ADDR
103                                        (nasid, SH_EVENT_OCCURRED));
104                 /* If the UART bit is set here, we may have received an 
105                  * interrupt from the UART that the driver missed.  To
106                  * make sure, we IPI ourselves to force us to look again.
107                  */
108                 if (event_occurred & SH_EVENT_OCCURRED_UART_INT_MASK) {
109                         platform_send_ipi(smp_processor_id(), SGI_UART_VECTOR,
110                                           IA64_IPI_DM_INT, 0);
111                 }
112         }
113         __clear_bit(ivec, (volatile void *)pda->sn_in_service_ivecs);
114         if (sn_force_interrupt_flag)
115                 force_interrupt(irq);
116 }
117
118 static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask)
119 {
120         struct sn_irq_info *sn_irq_info = sn_irq[irq];
121         struct sn_irq_info *tmp_sn_irq_info;
122         int cpuid, cpuphys;
123         nasid_t t_nasid;        /* nasid to target */
124         int t_slice;            /* slice to target */
125
126         /* allocate a temp sn_irq_info struct to get new target info */
127         tmp_sn_irq_info = kmalloc(sizeof(*tmp_sn_irq_info), GFP_KERNEL);
128         if (!tmp_sn_irq_info)
129                 return;
130
131         cpuid = first_cpu(mask);
132         cpuphys = cpu_physical_id(cpuid);
133         t_nasid = cpuid_to_nasid(cpuid);
134         t_slice = cpuid_to_slice(cpuid);
135
136         while (sn_irq_info) {
137                 int status;
138                 int local_widget;
139                 uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge;
140                 nasid_t local_nasid = NASID_GET(bridge);
141
142                 if (!bridge)
143                         break;  /* irq is not a device interrupt */
144
145                 if (local_nasid & 1)
146                         local_widget = TIO_SWIN_WIDGETNUM(bridge);
147                 else
148                         local_widget = SWIN_WIDGETNUM(bridge);
149
150                 /* Free the old PROM sn_irq_info structure */
151                 sn_intr_free(local_nasid, local_widget, sn_irq_info);
152
153                 /* allocate a new PROM sn_irq_info struct */
154                 status = sn_intr_alloc(local_nasid, local_widget,
155                                        __pa(tmp_sn_irq_info), irq, t_nasid,
156                                        t_slice);
157
158                 if (status == 0) {
159                         /* Update kernels sn_irq_info with new target info */
160                         unregister_intr_pda(sn_irq_info);
161                         sn_irq_info->irq_cpuid = cpuid;
162                         sn_irq_info->irq_nasid = t_nasid;
163                         sn_irq_info->irq_slice = t_slice;
164                         sn_irq_info->irq_xtalkaddr =
165                             tmp_sn_irq_info->irq_xtalkaddr;
166                         sn_irq_info->irq_cookie = tmp_sn_irq_info->irq_cookie;
167                         register_intr_pda(sn_irq_info);
168
169                         if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type)) {
170                                 pcibr_change_devices_irq(sn_irq_info);
171                         }
172
173                         sn_irq_info = sn_irq_info->irq_next;
174
175 #ifdef CONFIG_SMP
176                         set_irq_affinity_info((irq & 0xff), cpuphys, 0);
177 #endif
178                 } else {
179                         break;  /* snp_affinity failed the intr_alloc */
180                 }
181         }
182         kfree(tmp_sn_irq_info);
183 }
184
185 struct hw_interrupt_type irq_type_sn = {
186         "SN hub",
187         sn_startup_irq,
188         sn_shutdown_irq,
189         sn_enable_irq,
190         sn_disable_irq,
191         sn_ack_irq,
192         sn_end_irq,
193         sn_set_affinity_irq
194 };
195
196 unsigned int sn_local_vector_to_irq(u8 vector)
197 {
198         return (CPU_VECTOR_TO_IRQ(smp_processor_id(), vector));
199 }
200
201 void sn_irq_init(void)
202 {
203         int i;
204         irq_desc_t *base_desc = irq_desc;
205
206         for (i = 0; i < NR_IRQS; i++) {
207                 if (base_desc[i].handler == &no_irq_type) {
208                         base_desc[i].handler = &irq_type_sn;
209                 }
210         }
211 }
212
213 static void register_intr_pda(struct sn_irq_info *sn_irq_info)
214 {
215         int irq = sn_irq_info->irq_irq;
216         int cpu = sn_irq_info->irq_cpuid;
217
218         if (pdacpu(cpu)->sn_last_irq < irq) {
219                 pdacpu(cpu)->sn_last_irq = irq;
220         }
221
222         if (pdacpu(cpu)->sn_first_irq == 0 || pdacpu(cpu)->sn_first_irq > irq) {
223                 pdacpu(cpu)->sn_first_irq = irq;
224         }
225 }
226
227 static void unregister_intr_pda(struct sn_irq_info *sn_irq_info)
228 {
229         int irq = sn_irq_info->irq_irq;
230         int cpu = sn_irq_info->irq_cpuid;
231         struct sn_irq_info *tmp_irq_info;
232         int i, foundmatch;
233
234         if (pdacpu(cpu)->sn_last_irq == irq) {
235                 foundmatch = 0;
236                 for (i = pdacpu(cpu)->sn_last_irq - 1; i; i--) {
237                         tmp_irq_info = sn_irq[i];
238                         while (tmp_irq_info) {
239                                 if (tmp_irq_info->irq_cpuid == cpu) {
240                                         foundmatch++;
241                                         break;
242                                 }
243                                 tmp_irq_info = tmp_irq_info->irq_next;
244                         }
245                         if (foundmatch) {
246                                 break;
247                         }
248                 }
249                 pdacpu(cpu)->sn_last_irq = i;
250         }
251
252         if (pdacpu(cpu)->sn_first_irq == irq) {
253                 foundmatch = 0;
254                 for (i = pdacpu(cpu)->sn_first_irq + 1; i < NR_IRQS; i++) {
255                         tmp_irq_info = sn_irq[i];
256                         while (tmp_irq_info) {
257                                 if (tmp_irq_info->irq_cpuid == cpu) {
258                                         foundmatch++;
259                                         break;
260                                 }
261                                 tmp_irq_info = tmp_irq_info->irq_next;
262                         }
263                         if (foundmatch) {
264                                 break;
265                         }
266                 }
267                 pdacpu(cpu)->sn_first_irq = ((i == NR_IRQS) ? 0 : i);
268         }
269 }
270
271 struct sn_irq_info *sn_irq_alloc(nasid_t local_nasid, int local_widget, int irq,
272                                  nasid_t nasid, int slice)
273 {
274         struct sn_irq_info *sn_irq_info;
275         int status;
276
277         sn_irq_info = kmalloc(sizeof(*sn_irq_info), GFP_KERNEL);
278         if (sn_irq_info == NULL)
279                 return NULL;
280
281         memset(sn_irq_info, 0x0, sizeof(*sn_irq_info));
282
283         status =
284             sn_intr_alloc(local_nasid, local_widget, __pa(sn_irq_info), irq,
285                           nasid, slice);
286
287         if (status) {
288                 kfree(sn_irq_info);
289                 return NULL;
290         } else {
291                 return sn_irq_info;
292         }
293 }
294
295 void sn_irq_free(struct sn_irq_info *sn_irq_info)
296 {
297         uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge;
298         nasid_t local_nasid = NASID_GET(bridge);
299         int local_widget;
300
301         if (local_nasid & 1)    /* tio check */
302                 local_widget = TIO_SWIN_WIDGETNUM(bridge);
303         else
304                 local_widget = SWIN_WIDGETNUM(bridge);
305
306         sn_intr_free(local_nasid, local_widget, sn_irq_info);
307
308         kfree(sn_irq_info);
309 }
310
311 void sn_irq_fixup(struct pci_dev *pci_dev, struct sn_irq_info *sn_irq_info)
312 {
313         nasid_t nasid = sn_irq_info->irq_nasid;
314         int slice = sn_irq_info->irq_slice;
315         int cpu = nasid_slice_to_cpuid(nasid, slice);
316
317         sn_irq_info->irq_cpuid = cpu;
318         sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev);
319
320         /* link it into the sn_irq[irq] list */
321         sn_irq_info->irq_next = sn_irq[sn_irq_info->irq_irq];
322         sn_irq[sn_irq_info->irq_irq] = sn_irq_info;
323
324         (void)register_intr_pda(sn_irq_info);
325 }
326
327 static void force_interrupt(int irq)
328 {
329         struct sn_irq_info *sn_irq_info;
330
331         if (!sn_ioif_inited)
332                 return;
333         sn_irq_info = sn_irq[irq];
334         while (sn_irq_info) {
335                 if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) &&
336                     (sn_irq_info->irq_bridge != NULL)) {
337                         pcibr_force_interrupt(sn_irq_info);
338                 }
339                 sn_irq_info = sn_irq_info->irq_next;
340         }
341 }
342
343 /*
344  * Check for lost interrupts.  If the PIC int_status reg. says that
345  * an interrupt has been sent, but not handled, and the interrupt
346  * is not pending in either the cpu irr regs or in the soft irr regs,
347  * and the interrupt is not in service, then the interrupt may have
348  * been lost.  Force an interrupt on that pin.  It is possible that
349  * the interrupt is in flight, so we may generate a spurious interrupt,
350  * but we should never miss a real lost interrupt.
351  */
352 static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info)
353 {
354         uint64_t regval;
355         int irr_reg_num;
356         int irr_bit;
357         uint64_t irr_reg;
358         struct pcidev_info *pcidev_info;
359         struct pcibus_info *pcibus_info;
360
361         pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
362         if (!pcidev_info)
363                 return;
364
365         pcibus_info =
366             (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info->
367             pdi_pcibus_info;
368         regval = pcireg_intr_status_get(pcibus_info);
369
370         irr_reg_num = irq_to_vector(irq) / 64;
371         irr_bit = irq_to_vector(irq) % 64;
372         switch (irr_reg_num) {
373         case 0:
374                 irr_reg = ia64_getreg(_IA64_REG_CR_IRR0);
375                 break;
376         case 1:
377                 irr_reg = ia64_getreg(_IA64_REG_CR_IRR1);
378                 break;
379         case 2:
380                 irr_reg = ia64_getreg(_IA64_REG_CR_IRR2);
381                 break;
382         case 3:
383                 irr_reg = ia64_getreg(_IA64_REG_CR_IRR3);
384                 break;
385         }
386         if (!test_bit(irr_bit, &irr_reg)) {
387                 if (!test_bit(irq, pda->sn_soft_irr)) {
388                         if (!test_bit(irq, pda->sn_in_service_ivecs)) {
389                                 regval &= 0xff;
390                                 if (sn_irq_info->irq_int_bit & regval &
391                                     sn_irq_info->irq_last_intr) {
392                                         regval &=
393                                             ~(sn_irq_info->
394                                               irq_int_bit & regval);
395                                         pcibr_force_interrupt(sn_irq_info);
396                                 }
397                         }
398                 }
399         }
400         sn_irq_info->irq_last_intr = regval;
401 }
402
403 void sn_lb_int_war_check(void)
404 {
405         int i;
406
407         if (!sn_ioif_inited || pda->sn_first_irq == 0)
408                 return;
409         for (i = pda->sn_first_irq; i <= pda->sn_last_irq; i++) {
410                 struct sn_irq_info *sn_irq_info = sn_irq[i];
411                 while (sn_irq_info) {
412                         /* Only call for PCI bridges that are fully initialized. */
413                         if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) &&
414                             (sn_irq_info->irq_bridge != NULL)) {
415                                 sn_check_intr(i, sn_irq_info);
416                         }
417                         sn_irq_info = sn_irq_info->irq_next;
418                 }
419         }
420 }