irq.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,197 行 · 第 1/2 页

C
1,197
字号
	}#endif	if (irq >= NR_IRQS)		return -EINVAL;	if (!handler)		return -EINVAL;	action = (struct irqaction *)			kmalloc(sizeof(struct irqaction), GFP_ATOMIC);	if (!action)		return -ENOMEM;	action->handler = handler;	action->flags = irqflags;	cpus_clear(action->mask);	action->name = devname;	action->next = NULL;	action->dev_id = dev_id;	retval = setup_irq(irq, action);	if (retval)		kfree(action);	return retval;}EXPORT_SYMBOL(request_irq);/** *	free_irq - free an interrupt *	@irq: Interrupt line to free *	@dev_id: Device identity to free * *	Remove an interrupt handler. The handler is removed and if the *	interrupt line is no longer in use by any driver it is disabled. *	On a shared IRQ the caller must ensure the interrupt is disabled *	on the card it drives before calling this function. The function *	does not return until any executing interrupts for this IRQ *	have completed. * *	This function must not be called from interrupt context. */void free_irq(unsigned int irq, void *dev_id){	irq_desc_t *desc;	struct irqaction **p;	unsigned long flags;	if (irq >= NR_IRQS)		return;	desc = irq_descp(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;				desc->handler->shutdown(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(KERN_ERR "Trying to free free IRQ%d\n",irq);		spin_unlock_irqrestore(&desc->lock,flags);		return;	}}EXPORT_SYMBOL(free_irq);/* * IRQ autodetection code.. * * This depends on the fact that any interrupt that * comes in on to an unassigned handler will get stuck * with "IRQ_WAITING" cleared and the interrupt * disabled. */static DECLARE_MUTEX(probe_sem);/** *	probe_irq_on	- begin an interrupt autodetect * *	Commence probing for an interrupt. The interrupts are scanned *	and a mask of potential interrupt lines is returned. * */unsigned long probe_irq_on(void){	unsigned int i;	irq_desc_t *desc;	unsigned long val;	unsigned long delay;	down(&probe_sem);	/*	 * something may have generated an irq long ago and we want to	 * flush such a longstanding irq before considering it as spurious.	 */	for (i = NR_IRQS-1; i > 0; i--)  {		desc = irq_descp(i);		spin_lock_irq(&desc->lock);		if (!desc->action)			desc->handler->startup(i);		spin_unlock_irq(&desc->lock);	}	/* Wait for longstanding interrupts to trigger. */	for (delay = jiffies + HZ/50; time_after(delay, jiffies); )		/* about 20ms delay */ barrier();	/*	 * enable any unassigned irqs	 * (we must startup again here because if a longstanding irq	 * happened in the previous stage, it may have masked itself)	 */	for (i = NR_IRQS-1; i > 0; i--) {		desc = irq_descp(i);		spin_lock_irq(&desc->lock);		if (!desc->action) {			desc->status |= IRQ_AUTODETECT | IRQ_WAITING;			if (desc->handler->startup(i))				desc->status |= IRQ_PENDING;		}		spin_unlock_irq(&desc->lock);	}	/*	 * Wait for spurious interrupts to trigger	 */	for (delay = jiffies + HZ/10; time_after(delay, jiffies); )		/* about 100ms delay */ barrier();	/*	 * Now filter out any obviously spurious interrupts	 */	val = 0;	for (i = 0; i < NR_IRQS; i++) {		irq_desc_t *desc = irq_descp(i);		unsigned int status;		spin_lock_irq(&desc->lock);		status = desc->status;		if (status & IRQ_AUTODETECT) {			/* It triggered already - consider it spurious. */			if (!(status & IRQ_WAITING)) {				desc->status = status & ~IRQ_AUTODETECT;				desc->handler->shutdown(i);			} else				if (i < 32)					val |= 1 << i;		}		spin_unlock_irq(&desc->lock);	}	return val;}EXPORT_SYMBOL(probe_irq_on);/** *	probe_irq_mask - scan a bitmap of interrupt lines *	@val:	mask of interrupts to consider * *	Scan the ISA bus interrupt lines and return a bitmap of *	active interrupts. The interrupt probe logic state is then *	returned to its previous value. * *	Note: we need to scan all the irq's even though we will *	only return ISA irq numbers - just so that we reset them *	all to a known state. */unsigned int probe_irq_mask(unsigned long val){	int i;	unsigned int mask;	mask = 0;	for (i = 0; i < 16; i++) {		irq_desc_t *desc = irq_descp(i);		unsigned int status;		spin_lock_irq(&desc->lock);		status = desc->status;		if (status & IRQ_AUTODETECT) {			if (!(status & IRQ_WAITING))				mask |= 1 << i;			desc->status = status & ~IRQ_AUTODETECT;			desc->handler->shutdown(i);		}		spin_unlock_irq(&desc->lock);	}	up(&probe_sem);	return mask & val;}EXPORT_SYMBOL(probe_irq_mask);/** *	probe_irq_off	- end an interrupt autodetect *	@val: mask of potential interrupts (unused) * *	Scans the unused interrupt lines and returns the line which *	appears to have triggered the interrupt. If no interrupt was *	found then zero is returned. If more than one interrupt is *	found then minus the first candidate is returned to indicate *	their is doubt. * *	The interrupt probe logic state is returned to its previous *	value. * *	BUGS: When used in a module (which arguably shouldn't happen) *	nothing prevents two IRQ probe callers from overlapping. The *	results of this are non-optimal. */int probe_irq_off(unsigned long val){	int i, irq_found, nr_irqs;	nr_irqs = 0;	irq_found = 0;	for (i = 0; i < NR_IRQS; i++) {		irq_desc_t *desc = irq_descp(i);		unsigned int status;		spin_lock_irq(&desc->lock);		status = desc->status;		if (status & IRQ_AUTODETECT) {			if (!(status & IRQ_WAITING)) {				if (!nr_irqs)					irq_found = i;				nr_irqs++;			}			desc->status = status & ~IRQ_AUTODETECT;			desc->handler->shutdown(i);		}		spin_unlock_irq(&desc->lock);	}	up(&probe_sem);	if (nr_irqs > 1)		irq_found = -irq_found;	return irq_found;}EXPORT_SYMBOL(probe_irq_off);int setup_irq(unsigned int irq, struct irqaction * new){	int shared = 0;	unsigned long flags;	struct irqaction *old, **p;	irq_desc_t *desc = irq_descp(irq);	if (desc->handler == &no_irq_type)		return -ENOSYS;	/*	 * 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);	}	if (new->flags & SA_PERCPU_IRQ) {		desc->status |= IRQ_PER_CPU;		desc->handler = &irq_type_ia64_lsapic;	}	/*	 * 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);		desc->handler->startup(irq);	}	spin_unlock_irqrestore(&desc->lock,flags);	register_irq_proc(irq);	return 0;}static struct proc_dir_entry * root_irq_dir;static struct proc_dir_entry * irq_dir [NR_IRQS];#ifdef CONFIG_SMPstatic struct proc_dir_entry * smp_affinity_entry [NR_IRQS];static cpumask_t irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = CPU_MASK_ALL };static char irq_redir [NR_IRQS]; // = { [0 ... NR_IRQS-1] = 1 };void set_irq_affinity_info (unsigned int irq, int hwid, int redir){	cpumask_t mask = CPU_MASK_NONE;	cpu_set(cpu_logical_id(hwid), mask);	if (irq < NR_IRQS) {		irq_affinity[irq] = mask;		irq_redir[irq] = (char) (redir & 0xff);	}}static int irq_affinity_read_proc (char *page, char **start, off_t off,			int count, int *eof, void *data){	int len = sprintf(page, "%s", irq_redir[(long)data] ? "r " : "");	len += cpumask_scnprintf(page+len, count, irq_affinity[(long)data]);	if (count - len < 2)		return -EINVAL;	len += sprintf(page + len, "\n");	return len;}static int irq_affinity_write_proc (struct file *file, const char __user *buffer,				    unsigned long count, void *data){	unsigned int irq = (unsigned long) data;	int full_count = count, err;	cpumask_t new_value, tmp;#	define R_PREFIX_LEN 16	char rbuf[R_PREFIX_LEN];	int rlen;	int prelen;	irq_desc_t *desc = irq_descp(irq);	unsigned long flags;	int redir = 0;	if (!desc->handler->set_affinity)		return -EIO;	/*	 * If string being written starts with a prefix of 'r' or 'R'	 * and some limited number of spaces, set IA64_IRQ_REDIRECTED.	 * If more than (R_PREFIX_LEN - 2) spaces are passed, they won't	 * all be trimmed as part of prelen, the untrimmed spaces will	 * cause the hex parsing to fail, and this write() syscall will	 * fail with EINVAL.	 */	if (!count)		return -EINVAL;	rlen = min(sizeof(rbuf)-1, count);	if (copy_from_user(rbuf, buffer, rlen))		return -EFAULT;	rbuf[rlen] = 0;	prelen = 0;	if (tolower(*rbuf) == 'r') {		prelen = strspn(rbuf, "Rr ");		redir++;	}	err = cpumask_parse(buffer+prelen, count-prelen, new_value);	if (err)		return err;	/*	 * 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.	 */	cpus_and(tmp, new_value, cpu_online_map);	if (cpus_empty(tmp))		return -EINVAL;	spin_lock_irqsave(&desc->lock, flags);	pending_irq_cpumask[irq] = new_value;	if (redir)		set_bit(irq, pending_irq_redir);	else		clear_bit(irq, pending_irq_redir);	spin_unlock_irqrestore(&desc->lock, flags);	return full_count;}void move_irq(int irq){	/* note - we hold desc->lock */	cpumask_t tmp;	irq_desc_t *desc = irq_descp(irq);	int redir = test_bit(irq, pending_irq_redir);	if (!cpus_empty(pending_irq_cpumask[irq])) {		cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map);		if (unlikely(!cpus_empty(tmp))) {			desc->handler->set_affinity(irq | (redir ? IA64_IRQ_REDIRECTED : 0),						    pending_irq_cpumask[irq]);		}		cpus_clear(pending_irq_cpumask[irq]);	}}#endif /* CONFIG_SMP */#ifdef CONFIG_HOTPLUG_CPUunsigned int vectors_in_migration[NR_IRQS];/* * Since cpu_online_map is already updated, we just need to check for * affinity that has zeros */static void migrate_irqs(void){	cpumask_t	mask;	irq_desc_t *desc;	int 		irq, new_cpu;	for (irq=0; irq < NR_IRQS; irq++) {		desc = irq_descp(irq);		/*		 * No handling for now.		 * TBD: Implement a disable function so we can now		 * tell CPU not to respond to these local intr sources.		 * such as ITV,CPEI,MCA etc.		 */		if (desc->status == IRQ_PER_CPU)			continue;		cpus_and(mask, irq_affinity[irq], cpu_online_map);		if (any_online_cpu(mask) == NR_CPUS) {			/*			 * Save it for phase 2 processing			 */			vectors_in_migration[irq] = irq;			new_cpu = any_online_cpu(cpu_online_map);			mask = cpumask_of_cpu(new_cpu);			/*			 * Al three are essential, currently WARN_ON.. maybe panic?			 */			if (desc->handler && desc->handler->disable &&				desc->handler->enable && desc->handler->set_affinity) {				desc->handler->disable(irq);				desc->handler->set_affinity(irq, mask);				desc->handler->enable(irq);			} else {				WARN_ON((!(desc->handler) || !(desc->handler->disable) ||						!(desc->handler->enable) ||						!(desc->handler->set_affinity)));			}		}	}}void fixup_irqs(void){	unsigned int irq;	extern void ia64_process_pending_intr(void);	ia64_set_itv(1<<16);	/*	 * Phase 1: Locate irq's bound to this cpu and	 * relocate them for cpu removal.	 */	migrate_irqs();	/*	 * Phase 2: Perform interrupt processing for all entries reported in	 * local APIC.	 */	ia64_process_pending_intr();	/*	 * Phase 3: Now handle any interrupts not captured in local APIC.	 * This is to account for cases that device interrupted during the time the	 * rte was being disabled and re-programmed.	 */	for (irq=0; irq < NR_IRQS; irq++) {		if (vectors_in_migration[irq]) {			vectors_in_migration[irq]=0;			do_IRQ(irq, NULL);		}	}	/*	 * Now let processor die. We do irq disable and max_xtp() to	 * ensure there is no more interrupts routed to this processor.	 * But the local timer interrupt can have 1 pending which we	 * take care in timer_interrupt().	 */	max_xtp();	local_irq_disable();}#endif#define MAX_NAMELEN 10static void register_irq_proc (unsigned int irq){	char name [MAX_NAMELEN];	if (!root_irq_dir || (irq_descp(irq)->handler == &no_irq_type) || irq_dir[irq])		return;	memset(name, 0, MAX_NAMELEN);	sprintf(name, "%d", irq);	/* create /proc/irq/1234 */	irq_dir[irq] = proc_mkdir(name, root_irq_dir);#ifdef CONFIG_SMP	{		struct proc_dir_entry *entry;		/* create /proc/irq/1234/smp_affinity */		entry = create_proc_entry("smp_affinity", 0600, irq_dir[irq]);		if (entry) {			entry->nlink = 1;			entry->data = (void *)(long)irq;			entry->read_proc = irq_affinity_read_proc;			entry->write_proc = irq_affinity_write_proc;		}		smp_affinity_entry[irq] = entry;	}#endif}void init_irq_proc (void){	int i;	/* create /proc/irq */	root_irq_dir = proc_mkdir("irq", NULL);	/* create /proc/irq/prof_cpu_mask */	create_prof_cpu_mask(root_irq_dir);	/*	 * Create entries for all existing IRQs.	 */	for (i = 0; i < NR_IRQS; i++) {		if (irq_descp(i)->handler == &no_irq_type)			continue;		register_irq_proc(i);	}}

⌨️ 快捷键说明

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