irq.c

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

C
1,262
字号
					vector[0] = orig;					vector[1] = action;					vector[2] = NULL;					vector[3] = NULL;					bucket->irq_info = vector;					bucket->flags |= IBF_MULTI;				}			} else {				int ent;				vector = (void **)orig;				for (ent = 0; ent < 4; ent++) {					if (vector[ent] == NULL) {						vector[ent] = action;						break;					}				}				if (ent == 4)					goto free_and_ebusy;			}		} else {			bucket->irq_info = action;			bucket->flags |= IBF_ACTIVE;		}		pending = bucket->pending;		if (pending)			bucket->pending = 0;	}	action->handler = handler;	action->flags = irqflags;	action->name = name;	action->next = NULL;	action->dev_id = dev_id;	put_ino_in_irqaction(action, irq);	put_smpaff_in_irqaction(action, CPU_MASK_NONE);	if (tmp)		tmp->next = action;	else		*(bucket->pil + irq_action) = action;	enable_irq(irq);	/* We ate the IVEC already, this makes sure it does not get lost. */	if (pending) {		atomic_bucket_insert(bucket);		set_softint(1 << bucket->pil);	}	spin_unlock_irqrestore(&irq_action_lock, flags);	if ((bucket != &pil0_dummy_bucket) && (!(irqflags & SA_STATIC_ALLOC)))		register_irq_proc(__irq_ino(irq));#ifdef CONFIG_SMP	distribute_irqs();#endif	return 0;free_and_ebusy:	kfree(action);	spin_unlock_irqrestore(&irq_action_lock, flags);	return -EBUSY;free_and_enomem:	kfree(action);	spin_unlock_irqrestore(&irq_action_lock, flags);	return -ENOMEM;}EXPORT_SYMBOL(request_irq);void free_irq(unsigned int irq, void *dev_id){	struct irqaction *action;	struct irqaction *tmp = NULL;	unsigned long flags;	struct ino_bucket *bucket = __bucket(irq), *bp;	if ((bucket != &pil0_dummy_bucket) &&	    (bucket < &ivector_table[0] ||	     bucket >= &ivector_table[NUM_IVECS])) {		unsigned int *caller;		__asm__ __volatile__("mov %%i7, %0" : "=r" (caller));		printk(KERN_CRIT "free_irq: Old style IRQ removal attempt "		       "from %p, irq %08x.\n", caller, irq);		return;	}		spin_lock_irqsave(&irq_action_lock, flags);	action = *(bucket->pil + irq_action);	if (!action->handler) {		printk("Freeing free IRQ %d\n", bucket->pil);		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", bucket->pil);			spin_unlock_irqrestore(&irq_action_lock, flags);			return;		}	} else if (action->flags & SA_SHIRQ) {		printk("Trying to free shared IRQ %d with NULL device ID\n", bucket->pil);		spin_unlock_irqrestore(&irq_action_lock, flags);		return;	}	if (action->flags & SA_STATIC_ALLOC) {		printk("Attempt to free statically allocated IRQ %d (%s)\n",		       bucket->pil, action->name);		spin_unlock_irqrestore(&irq_action_lock, flags);		return;	}	if (action && tmp)		tmp->next = action->next;	else		*(bucket->pil + irq_action) = action->next;	spin_unlock_irqrestore(&irq_action_lock, flags);	synchronize_irq(irq);	spin_lock_irqsave(&irq_action_lock, flags);	if (bucket != &pil0_dummy_bucket) {		unsigned long imap = bucket->imap;		void **vector, *orig;		int ent;		orig = bucket->irq_info;		vector = (void **)orig;		if ((bucket->flags & IBF_MULTI) != 0) {			int other = 0;			void *orphan = NULL;			for (ent = 0; ent < 4; ent++) {				if (vector[ent] == action)					vector[ent] = NULL;				else if (vector[ent] != NULL) {					orphan = vector[ent];					other++;				}			}			/* Only free when no other shared irq			 * uses this bucket.			 */			if (other) {				if (other == 1) {					/* Convert back to non-shared bucket. */					bucket->irq_info = orphan;					bucket->flags &= ~(IBF_MULTI);					kfree(vector);				}				goto out;			}		} else {			bucket->irq_info = NULL;		}		/* This unique interrupt source is now inactive. */		bucket->flags &= ~IBF_ACTIVE;		/* See if any other buckets share this bucket's IMAP		 * and are still active.		 */		for (ent = 0; ent < NUM_IVECS; ent++) {			bp = &ivector_table[ent];			if (bp != bucket	&&			    bp->imap == imap	&&			    (bp->flags & IBF_ACTIVE) != 0)				break;		}		/* Only disable when no other sub-irq levels of		 * the same IMAP are active.		 */		if (ent == NUM_IVECS)			disable_irq(irq);	}out:	kfree(action);	spin_unlock_irqrestore(&irq_action_lock, flags);}EXPORT_SYMBOL(free_irq);#ifdef CONFIG_SMPvoid synchronize_irq(unsigned int irq){	struct ino_bucket *bucket = __bucket(irq);#if 0	/* The following is how I wish I could implement this.	 * Unfortunately the ICLR registers are read-only, you can	 * only write ICLR_foo values to them.  To get the current	 * IRQ status you would need to get at the IRQ diag registers	 * in the PCI/SBUS controller and the layout of those vary	 * from one controller to the next, sigh... -DaveM	 */	unsigned long iclr = bucket->iclr;	while (1) {		u32 tmp = upa_readl(iclr);				if (tmp == ICLR_TRANSMIT ||		    tmp == ICLR_PENDING) {			cpu_relax();			continue;		}		break;	}#else	/* So we have to do this with a INPROGRESS bit just like x86.  */	while (bucket->flags & IBF_INPROGRESS)		cpu_relax();#endif}#endif /* CONFIG_SMP */void catch_disabled_ivec(struct pt_regs *regs){	int cpu = smp_processor_id();	struct ino_bucket *bucket = __bucket(*irq_work(cpu, 0));	/* We can actually see this on Ultra/PCI PCI cards, which are bridges	 * to other devices.  Here a single IMAP enabled potentially multiple	 * unique interrupt sources (which each do have a unique ICLR register.	 *	 * So what we do is just register that the IVEC arrived, when registered	 * for real the request_irq() code will check the bit and signal	 * a local CPU interrupt for it.	 */#if 0	printk("IVEC: Spurious interrupt vector (%x) received at (%016lx)\n",	       bucket - &ivector_table[0], regs->tpc);#endif	*irq_work(cpu, 0) = 0;	bucket->pending = 1;}/* Tune this... */#define FORWARD_VOLUME		12#ifdef CONFIG_SMPstatic inline void redirect_intr(int cpu, struct ino_bucket *bp){	/* Ok, here is what is going on:	 * 1) Retargeting IRQs on Starfire is very	 *    expensive so just forget about it on them.	 * 2) Moving around very high priority interrupts	 *    is a losing game.	 * 3) If the current cpu is idle, interrupts are	 *    useful work, so keep them here.  But do not	 *    pass to our neighbour if he is not very idle.	 * 4) If sysadmin explicitly asks for directed intrs,	 *    Just Do It.	 */	struct irqaction *ap = bp->irq_info;	cpumask_t cpu_mask;	unsigned int buddy, ticks;	cpu_mask = get_smpaff_in_irqaction(ap);	cpus_and(cpu_mask, cpu_mask, cpu_online_map);	if (cpus_empty(cpu_mask))		cpu_mask = cpu_online_map;	if (this_is_starfire != 0 ||	    bp->pil >= 10 || current->pid == 0)		goto out;	/* 'cpu' is the MID (ie. UPAID), calculate the MID	 * of our buddy.	 */	buddy = cpu + 1;	if (buddy >= NR_CPUS)		buddy = 0;	ticks = 0;	while (!cpu_isset(buddy, cpu_mask)) {		if (++buddy >= NR_CPUS)			buddy = 0;		if (++ticks > NR_CPUS) {			put_smpaff_in_irqaction(ap, CPU_MASK_NONE);			goto out;		}	}	if (buddy == cpu)		goto out;	/* Voo-doo programming. */	if (cpu_data(buddy).idle_volume < FORWARD_VOLUME)		goto out;	/* This just so happens to be correct on Cheetah	 * at the moment.	 */	buddy <<= 26;	/* Push it to our buddy. */	upa_writel(buddy | IMAP_VALID, bp->imap);out:	return;}#endifvoid handler_irq(int irq, struct pt_regs *regs){	struct ino_bucket *bp, *nbp;	int cpu = smp_processor_id();#ifndef CONFIG_SMP	/*	 * Check for TICK_INT on level 14 softint.	 */	{		unsigned long clr_mask = 1 << irq;		unsigned long tick_mask = tick_ops->softint_mask;		if ((irq == 14) && (get_softint() & tick_mask)) {			irq = 0;			clr_mask = tick_mask;		}		clear_softint(clr_mask);	}#else	int should_forward = 1;	clear_softint(1 << irq);#endif	irq_enter();	kstat_this_cpu.irqs[irq]++;	/* Sliiiick... */#ifndef CONFIG_SMP	bp = ((irq != 0) ?	      __bucket(xchg32(irq_work(cpu, irq), 0)) :	      &pil0_dummy_bucket);#else	bp = __bucket(xchg32(irq_work(cpu, irq), 0));#endif	for ( ; bp != NULL; bp = nbp) {		unsigned char flags = bp->flags;		unsigned char random = 0;		nbp = __bucket(bp->irq_chain);		bp->irq_chain = 0;		bp->flags |= IBF_INPROGRESS;		if ((flags & IBF_ACTIVE) != 0) {#ifdef CONFIG_PCI			if ((flags & IBF_DMA_SYNC) != 0) {				upa_readl(dma_sync_reg_table[bp->synctab_ent]);				upa_readq(pci_dma_wsync);			}#endif			if ((flags & IBF_MULTI) == 0) {				struct irqaction *ap = bp->irq_info;				ap->handler(__irq(bp), ap->dev_id, regs);				random |= ap->flags & SA_SAMPLE_RANDOM;			} else {				void **vector = (void **)bp->irq_info;				int ent;				for (ent = 0; ent < 4; ent++) {					struct irqaction *ap = vector[ent];					if (ap != NULL) {						ap->handler(__irq(bp), ap->dev_id, regs);						random |= ap->flags & SA_SAMPLE_RANDOM;					}				}			}			/* Only the dummy bucket lacks IMAP/ICLR. */			if (bp->pil != 0) {#ifdef CONFIG_SMP				if (should_forward) {					redirect_intr(cpu, bp);					should_forward = 0;				}#endif				upa_writel(ICLR_IDLE, bp->iclr);				/* Test and add entropy */				if (random)					add_interrupt_randomness(irq);			}		} else			bp->pending = 1;		bp->flags &= ~IBF_INPROGRESS;	}	irq_exit();}#ifdef CONFIG_BLK_DEV_FDextern void floppy_interrupt(int irq, void *dev_cookie, struct pt_regs *regs);void sparc_floppy_irq(int irq, void *dev_cookie, struct pt_regs *regs){	struct irqaction *action = *(irq + irq_action);	struct ino_bucket *bucket;	int cpu = smp_processor_id();	irq_enter();	kstat_this_cpu.irqs[irq]++;	*(irq_work(cpu, irq)) = 0;	bucket = get_ino_in_irqaction(action) + ivector_table;

⌨️ 快捷键说明

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