diff options
Diffstat (limited to 'arch/sparc/kernel/irq_32.c')
| -rw-r--r-- | arch/sparc/kernel/irq_32.c | 513 | 
1 files changed, 124 insertions, 389 deletions
diff --git a/arch/sparc/kernel/irq_32.c b/arch/sparc/kernel/irq_32.c index 7c93df4099c..9b89d842913 100644 --- a/arch/sparc/kernel/irq_32.c +++ b/arch/sparc/kernel/irq_32.c @@ -15,6 +15,7 @@  #include <linux/seq_file.h>  #include <asm/cacheflush.h> +#include <asm/cpudata.h>  #include <asm/pcic.h>  #include <asm/leon.h> @@ -101,284 +102,173 @@ EXPORT_SYMBOL(arch_local_irq_restore);   * directed CPU interrupts using the existing enable/disable irq code   * with tweaks.   * + * Sun4d complicates things even further.  IRQ numbers are arbitrary + * 32-bit values in that case.  Since this is similar to sparc64, + * we adopt a virtual IRQ numbering scheme as is done there. + * Virutal interrupt numbers are allocated by build_irq().  So NR_IRQS + * just becomes a limit of how many interrupt sources we can handle in + * a single system.  Even fully loaded SS2000 machines top off at + * about 32 interrupt sources or so, therefore a NR_IRQS value of 64 + * is more than enough. +  * + * We keep a map of per-PIL enable interrupts.  These get wired + * up via the irq_chip->startup() method which gets invoked by + * the generic IRQ layer during request_irq().   */ +/* Table of allocated irqs. Unused entries has irq == 0 */ +static struct irq_bucket irq_table[NR_IRQS]; +/* Protect access to irq_table */ +static DEFINE_SPINLOCK(irq_table_lock); -/* - * 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	4 -struct irqaction static_irqaction[MAX_STATIC_ALLOC]; -int static_irq_count; - -static struct { -	struct irqaction *action; -	int flags; -} sparc_irq[NR_IRQS]; -#define SPARC_IRQ_INPROGRESS 1 +/* Map between the irq identifier used in hw to the irq_bucket. */ +struct irq_bucket *irq_map[SUN4D_MAX_IRQ]; +/* Protect access to irq_map */ +static DEFINE_SPINLOCK(irq_map_lock); -/* Used to protect the IRQ action lists */ -DEFINE_SPINLOCK(irq_action_lock); - -int show_interrupts(struct seq_file *p, void *v) +/* Allocate a new irq from the irq_table */ +unsigned int irq_alloc(unsigned int real_irq, unsigned int pil)  { -	int i = *(loff_t *)v; -	struct irqaction *action;  	unsigned long flags; -#ifdef CONFIG_SMP -	int j; -#endif +	unsigned int i; + +	spin_lock_irqsave(&irq_table_lock, flags); +	for (i = 1; i < NR_IRQS; i++) { +		if (irq_table[i].real_irq == real_irq && irq_table[i].pil == pil) +			goto found; +	} -	if (sparc_cpu_model == sun4d) -		return show_sun4d_interrupts(p, v); +	for (i = 1; i < NR_IRQS; i++) { +		if (!irq_table[i].irq) +			break; +	} -	spin_lock_irqsave(&irq_action_lock, flags);  	if (i < NR_IRQS) { -		action = sparc_irq[i].action; -		if (!action) -			goto out_unlock; -		seq_printf(p, "%3d: ", i); -#ifndef CONFIG_SMP -		seq_printf(p, "%10u ", kstat_irqs(i)); -#else -		for_each_online_cpu(j) { -			seq_printf(p, "%10u ", -				    kstat_cpu(j).irqs[i]); -		} -#endif -		seq_printf(p, " %c %s", -			(action->flags & IRQF_DISABLED) ? '+' : ' ', -			action->name); -		for (action = action->next; action; action = action->next) { -			seq_printf(p, ",%s %s", -				(action->flags & IRQF_DISABLED) ? " +" : "", -				action->name); -		} -		seq_putc(p, '\n'); +		irq_table[i].real_irq = real_irq; +		irq_table[i].irq = i; +		irq_table[i].pil = pil; +	} else { +		printk(KERN_ERR "IRQ: Out of virtual IRQs.\n"); +		i = 0;  	} -out_unlock: -	spin_unlock_irqrestore(&irq_action_lock, flags); -	return 0; +found: +	spin_unlock_irqrestore(&irq_table_lock, flags); + +	return i;  } -void free_irq(unsigned int irq, void *dev_id) +/* Based on a single pil handler_irq may need to call several + * interrupt handlers. Use irq_map as entry to irq_table, + * and let each entry in irq_table point to the next entry. + */ +void irq_link(unsigned int irq)  { -	struct irqaction *action; -	struct irqaction **actionp; +	struct irq_bucket *p;  	unsigned long flags; -	unsigned int cpu_irq; - -	if (sparc_cpu_model == sun4d) { -		sun4d_free_irq(irq, dev_id); -		return; -	} -	cpu_irq = irq & (NR_IRQS - 1); -	if (cpu_irq > 14) {  /* 14 irq levels on the sparc */ -		printk(KERN_ERR "Trying to free bogus IRQ %d\n", irq); -		return; -	} - -	spin_lock_irqsave(&irq_action_lock, flags); +	unsigned int pil; -	actionp = &sparc_irq[cpu_irq].action; -	action = *actionp; +	BUG_ON(irq >= NR_IRQS); -	if (!action->handler) { -		printk(KERN_ERR "Trying to free free IRQ%d\n", irq); -		goto out_unlock; -	} -	if (dev_id) { -		for (; action; action = action->next) { -			if (action->dev_id == dev_id) -				break; -			actionp = &action->next; -		} -		if (!action) { -			printk(KERN_ERR "Trying to free free shared IRQ%d\n", -			       irq); -			goto out_unlock; -		} -	} else if (action->flags & IRQF_SHARED) { -		printk(KERN_ERR "Trying to free shared IRQ%d with NULL device ID\n", -		       irq); -		goto out_unlock; -	} -	if (action->flags & SA_STATIC_ALLOC) { -		/* -		 * This interrupt is marked as specially allocated -		 * so it is a bad idea to free it. -		 */ -		printk(KERN_ERR "Attempt to free statically allocated IRQ%d (%s)\n", -		       irq, action->name); -		goto out_unlock; -	} +	spin_lock_irqsave(&irq_map_lock, flags); -	*actionp = action->next; +	p = &irq_table[irq]; +	pil = p->pil; +	BUG_ON(pil > SUN4D_MAX_IRQ); +	p->next = irq_map[pil]; +	irq_map[pil] = p; -	spin_unlock_irqrestore(&irq_action_lock, flags); +	spin_unlock_irqrestore(&irq_map_lock, flags); +} -	synchronize_irq(irq); +void irq_unlink(unsigned int irq) +{ +	struct irq_bucket *p, **pnext; +	unsigned long flags; -	spin_lock_irqsave(&irq_action_lock, flags); +	BUG_ON(irq >= NR_IRQS); -	kfree(action); +	spin_lock_irqsave(&irq_map_lock, flags); -	if (!sparc_irq[cpu_irq].action) -		__disable_irq(irq); +	p = &irq_table[irq]; +	BUG_ON(p->pil > SUN4D_MAX_IRQ); +	pnext = &irq_map[p->pil]; +	while (*pnext != p) +		pnext = &(*pnext)->next; +	*pnext = p->next; -out_unlock: -	spin_unlock_irqrestore(&irq_action_lock, flags); +	spin_unlock_irqrestore(&irq_map_lock, flags);  } -EXPORT_SYMBOL(free_irq); -/* - * 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. - */ -#ifdef CONFIG_SMP -void synchronize_irq(unsigned int irq) -{ -	unsigned int cpu_irq; - -	cpu_irq = irq & (NR_IRQS - 1); -	while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS) -		cpu_relax(); -} -EXPORT_SYMBOL(synchronize_irq); -#endif /* SMP */ -void unexpected_irq(int irq, void *dev_id, struct pt_regs *regs) +/* /proc/interrupts printing */ +int arch_show_interrupts(struct seq_file *p, int prec)  { -	int i; -	struct irqaction *action; -	unsigned int cpu_irq; - -	cpu_irq = irq & (NR_IRQS - 1); -	action = sparc_irq[cpu_irq].action; +	int j; -	printk(KERN_ERR "IO device interrupt, irq = %d\n", irq); -	printk(KERN_ERR "PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc, -		    regs->npc, regs->u_regs[14]); -	if (action) { -		printk(KERN_ERR "Expecting: "); -		for (i = 0; i < 16; i++) -			if (action->handler) -				printk(KERN_CONT "[%s:%d:0x%x] ", action->name, -				       i, (unsigned int)action->handler); -	} -	printk(KERN_ERR "AIEEE\n"); -	panic("bogus interrupt received"); +#ifdef CONFIG_SMP +	seq_printf(p, "RES: "); +	for_each_online_cpu(j) +		seq_printf(p, "%10u ", cpu_data(j).irq_resched_count); +	seq_printf(p, "     IPI rescheduling interrupts\n"); +	seq_printf(p, "CAL: "); +	for_each_online_cpu(j) +		seq_printf(p, "%10u ", cpu_data(j).irq_call_count); +	seq_printf(p, "     IPI function call interrupts\n"); +#endif +	seq_printf(p, "NMI: "); +	for_each_online_cpu(j) +		seq_printf(p, "%10u ", cpu_data(j).counter); +	seq_printf(p, "     Non-maskable interrupts\n"); +	return 0;  } -void handler_irq(int pil, struct pt_regs *regs) +void handler_irq(unsigned int pil, struct pt_regs *regs)  {  	struct pt_regs *old_regs; -	struct irqaction *action; -	int cpu = smp_processor_id(); +	struct irq_bucket *p; +	BUG_ON(pil > 15);  	old_regs = set_irq_regs(regs);  	irq_enter(); -	disable_pil_irq(pil); -#ifdef CONFIG_SMP -	/* Only rotate on lower priority IRQs (scsi, ethernet, etc.). */ -	if ((sparc_cpu_model==sun4m) && (pil < 10)) -		smp4m_irq_rotate(cpu); -#endif -	action = sparc_irq[pil].action; -	sparc_irq[pil].flags |= SPARC_IRQ_INPROGRESS; -	kstat_cpu(cpu).irqs[pil]++; -	do { -		if (!action || !action->handler) -			unexpected_irq(pil, NULL, regs); -		action->handler(pil, action->dev_id); -		action = action->next; -	} while (action); -	sparc_irq[pil].flags &= ~SPARC_IRQ_INPROGRESS; -	enable_pil_irq(pil); + +	p = irq_map[pil]; +	while (p) { +		struct irq_bucket *next = p->next; + +		generic_handle_irq(p->irq); +		p = next; +	}  	irq_exit();  	set_irq_regs(old_regs);  }  #if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE) +static unsigned int floppy_irq; -/* - * Fast IRQs on the Sparc can only have one routine attached to them, - * thus no sharing possible. - */ -static int request_fast_irq(unsigned int irq, -			    void (*handler)(void), -			    unsigned long irqflags, const char *devname) +int sparc_floppy_request_irq(unsigned int irq, irq_handler_t irq_handler)  { -	struct irqaction *action; -	unsigned long flags;  	unsigned int cpu_irq; -	int ret; +	int err; +  #if defined CONFIG_SMP && !defined CONFIG_SPARC_LEON  	struct tt_entry *trap_table;  #endif -	cpu_irq = irq & (NR_IRQS - 1); -	if (cpu_irq > 14) { -		ret = -EINVAL; -		goto out; -	} -	if (!handler) { -		ret = -EINVAL; -		goto out; -	} -	spin_lock_irqsave(&irq_action_lock, flags); +	err = request_irq(irq, irq_handler, 0, "floppy", NULL); +	if (err) +		return -1; -	action = sparc_irq[cpu_irq].action; -	if (action) { -		if (action->flags & IRQF_SHARED) -			panic("Trying to register fast irq when already shared.\n"); -		if (irqflags & IRQF_SHARED) -			panic("Trying to register fast irq as shared.\n"); +	/* Save for later use in floppy interrupt handler */ +	floppy_irq = irq; -		/* Anyway, someone already owns it so cannot be made fast. */ -		printk(KERN_ERR "request_fast_irq: Trying to register yet already owned.\n"); -		ret = -EBUSY; -		goto out_unlock; -	} - -	/* -	 * If this is flagged as statically allocated then we use our -	 * private struct which is never freed. -	 */ -	if (irqflags & SA_STATIC_ALLOC) { -		if (static_irq_count < MAX_STATIC_ALLOC) -			action = &static_irqaction[static_irq_count++]; -		else -			printk(KERN_ERR "Fast IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n", -			       irq, devname); -	} - -	if (action == NULL) -		action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); -	if (!action) { -		ret = -ENOMEM; -		goto out_unlock; -	} +	cpu_irq = (irq & (NR_IRQS - 1));  	/* Dork with trap table if we get this far. */  #define INSTANTIATE(table) \  	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_one = SPARC_RD_PSR_L0; \  	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two = \ -		SPARC_BRANCH((unsigned long) handler, \ +		SPARC_BRANCH((unsigned long) floppy_hardint, \  			     (unsigned long) &table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two);\  	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_three = SPARC_RD_WIM_L3; \  	table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP; @@ -399,22 +289,9 @@ static int request_fast_irq(unsigned int irq,  	 * writing we have no CPU-neutral interface to fine-grained flushes.  	 */  	flush_cache_all(); - -	action->flags = irqflags; -	action->name = devname; -	action->dev_id = NULL; -	action->next = NULL; - -	sparc_irq[cpu_irq].action = action; - -	__enable_irq(irq); - -	ret = 0; -out_unlock: -	spin_unlock_irqrestore(&irq_action_lock, flags); -out: -	return ret; +	return 0;  } +EXPORT_SYMBOL(sparc_floppy_request_irq);  /*   * These variables are used to access state from the assembler @@ -440,154 +317,23 @@ EXPORT_SYMBOL(pdma_base);  unsigned long pdma_areasize;  EXPORT_SYMBOL(pdma_areasize); -static irq_handler_t floppy_irq_handler; - +/* Use the generic irq support to call floppy_interrupt + * which was setup using request_irq() in sparc_floppy_request_irq(). + * We only have one floppy interrupt so we do not need to check + * for additional handlers being wired up by irq_link() + */  void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs)  {  	struct pt_regs *old_regs; -	int cpu = smp_processor_id();  	old_regs = set_irq_regs(regs); -	disable_pil_irq(irq);  	irq_enter(); -	kstat_cpu(cpu).irqs[irq]++; -	floppy_irq_handler(irq, dev_id); +	generic_handle_irq(floppy_irq);  	irq_exit(); -	enable_pil_irq(irq);  	set_irq_regs(old_regs); -	/* -	 * XXX Eek, it's totally changed with preempt_count() and such -	 * if (softirq_pending(cpu)) -	 *	do_softirq(); -	 */ -} - -int sparc_floppy_request_irq(int irq, unsigned long flags, -			     irq_handler_t irq_handler) -{ -	floppy_irq_handler = irq_handler; -	return request_fast_irq(irq, floppy_hardint, flags, "floppy");  } -EXPORT_SYMBOL(sparc_floppy_request_irq); -  #endif -int request_irq(unsigned int irq, -		irq_handler_t handler, -		unsigned long irqflags, const char *devname, void *dev_id) -{ -	struct irqaction *action, **actionp; -	unsigned long flags; -	unsigned int cpu_irq; -	int ret; - -	if (sparc_cpu_model == sun4d) -		return sun4d_request_irq(irq, handler, irqflags, devname, dev_id); - -	cpu_irq = irq & (NR_IRQS - 1); -	if (cpu_irq > 14) { -		ret = -EINVAL; -		goto out; -	} -	if (!handler) { -		ret = -EINVAL; -		goto out; -	} - -	spin_lock_irqsave(&irq_action_lock, flags); - -	actionp = &sparc_irq[cpu_irq].action; -	action = *actionp; -	if (action) { -		if (!(action->flags & IRQF_SHARED) || !(irqflags & IRQF_SHARED)) { -			ret = -EBUSY; -			goto out_unlock; -		} -		if ((action->flags & IRQF_DISABLED) != (irqflags & IRQF_DISABLED)) { -			printk(KERN_ERR "Attempt to mix fast and slow interrupts on IRQ%d denied\n", -			       irq); -			ret = -EBUSY; -			goto out_unlock; -		} -		for ( ; action; action = *actionp) -			actionp = &action->next; -	} - -	/* If this is flagged as statically allocated then we use our -	 * private struct which is never freed. -	 */ -	if (irqflags & SA_STATIC_ALLOC) { -		if (static_irq_count < MAX_STATIC_ALLOC) -			action = &static_irqaction[static_irq_count++]; -		else -			printk(KERN_ERR "Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n", -			       irq, devname); -	} -	if (action == NULL) -		action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); -	if (!action) { -		ret = -ENOMEM; -		goto out_unlock; -	} - -	action->handler = handler; -	action->flags = irqflags; -	action->name = devname; -	action->next = NULL; -	action->dev_id = dev_id; - -	*actionp = action; - -	__enable_irq(irq); - -	ret = 0; -out_unlock: -	spin_unlock_irqrestore(&irq_action_lock, flags); -out: -	return ret; -} -EXPORT_SYMBOL(request_irq); - -void disable_irq_nosync(unsigned int irq) -{ -	__disable_irq(irq); -} -EXPORT_SYMBOL(disable_irq_nosync); - -void disable_irq(unsigned int irq) -{ -	__disable_irq(irq); -} -EXPORT_SYMBOL(disable_irq); - -void enable_irq(unsigned int irq) -{ -	__enable_irq(irq); -} -EXPORT_SYMBOL(enable_irq); - -/* - * We really don't need these at all on the Sparc.  We only have - * stubs here because they are exported to modules. - */ -unsigned long probe_irq_on(void) -{ -	return 0; -} -EXPORT_SYMBOL(probe_irq_on); - -int probe_irq_off(unsigned long mask) -{ -	return 0; -} -EXPORT_SYMBOL(probe_irq_off); - -static unsigned int build_device_irq(struct platform_device *op, -                                     unsigned int real_irq) -{ -	return real_irq; -} -  /* djhr   * This could probably be made indirect too and assigned in the CPU   * bits of the code. That would be much nicer I think and would also @@ -598,8 +344,6 @@ static unsigned int build_device_irq(struct platform_device *op,  void __init init_IRQ(void)  { -	sparc_irq_config.build_device_irq = build_device_irq; -  	switch (sparc_cpu_model) {  	case sun4c:  	case sun4: @@ -607,14 +351,11 @@ void __init init_IRQ(void)  		break;  	case sun4m: -#ifdef CONFIG_PCI  		pcic_probe(); -		if (pcic_present()) { +		if (pcic_present())  			sun4m_pci_init_IRQ(); -			break; -		} -#endif -		sun4m_init_IRQ(); +		else +			sun4m_init_IRQ();  		break;  	case sun4d: @@ -632,9 +373,3 @@ void __init init_IRQ(void)  	btfixup();  } -#ifdef CONFIG_PROC_FS -void init_irq_proc(void) -{ -	/* For now, nothing... */ -} -#endif /* CONFIG_PROC_FS */  |