From a729672f117df3602b6d3171d8ab7a84bf53b053 Mon Sep 17 00:00:00 2001
From: Daniel Hellstrom <daniel@gaisler.com>
Date: Thu, 16 Sep 2010 11:12:41 +0200
Subject: [PATCH] SPARC/LEON: added support for Extended IRQ controller, partial patches are already in git tree.

Signed-off-by: Daniel Hellstrom <daniel@gaisler.com>
---
 arch/sparc/include/asm/irq_32.h |    4 ++++
 arch/sparc/kernel/irq_32.c      |   32 ++++++++++++++++++++++++++------
 arch/sparc/kernel/leon_kernel.c |    8 +++++++-
 3 files changed, 37 insertions(+), 7 deletions(-)

--- a/arch/sparc/include/asm/irq_32.h
+++ b/arch/sparc/include/asm/irq_32.h
@@ -6,7 +6,11 @@
 #ifndef _SPARC_IRQ_H
 #define _SPARC_IRQ_H
 
+#ifdef CONFIG_SPARC_LEON
+#define NR_IRQS    32
+#else
 #define NR_IRQS    16
+#endif
 
 #include <linux/interrupt.h>
 
--- a/arch/sparc/kernel/irq_32.c
+++ b/arch/sparc/kernel/irq_32.c
@@ -110,6 +110,11 @@ EXPORT_SYMBOL(__raw_local_irq_save);
 EXPORT_SYMBOL(raw_local_irq_enable);
 EXPORT_SYMBOL(raw_local_irq_restore);
 
+#ifdef CONFIG_SPARC_LEON
+extern unsigned int sparc_leon_eirq;
+extern int sparc_leon_eirq_get(int eirq, int cpu);
+#endif
+
 /*
  * Dave Redman (djhr@tadpole.co.uk)
  *
@@ -222,10 +227,11 @@ void free_irq(unsigned int irq, void *de
 		return;
 	}
 	cpu_irq = irq & (NR_IRQS - 1);
-        if (cpu_irq > 14) {  /* 14 irq levels on the sparc */
-                printk("Trying to free bogus IRQ %d\n", irq);
-                return;
-        }
+	/* 14 irq levels on the sparc, however some LEON systems have 31 IRQs */
+	if ((cpu_irq == 15) || (cpu_irq >= NR_IRQS)) {  
+		printk("Trying to free bogus IRQ %d\n", irq);
+		return;
+	}
 
 	spin_lock_irqsave(&irq_action_lock, flags);
 
@@ -303,7 +309,14 @@ void unexpected_irq(int irq, void *dev_i
         int i;
 	struct irqaction * action;
 	unsigned int cpu_irq;
-	
+
+#ifdef CONFIG_SPARC_LEON
+	/* LEON Extended IRQ requires one extra IRQ Number fetch stage */
+	if ( sparc_leon_eirq == irq ) {
+		irq = sparc_leon_eirq_get(irq, smp_processor_id());
+	}
+#endif
+
 	cpu_irq = irq & (NR_IRQS - 1);
 	action = sparc_irq[cpu_irq].action;
 
@@ -330,6 +343,13 @@ void handler_irq(int irq, struct pt_regs
 	extern void smp4m_irq_rotate(int cpu);
 #endif
 
+#ifdef CONFIG_SPARC_LEON
+	/* LEON Extended IRQ requires one extra IRQ Number fetch stage */
+	if ( sparc_leon_eirq == irq ) {
+		irq = sparc_leon_eirq_get(irq, cpu);
+	}
+#endif
+
 	old_regs = set_irq_regs(regs);
 	irq_enter();
 	disable_pil_irq(irq);
@@ -526,7 +546,7 @@ int request_irq(unsigned int irq,
 		return sun4d_request_irq(irq, handler, irqflags, devname, dev_id);
 	}
 	cpu_irq = irq & (NR_IRQS - 1);
-	if(cpu_irq > 14) {
+	if(cpu_irq == 15) {
 		ret = -EINVAL;
 		goto out;
 	}
--- a/arch/sparc/kernel/leon_kernel.c
+++ b/arch/sparc/kernel/leon_kernel.c
@@ -104,7 +104,7 @@ static void leon_disable_irq(unsigned in
 
 void __init leon_init_timers(irq_handler_t counter_fn)
 {
-	int irq;
+	int irq, eirq;
 	struct device_node *rootnp, *np;
 	struct property *pp;
 	int len;
@@ -153,6 +153,12 @@ void __init leon_init_timers(irq_handler
 		LEON3_BYPASS_STORE_PA(&leon3_gptimer_regs->e[1].ctrl, 0);
 # endif
 
+		LEON3_BYPASS_STORE_PA(&(leon3_irqctrl_regs->mask[0]), 0);
+		eirq = (LEON3_BYPASS_LOAD_PA(&leon3_irqctrl_regs->mpstatus) >> 16) & 0xf;
+		if ( eirq != 0 ) {
+			/* Extended IRQ controller available */
+			sparc_leon_eirq_register(eirq);
+		}
 	} else {
 		printk(KERN_ERR "No Timer/irqctrl found\n");
 		BUG();
