X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fia64%2Fsn%2Fkernel%2Firq.c;h=3be44724f6c8f71e2112a1e501668131be5090fe;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=6bcf70ea155ca71f90091c8fe26596ce7806574e;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c index 6bcf70ea1..3be44724f 100644 --- a/arch/ia64/sn/kernel/irq.c +++ b/arch/ia64/sn/kernel/irq.c @@ -5,97 +5,83 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. */ -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include #include -#include -#include -#include -#include -#include +#include "xtalk/xwidgetdev.h" +#include "pci/pcibus_provider_defs.h" +#include "pci/pcidev.h" +#include "pci/pcibr_provider.h" +#include +#include static void force_interrupt(int irq); -extern void pcibr_force_interrupt(pcibr_intr_t intr); -extern int sn_force_interrupt_flag; -struct irq_desc * sn_irq_desc(unsigned int irq); -extern cpumask_t __cacheline_aligned pending_irq_cpumask[NR_IRQS]; - -struct sn_intr_list_t { - struct sn_intr_list_t *next; - pcibr_intr_t intr; -}; +static void register_intr_pda(struct sn_irq_info *sn_irq_info); +static void unregister_intr_pda(struct sn_irq_info *sn_irq_info); -static struct sn_intr_list_t *sn_intr_list[NR_IRQS]; +extern int sn_force_interrupt_flag; +extern int sn_ioif_inited; +struct sn_irq_info **sn_irq; +static inline uint64_t sn_intr_alloc(nasid_t local_nasid, int local_widget, + u64 sn_irq_info, + int req_irq, nasid_t req_nasid, + int req_slice) +{ + struct ia64_sal_retval ret_stuff; + ret_stuff.status = 0; + ret_stuff.v0 = 0; + + SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT, + (u64) SAL_INTR_ALLOC, (u64) local_nasid, + (u64) local_widget, (u64) sn_irq_info, (u64) req_irq, + (u64) req_nasid, (u64) req_slice); + return ret_stuff.status; +} -static unsigned int -sn_startup_irq(unsigned int irq) +static inline void sn_intr_free(nasid_t local_nasid, int local_widget, + struct sn_irq_info *sn_irq_info) { - return(0); + struct ia64_sal_retval ret_stuff; + ret_stuff.status = 0; + ret_stuff.v0 = 0; + + SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT, + (u64) SAL_INTR_FREE, (u64) local_nasid, + (u64) local_widget, (u64) sn_irq_info->irq_irq, + (u64) sn_irq_info->irq_cookie, 0, 0); } -static void -sn_shutdown_irq(unsigned int irq) +static unsigned int sn_startup_irq(unsigned int irq) { + return 0; } -static void -sn_disable_irq(unsigned int irq) +static void sn_shutdown_irq(unsigned int irq) { } -static void -sn_enable_irq(unsigned int irq) +static void sn_disable_irq(unsigned int irq) { } -static inline void sn_move_irq(int irq) +static void sn_enable_irq(unsigned int irq) { - /* note - we hold desc->lock */ - cpumask_t tmp; - irq_desc_t *desc = irq_descp(irq); - - if (!cpus_empty(pending_irq_cpumask[irq])) { - cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map); - if (unlikely(!cpus_empty(tmp))) { - desc->handler->set_affinity(irq, pending_irq_cpumask[irq]); - } - cpus_clear(pending_irq_cpumask[irq]); - } } -static void -sn_ack_irq(unsigned int irq) +static void sn_ack_irq(unsigned int irq) { - unsigned long event_occurred, mask = 0; + uint64_t event_occurred, mask = 0; int nasid; irq = irq & 0xff; - nasid = smp_physical_node_id(); - event_occurred = HUB_L( (unsigned long *)GLOBAL_MMR_ADDR(nasid,SH_EVENT_OCCURRED) ); + nasid = get_nasid(); + event_occurred = + HUB_L((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED)); if (event_occurred & SH_EVENT_OCCURRED_UART_INT_MASK) { mask |= (1 << SH_EVENT_OCCURRED_UART_INT_SHFT); } @@ -108,272 +94,338 @@ sn_ack_irq(unsigned int irq) if (event_occurred & SH_EVENT_OCCURRED_II_INT1_MASK) { mask |= (1 << SH_EVENT_OCCURRED_II_INT1_SHFT); } - HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED_ALIAS), mask ); + HUB_S((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED_ALIAS), + mask); __set_bit(irq, (volatile void *)pda->sn_in_service_ivecs); - sn_move_irq(irq); + + move_irq(irq); } -static void -sn_end_irq(unsigned int irq) +static void sn_end_irq(unsigned int irq) { int nasid; int ivec; - unsigned long event_occurred; - irq_desc_t *desc = sn_irq_desc(irq); - unsigned int status = desc->status; + uint64_t event_occurred; ivec = irq & 0xff; if (ivec == SGI_UART_VECTOR) { - nasid = smp_physical_node_id(); - event_occurred = HUB_L( (unsigned long *)GLOBAL_MMR_ADDR(nasid,SH_EVENT_OCCURRED) ); - // If the UART bit is set here, we may have received an interrupt from the - // UART that the driver missed. To make sure, we IPI ourselves to force us - // to look again. + nasid = get_nasid(); + event_occurred = HUB_L((uint64_t *) GLOBAL_MMR_ADDR + (nasid, SH_EVENT_OCCURRED)); + /* If the UART bit is set here, we may have received an + * interrupt from the UART that the driver missed. To + * make sure, we IPI ourselves to force us to look again. + */ if (event_occurred & SH_EVENT_OCCURRED_UART_INT_MASK) { - platform_send_ipi(smp_processor_id(), SGI_UART_VECTOR, IA64_IPI_DM_INT, 0); + platform_send_ipi(smp_processor_id(), SGI_UART_VECTOR, + IA64_IPI_DM_INT, 0); } } __clear_bit(ivec, (volatile void *)pda->sn_in_service_ivecs); if (sn_force_interrupt_flag) - if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) - force_interrupt(irq); + force_interrupt(irq); } -static void -sn_set_affinity_irq(unsigned int irq, cpumask_t mask) +static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask) { + struct sn_irq_info *sn_irq_info = sn_irq[irq]; + struct sn_irq_info *tmp_sn_irq_info; + int cpuid, cpuphys; + nasid_t t_nasid; /* nasid to target */ + int t_slice; /* slice to target */ + + /* allocate a temp sn_irq_info struct to get new target info */ + tmp_sn_irq_info = kmalloc(sizeof(*tmp_sn_irq_info), GFP_KERNEL); + if (!tmp_sn_irq_info) + return; + + cpuid = first_cpu(mask); + cpuphys = cpu_physical_id(cpuid); + t_nasid = cpuid_to_nasid(cpuid); + t_slice = cpuid_to_slice(cpuid); + + while (sn_irq_info) { + int status; + int local_widget; + uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge; + nasid_t local_nasid = NASID_GET(bridge); + + if (!bridge) + break; /* irq is not a device interrupt */ + + if (local_nasid & 1) + local_widget = TIO_SWIN_WIDGETNUM(bridge); + else + local_widget = SWIN_WIDGETNUM(bridge); + + /* Free the old PROM sn_irq_info structure */ + sn_intr_free(local_nasid, local_widget, sn_irq_info); + + /* allocate a new PROM sn_irq_info struct */ + status = sn_intr_alloc(local_nasid, local_widget, + __pa(tmp_sn_irq_info), irq, t_nasid, + t_slice); + + if (status == 0) { + /* Update kernels sn_irq_info with new target info */ + unregister_intr_pda(sn_irq_info); + sn_irq_info->irq_cpuid = cpuid; + sn_irq_info->irq_nasid = t_nasid; + sn_irq_info->irq_slice = t_slice; + sn_irq_info->irq_xtalkaddr = + tmp_sn_irq_info->irq_xtalkaddr; + sn_irq_info->irq_cookie = tmp_sn_irq_info->irq_cookie; + register_intr_pda(sn_irq_info); + + if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type)) { + pcibr_change_devices_irq(sn_irq_info); + } + + sn_irq_info = sn_irq_info->irq_next; + #ifdef CONFIG_SMP - int redir = 0; - int cpu; - struct sn_intr_list_t *p = sn_intr_list[irq]; - pcibr_intr_t intr; - extern void sn_shub_redirect_intr(pcibr_intr_t intr, unsigned long cpu); - extern void sn_tio_redirect_intr(pcibr_intr_t intr, unsigned long cpu); - - if (p == NULL) - return; - - intr = p->intr; - - if (intr == NULL) - return; - - cpu = first_cpu(mask); - sn_shub_redirect_intr(intr, cpu); - irq = irq & 0xff; /* strip off redirect bit, if someone stuck it on. */ - (void) set_irq_affinity_info(irq, cpu_physical_id(intr->bi_cpu), redir); -#endif /* CONFIG_SMP */ + set_irq_affinity_info((irq & 0xff), cpuphys, 0); +#endif + } else { + break; /* snp_affinity failed the intr_alloc */ + } + } + kfree(tmp_sn_irq_info); } - struct hw_interrupt_type irq_type_sn = { "SN hub", sn_startup_irq, sn_shutdown_irq, sn_enable_irq, sn_disable_irq, - sn_ack_irq, + sn_ack_irq, sn_end_irq, sn_set_affinity_irq }; - -struct irq_desc * -sn_irq_desc(unsigned int irq) -{ - - irq = SN_IVEC_FROM_IRQ(irq); - - return(_irq_desc + irq); -} - -u8 -sn_irq_to_vector(unsigned int irq) -{ - return(irq); -} - -unsigned int -sn_local_vector_to_irq(u8 vector) +unsigned int sn_local_vector_to_irq(u8 vector) { return (CPU_VECTOR_TO_IRQ(smp_processor_id(), vector)); } -void -sn_irq_init (void) +void sn_irq_init(void) { int i; - irq_desc_t *base_desc = _irq_desc; + irq_desc_t *base_desc = irq_desc; - for (i=0; ibi_cpu; + int irq = sn_irq_info->irq_irq; + int cpu = sn_irq_info->irq_cpuid; if (pdacpu(cpu)->sn_last_irq < irq) { pdacpu(cpu)->sn_last_irq = irq; } - if (pdacpu(cpu)->sn_first_irq == 0 || pdacpu(cpu)->sn_first_irq > irq) pdacpu(cpu)->sn_first_irq = irq; - if (!p) panic("Could not allocate memory for sn_intr_list_t\n"); - if ((list = sn_intr_list[irq])) { - while (list->next) list = list->next; - list->next = p; - p->next = NULL; - p->intr = intr; - } else { - sn_intr_list[irq] = p; - p->next = NULL; - p->intr = intr; + + if (pdacpu(cpu)->sn_first_irq == 0 || pdacpu(cpu)->sn_first_irq > irq) { + pdacpu(cpu)->sn_first_irq = irq; } } -void -unregister_pcibr_intr(int irq, pcibr_intr_t intr) +static void unregister_intr_pda(struct sn_irq_info *sn_irq_info) { - - struct sn_intr_list_t **prev, *curr; - int cpu = intr->bi_cpu; - int i; - - if (sn_intr_list[irq] == NULL) - return; - - prev = &sn_intr_list[irq]; - curr = sn_intr_list[irq]; - while (curr) { - if (curr->intr == intr) { - *prev = curr->next; - break; + int irq = sn_irq_info->irq_irq; + int cpu = sn_irq_info->irq_cpuid; + struct sn_irq_info *tmp_irq_info; + int i, foundmatch; + + if (pdacpu(cpu)->sn_last_irq == irq) { + foundmatch = 0; + for (i = pdacpu(cpu)->sn_last_irq - 1; i; i--) { + tmp_irq_info = sn_irq[i]; + while (tmp_irq_info) { + if (tmp_irq_info->irq_cpuid == cpu) { + foundmatch++; + break; + } + tmp_irq_info = tmp_irq_info->irq_next; + } + if (foundmatch) { + break; + } } - prev = &curr->next; - curr = curr->next; + pdacpu(cpu)->sn_last_irq = i; } - if (curr) - kfree(curr); - - if (!sn_intr_list[irq]) { - if (pdacpu(cpu)->sn_last_irq == irq) { - for (i = pdacpu(cpu)->sn_last_irq - 1; i; i--) - if (sn_intr_list[i]) + if (pdacpu(cpu)->sn_first_irq == irq) { + foundmatch = 0; + for (i = pdacpu(cpu)->sn_first_irq + 1; i < NR_IRQS; i++) { + tmp_irq_info = sn_irq[i]; + while (tmp_irq_info) { + if (tmp_irq_info->irq_cpuid == cpu) { + foundmatch++; break; - pdacpu(cpu)->sn_last_irq = i; + } + tmp_irq_info = tmp_irq_info->irq_next; + } + if (foundmatch) { + break; + } } + pdacpu(cpu)->sn_first_irq = ((i == NR_IRQS) ? 0 : i); + } +} - if (pdacpu(cpu)->sn_first_irq == irq) { - pdacpu(cpu)->sn_first_irq = 0; - for (i = pdacpu(cpu)->sn_first_irq + 1; i < NR_IRQS; i++) - if (sn_intr_list[i]) - pdacpu(cpu)->sn_first_irq = i; - } +struct sn_irq_info *sn_irq_alloc(nasid_t local_nasid, int local_widget, int irq, + nasid_t nasid, int slice) +{ + struct sn_irq_info *sn_irq_info; + int status; + + sn_irq_info = kmalloc(sizeof(*sn_irq_info), GFP_KERNEL); + if (sn_irq_info == NULL) + return NULL; + + memset(sn_irq_info, 0x0, sizeof(*sn_irq_info)); + + status = + sn_intr_alloc(local_nasid, local_widget, __pa(sn_irq_info), irq, + nasid, slice); + + if (status) { + kfree(sn_irq_info); + return NULL; + } else { + return sn_irq_info; } +} +void sn_irq_free(struct sn_irq_info *sn_irq_info) +{ + uint64_t bridge = (uint64_t) sn_irq_info->irq_bridge; + nasid_t local_nasid = NASID_GET(bridge); + int local_widget; + + if (local_nasid & 1) /* tio check */ + local_widget = TIO_SWIN_WIDGETNUM(bridge); + else + local_widget = SWIN_WIDGETNUM(bridge); + + sn_intr_free(local_nasid, local_widget, sn_irq_info); + + kfree(sn_irq_info); } -void -force_polled_int(void) +void sn_irq_fixup(struct pci_dev *pci_dev, struct sn_irq_info *sn_irq_info) { - int i; - struct sn_intr_list_t *p; + nasid_t nasid = sn_irq_info->irq_nasid; + int slice = sn_irq_info->irq_slice; + int cpu = nasid_slice_to_cpuid(nasid, slice); - for (i=0; iintr){ - pcibr_force_interrupt(p->intr); - } - p = p->next; - } - } + sn_irq_info->irq_cpuid = cpu; + sn_irq_info->irq_pciioinfo = SN_PCIDEV_INFO(pci_dev); + + /* link it into the sn_irq[irq] list */ + sn_irq_info->irq_next = sn_irq[sn_irq_info->irq_irq]; + sn_irq[sn_irq_info->irq_irq] = sn_irq_info; + + (void)register_intr_pda(sn_irq_info); } -static void -force_interrupt(int irq) +static void force_interrupt(int irq) { - struct sn_intr_list_t *p = sn_intr_list[irq]; + struct sn_irq_info *sn_irq_info; - while (p) { - if (p->intr) { - pcibr_force_interrupt(p->intr); + if (!sn_ioif_inited) + return; + sn_irq_info = sn_irq[irq]; + while (sn_irq_info) { + if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) && + (sn_irq_info->irq_bridge != NULL)) { + pcibr_force_interrupt(sn_irq_info); } - p = p->next; + sn_irq_info = sn_irq_info->irq_next; } } /* -Check for lost interrupts. If the PIC int_status reg. says that -an interrupt has been sent, but not handled, and the interrupt -is not pending in either the cpu irr regs or in the soft irr regs, -and the interrupt is not in service, then the interrupt may have -been lost. Force an interrupt on that pin. It is possible that -the interrupt is in flight, so we may generate a spurious interrupt, -but we should never miss a real lost interrupt. -*/ - -static void -sn_check_intr(int irq, pcibr_intr_t intr) + * Check for lost interrupts. If the PIC int_status reg. says that + * an interrupt has been sent, but not handled, and the interrupt + * is not pending in either the cpu irr regs or in the soft irr regs, + * and the interrupt is not in service, then the interrupt may have + * been lost. Force an interrupt on that pin. It is possible that + * the interrupt is in flight, so we may generate a spurious interrupt, + * but we should never miss a real lost interrupt. + */ +static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info) { - unsigned long regval; + uint64_t regval; int irr_reg_num; int irr_bit; - unsigned long irr_reg; + uint64_t irr_reg; + struct pcidev_info *pcidev_info; + struct pcibus_info *pcibus_info; + + pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo; + if (!pcidev_info) + return; + pcibus_info = + (struct pcibus_info *)pcidev_info->pdi_host_pcidev_info-> + pdi_pcibus_info; + regval = pcireg_intr_status_get(pcibus_info); - regval = pcireg_intr_status_get(intr->bi_soft); irr_reg_num = irq_to_vector(irq) / 64; irr_bit = irq_to_vector(irq) % 64; switch (irr_reg_num) { - case 0: - irr_reg = ia64_getreg(_IA64_REG_CR_IRR0); - break; - case 1: - irr_reg = ia64_getreg(_IA64_REG_CR_IRR1); - break; - case 2: - irr_reg = ia64_getreg(_IA64_REG_CR_IRR2); - break; - case 3: - irr_reg = ia64_getreg(_IA64_REG_CR_IRR3); - break; + case 0: + irr_reg = ia64_getreg(_IA64_REG_CR_IRR0); + break; + case 1: + irr_reg = ia64_getreg(_IA64_REG_CR_IRR1); + break; + case 2: + irr_reg = ia64_getreg(_IA64_REG_CR_IRR2); + break; + case 3: + irr_reg = ia64_getreg(_IA64_REG_CR_IRR3); + break; } - if (!test_bit(irr_bit, &irr_reg) ) { - if (!test_bit(irq, pda->sn_soft_irr) ) { - if (!test_bit(irq, pda->sn_in_service_ivecs) ) { + if (!test_bit(irr_bit, &irr_reg)) { + if (!test_bit(irq, pda->sn_soft_irr)) { + if (!test_bit(irq, pda->sn_in_service_ivecs)) { regval &= 0xff; - if (intr->bi_ibits & regval & intr->bi_last_intr) { - regval &= ~(intr->bi_ibits & regval); - pcibr_force_interrupt(intr); + if (sn_irq_info->irq_int_bit & regval & + sn_irq_info->irq_last_intr) { + regval &= + ~(sn_irq_info-> + irq_int_bit & regval); + pcibr_force_interrupt(sn_irq_info); } } } } - intr->bi_last_intr = regval; + sn_irq_info->irq_last_intr = regval; } -void -sn_lb_int_war_check(void) +void sn_lb_int_war_check(void) { int i; - if (pda->sn_first_irq == 0) return; - for (i=pda->sn_first_irq; - i <= pda->sn_last_irq; i++) { - struct sn_intr_list_t *p = sn_intr_list[i]; - if (p == NULL) { - continue; - } - while (p) { - sn_check_intr(i, p->intr); - p = p->next; + if (!sn_ioif_inited || pda->sn_first_irq == 0) + return; + for (i = pda->sn_first_irq; i <= pda->sn_last_irq; i++) { + struct sn_irq_info *sn_irq_info = sn_irq[i]; + while (sn_irq_info) { + /* Only call for PCI bridges that are fully initialized. */ + if (IS_PCI_BRIDGE_ASIC(sn_irq_info->irq_bridge_type) && + (sn_irq_info->irq_bridge != NULL)) { + sn_check_intr(i, sn_irq_info); } + sn_irq_info = sn_irq_info->irq_next; + } } }