irq.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 1,017 行 · 第 1/2 页

C
1,017
字号
/* *  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 program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * 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. */#include <linux/errno.h>#include <linux/module.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/slab.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/irq.h>#include <linux/proc_fs.h>#include <linux/random.h>#include <linux/kallsyms.h>#include <asm/uaccess.h>#include <asm/bitops.h>#include <asm/system.h>#include <asm/io.h>#include <asm/pgtable.h>#include <asm/irq.h>#include <asm/cache.h>#include <asm/prom.h>#include <asm/ptrace.h>#include <asm/iSeries/LparData.h>#include <asm/machdep.h>#include <asm/paca.h>#ifdef CONFIG_SMPextern void iSeries_smp_message_recv( struct pt_regs * );#endifstatic void register_irq_proc (unsigned int irq);irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = {	[0 ... NR_IRQS-1] = {		.lock = SPIN_LOCK_UNLOCKED	}};int __irq_offset_value;int ppc_spurious_interrupts;unsigned long lpevent_count;intsetup_irq(unsigned int irq, struct irqaction * new){	int shared = 0;	unsigned long flags;	struct irqaction *old, **p;	irq_desc_t *desc = get_irq_desc(irq);	/*	 * Some drivers like serial.c use request_irq() heavily,	 * so we have to be careful not to interfere with a	 * running system.	 */	if (new->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 | IRQ_INPROGRESS);		if (desc->handler && desc->handler->startup)			desc->handler->startup(irq);		unmask_irq(irq);	}	spin_unlock_irqrestore(&desc->lock,flags);	register_irq_proc(irq);	return 0;}#ifdef CONFIG_SMPinline void synchronize_irq(unsigned int irq){	while (get_irq_desc(irq)->status & IRQ_INPROGRESS)		cpu_relax();}EXPORT_SYMBOL(synchronize_irq);#endif /* CONFIG_SMP */int request_irq(unsigned int irq,	irqreturn_t (*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)		return -EINVAL;	action = (struct irqaction *)		kmalloc(sizeof(struct irqaction), GFP_KERNEL);	if (!action) {		printk(KERN_ERR "kmalloc() failed for irq %d !\n", irq);		return -ENOMEM;	}	action->handler = handler;	action->flags = irqflags;	cpus_clear(action->mask);	action->name = devname;	action->dev_id = dev_id;	action->next = NULL;	retval = setup_irq(irq, action);	if (retval)		kfree(action);	return 0;}EXPORT_SYMBOL(request_irq);void free_irq(unsigned int irq, void *dev_id){	irq_desc_t *desc = get_irq_desc(irq);	struct irqaction **p;	unsigned long flags;	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);			/* Wait to make sure it's not being used on another CPU */			synchronize_irq(irq);			kfree(action);			return;		}		printk("Trying to free free IRQ%d\n",irq);		spin_unlock_irqrestore(&desc->lock,flags);		break;	}	return;}EXPORT_SYMBOL(free_irq);/* * 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. */ inline void disable_irq_nosync(unsigned int irq){	irq_desc_t *desc = get_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);}EXPORT_SYMBOL(disable_irq_nosync);/** *	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){	irq_desc_t *desc = get_irq_desc(irq);	disable_irq_nosync(irq);	if (desc->action)		synchronize_irq(irq);}EXPORT_SYMBOL(disable_irq);/** *	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 = get_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 from %p\n", irq,		       __builtin_return_address(0));	}	spin_unlock_irqrestore(&desc->lock, flags);}EXPORT_SYMBOL(enable_irq);int show_interrupts(struct seq_file *p, void *v){	int i = *(loff_t *) v, j;	struct irqaction * action;	irq_desc_t *desc;	unsigned long flags;	if (i == 0) {		seq_printf(p, "           ");		for (j=0; j<NR_CPUS; j++) {			if (cpu_online(j))				seq_printf(p, "CPU%d       ",j);		}		seq_putc(p, '\n');	}	if (i < NR_IRQS) {		desc = get_irq_desc(i);		spin_lock_irqsave(&desc->lock, flags);		action = desc->action;		if (!action || !action->handler)			goto skip;		seq_printf(p, "%3d: ", i);#ifdef CONFIG_SMP		for (j = 0; j < NR_CPUS; j++) {			if (cpu_online(j))				seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);		}#else		seq_printf(p, "%10u ", kstat_irqs(i));#endif /* CONFIG_SMP */		if (desc->handler)			seq_printf(p, " %s ", desc->handler->typename );		else			seq_printf(p, "  None      ");		seq_printf(p, "%s", (desc->status & IRQ_LEVEL) ? "Level " : "Edge  ");		seq_printf(p, "    %s",action->name);		for (action=action->next; action; action = action->next)			seq_printf(p, ", %s", action->name);		seq_putc(p, '\n');skip:		spin_unlock_irqrestore(&desc->lock, flags);	} else if (i == NR_IRQS)		seq_printf(p, "BAD: %10u\n", ppc_spurious_interrupts);	return 0;}int handle_irq_event(int irq, struct pt_regs *regs, struct irqaction *action){	int status = 0;	int retval = 0;	if (!(action->flags & SA_INTERRUPT))		local_irq_enable();	do {		status |= action->flags;		retval |= action->handler(irq, action->dev_id, regs);		action = action->next;	} while (action);	if (status & SA_SAMPLE_RANDOM)		add_interrupt_randomness(irq);	local_irq_disable();	return retval;}static void __report_bad_irq(int irq, irq_desc_t *desc, irqreturn_t action_ret){	struct irqaction *action;	if (action_ret != IRQ_HANDLED && action_ret != IRQ_NONE) {		printk(KERN_ERR "irq event %d: bogus return value %x\n",				irq, action_ret);	} else {		printk(KERN_ERR "irq %d: nobody cared!\n", irq);	}	dump_stack();	printk(KERN_ERR "handlers:\n");	action = desc->action;	do {		printk(KERN_ERR "[<%p>]", action->handler);		print_symbol(" (%s)",			(unsigned long)action->handler);		printk("\n");		action = action->next;	} while (action);}static void report_bad_irq(int irq, irq_desc_t *desc, irqreturn_t action_ret){	static int count = 100;	if (count) {		count--;		__report_bad_irq(irq, desc, action_ret);	}}static int noirqdebug;static int __init noirqdebug_setup(char *str){	noirqdebug = 1;	printk("IRQ lockup detection disabled\n");	return 1;}__setup("noirqdebug", noirqdebug_setup);/* * If 99,900 of the previous 100,000 interrupts have not been handled then * assume that the IRQ is stuck in some manner.  Drop a diagnostic and try to * turn the IRQ off. * * (The other 100-of-100,000 interrupts may have been a correctly-functioning *  device sharing an IRQ with the failing one) * * Called under desc->lock */static void note_interrupt(int irq, irq_desc_t *desc, irqreturn_t action_ret){	if (action_ret != IRQ_HANDLED) {		desc->irqs_unhandled++;		if (action_ret != IRQ_NONE)			report_bad_irq(irq, desc, action_ret);	}	desc->irq_count++;	if (desc->irq_count < 100000)		return;	desc->irq_count = 0;	if (desc->irqs_unhandled > 99900) {		/*		 * The interrupt is stuck		 */		__report_bad_irq(irq, desc, action_ret);		/*		 * Now kill the IRQ		 */		printk(KERN_EMERG "Disabling IRQ #%d\n", irq);		desc->status |= IRQ_DISABLED;		desc->handler->disable(irq);	}	desc->irqs_unhandled = 0;}/* * 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 = get_irq_desc(irq);	irqreturn_t action_ret;#ifdef CONFIG_IRQSTACKS	struct thread_info *curtp, *irqtp;#endif	kstat_cpu(cpu).irqs[irq]++;	if (desc->status & IRQ_PER_CPU) {		/* no locking required for CPU-local interrupts: */		ack_irq(irq);		action_ret = handle_irq_event(irq, regs, desc->action);		desc->handler->end(irq);		return;	}	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);	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 (likely(!(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 */			if (!desc->depth)				desc->depth = 1;			desc->status |= IRQ_DISABLED;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?