diff options
Diffstat (limited to 'arch/microblaze/kernel/intc.c')
| -rw-r--r-- | arch/microblaze/kernel/intc.c | 61 | 
1 files changed, 37 insertions, 24 deletions
diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c index 44b177e2ab1..ad120672cee 100644 --- a/arch/microblaze/kernel/intc.c +++ b/arch/microblaze/kernel/intc.c @@ -9,6 +9,7 @@   */  #include <linux/init.h> +#include <linux/irqdomain.h>  #include <linux/irq.h>  #include <asm/page.h>  #include <linux/io.h> @@ -25,8 +26,6 @@ static unsigned int intc_baseaddr;  #define INTC_BASE	intc_baseaddr  #endif -unsigned int nr_irq; -  /* No one else should require these constants, so define them locally here. */  #define ISR 0x00			/* Interrupt Status Register */  #define IPR 0x04			/* Interrupt Pending Register */ @@ -84,24 +83,45 @@ static struct irq_chip intc_dev = {  	.irq_mask_ack = intc_mask_ack,  }; -unsigned int get_irq(struct pt_regs *regs) +static struct irq_domain *root_domain; + +unsigned int get_irq(void)  { -	int irq; +	unsigned int hwirq, irq = -1; -	/* -	 * NOTE: This function is the one that needs to be improved in -	 * order to handle multiple interrupt controllers. It currently -	 * is hardcoded to check for interrupts only on the first INTC. -	 */ -	irq = in_be32(INTC_BASE + IVR) + NO_IRQ_OFFSET; -	pr_debug("get_irq: %d\n", irq); +	hwirq = in_be32(INTC_BASE + IVR); +	if (hwirq != -1U) +		irq = irq_find_mapping(root_domain, hwirq); + +	pr_debug("get_irq: hwirq=%d, irq=%d\n", hwirq, irq);  	return irq;  } +int xintc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) +{ +	u32 intr_mask = (u32)d->host_data; + +	if (intr_mask & (1 << hw)) { +		irq_set_chip_and_handler_name(irq, &intc_dev, +						handle_edge_irq, "edge"); +		irq_clear_status_flags(irq, IRQ_LEVEL); +	} else { +		irq_set_chip_and_handler_name(irq, &intc_dev, +						handle_level_irq, "level"); +		irq_set_status_flags(irq, IRQ_LEVEL); +	} +	return 0; +} + +static const struct irq_domain_ops xintc_irq_domain_ops = { +	.xlate = irq_domain_xlate_onetwocell, +	.map = xintc_map, +}; +  void __init init_IRQ(void)  { -	u32 i, intr_mask; +	u32 nr_irq, intr_mask;  	struct device_node *intc = NULL;  #ifdef CONFIG_SELFMOD_INTC  	unsigned int intc_baseaddr = 0; @@ -146,16 +166,9 @@ void __init init_IRQ(void)  	/* Turn on the Master Enable. */  	out_be32(intc_baseaddr + MER, MER_HIE | MER_ME); -	for (i = IRQ_OFFSET; i < (nr_irq + IRQ_OFFSET); ++i) { -		if (intr_mask & (0x00000001 << (i - IRQ_OFFSET))) { -			irq_set_chip_and_handler_name(i, &intc_dev, -				handle_edge_irq, "edge"); -			irq_clear_status_flags(i, IRQ_LEVEL); -		} else { -			irq_set_chip_and_handler_name(i, &intc_dev, -				handle_level_irq, "level"); -			irq_set_status_flags(i, IRQ_LEVEL); -		} -		irq_get_irq_data(i)->hwirq = i - IRQ_OFFSET; -	} +	/* Yeah, okay, casting the intr_mask to a void* is butt-ugly, but I'm +	 * lazy and Michal can clean it up to something nicer when he tests +	 * and commits this patch.  ~~gcl */ +	root_domain = irq_domain_add_linear(intc, nr_irq, &xintc_irq_domain_ops, +							(void *)intr_mask);  }  |