/* * linux/arch/ia64/kernel/irq.c * * Copyright (C) 1998-2000 Hewlett-Packard Co * Copyright (C) 1998, 1999 Stephane Eranian * Copyright (C) 1999-2000 David Mosberger-Tang * * 6/10/99: Updated to bring in sync with x86 version to facilitate * support for SMP and different interrupt controllers. */ #include #include #include #include #include #include #include #include #include #include /* for rand_initialize_irq() */ #include #include #include #include #ifdef CONFIG_KDB # include #endif #include #include #include #include #include #include #include /* This is used to detect bad usage of probe_irq_on()/probe_irq_off(). */ #define PROBE_IRQ_COOKIE 0xfeedC0FFEE struct irq_desc irq_desc[NR_IRQS]; /* * Micro-access to controllers is serialized over the whole * system. We never hold this lock when we call the actual * IRQ handler. */ spinlock_t irq_controller_lock; #ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC spinlock_t ivr_read_lock; #endif unsigned int local_bh_count[NR_CPUS]; /* * used in irq_enter()/irq_exit() */ unsigned int local_irq_count[NR_CPUS]; static struct irqaction timer_action = { NULL, 0, 0, NULL, NULL, NULL}; #ifdef CONFIG_SMP static struct irqaction ipi_action = { NULL, 0, 0, NULL, NULL, NULL}; #endif /* * Legacy IRQ to IA-64 vector translation table. Any vector not in * this table maps to itself (ie: irq 0x30 => IA64 vector 0x30) */ __u8 irq_to_vector_map[IA64_MIN_VECTORED_IRQ] = { /* 8259 IRQ translation, first 16 entries */ TIMER_IRQ, 0x50, 0x0f, 0x51, 0x52, 0x53, 0x43, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x40, 0x41, }; /* * Reverse of the above table. */ static __u8 vector_to_legacy_map[256]; /* * used by proc fs (/proc/interrupts) */ int get_irq_list (char *buf) { int i; struct irqaction * action; char *p = buf; #ifdef CONFIG_SMP p += sprintf(p, " "); for (i = 0; i < smp_num_cpus; i++) p += sprintf(p, "CPU%d ", i); *p++ = '\n'; #endif /* * Simply scans the external vectored interrupts */ for (i = 0; i < NR_IRQS; i++) { action = irq_desc[i].action; if (!action) continue; p += sprintf(p, "%3d: ",i); #ifndef CONFIG_SMP p += sprintf(p, "%10u ", kstat_irqs(i)); #else { int j; for (j = 0; j < smp_num_cpus; j++) p += sprintf(p, "%10u ", kstat.irqs[cpu_logical_map(j)][i]); } #endif p += sprintf(p, " %14s", irq_desc[i].handler->typename); p += sprintf(p, " %c%s", (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action = action->next; action; action = action->next) { p += sprintf(p, ", %c%s", (action->flags & SA_INTERRUPT)?'+':' ', action->name); } *p++ = '\n'; } return p - buf; } int usbfix; static int __init usbfix_option (char *str) { printk("irq: enabling USB workaround\n"); usbfix = 1; return 1; } __setup("usbfix", usbfix_option); /* * That's where the IVT branches when we get an external * interrupt. This branches to the correct hardware IRQ handler via * function ptr. */ void ia64_handle_irq (unsigned long irq, struct pt_regs *regs) { unsigned long bsp, sp, saved_tpr; #ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC # ifndef CONFIG_SMP static unsigned int max_prio = 0; # endif unsigned int prev_prio; unsigned long eoi_ptr; # ifdef CONFIG_USB extern void reenable_usb (void); extern void disable_usb (void); if (usbfix) disable_usb(); # endif /* * Stop IPIs by getting the ivr_read_lock */ spin_lock(&ivr_read_lock); /* * Disable PCI writes */ outl(0x80ff81c0, 0xcf8); outl(0x73002188, 0xcfc); eoi_ptr = inl(0xcfc); irq = ia64_get_ivr(); /* * Enable PCI writes */ outl(0x73182188, 0xcfc); spin_unlock(&ivr_read_lock); # ifdef CONFIG_USB if (usbfix) reenable_usb(); # endif # ifndef CONFIG_SMP prev_prio = max_prio; if (irq < max_prio) { printk ("ia64_handle_irq: got irq %lu while %u was in progress!\n", irq, max_prio); } else max_prio = irq; # endif /* !CONFIG_SMP */ #endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ /* Always set TPR to limit maximum interrupt nesting depth to * 16 (without this, it would be ~240, which could easily lead * to kernel stack overflows. */ saved_tpr = ia64_get_tpr(); ia64_srlz_d(); ia64_set_tpr(irq); ia64_srlz_d(); asm ("mov %0=ar.bsp" : "=r"(bsp)); asm ("mov %0=sp" : "=r"(sp)); if ((sp - bsp) < 1024) { static long last_time; static unsigned char count; if (count > 5 && jiffies - last_time > 5*HZ) count = 0; if (++count < 5) { last_time = jiffies; printk("ia64_handle_irq: DANGER: less than 1KB of free stack space!!\n" "(bsp=0x%lx, sp=%lx)\n", bsp, sp); } #ifdef CONFIG_KDB kdb(KDB_REASON_PANIC, 0, regs); #endif } /* * The interrupt is now said to be in service */ if (irq >= NR_IRQS) { printk("handle_irq: invalid irq=%lu\n", irq); goto out; } ++kstat.irqs[smp_processor_id()][irq]; if (irq == IA64_SPURIOUS_INT) { printk("handle_irq: spurious interrupt\n"); goto out; } /* * Handle the interrupt by calling the hardware specific handler (IOSAPIC, Internal, etc). */ (*irq_desc[irq].handler->handle)(irq, regs); out: #ifdef CONFIG_ITANIUM_ASTEP_SPECIFIC { long pEOI; asm ("mov %0=0;; (p1) mov %0=1" : "=r"(pEOI)); if (!pEOI) { printk("Yikes: ia64_handle_irq() without pEOI!!\n"); asm volatile ("cmp.eq p1,p0=r0,r0" : "=r"(pEOI)); # ifdef CONFIG_KDB kdb(KDB_REASON_PANIC, 0, regs); # endif } } local_irq_disable(); # ifndef CONFIG_SMP if (max_prio == irq) max_prio = prev_prio; # endif /* !CONFIG_SMP */ #endif /* CONFIG_ITANIUM_ASTEP_SPECIFIC */ ia64_srlz_d(); ia64_set_tpr(saved_tpr); ia64_srlz_d(); } /* * This should really return information about whether we should do * bottom half handling etc. Right now we end up _always_ checking the * bottom half, which is a waste of time and is not what some drivers * would prefer. */ int invoke_irq_handlers (unsigned int irq, struct pt_regs *regs, struct irqaction *action) { void (*handler)(int, void *, struct pt_regs *); unsigned long flags, flags_union = 0; int cpu = smp_processor_id(); unsigned int requested_irq; void *dev_id; irq_enter(cpu, irq); if ((action->flags & SA_INTERRUPT) == 0) __sti(); do { flags = action->flags; requested_irq = irq; if ((flags & SA_LEGACY) != 0) requested_irq = vector_to_legacy_map[irq]; flags_union |= flags; handler = action->handler; dev_id = action->dev_id; action = action->next; (*handler)(requested_irq, dev_id, regs); } while (action); if ((flags_union & SA_SAMPLE_RANDOM) != 0) add_interrupt_randomness(irq); __cli(); irq_exit(cpu, irq); return flags_union | 1; /* force the "do bottom halves" bit */ } void disable_irq_nosync (unsigned int irq) { unsigned long flags; irq = map_legacy_irq(irq); spin_lock_irqsave(&irq_controller_lock, flags); if (irq_desc[irq].depth++ > 0) { irq_desc[irq].status &= ~IRQ_ENABLED; irq_desc[irq].handler->disable(irq); } spin_unlock_irqrestore(&irq_controller_lock, flags); } /* * Synchronous version of the above, making sure the IRQ is * no longer running on any other IRQ.. */ void disable_irq (unsigned int irq) { disable_irq_nosync(irq); irq = map_legacy_irq(irq); if (!local_irq_count[smp_processor_id()]) { do { barrier(); } while ((irq_desc[irq].status & IRQ_INPROGRESS) != 0); } } void enable_irq (unsigned int irq) { unsigned long flags; irq = map_legacy_irq(irq); spin_lock_irqsave(&irq_controller_lock, flags); switch (irq_desc[irq].depth) { case 1: irq_desc[irq].status |= IRQ_ENABLED; (*irq_desc[irq].handler->enable)(irq); /* fall through */ default: --irq_desc[irq].depth; break; case 0: printk("enable_irq: unbalanced from %p\n", __builtin_return_address(0)); } spin_unlock_irqrestore(&irq_controller_lock, flags); } /* * This function encapsulates the initialization that needs to be * performed under the protection of lock irq_controller_lock. The * lock must have been acquired by the time this is called. */ static inline int setup_irq (unsigned int irq, struct irqaction *new) { int shared = 0; struct irqaction *old, **p; p = &irq_desc[irq].action; old = *p; if (old) { if (!(old->flags & new->flags & SA_SHIRQ)) { return -EBUSY; } /* add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); shared = 1; } *p = new; /* when sharing do not unmask */ if (!shared) { irq_desc[irq].depth = 0; irq_desc[irq].status |= IRQ_ENABLED; (*irq_desc[irq].handler->startup)(irq); } return 0; } int request_irq (unsigned int requested_irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) { int retval, need_kfree = 0; struct irqaction *action; unsigned long flags; unsigned int irq; #ifdef IA64_DEBUG printk("request_irq(0x%x) called\n", requested_irq); #endif /* * Sanity-check: shared interrupts should REALLY pass in * a real dev-ID, otherwise we'll have trouble later trying * to figure out which interrupt is which (messes up the * interrupt freeing logic etc). */ if ((irqflags & SA_SHIRQ) && !dev_id) printk("Bad boy: %s (at %p) called us without a dev_id!\n", devname, current_text_addr()); irq = map_legacy_irq(requested_irq); if (irq != requested_irq) irqflags |= SA_LEGACY; if (irq >= NR_IRQS) return -EINVAL; if (!handler) return -EINVAL; /* * The timer_action and ipi_action cannot be allocated * dynamically because its initialization happens really early * on in init/main.c at this point the memory allocator has * not yet been initialized. So we use a statically reserved * buffer for it. In some sense that's no big deal because we * need one no matter what. */ if (irq == TIMER_IRQ) action = &timer_action; #ifdef CONFIG_SMP else if (irq == IPI_IRQ) action = &ipi_action; #endif else { action = kmalloc(sizeof(struct irqaction), GFP_KERNEL); need_kfree = 1; } if (!action) return -ENOMEM; action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; action->next = NULL; action->dev_id = dev_id; if ((irqflags & SA_SAMPLE_RANDOM) != 0) rand_initialize_irq(irq); spin_lock_irqsave(&irq_controller_lock, flags); retval = setup_irq(irq, action); spin_unlock_irqrestore(&irq_controller_lock, flags); if (need_kfree && retval) kfree(action); return retval; } void free_irq (unsigned int irq, void *dev_id) { struct irqaction *action, **p; unsigned long flags; /* * some sanity checks first */ if (irq >= NR_IRQS) { printk("Trying to free IRQ%d\n",irq); return; } irq = map_legacy_irq(irq); /* * Find the corresponding irqaction */ spin_lock_irqsave(&irq_controller_lock, flags); for (p = &irq_desc[irq].action; (action = *p) != NULL; p = &action->next) { if (action->dev_id != dev_id) continue; /* Found it - now remove it from the list of entries */ *p = action->next; if (!irq_desc[irq].action) { irq_desc[irq].status &= ~IRQ_ENABLED; (*irq_desc[irq].handler->shutdown)(irq); } spin_unlock_irqrestore(&irq_controller_lock, flags); #ifdef CONFIG_SMP /* Wait to make sure it's not being used on another CPU */ while (irq_desc[irq].status & IRQ_INPROGRESS) barrier(); #endif if (action != &timer_action #ifdef CONFIG_SMP && action != &ipi_action #endif ) kfree(action); return; } printk("Trying to free free IRQ%d\n", irq); } /* * IRQ autodetection code. Note that the return value of * probe_irq_on() is no longer being used (it's role has been replaced * by the IRQ_AUTODETECT flag). */ unsigned long probe_irq_on (void) { struct irq_desc *id; unsigned long delay; #ifdef IA64_DEBUG printk("probe_irq_on() called\n"); #endif spin_lock_irq(&irq_controller_lock); for (id = irq_desc; id < irq_desc + NR_IRQS; ++id) { if (!id->action) { id->status |= IRQ_AUTODETECT | IRQ_WAITING; (*id->handler->startup)(id - irq_desc); } } spin_unlock_irq(&irq_controller_lock); /* wait for spurious interrupts to trigger: */ for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) /* about 100ms delay */ synchronize_irq(); /* filter out obviously spurious interrupts: */ spin_lock_irq(&irq_controller_lock); for (id = irq_desc; id < irq_desc + NR_IRQS; ++id) { unsigned int status = id->status; if (!(status & IRQ_AUTODETECT)) continue; if (!(status & IRQ_WAITING)) { id->status = status & ~IRQ_AUTODETECT; (*id->handler->shutdown)(id - irq_desc); } } spin_unlock_irq(&irq_controller_lock); return PROBE_IRQ_COOKIE; /* return meaningless return value */ } int probe_irq_off (unsigned long cookie) { int irq_found, nr_irqs; struct irq_desc *id; #ifdef IA64_DEBUG printk("probe_irq_off(cookie=0x%lx) -> ", cookie); #endif if (cookie != PROBE_IRQ_COOKIE) printk("bad irq probe from %p\n", __builtin_return_address(0)); nr_irqs = 0; irq_found = 0; spin_lock_irq(&irq_controller_lock); for (id = irq_desc + IA64_MIN_VECTORED_IRQ; id < irq_desc + NR_IRQS; ++id) { unsigned int status = id->status; if (!(status & IRQ_AUTODETECT)) continue; if (!(status & IRQ_WAITING)) { if (!nr_irqs) irq_found = (id - irq_desc); ++nr_irqs; } id->status = status & ~IRQ_AUTODETECT; (*id->handler->shutdown)(id - irq_desc); } spin_unlock_irq(&irq_controller_lock); if (nr_irqs > 1) irq_found = -irq_found; #ifdef IA64_DEBUG printk("%d\n", irq_found); #endif return irq_found; } #ifdef CONFIG_SMP void __init init_IRQ_SMP (void) { if (request_irq(IPI_IRQ, handle_IPI, 0, "IPI", NULL)) panic("Could not allocate IPI Interrupt Handler!"); } #endif void __init init_IRQ (void) { int i; for (i = 0; i < IA64_MIN_VECTORED_IRQ; ++i) vector_to_legacy_map[irq_to_vector_map[i]] = i; for (i = 0; i < NR_IRQS; ++i) { irq_desc[i].handler = &irq_type_default; } irq_desc[TIMER_IRQ].handler = &irq_type_ia64_internal; #ifdef CONFIG_SMP /* * Configure the IPI vector and handler */ irq_desc[IPI_IRQ].handler = &irq_type_ia64_internal; init_IRQ_SMP(); #endif ia64_set_pmv(1 << 16); ia64_set_cmcv(CMC_IRQ); /* XXX fix me */ platform_irq_init(irq_desc); /* clear TPR to enable all interrupt classes: */ ia64_set_tpr(0); } /* TBD: * Certain IA64 platforms can have inter-processor interrupt support. * This interface is supposed to default to the IA64 IPI block-based * mechanism if the platform doesn't provide a separate mechanism * for IPIs. * Choices : (1) Extend hw_interrupt_type interfaces * (2) Use machine vector mechanism * For now defining the following interface as a place holder. */ void ipi_send (int cpu, int vector, int delivery_mode) { }