diff options
Diffstat (limited to 'arch/ia64/sn/kernel/irq.c')
| -rw-r--r-- | arch/ia64/sn/kernel/irq.c | 524 |
1 files changed, 296 insertions, 228 deletions
diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c index 0f4e8138658..85d09515490 100644 --- a/arch/ia64/sn/kernel/irq.c +++ b/arch/ia64/sn/kernel/irq.c @@ -5,30 +5,33 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. + * Copyright (c) 2000-2008 Silicon Graphics, Inc. All Rights Reserved. */ #include <linux/irq.h> -#include <asm/sn/intr.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <linux/rculist.h> +#include <linux/slab.h> #include <asm/sn/addrs.h> #include <asm/sn/arch.h> -#include "xtalk/xwidgetdev.h" +#include <asm/sn/intr.h> +#include <asm/sn/pcibr_provider.h> #include <asm/sn/pcibus_provider_defs.h> #include <asm/sn/pcidev.h> -#include "pci/pcibr_provider.h" #include <asm/sn/shub_mmr.h> #include <asm/sn/sn_sal.h> +#include <asm/sn/sn_feature_sets.h> -static void force_interrupt(int irq); static void register_intr_pda(struct sn_irq_info *sn_irq_info); static void unregister_intr_pda(struct sn_irq_info *sn_irq_info); -extern int sn_force_interrupt_flag; extern int sn_ioif_inited; -struct sn_irq_info **sn_irq; +struct list_head **sn_irq_lh; +static DEFINE_SPINLOCK(sn_irq_info_lock); /* non-IRQ lock */ -static inline uint64_t sn_intr_alloc(nasid_t local_nasid, int local_widget, - u64 sn_irq_info, +u64 sn_intr_alloc(nasid_t local_nasid, int local_widget, + struct sn_irq_info *sn_irq_info, int req_irq, nasid_t req_nasid, int req_slice) { @@ -38,12 +41,13 @@ static inline uint64_t sn_intr_alloc(nasid_t local_nasid, int local_widget, 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) local_widget, __pa(sn_irq_info), (u64) req_irq, (u64) req_nasid, (u64) req_slice); + return ret_stuff.status; } -static inline void sn_intr_free(nasid_t local_nasid, int local_widget, +void sn_intr_free(nasid_t local_nasid, int local_widget, struct sn_irq_info *sn_irq_info) { struct ia64_sal_retval ret_stuff; @@ -56,143 +60,208 @@ static inline void sn_intr_free(nasid_t local_nasid, int local_widget, (u64) sn_irq_info->irq_cookie, 0, 0); } -static unsigned int sn_startup_irq(unsigned int irq) +u64 sn_intr_redirect(nasid_t local_nasid, int local_widget, + struct sn_irq_info *sn_irq_info, + 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_REDIRECT, (u64) local_nasid, + (u64) local_widget, __pa(sn_irq_info), + (u64) req_nasid, (u64) req_slice, 0); + + return ret_stuff.status; +} + +static unsigned int sn_startup_irq(struct irq_data *data) { return 0; } -static void sn_shutdown_irq(unsigned int irq) +static void sn_shutdown_irq(struct irq_data *data) { } -static void sn_disable_irq(unsigned int irq) +extern void ia64_mca_register_cpev(int); + +static void sn_disable_irq(struct irq_data *data) { + if (data->irq == local_vector_to_irq(IA64_CPE_VECTOR)) + ia64_mca_register_cpev(0); } -static void sn_enable_irq(unsigned int irq) +static void sn_enable_irq(struct irq_data *data) { + if (data->irq == local_vector_to_irq(IA64_CPE_VECTOR)) + ia64_mca_register_cpev(data->irq); } -static void sn_ack_irq(unsigned int irq) +static void sn_ack_irq(struct irq_data *data) { - uint64_t event_occurred, mask = 0; - int nasid; + u64 event_occurred, mask; + unsigned int irq = data->irq & 0xff; - irq = irq & 0xff; - nasid = get_nasid(); - event_occurred = - HUB_L((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED)); + event_occurred = HUB_L((u64*)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED)); mask = event_occurred & SH_ALL_INT_MASK; - HUB_S((uint64_t *) GLOBAL_MMR_ADDR(nasid, SH_EVENT_OCCURRED_ALIAS), - mask); + HUB_S((u64*)LOCAL_MMR_ADDR(SH_EVENT_OCCURRED_ALIAS), mask); __set_bit(irq, (volatile void *)pda->sn_in_service_ivecs); - move_irq(irq); + irq_move_irq(data); } -static void sn_end_irq(unsigned int irq) +struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info, + nasid_t nasid, int slice) { - int nasid; - int ivec; - uint64_t event_occurred; - - ivec = irq & 0xff; - if (ivec == SGI_UART_VECTOR) { - 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); - } + int vector; + int cpuid; +#ifdef CONFIG_SMP + int cpuphys; +#endif + int64_t bridge; + int local_widget, status; + nasid_t local_nasid; + struct sn_irq_info *new_irq_info; + struct sn_pcibus_provider *pci_provider; + + bridge = (u64) sn_irq_info->irq_bridge; + if (!bridge) { + return NULL; /* irq is not a device interrupt */ + } + + local_nasid = NASID_GET(bridge); + + if (local_nasid & 1) + local_widget = TIO_SWIN_WIDGETNUM(bridge); + else + local_widget = SWIN_WIDGETNUM(bridge); + vector = sn_irq_info->irq_irq; + + /* Make use of SAL_INTR_REDIRECT if PROM supports it */ + status = sn_intr_redirect(local_nasid, local_widget, sn_irq_info, nasid, slice); + if (!status) { + new_irq_info = sn_irq_info; + goto finish_up; + } + + /* + * PROM does not support SAL_INTR_REDIRECT, or it failed. + * Revert to old method. + */ + new_irq_info = kmemdup(sn_irq_info, sizeof(struct sn_irq_info), + GFP_ATOMIC); + if (new_irq_info == NULL) + return NULL; + + /* Free the old PROM new_irq_info structure */ + sn_intr_free(local_nasid, local_widget, new_irq_info); + unregister_intr_pda(new_irq_info); + + /* allocate a new PROM new_irq_info struct */ + status = sn_intr_alloc(local_nasid, local_widget, + new_irq_info, vector, + nasid, slice); + + /* SAL call failed */ + if (status) { + kfree(new_irq_info); + return NULL; } - __clear_bit(ivec, (volatile void *)pda->sn_in_service_ivecs); - if (sn_force_interrupt_flag) - force_interrupt(irq); + + register_intr_pda(new_irq_info); + spin_lock(&sn_irq_info_lock); + list_replace_rcu(&sn_irq_info->list, &new_irq_info->list); + spin_unlock(&sn_irq_info_lock); + kfree_rcu(sn_irq_info, rcu); + + +finish_up: + /* Update kernels new_irq_info with new target info */ + cpuid = nasid_slice_to_cpuid(new_irq_info->irq_nasid, + new_irq_info->irq_slice); + new_irq_info->irq_cpuid = cpuid; + + pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type]; + + /* + * If this represents a line interrupt, target it. If it's + * an msi (irq_int_bit < 0), it's already targeted. + */ + if (new_irq_info->irq_int_bit >= 0 && + pci_provider && pci_provider->target_interrupt) + (pci_provider->target_interrupt)(new_irq_info); + +#ifdef CONFIG_SMP + cpuphys = cpu_physical_id(cpuid); + set_irq_affinity_info((vector & 0xff), cpuphys, 0); +#endif + + return new_irq_info; } -static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask) +static int sn_set_affinity_irq(struct irq_data *data, + const struct cpumask *mask, bool force) { - 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; + struct sn_irq_info *sn_irq_info, *sn_irq_info_safe; + unsigned int irq = data->irq; + nasid_t nasid; + int slice; - 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); - } + nasid = cpuid_to_nasid(cpumask_first_and(mask, cpu_online_mask)); + slice = cpuid_to_slice(cpumask_first_and(mask, cpu_online_mask)); - sn_irq_info = sn_irq_info->irq_next; + list_for_each_entry_safe(sn_irq_info, sn_irq_info_safe, + sn_irq_lh[irq], list) + (void)sn_retarget_vector(sn_irq_info, nasid, slice); + + return 0; +} #ifdef CONFIG_SMP - set_irq_affinity_info((irq & 0xff), cpuphys, 0); +void sn_set_err_irq_affinity(unsigned int irq) +{ + /* + * On systems which support CPU disabling (SHub2), all error interrupts + * are targeted at the boot CPU. + */ + if (is_shub2() && sn_prom_feature_available(PRF_CPU_DISABLE_SUPPORT)) + set_irq_affinity_info(irq, cpu_physical_id(0), 0); +} +#else +void sn_set_err_irq_affinity(unsigned int irq) { } #endif - } else { - break; /* snp_affinity failed the intr_alloc */ - } - } - kfree(tmp_sn_irq_info); + +static void +sn_mask_irq(struct irq_data *data) +{ +} + +static void +sn_unmask_irq(struct irq_data *data) +{ } -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_end_irq, - sn_set_affinity_irq +struct irq_chip irq_type_sn = { + .name = "SN hub", + .irq_startup = sn_startup_irq, + .irq_shutdown = sn_shutdown_irq, + .irq_enable = sn_enable_irq, + .irq_disable = sn_disable_irq, + .irq_ack = sn_ack_irq, + .irq_mask = sn_mask_irq, + .irq_unmask = sn_unmask_irq, + .irq_set_affinity = sn_set_affinity_irq }; +ia64_vector sn_irq_to_vector(int irq) +{ + if (irq >= IA64_NUM_VECTORS) + return 0; + return (ia64_vector)irq; +} + unsigned int sn_local_vector_to_irq(u8 vector) { return (CPU_VECTOR_TO_IRQ(smp_processor_id(), vector)); @@ -201,12 +270,13 @@ unsigned int sn_local_vector_to_irq(u8 vector) void sn_irq_init(void) { int i; - irq_desc_t *base_desc = irq_desc; + + ia64_first_device_vector = IA64_SN2_FIRST_DEVICE_VECTOR; + ia64_last_device_vector = IA64_SN2_LAST_DEVICE_VECTOR; for (i = 0; i < NR_IRQS; i++) { - if (base_desc[i].handler == &no_irq_type) { - base_desc[i].handler = &irq_type_sn; - } + if (irq_get_chip(i) == &no_irq_chip) + irq_set_chip(i, &irq_type_sn); } } @@ -219,9 +289,8 @@ static void register_intr_pda(struct sn_irq_info *sn_irq_info) pdacpu(cpu)->sn_last_irq = irq; } - if (pdacpu(cpu)->sn_first_irq == 0 || pdacpu(cpu)->sn_first_irq > irq) { + if (pdacpu(cpu)->sn_first_irq == 0 || pdacpu(cpu)->sn_first_irq > irq) pdacpu(cpu)->sn_first_irq = irq; - } } static void unregister_intr_pda(struct sn_irq_info *sn_irq_info) @@ -231,19 +300,18 @@ static void unregister_intr_pda(struct sn_irq_info *sn_irq_info) struct sn_irq_info *tmp_irq_info; int i, foundmatch; + rcu_read_lock(); 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) { + for (i = pdacpu(cpu)->sn_last_irq - 1; + i && !foundmatch; i--) { + list_for_each_entry_rcu(tmp_irq_info, + sn_irq_lh[i], + list) { if (tmp_irq_info->irq_cpuid == cpu) { - foundmatch++; + foundmatch = 1; break; } - tmp_irq_info = tmp_irq_info->irq_next; - } - if (foundmatch) { - break; } } pdacpu(cpu)->sn_last_irq = i; @@ -251,61 +319,20 @@ static void unregister_intr_pda(struct sn_irq_info *sn_irq_info) 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) { + for (i = pdacpu(cpu)->sn_first_irq + 1; + i < NR_IRQS && !foundmatch; i++) { + list_for_each_entry_rcu(tmp_irq_info, + sn_irq_lh[i], + list) { if (tmp_irq_info->irq_cpuid == cpu) { - foundmatch++; + foundmatch = 1; break; } - tmp_irq_info = tmp_irq_info->irq_next; - } - if (foundmatch) { - break; } } pdacpu(cpu)->sn_first_irq = ((i == NR_IRQS) ? 0 : 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); + rcu_read_unlock(); } void sn_irq_fixup(struct pci_dev *pci_dev, struct sn_irq_info *sn_irq_info) @@ -313,31 +340,72 @@ void sn_irq_fixup(struct pci_dev *pci_dev, struct sn_irq_info *sn_irq_info) nasid_t nasid = sn_irq_info->irq_nasid; int slice = sn_irq_info->irq_slice; int cpu = nasid_slice_to_cpuid(nasid, slice); +#ifdef CONFIG_SMP + int cpuphys; +#endif + pci_dev_get(pci_dev); 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); + spin_lock(&sn_irq_info_lock); + list_add_rcu(&sn_irq_info->list, sn_irq_lh[sn_irq_info->irq_irq]); + reserve_irq_vector(sn_irq_info->irq_irq); + if (sn_irq_info->irq_int_bit != -1) + irq_set_handler(sn_irq_info->irq_irq, handle_level_irq); + spin_unlock(&sn_irq_info_lock); + + register_intr_pda(sn_irq_info); +#ifdef CONFIG_SMP + cpuphys = cpu_physical_id(cpu); + set_irq_affinity_info(sn_irq_info->irq_irq, cpuphys, 0); + /* + * Affinity was set by the PROM, prevent it from + * being reset by the request_irq() path. + */ + irqd_mark_affinity_was_set(irq_get_irq_data(sn_irq_info->irq_irq)); +#endif } -static void force_interrupt(int irq) +void sn_irq_unfixup(struct pci_dev *pci_dev) { struct sn_irq_info *sn_irq_info; - if (!sn_ioif_inited) + /* Only cleanup IRQ stuff if this device has a host bus context */ + if (!SN_PCIDEV_BUSSOFT(pci_dev)) + return; + + sn_irq_info = SN_PCIDEV_INFO(pci_dev)->pdi_sn_irq_info; + if (!sn_irq_info) + return; + if (!sn_irq_info->irq_irq) { + kfree(sn_irq_info); 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); - } - sn_irq_info = sn_irq_info->irq_next; } + + unregister_intr_pda(sn_irq_info); + spin_lock(&sn_irq_info_lock); + list_del_rcu(&sn_irq_info->list); + spin_unlock(&sn_irq_info_lock); + if (list_empty(sn_irq_lh[sn_irq_info->irq_irq])) + free_irq_vector(sn_irq_info->irq_irq); + kfree_rcu(sn_irq_info, rcu); + pci_dev_put(pci_dev); + +} + +static inline void +sn_call_force_intr_provider(struct sn_irq_info *sn_irq_info) +{ + struct sn_pcibus_provider *pci_provider; + + pci_provider = sn_pci_provider[sn_irq_info->irq_bridge_type]; + + /* Don't force an interrupt if the irq has been disabled */ + if (!irqd_irq_disabled(irq_get_irq_data(sn_irq_info->irq_irq)) && + pci_provider && pci_provider->force_interrupt) + (*pci_provider->force_interrupt)(sn_irq_info); } /* @@ -351,13 +419,18 @@ static void force_interrupt(int irq) */ static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info) { - uint64_t regval; - int irr_reg_num; - int irr_bit; - uint64_t irr_reg; + u64 regval; struct pcidev_info *pcidev_info; struct pcibus_info *pcibus_info; + /* + * Bridge types attached to TIO (anything but PIC) do not need this WAR + * since they do not target Shub II interrupt registers. If that + * ever changes, this check needs to accommodate. + */ + if (sn_irq_info->irq_bridge_type != PCIIO_ASIC_TYPE_PIC) + return; + pcidev_info = (struct pcidev_info *)sn_irq_info->irq_pciioinfo; if (!pcidev_info) return; @@ -367,33 +440,13 @@ static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info) pdi_pcibus_info; regval = pcireg_intr_status_get(pcibus_info); - 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; - } - 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 (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); - } + if (!ia64_get_irr(irq_to_vector(irq))) { + if (!test_bit(irq, pda->sn_in_service_ivecs)) { + regval &= 0xff; + if (sn_irq_info->irq_int_bit & regval & + sn_irq_info->irq_last_intr) { + regval &= ~(sn_irq_info->irq_int_bit & regval); + sn_call_force_intr_provider(sn_irq_info); } } } @@ -402,19 +455,34 @@ static void sn_check_intr(int irq, struct sn_irq_info *sn_irq_info) void sn_lb_int_war_check(void) { + struct sn_irq_info *sn_irq_info; int i; if (!sn_ioif_inited || pda->sn_first_irq == 0) return; + + rcu_read_lock(); 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; + list_for_each_entry_rcu(sn_irq_info, sn_irq_lh[i], list) { + sn_check_intr(i, sn_irq_info); } } + rcu_read_unlock(); +} + +void __init sn_irq_lh_init(void) +{ + int i; + + sn_irq_lh = kmalloc(sizeof(struct list_head *) * NR_IRQS, GFP_KERNEL); + if (!sn_irq_lh) + panic("SN PCI INIT: Failed to allocate memory for PCI init\n"); + + for (i = 0; i < NR_IRQS; i++) { + sn_irq_lh[i] = kmalloc(sizeof(struct list_head), GFP_KERNEL); + if (!sn_irq_lh[i]) + panic("SN PCI INIT: Failed IRQ memory allocation\n"); + + INIT_LIST_HEAD(sn_irq_lh[i]); + } } |
