Linux-2.6.12-rc2
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / arch / ia64 / sn / kernel / irq.c
blob3be44724f6c8f71e2112a1e501668131be5090fe
1 /*
2 * Platform dependent support for SGI SN
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.
8 * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved.
9 */
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 "pci/pcibus_provider_defs.h"
17 #include "pci/pcidev.h"
18 #include "pci/pcibr_provider.h"
19 #include <asm/sn/shub_mmr.h>
20 #include <asm/sn/sn_sal.h>
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);
26 extern int sn_force_interrupt_flag;
27 extern int sn_ioif_inited;
28 struct sn_irq_info **sn_irq;
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)
35 struct ia64_sal_retval ret_stuff;
36 ret_stuff.status = 0;
37 ret_stuff.v0 = 0;
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;
46 static inline void sn_intr_free(nasid_t local_nasid, int local_widget,
47 struct sn_irq_info *sn_irq_info)
49 struct ia64_sal_retval ret_stuff;
50 ret_stuff.status = 0;
51 ret_stuff.v0 = 0;
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);
59 static unsigned int sn_startup_irq(unsigned int irq)
61 return 0;
64 static void sn_shutdown_irq(unsigned int irq)
68 static void sn_disable_irq(unsigned int irq)
72 static void sn_enable_irq(unsigned int irq)
76 static void sn_ack_irq(unsigned int irq)
78 uint64_t event_occurred, mask = 0;
79 int nasid;
81 irq = irq & 0xff;
82 nasid = get_nasid();
83 event_occurred =
84 HUB_L((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED));
85 if (event_occurred & SH_EVENT_OCCURRED_UART_INT_MASK) {
86 mask |= (1 << SH_EVENT_OCCURRED_UART_INT_SHFT);
88 if (event_occurred & SH_EVENT_OCCURRED_IPI_INT_MASK) {
89 mask |= (1 << SH_EVENT_OCCURRED_IPI_INT_SHFT);
91 if (event_occurred & SH_EVENT_OCCURRED_II_INT0_MASK) {
92 mask |= (1 << SH_EVENT_OCCURRED_II_INT0_SHFT);
94 if (event_occurred & SH_EVENT_OCCURRED_II_INT1_MASK) {
95 mask |= (1 << SH_EVENT_OCCURRED_II_INT1_SHFT);
97 HUB_S((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED_ALIAS),
98 mask);
99 __set_bit(irq, (volatile void *)pda->sn_in_service_ivecs);
101 move_irq(irq);
104 static void sn_end_irq(unsigned int irq)
106 int nasid;
107 int ivec;
108 uint64_t event_occurred;
110 ivec = irq & 0xff;
111 if (ivec == SGI_UART_VECTOR) {
112 nasid = get_nasid();
113 event_occurred = HUB_L((uint64_t *) GLOBAL_MMR_ADDR
114 (nasid, SH_EVENT_OCCURRED));
115 /* If the UART bit is set here, we may have received an
116 * interrupt from the UART that the driver missed. To
117 * make sure, we IPI ourselves to force us to look again.
119 if (event_occurred & SH_EVENT_OCCURRED_UART_INT_MASK) {
120 platform_send_ipi(smp_processor_id(), SGI_UART_VECTOR,
121 IA64_IPI_DM_INT, 0);
124 __clear_bit(ivec, (volatile void *)pda->sn_in_service_ivecs);
125 if (sn_force_interrupt_flag)
126 force_interrupt(irq);
129 static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask)
131 struct sn_irq_info *sn_irq_info = sn_irq[irq];
132 struct sn_irq_info *tmp_sn_irq_info;
133 int cpuid, cpuphys;
134 nasid_t t_nasid; /* nasid to target */
135 int t_slice; /* slice to target */
137 /* allocate a temp sn_irq_info struct to get new target info */
138 tmp_sn_irq_info = kmalloc(sizeof(*tmp_sn_irq_info), GFP_KERNEL);
139 if (!tmp_sn_irq_info)
140 return;
142 cpuid = first_cpu(mask);
143 cpuphys = cpu_physical_id(cpuid);
144 t_nasid = cpuid_to_nasid(cpuid);
145 t_slice = cpuid_to_slice(cpuid);
147 while (sn_irq_info) {
148 int status;
149 int local_widget;
150 uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge;
151 nasid_t local_nasid = NASID_GET(bridge);
153 if (!bridge)
154 break; /* irq is not a device interrupt */
156 if (local_nasid & 1)
157 local_widget = TIO_SWIN_WIDGETNUM(bridge);
158 else
159 local_widget = SWIN_WIDGETNUM(bridge);
161 /* Free the old PROM sn_irq_info structure */
162 sn_intr_free(local_nasid, local_widget, sn_irq_info);
164 /* allocate a new PROM sn_irq_info struct */
165 status = sn_intr_alloc(local_nasid, local_widget,
166 __pa(tmp_sn_irq_info), irq, t_nasid,
167 t_slice);
169 if (status == 0) {
170 /* Update kernels sn_irq_info with new target info */
171 unregister_intr_pda(sn_irq_info);
172 sn_irq_info->irq_cpuid = cpuid;
173 sn_irq_info->irq_nasid = t_nasid;
174 sn_irq_info->irq_slice = t_slice;
175 sn_irq_info->irq_xtalkaddr =
176 tmp_sn_irq_info->irq_xtalkaddr;
177 sn_irq_info->irq_cookie = tmp_sn_irq_info->irq_cookie;
178 register_intr_pda(sn_irq_info);
180 if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type)) {
181 pcibr_change_devices_irq(sn_irq_info);
184 sn_irq_info = sn_irq_info->irq_next;
186 #ifdef CONFIG_SMP
187 set_irq_affinity_info((irq & 0xff), cpuphys, 0);
188 #endif
189 } else {
190 break; /* snp_affinity failed the intr_alloc */
193 kfree(tmp_sn_irq_info);
196 struct hw_interrupt_type irq_type_sn = {
197 "SN hub",
198 sn_startup_irq,
199 sn_shutdown_irq,
200 sn_enable_irq,
201 sn_disable_irq,
202 sn_ack_irq,
203 sn_end_irq,
204 sn_set_affinity_irq
207 unsigned int sn_local_vector_to_irq(u8 vector)
209 return (CPU_VECTOR_TO_IRQ(smp_processor_id(), vector));
212 void sn_irq_init(void)
214 int i;
215 irq_desc_t *base_desc = irq_desc;
217 for (i = 0; i < NR_IRQS; i++) {
218 if (base_desc[i].handler == &no_irq_type) {
219 base_desc[i].handler = &irq_type_sn;
224 static void register_intr_pda(struct sn_irq_info *sn_irq_info)
226 int irq = sn_irq_info->irq_irq;
227 int cpu = sn_irq_info->irq_cpuid;
229 if (pdacpu(cpu)->sn_last_irq < irq) {
230 pdacpu(cpu)->sn_last_irq = irq;
233 if (pdacpu(cpu)->sn_first_irq == 0 || pdacpu(cpu)->sn_first_irq > irq) {
234 pdacpu(cpu)->sn_first_irq = irq;
238 static void unregister_intr_pda(struct sn_irq_info *sn_irq_info)
240 int irq = sn_irq_info->irq_irq;
241 int cpu = sn_irq_info->irq_cpuid;
242 struct sn_irq_info *tmp_irq_info;
243 int i, foundmatch;
245 if (pdacpu(cpu)->sn_last_irq == irq) {
246 foundmatch = 0;
247 for (i = pdacpu(cpu)->sn_last_irq - 1; i; i--) {
248 tmp_irq_info = sn_irq[i];
249 while (tmp_irq_info) {
250 if (tmp_irq_info->irq_cpuid == cpu) {
251 foundmatch++;
252 break;
254 tmp_irq_info = tmp_irq_info->irq_next;
256 if (foundmatch) {
257 break;
260 pdacpu(cpu)->sn_last_irq = i;
263 if (pdacpu(cpu)->sn_first_irq == irq) {
264 foundmatch = 0;
265 for (i = pdacpu(cpu)->sn_first_irq + 1; i < NR_IRQS; i++) {
266 tmp_irq_info = sn_irq[i];
267 while (tmp_irq_info) {
268 if (tmp_irq_info->irq_cpuid == cpu) {
269 foundmatch++;
270 break;
272 tmp_irq_info = tmp_irq_info->irq_next;
274 if (foundmatch) {
275 break;
278 pdacpu(cpu)->sn_first_irq = ((i == NR_IRQS) ? 0 : i);
282 struct sn_irq_info *sn_irq_alloc(nasid_t local_nasid, int local_widget, int irq,
283 nasid_t nasid, int slice)
285 struct sn_irq_info *sn_irq_info;
286 int status;
288 sn_irq_info = kmalloc(sizeof(*sn_irq_info), GFP_KERNEL);
289 if (sn_irq_info == NULL)
290 return NULL;
292 memset(sn_irq_info, 0x0, sizeof(*sn_irq_info));
294 status =
295 sn_intr_alloc(local_nasid, local_widget, __pa(sn_irq_info), irq,
296 nasid, slice);
298 if (status) {
299 kfree(sn_irq_info);
300 return NULL;
301 } else {
302 return sn_irq_info;
306 void sn_irq_free(struct sn_irq_info *sn_irq_info)
308 uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge;
309 nasid_t local_nasid = NASID_GET(bridge);
310 int local_widget;
312 if (local_nasid & 1) /* tio check */
313 local_widget = TIO_SWIN_WIDGETNUM(bridge);
314 else
315 local_widget = SWIN_WIDGETNUM(bridge);
317 sn_intr_free(local_nasid, local_widget, sn_irq_info);
319 kfree(sn_irq_info);
322 void sn_irq_fixup(struct pci_dev *pci_dev, struct sn_irq_info *sn_irq_info)
324 nasid_t nasid = sn_irq_info->irq_nasid;
325 int slice = sn_irq_info->irq_slice;
326 int cpu = nasid_slice_to_cpuid(nasid, slice);
328 sn_irq_info->irq_cpuid = cpu;
329 sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev);
331 /* link it into the sn_irq[irq] list */
332 sn_irq_info->irq_next = sn_irq[sn_irq_info->irq_irq];
333 sn_irq[sn_irq_info->irq_irq] = sn_irq_info;
335 (void)register_intr_pda(sn_irq_info);
338 static void force_interrupt(int irq)
340 struct sn_irq_info *sn_irq_info;
342 if (!sn_ioif_inited)
343 return;
344 sn_irq_info = sn_irq[irq];
345 while (sn_irq_info) {
346 if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) &&
347 (sn_irq_info->irq_bridge != NULL)) {
348 pcibr_force_interrupt(sn_irq_info);
350 sn_irq_info = sn_irq_info->irq_next;
355 * Check for lost interrupts. If the PIC int_status reg. says that
356 * an interrupt has been sent, but not handled, and the interrupt
357 * is not pending in either the cpu irr regs or in the soft irr regs,
358 * and the interrupt is not in service, then the interrupt may have
359 * been lost. Force an interrupt on that pin. It is possible that
360 * the interrupt is in flight, so we may generate a spurious interrupt,
361 * but we should never miss a real lost interrupt.
363 static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info)
365 uint64_t regval;
366 int irr_reg_num;
367 int irr_bit;
368 uint64_t irr_reg;
369 struct pcidev_info *pcidev_info;
370 struct pcibus_info *pcibus_info;
372 pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
373 if (!pcidev_info)
374 return;
376 pcibus_info =
377 (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info->
378 pdi_pcibus_info;
379 regval = pcireg_intr_status_get(pcibus_info);
381 irr_reg_num = irq_to_vector(irq) / 64;
382 irr_bit = irq_to_vector(irq) % 64;
383 switch (irr_reg_num) {
384 case 0:
385 irr_reg = ia64_getreg(_IA64_REG_CR_IRR0);
386 break;
387 case 1:
388 irr_reg = ia64_getreg(_IA64_REG_CR_IRR1);
389 break;
390 case 2:
391 irr_reg = ia64_getreg(_IA64_REG_CR_IRR2);
392 break;
393 case 3:
394 irr_reg = ia64_getreg(_IA64_REG_CR_IRR3);
395 break;
397 if (!test_bit(irr_bit, &irr_reg)) {
398 if (!test_bit(irq, pda->sn_soft_irr)) {
399 if (!test_bit(irq, pda->sn_in_service_ivecs)) {
400 regval &= 0xff;
401 if (sn_irq_info->irq_int_bit & regval &
402 sn_irq_info->irq_last_intr) {
403 regval &=
404 ~(sn_irq_info->
405 irq_int_bit & regval);
406 pcibr_force_interrupt(sn_irq_info);
411 sn_irq_info->irq_last_intr = regval;
414 void sn_lb_int_war_check(void)
416 int i;
418 if (!sn_ioif_inited || pda->sn_first_irq == 0)
419 return;
420 for (i = pda->sn_first_irq; i <= pda->sn_last_irq; i++) {
421 struct sn_irq_info *sn_irq_info = sn_irq[i];
422 while (sn_irq_info) {
423 /* Only call for PCI bridges that are fully initialized. */
424 if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) &&
425 (sn_irq_info->irq_bridge != NULL)) {
426 sn_check_intr(i, sn_irq_info);
428 sn_irq_info = sn_irq_info->irq_next;