📄 irq.c
字号:
/* * $Id: irq.c,v 1.113 1999/09/17 17:22:56 cort Exp $ * * 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 (cort@cs.nmt.edu) * Copyright (C) 1996 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 <linux/ptrace.h>#include <linux/errno.h>#include <linux/threads.h>#include <linux/kernel_stat.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/timex.h>#include <linux/config.h>#include <linux/init.h>#include <linux/malloc.h>#include <linux/openpic.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/irq.h>#include <linux/proc_fs.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/hydra.h>#include <asm/system.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/irq.h>#include <asm/gg2.h>#include <asm/cache.h>#include <asm/prom.h>#include <asm/amigaints.h>#include <asm/amigahw.h>#include <asm/amigappc.h>#include <asm/ptrace.h>#include "local_irq.h"extern volatile unsigned long ipi_count;void enable_irq(unsigned int irq_nr);void disable_irq(unsigned int irq_nr);volatile unsigned char *chrp_int_ack_special;#define MAXCOUNT 10000000irq_desc_t irq_desc[NR_IRQS];int ppc_spurious_interrupts = 0;struct irqaction *ppc_irq_action[NR_IRQS];unsigned int ppc_cached_irq_mask[NR_MASK_WORDS];unsigned int 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 8static int cache_bitmask = 0;static struct irqaction malloc_cache[IRQ_KMALLOC_ENTRIES];extern int mem_init_done;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<<i) ) ) { cache_bitmask |= (1<<i); return (void *)(&malloc_cache[i]); } return 0;}void irq_kfree(void *ptr){ unsigned int i; for ( i = 0 ; i < IRQ_KMALLOC_ENTRIES ; i++ ) if ( ptr == &malloc_cache[i] ) { cache_bitmask &= ~(1<<i); return; } kfree(ptr);}#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 */int request_8xxirq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),#elif defined(CONFIG_APUS)int request_sysirq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),#elseint request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),#endif unsigned long irqflags, const char * devname, void *dev_id){ struct irqaction *old, **p, *action; unsigned long flags; if (irq >= NR_IRQS) return -EINVAL; if (!handler) { /* Free */ p = &irq_desc[irq].action; while ((action = *p) != NULL && action->dev_id != dev_id) p = &action->next; if (action == NULL) return -ENOENT; /* Found it - now free it */ save_flags(flags); cli(); *p = action->next; if (irq_desc[irq].action == NULL) disable_irq(irq); restore_flags(flags); irq_kfree(action); return 0; } action = (struct irqaction *) irq_kmalloc(sizeof(struct irqaction), GFP_KERNEL); if (!action) return -ENOMEM; save_flags(flags); cli(); action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; action->dev_id = dev_id; action->next = NULL; enable_irq(irq); p = &irq_desc[irq].action; if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & action->flags & SA_SHIRQ)) return -EBUSY; /* add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); } *p = action; restore_flags(flags); return 0;}#ifdef CONFIG_APUSvoid sys_free_irq(unsigned int irq, void *dev_id){ sys_request_irq(irq, NULL, 0, NULL, dev_id);}#elsevoid free_irq(unsigned int irq, void *dev_id){#if (defined(CONFIG_8xx) || defined(CONFIG_8260)) request_8xxirq(irq, NULL, 0, NULL, dev_id);#else request_irq(irq, NULL, 0, NULL, dev_id);#endif}#endif/* XXX should implement irq disable depth like on intel */void disable_irq_nosync(unsigned int irq_nr){ mask_irq(irq_nr);}void disable_irq(unsigned int irq_nr){ mask_irq(irq_nr); synchronize_irq();}void enable_irq(unsigned int irq_nr){ unmask_irq(irq_nr);}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; j<smp_num_cpus; j++) len += sprintf(buf+len, "CPU%d ",j); *(char *)(buf+len++) = '\n'; for (i = 0 ; i < NR_IRQS ; i++) { action = irq_desc[i].action; if ( !action || !action->handler ) 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_SMP /* should this be per processor send/receive? */ len += sprintf(buf+len, "IPI: %10lu\n", ipi_count);#endif len += sprintf(buf+len, "BAD: %10u\n", ppc_spurious_interrupts); return len;#endif /* CONFIG_APUS */}/* * 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(); mask_and_ack_irq(irq); status = 0; action = irq_desc[irq].action; kstat.irqs[cpu][irq]++; if (action && action->handler) { if (!(action->flags & SA_INTERRUPT)) __sti(); do { status |= action->flags; action->handler(irq, action->dev_id, regs); action = action->next; } while ( action ); __cli(); 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); } } else { ppc_spurious_interrupts++; printk(KERN_DEBUG "Unhandled interrupt %x, disabled\n", irq); disable_irq(irq); if (irq_desc[irq].handler->end) irq_desc[irq].handler->end(irq); }}int do_IRQ(struct pt_regs *regs, int isfake){ 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 ) { /* -2 means ignore, already handled */ if (irq != -2) { printk(KERN_DEBUG "Bogus interrupt %d from PC = %lx\n", irq, regs->nip); ppc_spurious_interrupts++; } goto out; } ppc_irq_dispatch_handler( regs, irq ); if (ppc_md.post_irq) ppc_md.post_irq( regs, irq ); out: hardirq_exit( cpu ); 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_SMPunsigned char global_irq_holder = NO_PROC_ID;unsigned volatile int global_irq_lock;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 {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -