diff options
Diffstat (limited to 'arch/sparc/kernel/sun4d_irq.c')
| -rw-r--r-- | arch/sparc/kernel/sun4d_irq.c | 458 | 
1 files changed, 157 insertions, 301 deletions
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index ee35c45ffb8..14a043531dc 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -22,22 +22,20 @@   * cpu local.  CPU local interrupts cover the timer interrupts   * and whatnot, and we encode those as normal PILs between   * 0 and 15. - * - * SBUS interrupts are encoded integers including the board number - * (plus one), the SBUS level, and the SBUS slot number.  Sun4D - * IRQ dispatch is done by: - * - * 1) Reading the BW local interrupt table in order to get the bus - *    interrupt mask. - * - *    This table is indexed by SBUS interrupt level which can be - *    derived from the PIL we got interrupted on. - * - * 2) For each bus showing interrupt pending from #1, read the - *    SBI interrupt state register.  This will indicate which slots - *    have interrupts pending for that SBUS interrupt level. + * SBUS interrupts are encodes as a combination of board, level and slot.   */ +struct sun4d_handler_data { +	unsigned int cpuid;    /* target cpu */ +	unsigned int real_irq; /* interrupt level */ +}; + + +static unsigned int sun4d_encode_irq(int board, int lvl, int slot) +{ +	return (board + 1) << 5 | (lvl << 2) | slot; +} +  struct sun4d_timer_regs {  	u32	l10_timer_limit;  	u32	l10_cur_countx; @@ -48,22 +46,13 @@ struct sun4d_timer_regs {  static struct sun4d_timer_regs __iomem *sun4d_timers; -#define TIMER_IRQ	10 - -#define MAX_STATIC_ALLOC	4 +#define SUN4D_TIMER_IRQ        10  /* Specify which cpu handle interrupts from which board.   * Index is board - value is cpu.   */  static unsigned char board_to_cpu[32]; -static struct irqaction *irq_action[NR_IRQS]; - -static struct sbus_action { -	struct irqaction *action; -	/* For SMP this needs to be extended */ -} *sbus_actions; -  static int pil_to_sbus[] = {  	0,  	0, @@ -83,152 +72,81 @@ static int pil_to_sbus[] = {  	0,  }; -static int sbus_to_pil[] = { -	0, -	2, -	3, -	5, -	7, -	9, -	11, -	13, -}; - -static int nsbi; -  /* Exported for sun4d_smp.c */  DEFINE_SPINLOCK(sun4d_imsk_lock); -int show_sun4d_interrupts(struct seq_file *p, void *v) +/* SBUS interrupts are encoded integers including the board number + * (plus one), the SBUS level, and the SBUS slot number.  Sun4D + * IRQ dispatch is done by: + * + * 1) Reading the BW local interrupt table in order to get the bus + *    interrupt mask. + * + *    This table is indexed by SBUS interrupt level which can be + *    derived from the PIL we got interrupted on. + * + * 2) For each bus showing interrupt pending from #1, read the + *    SBI interrupt state register.  This will indicate which slots + *    have interrupts pending for that SBUS interrupt level. + * + * 3) Call the genreric IRQ support. + */ +static void sun4d_sbus_handler_irq(int sbusl)  { -	int i = *(loff_t *) v, j = 0, k = 0, sbusl; -	struct irqaction *action; -	unsigned long flags; -#ifdef CONFIG_SMP -	int x; -#endif +	unsigned int bus_mask; +	unsigned int sbino, slot; +	unsigned int sbil; -	spin_lock_irqsave(&irq_action_lock, flags); -	if (i < NR_IRQS) { -		sbusl = pil_to_sbus[i]; -		if (!sbusl) { -			action = *(i + irq_action); -			if (!action) -				goto out_unlock; -		} else { -			for (j = 0; j < nsbi; j++) { -				for (k = 0; k < 4; k++) -					action = sbus_actions[(j << 5) + (sbusl << 2) + k].action; -					if (action) -						goto found_it; -			} -			goto out_unlock; -		} -found_it:	seq_printf(p, "%3d: ", i); -#ifndef CONFIG_SMP -		seq_printf(p, "%10u ", kstat_irqs(i)); -#else -		for_each_online_cpu(x) -			seq_printf(p, "%10u ", -			       kstat_cpu(cpu_logical_map(x)).irqs[i]); -#endif -		seq_printf(p, "%c %s", -			(action->flags & IRQF_DISABLED) ? '+' : ' ', -			action->name); -		action = action->next; -		for (;;) { -			for (; action; action = action->next) { -				seq_printf(p, ",%s %s", -					(action->flags & IRQF_DISABLED) ? " +" : "", -					action->name); -			} -			if (!sbusl) -				break; -			k++; -			if (k < 4) { -				action = sbus_actions[(j << 5) + (sbusl << 2) + k].action; -			} else { -				j++; -				if (j == nsbi) -					break; -				k = 0; -				action = sbus_actions[(j << 5) + (sbusl << 2)].action; -			} -		} -		seq_putc(p, '\n'); -	} -out_unlock: -	spin_unlock_irqrestore(&irq_action_lock, flags); -	return 0; -} +	bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff; +	bw_clear_intr_mask(sbusl, bus_mask); -void sun4d_free_irq(unsigned int irq, void *dev_id) -{ -	struct irqaction *action, **actionp; -	struct irqaction *tmp = NULL; -	unsigned long flags; +	sbil = (sbusl << 2); +	/* Loop for each pending SBI */ +	for (sbino = 0; bus_mask; sbino++) { +		unsigned int idx, mask; -	spin_lock_irqsave(&irq_action_lock, flags); -	if (irq < 15) -		actionp = irq + irq_action; -	else -		actionp = &(sbus_actions[irq - (1 << 5)].action); -	action = *actionp; -	if (!action) { -		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; -			tmp = action; -		} -		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. +		bus_mask >>= 1; +		if (!(bus_mask & 1)) +			continue; +		/* XXX This seems to ACK the irq twice.  acquire_sbi() +		 * XXX uses swap, therefore this writes 0xf << sbil, +		 * XXX then later release_sbi() will write the individual +		 * XXX bits which were set again.  		 */ -		printk(KERN_ERR "Attempt to free statically allocated IRQ%d (%s)\n", -		       irq, action->name); -		goto out_unlock; -	} - -	if (tmp) -		tmp->next = action->next; -	else -		*actionp = action->next; - -	spin_unlock_irqrestore(&irq_action_lock, flags); +		mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil); +		mask &= (0xf << sbil); -	synchronize_irq(irq); +		/* Loop for each pending SBI slot */ +		idx = 0; +		slot = (1 << sbil); +		while (mask != 0) { +			unsigned int pil; +			struct irq_bucket *p; -	spin_lock_irqsave(&irq_action_lock, flags); +			idx++; +			slot <<= 1; +			if (!(mask & slot)) +				continue; -	kfree(action); +			mask &= ~slot; +			pil = sun4d_encode_irq(sbino, sbil, idx); -	if (!(*actionp)) -		__disable_irq(irq); +			p = irq_map[pil]; +			while (p) { +				struct irq_bucket *next; -out_unlock: -	spin_unlock_irqrestore(&irq_action_lock, flags); +				next = p->next; +				generic_handle_irq(p->irq); +				p = next; +			} +			release_sbi(SBI2DEVID(sbino), slot); +		} +	}  }  void sun4d_handler_irq(int pil, struct pt_regs *regs)  {  	struct pt_regs *old_regs; -	struct irqaction *action; -	int cpu = smp_processor_id();  	/* SBUS IRQ level (1 - 7) */  	int sbusl = pil_to_sbus[pil]; @@ -239,158 +157,85 @@ void sun4d_handler_irq(int pil, struct pt_regs *regs)  	old_regs = set_irq_regs(regs);  	irq_enter(); -	kstat_cpu(cpu).irqs[pil]++; -	if (!sbusl) { -		action = *(pil + irq_action); -		if (!action) -			unexpected_irq(pil, NULL, regs); -		do { -			action->handler(pil, action->dev_id); -			action = action->next; -		} while (action); -	} else { -		int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff; -		int sbino; -		struct sbus_action *actionp; -		unsigned mask, slot; -		int sbil = (sbusl << 2); - -		bw_clear_intr_mask(sbusl, bus_mask); +	if (sbusl == 0) { +		/* cpu interrupt */ +		struct irq_bucket *p; -		/* Loop for each pending SBI */ -		for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1) -			if (bus_mask & 1) { -				mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil); -				mask &= (0xf << sbil); -				actionp = sbus_actions + (sbino << 5) + (sbil); -				/* Loop for each pending SBI slot */ -				for (slot = (1 << sbil); mask; slot <<= 1, actionp++) -					if (mask & slot) { -						mask &= ~slot; -						action = actionp->action; +		p = irq_map[pil]; +		while (p) { +			struct irq_bucket *next; -						if (!action) -							unexpected_irq(pil, NULL, regs); -						do { -							action->handler(pil, action->dev_id); -							action = action->next; -						} while (action); -						release_sbi(SBI2DEVID(sbino), slot); -					} -			} +			next = p->next; +			generic_handle_irq(p->irq); +			p = next; +		} +	} else { +		/* SBUS interrupt */ +		sun4d_sbus_handler_irq(sbusl);  	}  	irq_exit();  	set_irq_regs(old_regs);  } -int sun4d_request_irq(unsigned int irq, -		irq_handler_t handler, -		unsigned long irqflags, const char *devname, void *dev_id) + +static void sun4d_mask_irq(struct irq_data *data)  { -	struct irqaction *action, *tmp = NULL, **actionp; +	struct sun4d_handler_data *handler_data = data->handler_data; +	unsigned int real_irq; +#ifdef CONFIG_SMP +	int cpuid = handler_data->cpuid;  	unsigned long flags; -	int ret; - -	if (irq > 14 && irq < (1 << 5)) { -		ret = -EINVAL; -		goto out; -	} - -	if (!handler) { -		ret = -EINVAL; -		goto out; -	} - -	spin_lock_irqsave(&irq_action_lock, flags); - -	if (irq >= (1 << 5)) -		actionp = &(sbus_actions[irq - (1 << 5)].action); -	else -		actionp = irq + irq_action; -	action = *actionp; - -	if (action) { -		if ((action->flags & IRQF_SHARED) && (irqflags & IRQF_SHARED)) { -			for (tmp = action; tmp->next; tmp = tmp->next) -				/* find last entry - tmp used below */; -		} else { -			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; -		} -		action = NULL;		/* Or else! */ -	} - -	/* 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; - -	if (tmp) -		tmp->next = action; -	else -		*actionp = action; - -	__enable_irq(irq); - -	ret = 0; -out_unlock: -	spin_unlock_irqrestore(&irq_action_lock, flags); -out: -	return ret; +#endif +	real_irq = handler_data->real_irq; +#ifdef CONFIG_SMP +	spin_lock_irqsave(&sun4d_imsk_lock, flags); +	cc_set_imsk_other(cpuid, cc_get_imsk_other(cpuid) | (1 << real_irq)); +	spin_unlock_irqrestore(&sun4d_imsk_lock, flags); +#else +	cc_set_imsk(cc_get_imsk() | (1 << real_irq)); +#endif  } -static void sun4d_disable_irq(unsigned int irq) +static void sun4d_unmask_irq(struct irq_data *data)  { -	int tid = board_to_cpu[(irq >> 5) - 1]; +	struct sun4d_handler_data *handler_data = data->handler_data; +	unsigned int real_irq; +#ifdef CONFIG_SMP +	int cpuid = handler_data->cpuid;  	unsigned long flags; +#endif +	real_irq = handler_data->real_irq; -	if (irq < NR_IRQS) -		return; - +#ifdef CONFIG_SMP  	spin_lock_irqsave(&sun4d_imsk_lock, flags); -	cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7])); +	cc_set_imsk_other(cpuid, cc_get_imsk_other(cpuid) | ~(1 << real_irq));  	spin_unlock_irqrestore(&sun4d_imsk_lock, flags); +#else +	cc_set_imsk(cc_get_imsk() | ~(1 << real_irq)); +#endif  } -static void sun4d_enable_irq(unsigned int irq) +static unsigned int sun4d_startup_irq(struct irq_data *data)  { -	int tid = board_to_cpu[(irq >> 5) - 1]; -	unsigned long flags; - -	if (irq < NR_IRQS) -		return; +	irq_link(data->irq); +	sun4d_unmask_irq(data); +	return 0; +} -	spin_lock_irqsave(&sun4d_imsk_lock, flags); -	cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7])); -	spin_unlock_irqrestore(&sun4d_imsk_lock, flags); +static void sun4d_shutdown_irq(struct irq_data *data) +{ +	sun4d_mask_irq(data); +	irq_unlink(data->irq);  } +struct irq_chip sun4d_irq = { +	.name		= "sun4d", +	.irq_startup	= sun4d_startup_irq, +	.irq_shutdown	= sun4d_shutdown_irq, +	.irq_unmask	= sun4d_unmask_irq, +	.irq_mask	= sun4d_mask_irq, +}; +  #ifdef CONFIG_SMP  static void sun4d_set_cpu_int(int cpu, int level)  { @@ -447,15 +292,16 @@ static void __init sun4d_load_profile_irqs(void)  unsigned int sun4d_build_device_irq(struct platform_device *op,                                      unsigned int real_irq)  { -	static int pil_to_sbus[] = { -		0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0, -	};  	struct device_node *dp = op->dev.of_node;  	struct device_node *io_unit, *sbi = dp->parent;  	const struct linux_prom_registers *regs; +	struct sun4d_handler_data *handler_data; +	unsigned int pil; +	unsigned int irq;  	int board, slot;  	int sbusl; +	irq = 0;  	while (sbi) {  		if (!strcmp(sbi->name, "sbi"))  			break; @@ -488,7 +334,28 @@ unsigned int sun4d_build_device_irq(struct platform_device *op,  	sbusl = pil_to_sbus[real_irq];  	if (sbusl) -		return (((board + 1) << 5) + (sbusl << 2) + slot); +		pil = sun4d_encode_irq(board, sbusl, slot); +	else +		pil = real_irq; + +	irq = irq_alloc(real_irq, pil); +	if (irq == 0) +		goto err_out; + +	handler_data = irq_get_handler_data(irq); +	if (unlikely(handler_data)) +		goto err_out; + +	handler_data = kzalloc(sizeof(struct sun4d_handler_data), GFP_ATOMIC); +	if (unlikely(!handler_data)) { +		prom_printf("IRQ: kzalloc(sun4d_handler_data) failed.\n"); +		prom_halt(); +	} +	handler_data->cpuid    = board_to_cpu[board]; +	handler_data->real_irq = real_irq; +	irq_set_chip_and_handler_name(irq, &sun4d_irq, +	                              handle_level_irq, "level"); +	irq_set_handler_data(irq, handler_data);  err_out:  	return real_irq; @@ -522,6 +389,7 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn)  {  	struct device_node *dp;  	struct resource res; +	unsigned int irq;  	const u32 *reg;  	int err; @@ -556,9 +424,8 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn)  	master_l10_counter = &sun4d_timers->l10_cur_count; -	err = request_irq(TIMER_IRQ, counter_fn, -			  (IRQF_DISABLED | SA_STATIC_ALLOC), -			  "timer", NULL); +	irq = sun4d_build_device_irq(NULL, SUN4D_TIMER_IRQ); +	err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL);  	if (err) {  		prom_printf("sun4d_init_timers: request_irq() failed with %d\n",  		             err); @@ -576,15 +443,6 @@ void __init sun4d_init_sbi_irq(void)  #ifdef CONFIG_SMP  	target_cpu = boot_cpu_id;  #endif - -	nsbi = 0; -	for_each_node_by_name(dp, "sbi") -		nsbi++; -	sbus_actions = kzalloc(nsbi * 8 * 4 * sizeof(struct sbus_action), GFP_ATOMIC); -	if (!sbus_actions) { -		prom_printf("SUN4D: Cannot allocate sbus_actions, halting.\n"); -		prom_halt(); -	}  	for_each_node_by_name(dp, "sbi") {  		int devid = of_getintprop_default(dp, "device-id", 0);  		int board = of_getintprop_default(dp, "board#", 0); @@ -607,12 +465,10 @@ void __init sun4d_init_IRQ(void)  {  	local_irq_disable(); -	BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM); -	BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM);  	BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM);  	BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM); -	sparc_irq_config.init_timers = sun4d_init_timers; +	sparc_irq_config.init_timers      = sun4d_init_timers;  	sparc_irq_config.build_device_irq = sun4d_build_device_irq;  #ifdef CONFIG_SMP  |