/****************************************************************************** * evtchn.c * * Communication via Xen event channels. * * Copyright (c) 2002-2005, K A Fraser * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation; or, when distributed * separately from the Linux kernel or incorporated into other * software packages, subject to the following license: * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this source file (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* RTC_IRQ */ /* * This lock protects updates to the following mapping and reference-count * arrays. The lock does not need to be acquired to read the mapping tables. */ static DEFINE_SPINLOCK(irq_mapping_update_lock); /* IRQ <-> event-channel mappings. */ static int evtchn_to_irq[NR_EVENT_CHANNELS] = { [0 ... NR_EVENT_CHANNELS-1] = -1 }; /* Packed IRQ information: binding type, sub-type index, and event channel. */ static u32 irq_info[NR_IRQS]; /* Binding types. */ enum { IRQT_UNBOUND, IRQT_PIRQ, IRQT_VIRQ, IRQT_IPI, IRQT_EVTCHN }; /* Constructor for packed IRQ information. */ static inline u32 mk_irq_info(u32 type, u32 index, u32 evtchn) { return ((type << 24) | (index << 16) | evtchn); } /* Convenient shorthand for packed representation of an unbound IRQ. */ #define IRQ_UNBOUND mk_irq_info(IRQT_UNBOUND, 0, 0) /* * Accessors for packed IRQ information. */ static inline unsigned int evtchn_from_irq(int irq) { return (u16)(irq_info[irq]); } static inline unsigned int index_from_irq(int irq) { return (u8)(irq_info[irq] >> 16); } static inline unsigned int type_from_irq(int irq) { return (u8)(irq_info[irq] >> 24); } /* IRQ <-> VIRQ mapping. */ DEFINE_PER_CPU(int, virq_to_irq[NR_VIRQS]) = {[0 ... NR_VIRQS-1] = -1}; /* IRQ <-> IPI mapping. */ #ifndef NR_IPIS #define NR_IPIS 1 #endif DEFINE_PER_CPU(int, ipi_to_irq[NR_IPIS]) = {[0 ... NR_IPIS-1] = -1}; /* Reference counts for bindings to IRQs. */ static int irq_bindcount[NR_IRQS]; /* Bitmap indicating which PIRQs require Xen to be notified on unmask. */ static unsigned long pirq_needs_eoi[NR_PIRQS/sizeof(unsigned long)]; #ifdef CONFIG_SMP static u8 cpu_evtchn[NR_EVENT_CHANNELS]; static unsigned long cpu_evtchn_mask[NR_CPUS][NR_EVENT_CHANNELS/BITS_PER_LONG]; static inline unsigned long active_evtchns(unsigned int cpu, shared_info_t *sh, unsigned int idx) { return (sh->evtchn_pending[idx] & cpu_evtchn_mask[cpu][idx] & ~sh->evtchn_mask[idx]); } static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu) { int irq = evtchn_to_irq[chn]; BUG_ON(irq == -1); set_native_irq_info(irq, cpumask_of_cpu(cpu)); clear_bit(chn, (unsigned long *)cpu_evtchn_mask[cpu_evtchn[chn]]); set_bit(chn, (unsigned long *)cpu_evtchn_mask[cpu]); cpu_evtchn[chn] = cpu; } static void init_evtchn_cpu_bindings(void) { int i; /* By default all event channels notify CPU#0. */ for (i = 0; i < NR_IRQS; i++) set_native_irq_info(i, cpumask_of_cpu(0)); memset(cpu_evtchn, 0, sizeof(cpu_evtchn)); memset(cpu_evtchn_mask[0], ~0, sizeof(cpu_evtchn_mask[0])); } static inline unsigned int cpu_from_evtchn(unsigned int evtchn) { return cpu_evtchn[evtchn]; } #else static inline unsigned long active_evtchns(unsigned int cpu, shared_info_t *sh, unsigned int idx) { return (sh->evtchn_pending[idx] & ~sh->evtchn_mask[idx]); } static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu) { } static void init_evtchn_cpu_bindings(void) { } static inline unsigned int cpu_from_evtchn(unsigned int evtchn) { return 0; } #endif /* Upcall to generic IRQ layer. */ #ifdef CONFIG_X86 extern fastcall unsigned int do_IRQ(struct pt_regs *regs); void __init xen_init_IRQ(void); void __init init_IRQ(void) { irq_ctx_init(0); xen_init_IRQ(); } #if defined (__i386__) static inline void exit_idle(void) {} #define IRQ_REG orig_eax #elif defined (__x86_64__) #include #define IRQ_REG orig_rax #endif #define do_IRQ(irq, regs) do { \ (regs)->IRQ_REG = ~(irq); \ do_IRQ((regs)); \ } while (0) #endif /* Xen will never allocate port zero for any purpose. */ #define VALID_EVTCHN(chn) ((chn) != 0) /* * Force a proper event-channel callback from Xen after clearing the * callback mask. We do this in a very simple manner, by making a call * down into Xen. The pending flag will be checked by Xen on return. */ void force_evtchn_callback(void) { (void)HYPERVISOR_xen_version(0, NULL); } /* Not a GPL symbol: used in ubiquitous macros, so too restrictive. */ EXPORT_SYMBOL(force_evtchn_callback); /* NB. Interrupts are disabled on entry. */ asmlinkage void evtchn_do_upcall(struct pt_regs *regs) { unsigned long l1, l2; unsigned int l1i, l2i, port; int irq, cpu = smp_processor_id(); shared_info_t *s = HYPERVISOR_shared_info; vcpu_info_t *vcpu_info = &s->vcpu_info[cpu]; vcpu_info->evtchn_upcall_pending = 0; #ifndef CONFIG_X86 /* No need for a barrier -- XCHG is a barrier on x86. */ /* Clear master pending flag /before/ clearing selector flag. */ rmb(); #endif l1 = xchg(&vcpu_info->evtchn_pending_sel, 0); while (l1 != 0) { l1i = __ffs(l1); l1 &= ~(1UL << l1i); while ((l2 = active_evtchns(cpu, s, l1i)) != 0) { l2i = __ffs(l2); port = (l1i * BITS_PER_LONG) + l2i; if ((irq = evtchn_to_irq[port]) != -1) do_IRQ(irq, regs); else { exit_idle(); evtchn_device_upcall(port); } } } } static int find_unbound_irq(void) { int irq; /* Only allocate from dynirq range */ for (irq = DYNIRQ_BASE; irq < NR_IRQS; irq++) if (irq_bindcount[irq] == 0) break; if (irq == NR_IRQS) panic("No available IRQ to bind to: increase NR_IRQS!\n"); return irq; } static int bind_evtchn_to_irq(unsigned int evtchn) { int irq; spin_lock(&irq_mapping_update_lock); if ((irq = evtchn_to_irq[evtchn]) == -1) { irq = find_unbound_irq(); evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_EVTCHN, 0, evtchn); } irq_bindcount[irq]++; spin_unlock(&irq_mapping_update_lock); return irq; } static int bind_virq_to_irq(unsigned int virq, unsigned int cpu) { struct evtchn_bind_virq bind_virq; int evtchn, irq; spin_lock(&irq_mapping_update_lock); if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1) { bind_virq.virq = virq; bind_virq.vcpu = cpu; if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind_virq) != 0) BUG(); evtchn = bind_virq.port; irq = find_unbound_irq(); evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); per_cpu(virq_to_irq, cpu)[virq] = irq; bind_evtchn_to_cpu(evtchn, cpu); } irq_bindcount[irq]++; spin_unlock(&irq_mapping_update_lock); return irq; } static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) { struct evtchn_bind_ipi bind_ipi; int evtchn, irq; spin_lock(&irq_mapping_update_lock); if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1) { bind_ipi.vcpu = cpu; if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi) != 0) BUG(); evtchn = bind_ipi.port; irq = find_unbound_irq(); evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); per_cpu(ipi_to_irq, cpu)[ipi] = irq; bind_evtchn_to_cpu(evtchn, cpu); } irq_bindcount[irq]++; spin_unlock(&irq_mapping_update_lock); return irq; } static void unbind_from_irq(unsigned int irq) { struct evtchn_close close; int evtchn = evtchn_from_irq(irq); spin_lock(&irq_mapping_update_lock); if ((--irq_bindcount[irq] == 0) && VALID_EVTCHN(evtchn)) { close.port = evtchn; if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0) BUG(); switch (type_from_irq(irq)) { case IRQT_VIRQ: per_cpu(virq_to_irq, cpu_from_evtchn(evtchn)) [index_from_irq(irq)] = -1; break; case IRQT_IPI: per_cpu(ipi_to_irq, cpu_from_evtchn(evtchn)) [index_from_irq(irq)] = -1; break; default: break; } /* Closed ports are implicitly re-bound to VCPU0. */ bind_evtchn_to_cpu(evtchn, 0); evtchn_to_irq[evtchn] = -1; irq_info[irq] = IRQ_UNBOUND; } spin_unlock(&irq_mapping_update_lock); } int bind_evtchn_to_irqhandler( unsigned int evtchn, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) { unsigned int irq; int retval; irq = bind_evtchn_to_irq(evtchn); retval = request_irq(irq, handler, irqflags, devname, dev_id); if (retval != 0) { unbind_from_irq(irq); return retval; } return irq; } EXPORT_SYMBOL_GPL(bind_evtchn_to_irqhandler); int bind_virq_to_irqhandler( unsigned int virq, unsigned int cpu, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) { unsigned int irq; int retval; irq = bind_virq_to_irq(virq, cpu); retval = request_irq(irq, handler, irqflags, devname, dev_id); if (retval != 0) { unbind_from_irq(irq); return retval; } return irq; } EXPORT_SYMBOL_GPL(bind_virq_to_irqhandler); int bind_ipi_to_irqhandler( unsigned int ipi, unsigned int cpu, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) { unsigned int irq; int retval; irq = bind_ipi_to_irq(ipi, cpu); retval = request_irq(irq, handler, irqflags, devname, dev_id); if (retval != 0) { unbind_from_irq(irq); return retval; } return irq; } EXPORT_SYMBOL_GPL(bind_ipi_to_irqhandler); void unbind_from_irqhandler(unsigned int irq, void *dev_id) { free_irq(irq, dev_id); unbind_from_irq(irq); } EXPORT_SYMBOL_GPL(unbind_from_irqhandler); /* Rebind an evtchn so that it gets delivered to a specific cpu */ static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu) { struct evtchn_bind_vcpu bind_vcpu; int evtchn = evtchn_from_irq(irq); if (!VALID_EVTCHN(evtchn)) return; /* Send future instances of this interrupt to other vcpu. */ bind_vcpu.port = evtchn; bind_vcpu.vcpu = tcpu; /* * If this fails, it usually just indicates that we're dealing with a * virq or IPI channel, which don't actually need to be rebound. Ignore * it, but don't do the xenlinux-level rebind in that case. */ if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0) bind_evtchn_to_cpu(evtchn, tcpu); } static void set_affinity_irq(unsigned irq, cpumask_t dest) { unsigned tcpu = first_cpu(dest); rebind_irq_to_cpu(irq, tcpu); } static int retrigger_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); shared_info_t *s = HYPERVISOR_shared_info; if (!VALID_EVTCHN(evtchn)) return 1; synch_set_bit(evtchn, &s->evtchn_pending[0]); return 1; } /* * Interface to generic handling in irq.c */ static unsigned int startup_dynirq_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); if (VALID_EVTCHN(evtchn)) unmask_evtchn(evtchn); return 0; } static void unmask_dynirq_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); if (VALID_EVTCHN(evtchn)) unmask_evtchn(evtchn); } static void mask_dynirq_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); if (VALID_EVTCHN(evtchn)) mask_evtchn(evtchn); } static void ack_dynirq_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); move_native_irq(irq); if (VALID_EVTCHN(evtchn)) { mask_evtchn(evtchn); clear_evtchn(evtchn); } } static void ack_dynirq_quirk_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); if (VALID_EVTCHN(evtchn) && !(irq_desc[irq].status & IRQ_DISABLED)) unmask_evtchn(evtchn); } static struct irq_chip dynirq_chip = { .name = "Dynamic-irq", .startup = startup_dynirq_vector, .mask = mask_dynirq_vector, .unmask = unmask_dynirq_vector, .ack = ack_dynirq_vector, .eoi = ack_dynirq_quirk_vector, #ifdef CONFIG_SMP .set_affinity = set_affinity_irq, #endif .retrigger = retrigger_vector, }; static inline void pirq_unmask_notify(int pirq) { struct physdev_eoi eoi = { .irq = pirq }; if (unlikely(test_bit(pirq, &pirq_needs_eoi[0]))) (void)HYPERVISOR_physdev_op(PHYSDEVOP_eoi, &eoi); } static inline void pirq_query_unmask(int pirq) { struct physdev_irq_status_query irq_status; irq_status.irq = pirq; (void)HYPERVISOR_physdev_op(PHYSDEVOP_irq_status_query, &irq_status); clear_bit(pirq, &pirq_needs_eoi[0]); if (irq_status.flags & XENIRQSTAT_needs_eoi) set_bit(pirq, &pirq_needs_eoi[0]); } /* * On startup, if there is no action associated with the IRQ then we are * probing. In this case we should not share with others as it will confuse us. */ #define probing_irq(_irq) (irq_desc[(_irq)].action == NULL) static unsigned int startup_pirq_vector(unsigned int irq) { struct evtchn_bind_pirq bind_pirq; int evtchn = evtchn_from_irq(irq); if (VALID_EVTCHN(evtchn)) goto out; bind_pirq.pirq = irq; /* NB. We are happy to share unless we are probing. */ bind_pirq.flags = probing_irq(irq) ? 0 : BIND_PIRQ__WILL_SHARE; if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_pirq, &bind_pirq) != 0) { if (!probing_irq(irq)) printk(KERN_INFO "Failed to obtain physical IRQ %d\n", irq); return 0; } evtchn = bind_pirq.port; pirq_query_unmask(irq_to_pirq(irq)); evtchn_to_irq[evtchn] = irq; bind_evtchn_to_cpu(evtchn, 0); irq_info[irq] = mk_irq_info(IRQT_PIRQ, irq, evtchn); out: unmask_evtchn(evtchn); pirq_unmask_notify(irq_to_pirq(irq)); return 0; } static void unmask_pirq_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); if (VALID_EVTCHN(evtchn)) { unmask_evtchn(evtchn); pirq_unmask_notify(irq_to_pirq(irq)); } } static void mask_pirq_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); if (VALID_EVTCHN(evtchn)) mask_evtchn(evtchn); } static void ack_pirq_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); move_native_irq(irq); if (VALID_EVTCHN(evtchn)) { mask_evtchn(evtchn); clear_evtchn(evtchn); } } static void ack_pirq_quirk_vector(unsigned int irq) { int evtchn = evtchn_from_irq(irq); if (VALID_EVTCHN(evtchn) && !(irq_desc[irq].status & IRQ_DISABLED)) { unmask_evtchn(evtchn); pirq_unmask_notify(irq_to_pirq(irq)); } } static struct irq_chip pirq_chip = { .name = "Phys-irq", .startup = startup_pirq_vector, .mask = mask_pirq_vector, .unmask = unmask_pirq_vector, .ack = ack_pirq_vector, .eoi = ack_pirq_quirk_vector, #ifdef CONFIG_SMP .set_affinity = set_affinity_irq, #endif .retrigger = retrigger_vector, }; int irq_ignore_unhandled(unsigned int irq) { struct physdev_irq_status_query irq_status = { .irq = irq }; if (!is_running_on_xen()) return 0; (void)HYPERVISOR_physdev_op(PHYSDEVOP_irq_status_query, &irq_status); return !!(irq_status.flags & XENIRQSTAT_shared); } void resend_irq_on_evtchn(unsigned int i) { int evtchn = evtchn_from_irq(i); shared_info_t *s = HYPERVISOR_shared_info; if (!VALID_EVTCHN(evtchn)) return; BUG_ON(!synch_test_bit(evtchn, &s->evtchn_mask[0])); synch_set_bit(evtchn, &s->evtchn_pending[0]); } void notify_remote_via_irq(int irq) { int evtchn = evtchn_from_irq(irq); if (VALID_EVTCHN(evtchn)) notify_remote_via_evtchn(evtchn); } EXPORT_SYMBOL_GPL(notify_remote_via_irq); void mask_evtchn(int port) { shared_info_t *s = HYPERVISOR_shared_info; synch_set_bit(port, &s->evtchn_mask[0]); } EXPORT_SYMBOL_GPL(mask_evtchn); void unmask_evtchn(int port) { shared_info_t *s = HYPERVISOR_shared_info; unsigned int cpu = smp_processor_id(); vcpu_info_t *vcpu_info = &s->vcpu_info[cpu]; BUG_ON(!irqs_disabled()); /* Slow path (hypercall) if this is a non-local port. */ if (unlikely(cpu != cpu_from_evtchn(port))) { struct evtchn_unmask unmask = { .port = port }; (void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask); return; } synch_clear_bit(port, &s->evtchn_mask[0]); /* * The following is basically the equivalent of 'hw_resend_irq'. Just * like a real IO-APIC we 'lose the interrupt edge' if the channel is * masked. */ if (synch_test_bit(port, &s->evtchn_pending[0]) && !synch_test_and_set_bit(port / BITS_PER_LONG, &vcpu_info->evtchn_pending_sel)) vcpu_info->evtchn_upcall_pending = 1; } EXPORT_SYMBOL_GPL(unmask_evtchn); void irq_resume(void) { struct evtchn_bind_virq bind_virq; struct evtchn_bind_ipi bind_ipi; int cpu, pirq, virq, ipi, irq, evtchn; init_evtchn_cpu_bindings(); /* New event-channel space is not 'live' yet. */ for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) mask_evtchn(evtchn); /* Check that no PIRQs are still bound. */ for (pirq = 0; pirq < NR_PIRQS; pirq++) BUG_ON(irq_info[pirq_to_irq(pirq)] != IRQ_UNBOUND); /* Secondary CPUs must have no VIRQ or IPI bindings. */ for_each_possible_cpu(cpu) { if (cpu == 0) continue; for (virq = 0; virq < NR_VIRQS; virq++) BUG_ON(per_cpu(virq_to_irq, cpu)[virq] != -1); for (ipi = 0; ipi < NR_IPIS; ipi++) BUG_ON(per_cpu(ipi_to_irq, cpu)[ipi] != -1); } /* No IRQ <-> event-channel mappings. */ for (irq = 0; irq < NR_IRQS; irq++) irq_info[irq] &= ~0xFFFF; /* zap event-channel binding */ for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++) evtchn_to_irq[evtchn] = -1; /* Primary CPU: rebind VIRQs automatically. */ for (virq = 0; virq < NR_VIRQS; virq++) { if ((irq = per_cpu(virq_to_irq, 0)[virq]) == -1) continue; BUG_ON(irq_info[irq] != mk_irq_info(IRQT_VIRQ, virq, 0)); /* Get a new binding from Xen. */ bind_virq.virq = virq; bind_virq.vcpu = 0; if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind_virq) != 0) BUG(); evtchn = bind_virq.port; /* Record the new mapping. */ evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); /* Ready for use. */ unmask_evtchn(evtchn); } /* Primary CPU: rebind IPIs automatically. */ for (ipi = 0; ipi < NR_IPIS; ipi++) { if ((irq = per_cpu(ipi_to_irq, 0)[ipi]) == -1) continue; BUG_ON(irq_info[irq] != mk_irq_info(IRQT_IPI, ipi, 0)); /* Get a new binding from Xen. */ bind_ipi.vcpu = 0; if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi) != 0) BUG(); evtchn = bind_ipi.port; /* Record the new mapping. */ evtchn_to_irq[evtchn] = irq; irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); /* Ready for use. */ unmask_evtchn(evtchn); } } void __init xen_init_IRQ(void) { int i; init_evtchn_cpu_bindings(); /* No event channels are 'live' right now. */ for (i = 0; i < NR_EVENT_CHANNELS; i++) mask_evtchn(i); /* No IRQ -> event-channel mappings. */ for (i = 0; i < NR_IRQS; i++) irq_info[i] = IRQ_UNBOUND; /* Dynamic IRQ space is currently unbound. Zero the refcnts. */ for (i = 0; i < NR_DYNIRQS; i++) { irq_bindcount[dynirq_to_irq(i)] = 0; irq_desc[dynirq_to_irq(i)].status = IRQ_DISABLED; irq_desc[dynirq_to_irq(i)].action = NULL; irq_desc[dynirq_to_irq(i)].depth = 1; set_irq_chip_and_handler_name(dynirq_to_irq(i), &dynirq_chip, handle_level_irq, "level"); } /* Phys IRQ space is statically bound (1:1 mapping). Nail refcnts. */ for (i = 0; i < NR_PIRQS; i++) { irq_bindcount[pirq_to_irq(i)] = 1; #ifdef RTC_IRQ /* If not domain 0, force our RTC driver to fail its probe. */ if ((i == RTC_IRQ) && !is_initial_xendomain()) continue; #endif irq_desc[pirq_to_irq(i)].status = IRQ_DISABLED; irq_desc[pirq_to_irq(i)].action = NULL; irq_desc[pirq_to_irq(i)].depth = 1; set_irq_chip_and_handler_name(pirq_to_irq(i), &pirq_chip, handle_level_irq, "level"); } }