/* * BK Id: SCCS/s.irq.c 1.32 08/24/01 20:07:37 paulus */ /* * arch/ppc/kernel/irq.c * * Derived from arch/i386/kernel/irq.c * Copyright (C) 1992 Linus Torvalds * Adapted from arch/i386 by Gary Thomas * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) * Updated and modified by Cort Dougan * Copyright (C) 1996-2001 Cort Dougan * Adapted for Power Macintosh by Paul Mackerras * Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au) * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). * * This file contains the code used by various IRQ handling routines: * asking for different IRQ's should be done through these routines * instead of just grabbing them. Thus setups with different IRQ numbers * shouldn't result in any weird surprises, and installing new handlers * should be easier. * * The MPC8xx has an interrupt mask in the SIU. If a bit is set, the * interrupt is _enabled_. As expected, IRQ0 is bit 0 in the 32-bit * mask register (of which only 16 are defined), hence the weird shifting * and compliment of the cached_irq_mask. I want to be able to stuff * this right into the SIU SMASK register. * Many of the prep/chrp functions are conditional compiled on CONFIG_8xx * to reduce code space and undefined function references. */ #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 #include #include #include "local_irq.h" extern atomic_t ipi_recv; extern atomic_t ipi_sent; void enable_irq(unsigned int irq_nr); void disable_irq(unsigned int irq_nr); static void register_irq_proc (unsigned int irq); #define MAXCOUNT 10000000 irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = { [0 ... NR_IRQS-1] = { 0, NULL, NULL, 0, SPIN_LOCK_UNLOCKED}}; int ppc_spurious_interrupts = 0; struct irqaction *ppc_irq_action[NR_IRQS]; unsigned long ppc_cached_irq_mask[NR_MASK_WORDS]; unsigned long ppc_lost_interrupts[NR_MASK_WORDS]; atomic_t ppc_n_lost_interrupts; /* nasty hack for shared irq's since we need to do kmalloc calls but * can't very early in the boot when we need to do a request irq. * this needs to be removed. * -- Cort */ #define IRQ_KMALLOC_ENTRIES 8 static int cache_bitmask = 0; static struct irqaction malloc_cache[IRQ_KMALLOC_ENTRIES]; extern int mem_init_done; #if defined(CONFIG_TAU_INT) extern int tau_interrupts(unsigned long cpu); extern int tau_initialized; #endif void *irq_kmalloc(size_t size, int pri) { unsigned int i; if ( mem_init_done ) return kmalloc(size,pri); for ( i = 0; i < IRQ_KMALLOC_ENTRIES ; i++ ) if ( ! ( cache_bitmask & (1<flags & SA_SAMPLE_RANDOM) { /* * This function might sleep, we want to call it first, * outside of the atomic block. * Yes, this might clear the entropy pool if the wrong * driver is attempted to be loaded, without actually * installing a new handler, but is this really a problem, * only the sysadmin is able to do this. */ rand_initialize_irq(irq); } /* * The following block of code has to be executed atomically */ spin_lock_irqsave(&desc->lock,flags); p = &desc->action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & new->flags & SA_SHIRQ)) { spin_unlock_irqrestore(&desc->lock,flags); return -EBUSY; } /* add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); shared = 1; } *p = new; if (!shared) { desc->depth = 0; desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); unmask_irq(irq); } spin_unlock_irqrestore(&desc->lock,flags); register_irq_proc(irq); return 0; } #if (defined(CONFIG_8xx) || defined(CONFIG_8260)) /* Name change so we can catch standard drivers that potentially mess up * the internal interrupt controller on 8xx and 8260. Just bear with me, * I don't like this either and I am searching a better solution. For * now, this is what I need. -- Dan */ #define request_irq request_8xxirq #elif defined(CONFIG_APUS) #define request_irq request_sysirq #define free_irq sys_free_irq #endif void free_irq(unsigned int irq, void* dev_id) { irq_desc_t *desc; struct irqaction **p; unsigned long flags; desc = irq_desc + irq; spin_lock_irqsave(&desc->lock,flags); p = &desc->action; for (;;) { struct irqaction * action = *p; if (action) { struct irqaction **pp = p; p = &action->next; if (action->dev_id != dev_id) continue; /* Found it - now remove it from the list of entries */ *pp = action->next; if (!desc->action) { desc->status |= IRQ_DISABLED; mask_irq(irq); } spin_unlock_irqrestore(&desc->lock,flags); #ifdef CONFIG_SMP /* Wait to make sure it's not being used on another CPU */ while (desc->status & IRQ_INPROGRESS) barrier(); #endif irq_kfree(action); return; } printk("Trying to free free IRQ%d\n",irq); spin_unlock_irqrestore(&desc->lock,flags); break; } return; } int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char * devname, void *dev_id) { struct irqaction *action; int retval; if (irq >= NR_IRQS) return -EINVAL; if (!handler) { /* * free_irq() used to be implemented as a call to * request_irq() with handler being NULL. Now we have * a real free_irq() but need to allow the old behavior * for old code that hasn't caught up yet. * -- Cort */ free_irq(irq, dev_id); return 0; } action = (struct irqaction *) irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); if (!action) { printk(KERN_ERR "irq_kmalloc() failed for irq %d !\n", irq); return -ENOMEM; } action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; action->dev_id = dev_id; action->next = NULL; retval = setup_irq(irq, action); if (retval) { kfree(action); return retval; } return 0; } /* * Generic enable/disable code: this just calls * down into the PIC-specific version for the actual * hardware disable after having gotten the irq * controller lock. */ /** * disable_irq_nosync - disable an irq without waiting * @irq: Interrupt to disable * * Disable the selected interrupt line. Disables of an interrupt * stack. Unlike disable_irq(), this function does not ensure existing * instances of the IRQ handler have completed before returning. * * This function may be called from IRQ context. */ void disable_irq_nosync(unsigned int irq) { irq_desc_t *desc = irq_desc + irq; unsigned long flags; spin_lock_irqsave(&desc->lock, flags); if (!desc->depth++) { if (!(desc->status & IRQ_PER_CPU)) desc->status |= IRQ_DISABLED; mask_irq(irq); } spin_unlock_irqrestore(&desc->lock, flags); } /** * disable_irq - disable an irq and wait for completion * @irq: Interrupt to disable * * Disable the selected interrupt line. Disables of an interrupt * stack. That is for two disables you need two enables. This * function waits for any pending IRQ handlers for this interrupt * to complete before returning. If you use this function while * holding a resource the IRQ handler may need you will deadlock. * * This function may be called - with care - from IRQ context. */ void disable_irq(unsigned int irq) { disable_irq_nosync(irq); if (!local_irq_count(smp_processor_id())) { do { barrier(); } while (irq_desc[irq].status & IRQ_INPROGRESS); } } /** * enable_irq - enable interrupt handling on an irq * @irq: Interrupt to enable * * Re-enables the processing of interrupts on this IRQ line * providing no disable_irq calls are now in effect. * * This function may be called from IRQ context. */ void enable_irq(unsigned int irq) { irq_desc_t *desc = irq_desc + irq; unsigned long flags; spin_lock_irqsave(&desc->lock, flags); switch (desc->depth) { case 1: { unsigned int status = desc->status & ~IRQ_DISABLED; desc->status = status; if ((status & (IRQ_PENDING | IRQ_REPLAY)) == IRQ_PENDING) { desc->status = status | IRQ_REPLAY; hw_resend_irq(desc->handler,irq); } unmask_irq(irq); /* fall-through */ } default: desc->depth--; break; case 0: printk("enable_irq(%u) unbalanced\n", irq); } spin_unlock_irqrestore(&desc->lock, flags); } int get_irq_list(char *buf) { #ifdef CONFIG_APUS return apus_get_irq_list (buf); #else int i, len = 0, j; struct irqaction * action; len += sprintf(buf+len, " "); for (j=0; jhandler ) continue; len += sprintf(buf+len, "%3d: ", i); #ifdef CONFIG_SMP for (j = 0; j < smp_num_cpus; j++) len += sprintf(buf+len, "%10u ", kstat.irqs[cpu_logical_map(j)][i]); #else len += sprintf(buf+len, "%10u ", kstat_irqs(i)); #endif /* CONFIG_SMP */ if ( irq_desc[i].handler ) len += sprintf(buf+len, " %s ", irq_desc[i].handler->typename ); else len += sprintf(buf+len, " None "); len += sprintf(buf+len, "%s", (irq_desc[i].status & IRQ_LEVEL) ? "Level " : "Edge "); len += sprintf(buf+len, " %s",action->name); for (action=action->next; action; action = action->next) { len += sprintf(buf+len, ", %s", action->name); } len += sprintf(buf+len, "\n"); } #ifdef CONFIG_TAU_INT if (tau_initialized){ len += sprintf(buf+len, "TAU: "); for (j = 0; j < smp_num_cpus; j++) len += sprintf(buf+len, "%10u ", tau_interrupts(j)); len += sprintf(buf+len, " PowerPC Thermal Assist (cpu temp)\n"); } #endif #ifdef CONFIG_SMP /* should this be per processor send/receive? */ len += sprintf(buf+len, "IPI (recv/sent): %10u/%u\n", atomic_read(&ipi_recv), atomic_read(&ipi_sent)); #endif len += sprintf(buf+len, "BAD: %10u\n", ppc_spurious_interrupts); return len; #endif /* CONFIG_APUS */ } static inline void handle_irq_event(int irq, struct pt_regs *regs, struct irqaction *action) { int status = 0; if (!(action->flags & SA_INTERRUPT)) __sti(); do { status |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while (action); if (status & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); __cli(); } /* * Eventually, this should take an array of interrupts and an array size * so it can dispatch multiple interrupts. */ void ppc_irq_dispatch_handler(struct pt_regs *regs, int irq) { int status; struct irqaction *action; int cpu = smp_processor_id(); irq_desc_t *desc = irq_desc + irq; kstat.irqs[cpu][irq]++; spin_lock(&desc->lock); ack_irq(irq); /* REPLAY is when Linux resends an IRQ that was dropped earlier WAITING is used by probe to mark irqs that are being tested */ status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); if (!(status & IRQ_PER_CPU)) status |= IRQ_PENDING; /* we _want_ to handle it */ /* * If the IRQ is disabled for whatever reason, we cannot * use the action we have. */ action = NULL; if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { action = desc->action; if (!action || !action->handler) { ppc_spurious_interrupts++; printk(KERN_DEBUG "Unhandled interrupt %x, disabled\n", irq); /* We can't call disable_irq here, it would deadlock */ ++desc->depth; desc->status |= IRQ_DISABLED; mask_irq(irq); /* This is a real interrupt, we have to eoi it, so we jump to out */ goto out; } status &= ~IRQ_PENDING; /* we commit to handling */ if (!(status & IRQ_PER_CPU)) status |= IRQ_INPROGRESS; /* we are handling it */ } desc->status = status; /* * If there is no IRQ handler or it was disabled, exit early. Since we set PENDING, if another processor is handling a different instance of this same irq, the other processor will take care of it. */ if (!action) goto out; /* * Edge triggered interrupts need to remember * pending events. * This applies to any hw interrupts that allow a second * instance of the same irq to arrive while we are in do_IRQ * or in the handler. But the code here only handles the _second_ * instance of the irq, not the third or fourth. So it is mostly * useful for irq hardware that does not mask cleanly in an * SMP environment. */ for (;;) { spin_unlock(&desc->lock); handle_irq_event(irq, regs, action); spin_lock(&desc->lock); if (!(desc->status & IRQ_PENDING)) break; desc->status &= ~IRQ_PENDING; } desc->status &= ~IRQ_INPROGRESS; out: /* * The ->end() handler has to deal with interrupts which got * disabled while the handler was running. */ if (irq_desc[irq].handler) { if (irq_desc[irq].handler->end) irq_desc[irq].handler->end(irq); else if (irq_desc[irq].handler->enable) irq_desc[irq].handler->enable(irq); } spin_unlock(&desc->lock); } int do_IRQ(struct pt_regs *regs) { int cpu = smp_processor_id(); int irq; hardirq_enter(cpu); /* every arch is required to have a get_irq -- Cort */ irq = ppc_md.get_irq(regs); if (irq >= 0) { ppc_irq_dispatch_handler( regs, irq ); } else if (irq != -2) { /* -2 means ignore, already handled */ if (ppc_spurious_interrupts < 10) printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", irq, regs->nip); /* That's not SMP safe ... but who cares ? */ ppc_spurious_interrupts++; } hardirq_exit( cpu ); if (softirq_pending(cpu)) do_softirq(); return 1; /* lets ret_from_int know we can do checks */ } unsigned long probe_irq_on (void) { return 0; } int probe_irq_off (unsigned long irqs) { return 0; } unsigned int probe_irq_mask(unsigned long irqs) { return 0; } void __init init_IRQ(void) { static int once = 0; if ( once ) return; else once++; ppc_md.init_IRQ(); } #ifdef CONFIG_SMP unsigned char global_irq_holder = NO_PROC_ID; unsigned volatile long global_irq_lock; /* pendantic :long for set_bit--RR*/ atomic_t global_irq_count; atomic_t global_bh_count; static void show(char * str) { int i; unsigned long *stack; int cpu = smp_processor_id(); printk("\n%s, CPU %d:\n", str, cpu); printk("irq: %d [%d %d]\n", atomic_read(&global_irq_count), local_irq_count(0), local_irq_count(1)); printk("bh: %d [%d %d]\n", atomic_read(&global_bh_count), local_bh_count(0), local_bh_count(1)); stack = (unsigned long *) &str; for (i = 40; i ; i--) { unsigned long x = *++stack; if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) { printk("<[%08lx]> ", x); } } } static inline void wait_on_bh(void) { int count = MAXCOUNT; do { if (!--count) { show("wait_on_bh"); count = ~0; } /* nothing .. wait for the other bh's to go away */ } while (atomic_read(&global_bh_count) != 0); } static inline void wait_on_irq(int cpu) { int count = MAXCOUNT; for (;;) { /* * Wait until all interrupts are gone. Wait * for bottom half handlers unless we're * already executing in one.. */ if (!atomic_read(&global_irq_count)) { if (local_bh_count(cpu) || !atomic_read(&global_bh_count)) break; } /* Duh, we have to loop. Release the lock to avoid deadlocks */ clear_bit(0,&global_irq_lock); for (;;) { if (!--count) { show("wait_on_irq"); count = ~0; } __sti(); /* don't worry about the lock race Linus found * on intel here. -- Cort */ __cli(); if (atomic_read(&global_irq_count)) continue; if (global_irq_lock) continue; if (!local_bh_count(cpu) && atomic_read(&global_bh_count)) continue; if (!test_and_set_bit(0,&global_irq_lock)) break; } } } /* * This is called when we want to synchronize with * bottom half handlers. We need to wait until * no other CPU is executing any bottom half handler. * * Don't wait if we're already running in an interrupt * context or are inside a bh handler. */ void synchronize_bh(void) { if (atomic_read(&global_bh_count) && !in_interrupt()) wait_on_bh(); } /* * This is called when we want to synchronize with * interrupts. We may for example tell a device to * stop sending interrupts: but to make sure there * are no interrupts that are executing on another * CPU we need to call this function. */ void synchronize_irq(void) { if (atomic_read(&global_irq_count)) { /* Stupid approach */ cli(); sti(); } } static inline void get_irqlock(int cpu) { unsigned int loops = MAXCOUNT; if (test_and_set_bit(0,&global_irq_lock)) { /* do we already hold the lock? */ if ((unsigned char) cpu == global_irq_holder) return; /* Uhhuh.. Somebody else got it. Wait.. */ do { do { if (loops-- == 0) { printk("get_irqlock(%d) waiting, global_irq_holder=%d\n", cpu, global_irq_holder); #ifdef CONFIG_XMON xmon(0); #endif } } while (test_bit(0,&global_irq_lock)); } while (test_and_set_bit(0,&global_irq_lock)); } /* * We also need to make sure that nobody else is running * in an interrupt context. */ wait_on_irq(cpu); /* * Ok, finally.. */ global_irq_holder = cpu; } /* * A global "cli()" while in an interrupt context * turns into just a local cli(). Interrupts * should use spinlocks for the (very unlikely) * case that they ever want to protect against * each other. * * If we already have local interrupts disabled, * this will not turn a local disable into a * global one (problems with spinlocks: this makes * save_flags+cli+sti usable inside a spinlock). */ void __global_cli(void) { unsigned long flags; __save_flags(flags); if (flags & (1 << 15)) { int cpu = smp_processor_id(); __cli(); if (!local_irq_count(cpu)) get_irqlock(cpu); } } void __global_sti(void) { int cpu = smp_processor_id(); if (!local_irq_count(cpu)) release_irqlock(cpu); __sti(); } /* * SMP flags value to restore to: * 0 - global cli * 1 - global sti * 2 - local cli * 3 - local sti */ unsigned long __global_save_flags(void) { int retval; int local_enabled; unsigned long flags; __save_flags(flags); local_enabled = (flags >> 15) & 1; /* default to local */ retval = 2 + local_enabled; /* check for global flags if we're not in an interrupt */ if (!local_irq_count(smp_processor_id())) { if (local_enabled) retval = 1; if (global_irq_holder == (unsigned char) smp_processor_id()) retval = 0; } return retval; } int tb(long vals[], int max_size) { register unsigned long *orig_sp __asm__ ("r1"); register unsigned long lr __asm__ ("r3"); unsigned long *sp; int i; asm volatile ("mflr 3"); vals[0] = lr; sp = (unsigned long *) *orig_sp; sp = (unsigned long *) *sp; for (i=1; i HEX_DIGITS) count = HEX_DIGITS; if (copy_from_user(hexnum, buffer, count)) return -EFAULT; /* * Parse the first 8 characters as a hex string, any non-hex char * is end-of-string. '00e1', 'e1', '00E1', 'E1' are all the same. */ value = 0; for (i = 0; i < count; i++) { unsigned int c = hexnum[i]; switch (c) { case '0' ... '9': c -= '0'; break; case 'a' ... 'f': c -= 'a'-10; break; case 'A' ... 'F': c -= 'A'-10; break; default: goto out; } value = (value << 4) | c; } out: *ret = value; return 0; } static int irq_affinity_write_proc (struct file *file, const char *buffer, unsigned long count, void *data) { int irq = (int) data, full_count = count, err; unsigned long new_value; if (!irq_desc[irq].handler->set_affinity) return -EIO; err = parse_hex_value(buffer, count, &new_value); /* * Do not allow disabling IRQs completely - it's a too easy * way to make the system unusable accidentally :-) At least * one online CPU still has to be targeted. * * We assume a 1-1 logical<->physical cpu mapping here. If * we assume that the cpu indices in /proc/irq/../smp_affinity * are actually logical cpu #'s then we have no problem. * -- Cort */ if (!(new_value & cpu_online_map)) return -EINVAL; irq_affinity[irq] = new_value; irq_desc[irq].handler->set_affinity(irq, new_value); return full_count; } static int prof_cpu_mask_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data) { unsigned long *mask = (unsigned long *) data; if (count < HEX_DIGITS+1) return -EINVAL; return sprintf (page, "%08lx\n", *mask); } static int prof_cpu_mask_write_proc (struct file *file, const char *buffer, unsigned long count, void *data) { unsigned long *mask = (unsigned long *) data, full_count = count, err; unsigned long new_value; err = parse_hex_value(buffer, count, &new_value); if (err) return err; *mask = new_value; return full_count; } #define MAX_NAMELEN 10 static void register_irq_proc (unsigned int irq) { struct proc_dir_entry *entry; char name [MAX_NAMELEN]; if (!root_irq_dir || (irq_desc[irq].handler == NULL)) return; memset(name, 0, MAX_NAMELEN); sprintf(name, "%d", irq); /* create /proc/irq/1234 */ irq_dir[irq] = proc_mkdir(name, root_irq_dir); /* create /proc/irq/1234/smp_affinity */ entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]); entry->nlink = 1; entry->data = (void *)irq; entry->read_proc = irq_affinity_read_proc; entry->write_proc = irq_affinity_write_proc; smp_affinity_entry[irq] = entry; } unsigned long prof_cpu_mask = -1; void init_irq_proc (void) { struct proc_dir_entry *entry; int i; /* create /proc/irq */ root_irq_dir = proc_mkdir("irq", 0); /* create /proc/irq/prof_cpu_mask */ entry = create_proc_entry("prof_cpu_mask", 0600, root_irq_dir); entry->nlink = 1; entry->data = (void *)&prof_cpu_mask; entry->read_proc = prof_cpu_mask_read_proc; entry->write_proc = prof_cpu_mask_write_proc; /* * Create entries for all existing IRQs. */ for (i = 0; i < NR_IRQS; i++) { if (irq_desc[i].handler == NULL) continue; register_irq_proc(i); } } void no_action(int irq, void *dev, struct pt_regs *regs) { }