120 lines
3.4 KiB
Diff
120 lines
3.4 KiB
Diff
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();
|