📄 irq.c
字号:
/* $Id: irq.c,v 1.109 2000/08/31 10:00:39 anton Exp $ * arch/sparc/kernel/irq.c: Interrupt request handling routines. On the * Sparc the IRQ's are basically 'cast in stone' * and you are supposed to probe the prom's device * node trees to find out who's got which IRQ. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx) * Copyright (C) 1995 Pete A. Zaitcev (zaitcev@metabyte.com) * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk) * Copyright (C) 1998-2000 Anton Blanchard (anton@linuxcare.com) */#include <linux/config.h>#include <linux/ptrace.h>#include <linux/errno.h>#include <linux/linkage.h>#include <linux/kernel_stat.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/malloc.h>#include <linux/random.h>#include <linux/init.h>#include <linux/smp.h>#include <linux/smp_lock.h>#include <linux/delay.h>#include <linux/threads.h>#include <linux/spinlock.h>#include <asm/ptrace.h>#include <asm/processor.h>#include <asm/system.h>#include <asm/psr.h>#include <asm/smp.h>#include <asm/vaddrs.h>#include <asm/timer.h>#include <asm/openprom.h>#include <asm/oplib.h>#include <asm/traps.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/pgalloc.h>#include <asm/pgtable.h>#include <asm/hardirq.h>#include <asm/softirq.h>#include <asm/pcic.h>/* * Dave Redman (djhr@tadpole.co.uk) * * IRQ numbers.. These are no longer restricted to 15.. * * this is done to enable SBUS cards and onboard IO to be masked * correctly. using the interrupt level isn't good enough. * * For example: * A device interrupting at sbus level6 and the Floppy both come in * at IRQ11, but enabling and disabling them requires writing to * different bits in the SLAVIO/SEC. * * As a result of these changes sun4m machines could now support * directed CPU interrupts using the existing enable/disable irq code * with tweaks. * */static void irq_panic(void){ extern char *cputypval; prom_printf("machine: %s doesn't have irq handlers defined!\n",cputypval); prom_halt();}void (*init_timers)(void (*)(int, void *,struct pt_regs *)) = (void (*)(void (*)(int, void *,struct pt_regs *))) irq_panic;/* * Dave Redman (djhr@tadpole.co.uk) * * There used to be extern calls and hard coded values here.. very sucky! * instead, because some of the devices attach very early, I do something * equally sucky but at least we'll never try to free statically allocated * space or call kmalloc before kmalloc_init :(. * * In fact it's the timer10 that attaches first.. then timer14 * then kmalloc_init is called.. then the tty interrupts attach. * hmmm.... * */#define MAX_STATIC_ALLOC 4struct irqaction static_irqaction[MAX_STATIC_ALLOC];int static_irq_count = 0;struct irqaction *irq_action[NR_IRQS+1] = { NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL};int get_irq_list(char *buf){ int i, len = 0; struct irqaction * action;#ifdef CONFIG_SMP int j;#endif if (sparc_cpu_model == sun4d) { extern int sun4d_get_irq_list(char *); return sun4d_get_irq_list(buf); } for (i = 0 ; i < (NR_IRQS+1) ; i++) { action = *(i + irq_action); if (!action) continue; len += sprintf(buf+len, "%3d: ", i);#ifndef CONFIG_SMP len += sprintf(buf+len, "%10u ", kstat_irqs(i));#else for (j = 0; j < smp_num_cpus; j++) len += sprintf(buf+len, "%10u ", kstat.irqs[cpu_logical_map(j)][i]);#endif len += sprintf(buf+len, " %c %s", (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action=action->next; action; action = action->next) { len += sprintf(buf+len, ",%s %s", (action->flags & SA_INTERRUPT) ? " +" : "", action->name); } len += sprintf(buf+len, "\n"); } return len;}void free_irq(unsigned int irq, void *dev_id){ struct irqaction * action; struct irqaction * tmp = NULL; unsigned long flags; unsigned int cpu_irq; if (sparc_cpu_model == sun4d) { extern void sun4d_free_irq(unsigned int, void *); return sun4d_free_irq(irq, dev_id); } cpu_irq = irq & NR_IRQS; action = *(cpu_irq + irq_action); if (cpu_irq > 14) { /* 14 irq levels on the sparc */ printk("Trying to free bogus IRQ %d\n", irq); return; } if (!action->handler) { printk("Trying to free free IRQ%d\n",irq); return; } if (dev_id) { for (; action; action = action->next) { if (action->dev_id == dev_id) break; tmp = action; } if (!action) { printk("Trying to free free shared IRQ%d\n",irq); return; } } else if (action->flags & SA_SHIRQ) { printk("Trying to free shared IRQ%d with NULL device ID\n", irq); return; } if (action->flags & SA_STATIC_ALLOC) { /* This interrupt is marked as specially allocated * so it is a bad idea to free it. */ printk("Attempt to free statically allocated IRQ%d (%s)\n", irq, action->name); return; } save_and_cli(flags); if (action && tmp) tmp->next = action->next; else *(cpu_irq + irq_action) = action->next; kfree(action); if (!(*(cpu_irq + irq_action))) disable_irq(irq); restore_flags(flags);}#ifdef CONFIG_SMP/* Who has the global irq brlock */unsigned char global_irq_holder = NO_PROC_ID;void smp_show_backtrace_all_cpus(void);void show_backtrace(void);#define VERBOSE_DEBUG_IRQLOCK#define MAXCOUNT 100000000static void show(char * str){ int cpu = smp_processor_id(); int i; printk("\n%s, CPU %d:\n", str, cpu); printk("irq: %d [ ", irqs_running()); for (i = 0; i < smp_num_cpus; i++) printk("%u ", __brlock_array[i][BR_GLOBALIRQ_LOCK]); printk("]\nbh: %d [ ", (spin_is_locked(&global_bh_lock) ? 1 : 0)); for (i = 0; i < smp_num_cpus; i++) printk("%u ", local_bh_count(i)); printk("]\n");#ifdef VERBOSE_DEBUG_IRQLOCK smp_show_backtrace_all_cpus();#else show_backtrace();#endif}/* * We have to allow irqs to arrive between __sti and __cli */#define SYNC_OTHER_CORES(x) barrier()/* * 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 (irqs_running()) { cli(); sti(); }}static inline void get_irqlock(int cpu){ int count; if ((unsigned char)cpu == global_irq_holder) return; count = MAXCOUNT;again: br_write_lock(BR_GLOBALIRQ_LOCK); for (;;) { spinlock_t *lock; if (!irqs_running() && (local_bh_count(smp_processor_id()) || !spin_is_locked(&global_bh_lock))) break; br_write_unlock(BR_GLOBALIRQ_LOCK); lock = &__br_write_locks[BR_GLOBALIRQ_LOCK].lock; while (irqs_running() || spin_is_locked(lock) || (!local_bh_count(smp_processor_id()) && spin_is_locked(&global_bh_lock))) { if (!--count) { show("get_irqlock"); count = (~0 >> 1); } __sti(); SYNC_OTHER_CORES(cpu); __cli(); } goto again; } 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 & PSR_PIL) != PSR_PIL) { 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){ unsigned long flags, retval; unsigned long local_enabled = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -