diff options
Diffstat (limited to 'arch/powerpc/sysdev/xilinx_intc.c')
| -rw-r--r-- | arch/powerpc/sysdev/xilinx_intc.c | 81 | 
1 files changed, 63 insertions, 18 deletions
diff --git a/arch/powerpc/sysdev/xilinx_intc.c b/arch/powerpc/sysdev/xilinx_intc.c index c658b413c9b..3ee1fd37bbf 100644 --- a/arch/powerpc/sysdev/xilinx_intc.c +++ b/arch/powerpc/sysdev/xilinx_intc.c @@ -25,6 +25,7 @@  #include <linux/of.h>  #include <asm/io.h>  #include <asm/processor.h> +#include <asm/i8259.h>  #include <asm/irq.h>  /* @@ -191,20 +192,14 @@ struct irq_host * __init  xilinx_intc_init(struct device_node *np)  {  	struct irq_host * irq; -	struct resource res;  	void * regs; -	int rc;  	/* Find and map the intc registers */ -	rc = of_address_to_resource(np, 0, &res); -	if (rc) { -		printk(KERN_ERR __FILE__ ": of_address_to_resource() failed\n"); +	regs = of_iomap(np, 0); +	if (!regs) { +		pr_err("xilinx_intc: could not map registers\n");  		return NULL;  	} -	regs = ioremap(res.start, 32); - -	printk(KERN_INFO "Xilinx intc at 0x%08llx mapped to 0x%p\n", -		(unsigned long long) res.start, regs);  	/* Setup interrupt controller */  	out_be32(regs + XINTC_IER, 0); /* disable all irqs */ @@ -217,6 +212,7 @@ xilinx_intc_init(struct device_node *np)  	if (!irq)  		panic(__FILE__ ": Cannot allocate IRQ host\n");  	irq->host_data = regs; +  	return irq;  } @@ -227,23 +223,70 @@ int xilinx_intc_get_irq(void)  	return irq_linear_revmap(master_irqhost, in_be32(regs + XINTC_IVR));  } +#if defined(CONFIG_PPC_I8259) +/* + * Support code for cascading to 8259 interrupt controllers + */ +static void xilinx_i8259_cascade(unsigned int irq, struct irq_desc *desc) +{ +	unsigned int cascade_irq = i8259_irq(); +	if (cascade_irq) +		generic_handle_irq(cascade_irq); + +	/* Let xilinx_intc end the interrupt */ +	desc->chip->ack(irq); +	desc->chip->unmask(irq); +} + +static void __init xilinx_i8259_setup_cascade(void) +{ +	struct device_node *cascade_node; +	int cascade_irq; + +	/* Initialize i8259 controller */ +	cascade_node = of_find_compatible_node(NULL, NULL, "chrp,iic"); +	if (!cascade_node) +		return; + +	cascade_irq = irq_of_parse_and_map(cascade_node, 0); +	if (!cascade_irq) { +		pr_err("virtex_ml510: Failed to map cascade interrupt\n"); +		goto out; +	} + +	i8259_init(cascade_node, 0); +	set_irq_chained_handler(cascade_irq, xilinx_i8259_cascade); + +	/* Program irq 7 (usb/audio), 14/15 (ide) to level sensitive */ +	/* This looks like a dirty hack to me --gcl */ +	outb(0xc0, 0x4d0); +	outb(0xc0, 0x4d1); + + out: +	of_node_put(cascade_node); +} +#else +static inline void xilinx_i8259_setup_cascade(void) { return; } +#endif /* defined(CONFIG_PPC_I8259) */ + +static struct of_device_id xilinx_intc_match[] __initconst = { +	{ .compatible = "xlnx,opb-intc-1.00.c", }, +	{ .compatible = "xlnx,xps-intc-1.00.a", }, +	{} +}; + +/* + * Initialize master Xilinx interrupt controller + */  void __init xilinx_intc_init_tree(void)  {  	struct device_node *np;  	/* find top level interrupt controller */ -	for_each_compatible_node(np, NULL, "xlnx,opb-intc-1.00.c") { +	for_each_matching_node(np, xilinx_intc_match) {  		if (!of_get_property(np, "interrupts", NULL))  			break;  	} -	if (!np) { -		for_each_compatible_node(np, NULL, "xlnx,xps-intc-1.00.a") { -			if (!of_get_property(np, "interrupts", NULL)) -				break; -		} -	} - -	/* xilinx interrupt controller needs to be top level */  	BUG_ON(!np);  	master_irqhost = xilinx_intc_init(np); @@ -251,4 +294,6 @@ void __init xilinx_intc_init_tree(void)  	irq_set_default_host(master_irqhost);  	of_node_put(np); + +	xilinx_i8259_setup_cascade();  }  |