@@ -25,6 +25,9 @@ config SPARC
select HAVE_DMA_ATTRS
select HAVE_DMA_API_DEBUG
select HAVE_ARCH_JUMP_LABEL
+ select HAVE_GENERIC_HARDIRQS
+ select GENERIC_HARDIRQS_NO_DEPRECATED
+
config SPARC32
def_bool !64BIT
@@ -50,8 +53,6 @@ config SPARC64
select RTC_DRV_STARFIRE
select HAVE_PERF_EVENTS
select PERF_USE_VMALLOC
- select HAVE_GENERIC_HARDIRQS
- select GENERIC_HARDIRQS_NO_DEPRECATED
config ARCH_DEFCONFIG
string
@@ -6,7 +6,13 @@
#ifndef _SPARC_IRQ_H
#define _SPARC_IRQ_H
-#define NR_IRQS 16
+/*
+ * Allocated number of logical irq numbers.
+ * sun4d boxes (ss2000e) should be OK with ~32.
+ * Be on the safe side and make room for 64
+ */
+
+#define NR_IRQS 64
#include <linux/interrupt.h>
@@ -15,11 +15,6 @@
#include <linux/irqflags.h>
-static inline unsigned int probe_irq_mask(unsigned long val)
-{
- return 0;
-}
-
/*
* Sparc (general) CPU types
*/
@@ -71,10 +71,6 @@ obj-$(CONFIG_SPARC64) += pcr.o
obj-$(CONFIG_SPARC64) += nmi.o
obj-$(CONFIG_SPARC64_SMP) += cpumap.o
-# sparc32 do not use GENERIC_HARDIRQS but uses the generic devres implementation
-obj-$(CONFIG_SPARC32) += devres.o
-devres-y := ../../../kernel/irq/devres.o
-
obj-y += dma.o
obj-$(CONFIG_SPARC32_PCI) += pcic.o
@@ -30,10 +30,15 @@ extern struct sun4m_irq_global __iomem *sun4m_irq_global;
*/
struct sparc_irq_config {
void (*init_timers)(irq_handler_t);
- int (*build_device_irq)(struct platform_device *op, int interrupt);
+ unsigned int (*build_device_irq)(struct platform_device *op,
+ unsigned int interrupt);
+ unsigned int profile_irq;
};
extern struct sparc_irq_config sparc_irq_config;
+unsigned int irq_alloc(unsigned int real_irq, unsigned int pil);
+void irq_link(unsigned int irq);
+void handler_irq(unsigned int pil, struct pt_regs *regs);
/* Dave Redman (djhr@tadpole.co.uk)
* changed these to function pointers.. it saves cycles and will allow
@@ -43,33 +48,9 @@ extern struct sparc_irq_config sparc_irq_config;
* Changed these to btfixup entities... It saves cycles :)
*/
-BTFIXUPDEF_CALL(void, disable_irq, unsigned int)
-BTFIXUPDEF_CALL(void, enable_irq, unsigned int)
-BTFIXUPDEF_CALL(void, disable_pil_irq, unsigned int)
-BTFIXUPDEF_CALL(void, enable_pil_irq, unsigned int)
BTFIXUPDEF_CALL(void, clear_clock_irq, void)
BTFIXUPDEF_CALL(void, load_profile_irq, int, unsigned int)
-static inline void __disable_irq(unsigned int irq)
-{
- BTFIXUP_CALL(disable_irq)(irq);
-}
-
-static inline void __enable_irq(unsigned int irq)
-{
- BTFIXUP_CALL(enable_irq)(irq);
-}
-
-static inline void disable_pil_irq(unsigned int irq)
-{
- BTFIXUP_CALL(disable_pil_irq)(irq);
-}
-
-static inline void enable_pil_irq(unsigned int irq)
-{
- BTFIXUP_CALL(enable_pil_irq)(irq);
-}
-
static inline void clear_clock_irq(void)
{
BTFIXUP_CALL(clear_clock_irq)();
@@ -14,6 +14,7 @@
#include <linux/kernel_stat.h>
#include <linux/seq_file.h>
+#include <asm/cpudata.h>
#include <asm/pcic.h>
#include <asm/leon.h>
@@ -106,278 +107,154 @@ static void irq_panic(irq_handler_t handler)
* directed CPU interrupts using the existing enable/disable irq code
* with tweaks.
*
+ * Sun4d complicates things even further. IRQ numbers are arbitrary
+ * 32-bit values in that case. Since this is similar to sparc64,
+ * we adopt a virtual IRQ numbering scheme as is done there.
+ * Virutal interrupt numbers are allocated by build_irq(). So NR_IRQS
+ * just becomes a limit of how many interrupt sources we can handle in
+ * a single system. Even fully loaded SS2000 machines top off at
+ * about 32 interrupt sources or so, therefore a NR_IRQS value of 64
+ * is more than enough.
+ *
+ * We keep a table of per-PIL enable interrupts. These get wired
+ * up via the irq_chip->startup() method which gets invoked by
+ * the generic IRQ layer during request_irq().
*/
+struct irq_bucket {
+ struct irq_bucket *next;
+ unsigned int irq;
+ unsigned int pil;
+};
+static struct irq_bucket irq_table[NR_IRQS];
+static struct irq_bucket *pil_buckets[16];
/*
- * Dave Redman (djhr@tadpole.co.uk)
- *
- * There used to be extern calls and hard coded values here.. very sucky!
- * instead, because some of the devices attach very early, I do something
- * equally sucky but at least we'll never try to free statically allocated
- * space or call kmalloc before kmalloc_init :(.
- *
- * In fact it's the timer10 that attaches first.. then timer14
- * then kmalloc_init is called.. then the tty interrupts attach.
- * hmmm....
- *
+ * Allocate a new irq from the irq_table
*/
-#define MAX_STATIC_ALLOC 4
-struct irqaction static_irqaction[MAX_STATIC_ALLOC];
-int static_irq_count;
+unsigned int irq_alloc(unsigned int real_irq, unsigned int pil)
+{
+ unsigned int i;
-static struct {
- struct irqaction *action;
- int flags;
-} sparc_irq[NR_IRQS];
-#define SPARC_IRQ_INPROGRESS 1
+ for (i = 1; i < NR_IRQS; i++) {
+ if (irq_table[i].irq == real_irq && irq_table[i].irq == pil)
+ return i;
+ }
+
+ for (i = 1; i < NR_IRQS; i++) {
+ if (!irq_table[i].irq)
+ break;
+ }
+
+ if (i >= NR_IRQS) {
+ printk(KERN_ERR "IRQ: Out of virtual IRQs.\n");
+ return 0;
+ }
+ irq_table[i].irq = i;
+ irq_table[i].pil = pil;
+
+ return i;
+}
-/* Used to protect the IRQ action lists */
-DEFINE_SPINLOCK(irq_action_lock);
+void irq_link(unsigned int irq)
+{
+ struct irq_bucket *p;
+ unsigned int pil;
+
+ BUG_ON(irq >= NR_IRQS);
+
+ p = &irq_table[irq];
+ pil = p->pil;
+ BUG_ON(pil > 15);
+ p->next = pil_buckets[pil];
+ pil_buckets[pil] = p;
+}
int show_interrupts(struct seq_file *p, void *v)
{
- int i = *(loff_t *)v;
+ int i = *(loff_t *) v, j;
struct irqaction *action;
unsigned long flags;
-#ifdef CONFIG_SMP
- int j;
-#endif
- if (sparc_cpu_model == sun4d)
- return show_sun4d_interrupts(p, v);
+ if (i == 0) {
+ seq_printf(p, " ");
+ for_each_online_cpu(j)
+ seq_printf(p, "CPU%d ",j);
+ seq_putc(p, '\n');
+ }
- spin_lock_irqsave(&irq_action_lock, flags);
if (i < NR_IRQS) {
- action = sparc_irq[i].action;
+ raw_spin_lock_irqsave(&irq_desc[i].lock, flags);
+ action = irq_desc[i].action;
if (!action)
- goto out_unlock;
- seq_printf(p, "%3d: ", i);
+ goto skip;
+ seq_printf(p, "%3d: ",i);
#ifndef CONFIG_SMP
seq_printf(p, "%10u ", kstat_irqs(i));
#else
- for_each_online_cpu(j) {
- seq_printf(p, "%10u ",
- kstat_cpu(j).irqs[i]);
- }
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", kstat_irqs_cpu(i, j));
#endif
- seq_printf(p, " %c %s",
- (action->flags & IRQF_DISABLED) ? '+' : ' ',
- action->name);
- for (action = action->next; action; action = action->next) {
- seq_printf(p, ",%s %s",
- (action->flags & IRQF_DISABLED) ? " +" : "",
- action->name);
- }
+ seq_printf(p, " %9s", irq_desc[i].irq_data.chip->name);
+ seq_printf(p, " %s", action->name);
+
+ for (action=action->next; action; action = action->next)
+ seq_printf(p, ", %s", action->name);
+
seq_putc(p, '\n');
+skip:
+ raw_spin_unlock_irqrestore(&irq_desc[i].lock, flags);
+ } else if (i == NR_IRQS) {
+ seq_printf(p, "NMI: ");
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", cpu_data(j).counter);
+ seq_printf(p, " Non-maskable interrupts\n");
}
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
return 0;
}
-void free_irq(unsigned int irq, void *dev_id)
+void handler_irq(unsigned int pil, struct pt_regs *regs)
{
- struct irqaction *action;
- struct irqaction **actionp;
- unsigned long flags;
- unsigned int cpu_irq;
-
- if (sparc_cpu_model == sun4d) {
- sun4d_free_irq(irq, dev_id);
- return;
- }
- cpu_irq = irq & (NR_IRQS - 1);
- if (cpu_irq > 14) { /* 14 irq levels on the sparc */
- printk(KERN_ERR "Trying to free bogus IRQ %d\n", irq);
- return;
- }
-
- spin_lock_irqsave(&irq_action_lock, flags);
-
- actionp = &sparc_irq[cpu_irq].action;
- action = *actionp;
-
- if (!action->handler) {
- 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;
- actionp = &action->next;
- }
- 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.
- */
- printk(KERN_ERR "Attempt to free statically allocated IRQ%d (%s)\n",
- irq, action->name);
- goto out_unlock;
- }
-
- *actionp = action->next;
-
- spin_unlock_irqrestore(&irq_action_lock, flags);
-
- synchronize_irq(irq);
-
- spin_lock_irqsave(&irq_action_lock, flags);
-
- kfree(action);
+ struct pt_regs *old_regs;
+ struct irq_bucket *p;
- if (!sparc_irq[cpu_irq].action)
- __disable_irq(irq);
+ BUG_ON(pil > 15);
+ old_regs = set_irq_regs(regs);
+ irq_enter();
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
-}
-EXPORT_SYMBOL(free_irq);
+ p = pil_buckets[pil];
+ while (p) {
+ struct irq_bucket *next = p->next;
+ struct irq_desc *desc;
+ unsigned int irq;
-/*
- * This is called when we want to synchronize with
- * interrupts. We may for example tell a device to
- * stop sending interrupts: but to make sure there
- * are no interrupts that are executing on another
- * CPU we need to call this function.
- */
-#ifdef CONFIG_SMP
-void synchronize_irq(unsigned int irq)
-{
- unsigned int cpu_irq;
+ irq = p->irq;
+ desc = irq_to_desc(irq);
- cpu_irq = irq & (NR_IRQS - 1);
- while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS)
- cpu_relax();
-}
-EXPORT_SYMBOL(synchronize_irq);
-#endif /* SMP */
+ if (!(desc->status & IRQ_DISABLED))
+ desc->handle_irq(irq, desc);
-void unexpected_irq(int irq, void *dev_id, struct pt_regs *regs)
-{
- int i;
- struct irqaction *action;
- unsigned int cpu_irq;
-
- cpu_irq = irq & (NR_IRQS - 1);
- action = sparc_irq[cpu_irq].action;
-
- printk(KERN_ERR "IO device interrupt, irq = %d\n", irq);
- printk(KERN_ERR "PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc,
- regs->npc, regs->u_regs[14]);
- if (action) {
- printk(KERN_ERR "Expecting: ");
- for (i = 0; i < 16; i++)
- if (action->handler)
- printk(KERN_CONT "[%s:%d:0x%x] ", action->name,
- i, (unsigned int)action->handler);
+ p = next;
}
- printk(KERN_ERR "AIEEE\n");
- panic("bogus interrupt received");
-}
-
-void handler_irq(int pil, struct pt_regs *regs)
-{
- struct pt_regs *old_regs;
- struct irqaction *action;
- int cpu = smp_processor_id();
-
- old_regs = set_irq_regs(regs);
- irq_enter();
- disable_pil_irq(pil);
-#ifdef CONFIG_SMP
- /* Only rotate on lower priority IRQs (scsi, ethernet, etc.). */
- if ((sparc_cpu_model==sun4m) && (pil < 10))
- smp4m_irq_rotate(cpu);
-#endif
- action = sparc_irq[pil].action;
- sparc_irq[pil].flags |= SPARC_IRQ_INPROGRESS;
- kstat_cpu(cpu).irqs[pil]++;
- do {
- if (!action || !action->handler)
- unexpected_irq(pil, NULL, regs);
- action->handler(pil, action->dev_id);
- action = action->next;
- } while (action);
- sparc_irq[pil].flags &= ~SPARC_IRQ_INPROGRESS;
- enable_pil_irq(pil);
irq_exit();
set_irq_regs(old_regs);
}
#if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE)
-/*
- * Fast IRQs on the Sparc can only have one routine attached to them,
- * thus no sharing possible.
- */
-static int request_fast_irq(unsigned int irq,
- void (*handler)(void),
- unsigned long irqflags, const char *devname)
+static int request_floppy_irq(unsigned int irq,
+ irq_handler_t handler,
+ unsigned long irqflags, const char *devname)
{
- struct irqaction *action;
- unsigned long flags;
- unsigned int cpu_irq;
- int ret;
-#if defined CONFIG_SMP && !defined CONFIG_SPARC_LEON
- struct tt_entry *trap_table;
-#endif
- cpu_irq = irq & (NR_IRQS - 1);
- if (cpu_irq > 14) {
- ret = -EINVAL;
- goto out;
- }
- if (!handler) {
- ret = -EINVAL;
- goto out;
- }
-
- spin_lock_irqsave(&irq_action_lock, flags);
+ unsigned int cpu_irq = (irq & (NR_IRQS - 1));
+ int err;
- action = sparc_irq[cpu_irq].action;
- if (action) {
- if (action->flags & IRQF_SHARED)
- panic("Trying to register fast irq when already shared.\n");
- if (irqflags & IRQF_SHARED)
- panic("Trying to register fast irq as shared.\n");
-
- /* Anyway, someone already owns it so cannot be made fast. */
- printk(KERN_ERR "request_fast_irq: Trying to register yet already owned.\n");
- ret = -EBUSY;
- goto out_unlock;
- }
-
- /*
- * 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 "Fast IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",
- irq, devname);
- }
+ err = request_irq(irq, floppy_interrupt, irqflags, devname, NULL);
+ if (err)
+ return err;
- if (action == NULL)
- action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
- if (!action) {
- ret = -ENOMEM;
- goto out_unlock;
- }
+ floppy_irq_num = irq;
/* Dork with trap table if we get this far. */
#define INSTANTIATE(table) \
@@ -404,22 +281,16 @@ static int request_fast_irq(unsigned int irq,
* writing we have no CPU-neutral interface to fine-grained flushes.
*/
flush_cache_all();
+ return 0;
+}
- action->flags = irqflags;
- action->name = devname;
- action->dev_id = NULL;
- action->next = NULL;
-
- sparc_irq[cpu_irq].action = action;
-
- __enable_irq(irq);
-
- ret = 0;
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
-out:
- return ret;
+int sparc_floppy_request_irq(int irq, unsigned long flags,
+ irq_handler_t irq_handler)
+{
+ floppy_irq_handler = irq_handler;
+ return request_floppy_irq(irq, floppy_hardint, flags, "floppy");
}
+EXPORT_SYMBOL(sparc_floppy_request_irq);
/*
* These variables are used to access state from the assembler
@@ -467,127 +338,11 @@ void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs)
*/
}
-int sparc_floppy_request_irq(int irq, unsigned long flags,
- irq_handler_t irq_handler)
-{
- floppy_irq_handler = irq_handler;
- return request_fast_irq(irq, floppy_hardint, flags, "floppy");
-}
-EXPORT_SYMBOL(sparc_floppy_request_irq);
#endif
-int request_irq(unsigned int irq,
- irq_handler_t handler,
- unsigned long irqflags, const char *devname, void *dev_id)
-{
- struct irqaction *action, **actionp;
- unsigned long flags;
- unsigned int cpu_irq;
- int ret;
-
- if (sparc_cpu_model == sun4d)
- return sun4d_request_irq(irq, handler, irqflags, devname, dev_id);
-
- cpu_irq = irq & (NR_IRQS - 1);
- if (cpu_irq > 14) {
- ret = -EINVAL;
- goto out;
- }
- if (!handler) {
- ret = -EINVAL;
- goto out;
- }
-
- spin_lock_irqsave(&irq_action_lock, flags);
-
- actionp = &sparc_irq[cpu_irq].action;
- action = *actionp;
- if (action) {
- if (!(action->flags & IRQF_SHARED) || !(irqflags & IRQF_SHARED)) {
- 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;
- }
- for ( ; action; action = *actionp)
- actionp = &action->next;
- }
-
- /* 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;
-
- *actionp = action;
-
- __enable_irq(irq);
-
- ret = 0;
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
-out:
- return ret;
-}
-EXPORT_SYMBOL(request_irq);
-
-void disable_irq_nosync(unsigned int irq)
-{
- __disable_irq(irq);
-}
-EXPORT_SYMBOL(disable_irq_nosync);
-
-void disable_irq(unsigned int irq)
-{
- __disable_irq(irq);
-}
-EXPORT_SYMBOL(disable_irq);
-
-void enable_irq(unsigned int irq)
-{
- __enable_irq(irq);
-}
-EXPORT_SYMBOL(enable_irq);
-
-/*
- * We really don't need these at all on the Sparc. We only have
- * stubs here because they are exported to modules.
- */
-unsigned long probe_irq_on(void)
-{
- return 0;
-}
-EXPORT_SYMBOL(probe_irq_on);
-
-int probe_irq_off(unsigned long mask)
-{
- return 0;
-}
-EXPORT_SYMBOL(probe_irq_off);
-
-static int build_device_irq(struct platform_device *op, int interrupt)
+static unsigned int build_device_irq(struct platform_device *op,
+ unsigned int interrupt)
{
return interrupt;
}
@@ -604,6 +359,7 @@ void __init init_IRQ(void)
{
sparc_irq_config.init_timers = irq_panic;
sparc_irq_config.build_device_irq = build_device_irq;
+ sparc_irq_config.profile_irq = 14;
switch (sparc_cpu_model) {
case sun4c:
@@ -637,9 +393,3 @@ void __init init_IRQ(void)
btfixup();
}
-#ifdef CONFIG_PROC_FS
-void init_irq_proc(void)
-{
- /* For now, nothing... */
-}
-#endif /* CONFIG_PROC_FS */
@@ -800,55 +800,13 @@ void pcic_nmi(unsigned int pend, struct pt_regs *regs)
regs->npc += 4;
}
-static inline unsigned long get_irqmask(int irq_nr)
-{
- return 1 << irq_nr;
-}
-
-static void pcic_disable_irq(unsigned int irq_nr)
-{
- unsigned long mask, flags;
-
- mask = get_irqmask(irq_nr);
- local_irq_save(flags);
- writel(mask, pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_SET);
- local_irq_restore(flags);
-}
-
-static void pcic_enable_irq(unsigned int irq_nr)
-{
- unsigned long mask, flags;
-
- mask = get_irqmask(irq_nr);
- local_irq_save(flags);
- writel(mask, pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_CLEAR);
- local_irq_restore(flags);
-}
-
static void pcic_load_profile_irq(int cpu, unsigned int limit)
{
printk("PCIC: unimplemented code: FILE=%s LINE=%d", __FILE__, __LINE__);
}
-/* We assume the caller has disabled local interrupts when these are called,
- * or else very bizarre behavior will result.
- */
-static void pcic_disable_pil_irq(unsigned int pil)
-{
- writel(get_irqmask(pil), pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_SET);
-}
-
-static void pcic_enable_pil_irq(unsigned int pil)
-{
- writel(get_irqmask(pil), pcic0.pcic_regs+PCI_SYS_INT_TARGET_MASK_CLEAR);
-}
-
void __init sun4m_pci_init_IRQ(void)
{
- BTFIXUPSET_CALL(enable_irq, pcic_enable_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(disable_irq, pcic_disable_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(enable_pil_irq, pcic_enable_pil_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(disable_pil_irq, pcic_disable_pil_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(clear_clock_irq, pcic_clear_clock_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(load_profile_irq, pcic_load_profile_irq, BTFIXUPCALL_NORM);
}
@@ -65,62 +65,88 @@
*/
unsigned char __iomem *interrupt_enable;
-static void sun4c_disable_irq(unsigned int irq_nr)
+static void sun4c_disable_irq(struct irq_data *data)
{
- unsigned long flags;
- unsigned char current_mask, new_mask;
-
- local_irq_save(flags);
- irq_nr &= (NR_IRQS - 1);
- current_mask = sbus_readb(interrupt_enable);
- switch (irq_nr) {
- case 1:
- new_mask = ((current_mask) & (~(SUN4C_INT_E1)));
- break;
- case 8:
- new_mask = ((current_mask) & (~(SUN4C_INT_E8)));
- break;
- case 10:
- new_mask = ((current_mask) & (~(SUN4C_INT_E10)));
- break;
- case 14:
- new_mask = ((current_mask) & (~(SUN4C_INT_E14)));
- break;
- default:
+ unsigned long mask = (unsigned long)data->chip_data;
+
+ if (mask) {
+ unsigned long flags;
+
+ local_irq_save(flags);
+ mask = sbus_readb(interrupt_enable) & ~mask;
+ sbus_writeb(mask, interrupt_enable);
local_irq_restore(flags);
- return;
}
- sbus_writeb(new_mask, interrupt_enable);
- local_irq_restore(flags);
}
-static void sun4c_enable_irq(unsigned int irq_nr)
+static void sun4c_enable_irq(struct irq_data *data)
{
- unsigned long flags;
- unsigned char current_mask, new_mask;
-
- local_irq_save(flags);
- irq_nr &= (NR_IRQS - 1);
- current_mask = sbus_readb(interrupt_enable);
- switch (irq_nr) {
- case 1:
- new_mask = ((current_mask) | SUN4C_INT_E1);
- break;
- case 8:
- new_mask = ((current_mask) | SUN4C_INT_E8);
- break;
- case 10:
- new_mask = ((current_mask) | SUN4C_INT_E10);
- break;
- case 14:
- new_mask = ((current_mask) | SUN4C_INT_E14);
- break;
- default:
+ unsigned long mask = (unsigned long)data->chip_data;
+
+ if (mask) {
+ unsigned long flags;
+
+ local_irq_save(flags);
+ mask = sbus_readb(interrupt_enable) | mask;
+ sbus_writeb(mask, interrupt_enable);
local_irq_restore(flags);
- return;
}
- sbus_writeb(new_mask, interrupt_enable);
- local_irq_restore(flags);
+}
+
+static unsigned int sun4c_startup_irq(struct irq_data *data)
+{
+ irq_link(data->irq);
+ sun4c_enable_irq(data);
+
+ return 0;
+}
+
+static struct irq_chip sun4c_irq = {
+ .name = "sun4c",
+ .irq_startup = sun4c_startup_irq,
+ .irq_enable = sun4c_enable_irq,
+ .irq_disable = sun4c_disable_irq,
+ .irq_ack = sun4c_disable_irq,
+};
+
+static unsigned int sun4c_build_device_irq(struct platform_device *op,
+ unsigned int real_irq)
+{
+ unsigned int irq;
+
+ if (real_irq >= 16) {
+ prom_printf("Bogus sun4c IRQ %u\n", real_irq);
+ prom_halt();
+ }
+
+ irq = irq_alloc(real_irq, real_irq);
+ if (irq) {
+ unsigned long mask = 0UL;
+
+ switch (real_irq) {
+ case 1:
+ mask = SUN4C_INT_E1;
+ break;
+ case 8:
+ mask = SUN4C_INT_E8;
+ break;
+ case 10:
+ mask = SUN4C_INT_E10;
+ break;
+ case 14:
+ mask = SUN4C_INT_E14;
+ break;
+ default:
+ /*
+ * All the rest are either always enabled,
+ * or are for signalling software interrupts.
+ */
+ break;
+ }
+ set_irq_chip(irq, &sun4c_irq);
+ set_irq_chip_data(irq, (void *)mask);
+ }
+ return irq;
}
struct sun4c_timer_info {
@@ -146,6 +172,7 @@ static void __init sun4c_init_timers(irq_handler_t counter_fn)
{
const struct linux_prom_irqs *irq;
struct device_node *dp;
+ unsigned long mask;
const u32 *addr;
int err;
@@ -179,14 +206,15 @@ static void __init sun4c_init_timers(irq_handler_t counter_fn)
master_l10_counter = &sun4c_timers->l10_count;
err = request_irq(irq[0].pri, counter_fn,
- (IRQF_DISABLED | SA_STATIC_ALLOC),
- "timer", NULL);
+ IRQF_DISABLED, "timer", NULL);
if (err) {
prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err);
prom_halt();
}
- sun4c_disable_irq(irq[1].pri);
+ /* disable timer interrupt */
+ mask = sbus_readb(interrupt_enable) & ~SUN4C_INT_E10;
+ sbus_writeb(mask, interrupt_enable);
}
#ifdef CONFIG_SMP
@@ -215,13 +243,12 @@ void __init sun4c_init_IRQ(void)
interrupt_enable = (void __iomem *) (unsigned long) addr[0];
- BTFIXUPSET_CALL(enable_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(disable_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(enable_pil_irq, sun4c_enable_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(disable_pil_irq, sun4c_disable_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(clear_clock_irq, sun4c_clear_clock_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(load_profile_irq, sun4c_load_profile_irq, BTFIXUPCALL_NOP);
+
sparc_irq_config.init_timers = sun4c_init_timers;
+ sparc_irq_config.build_device_irq = sun4c_build_device_irq;
+
#ifdef CONFIG_SMP
BTFIXUPSET_CALL(set_cpu_int, sun4c_nop, BTFIXUPCALL_NOP);
BTFIXUPSET_CALL(clear_cpu_int, sun4c_nop, BTFIXUPCALL_NOP);
@@ -38,6 +38,33 @@
* have interrupts pending for that SBUS interrupt level.
*/
+struct sun4d_sbus_handler_data {
+ unsigned int tid; /* thread ID? */
+ unsigned int pil;
+ bool cpu_local; /* true for local intrrupts */
+};
+
+
+static unsigned int sun4d_encode_real_irq(int board, int lvl, int slot)
+{
+ return (board + 1) << 5 | (lvl << 2) | slot;
+}
+/*
+static unsigned int sun4d_irq_board(unsigned int real_irq)
+{
+ return (real_irq >> 5) - 1;
+}
+
+static unsigned int sun4d_irq_level(unsigned int real_irq)
+{
+ return (real_irq >> 2) & 0x7;
+}
+
+static unsigned int sun4d_irq_slot(unsigned int real_irq)
+{
+ return (real_irq >> 0) & 0x3;
+}
+*/
struct sun4d_timer_regs {
u32 l10_timer_limit;
u32 l10_cur_countx;
@@ -50,16 +77,8 @@ static struct sun4d_timer_regs __iomem *sun4d_timers;
#define TIMER_IRQ 10
-#define MAX_STATIC_ALLOC 4
static unsigned char sbus_tid[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,
@@ -78,7 +97,7 @@ static int pil_to_sbus[] = {
0,
0,
};
-
+/*
static int sbus_to_pil[] = {
0,
2,
@@ -89,142 +108,50 @@ static int sbus_to_pil[] = {
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)
+static void __sun4d_sbus_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 = bw_get_intr_mask(sbusl) & 0x3ffff;
+ unsigned int sbino, slot;
+ unsigned int sbil = (sbusl << 2);
+
+ bw_clear_intr_mask(sbusl, bus_mask);
+ /* Loop for each pending SBI */
+ for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1) {
+ unsigned int idx, mask;
+
+ 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.
+ */
+ mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil);
+ mask &= (0xf << 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;
-}
+ /* Loop for each pending SBI slot */
+ idx = 0;
+ for (slot = (1 << sbil); mask; slot <<= 1, idx++) {
+ unsigned int real_irq;
-void sun4d_free_irq(unsigned int irq, void *dev_id)
-{
- struct irqaction *action, **actionp;
- struct irqaction *tmp = NULL;
- unsigned long flags;
+ if (!(mask & slot))
+ continue;
- 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;
+ mask &= ~slot;
+ real_irq = sun4d_encode_real_irq(sbino, sbil, idx);
+ /* TODO__do_IRQ(real_irq); */
+ release_sbi(SBI2DEVID(sbino), slot);
}
- 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.
- */
- 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);
-
- synchronize_irq(irq);
-
- spin_lock_irqsave(&irq_action_lock, flags);
-
- kfree(action);
-
- if (!(*actionp))
- __disable_irq(irq);
-
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
}
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];
@@ -235,157 +162,79 @@ 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);
+ /* TODO__do_IRQ(pil); */
} 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);
-
- /* 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;
-
- if (!action)
- unexpected_irq(pil, NULL, regs);
- do {
- action->handler(pil, action->dev_id);
- action = action->next;
- } while (action);
- release_sbi(SBI2DEVID(sbino), slot);
- }
- }
+ __sun4d_sbus_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_sbus_disable_irq(struct irq_data *data)
{
- struct irqaction *action, *tmp = NULL, **actionp;
+ struct sun4d_sbus_handler_data *handler_data = data->handler_data;
+ unsigned int pil;
+#ifdef CONFIG_SMP
+ int tid = handler_data->tid;
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;
+#endif
- __enable_irq(irq);
+ pil = handler_data->pil;
- ret = 0;
-out_unlock:
- spin_unlock_irqrestore(&irq_action_lock, flags);
-out:
- return ret;
+#ifdef CONFIG_SMP
+ spin_lock_irqsave(&sun4d_imsk_lock, flags);
+ cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << pil));
+ spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else
+ cc_set_imsk(cc_get_imsk() | (1 << pil));
+#endif
}
-static void sun4d_disable_irq(unsigned int irq)
+static void sun4d_sbus_enable_irq(struct irq_data *data)
{
- int tid = sbus_tid[(irq >> 5) - 1];
+ struct sun4d_sbus_handler_data *handler_data = data->handler_data;
+ unsigned int pil;
+#ifdef CONFIG_SMP
+ int tid = handler_data->tid;
unsigned long flags;
+#endif
- if (irq < NR_IRQS)
- return;
+ pil = handler_data->pil;
+#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(tid, cc_get_imsk_other(tid) | ~(1 << pil));
spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
+#else
+ cc_set_imsk(cc_get_imsk() | ~(1 << pil));
+#endif
}
-static void sun4d_enable_irq(unsigned int irq)
+/*
+ * XXX need shutdown too and virt_irq_unlock() plus appropriate
+ * XXX smp locking
+ */
+static unsigned int sun4d_sbus_startup_irq(struct irq_data *data)
{
- int tid = sbus_tid[(irq >> 5) - 1];
- unsigned long flags;
+ irq_link(data->irq);
+ sun4d_sbus_enable_irq(data);
+ return 0;
+}
+
+struct irq_chip sun4d_sbus_irq = {
+ .name = "sun4di-sbus",
+ .irq_startup = sun4d_sbus_startup_irq,
+ .irq_enable = sun4d_sbus_enable_irq,
+ .irq_disable = sun4d_sbus_disable_irq,
+ .irq_ack = sun4d_sbus_disable_irq,
+};
+
+struct irq_chip sun4d_cpu_irq = {
+ .name = "sun4d-cpu",
+};
- if (irq < NR_IRQS)
- return;
- 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);
-}
#ifdef CONFIG_SMP
static void sun4d_set_cpu_int(int cpu, int level)
@@ -503,8 +352,7 @@ 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);
+ IRQF_DISABLED, "timer", NULL);
if (err) {
prom_printf("sun4d_init_timers: request_irq() failed with %d\n",
err);
@@ -522,15 +370,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);
@@ -549,11 +388,31 @@ void __init sun4d_init_sbi_irq(void)
}
}
-int sun4d_build_device_irq(struct platform_device *op, int interrupt)
+/* XXX This isn't going to work properly for of_devices, must fix... */
+/*
+unsigned int sun4d_build_irq(struct sbus_dev *sdev, unsigned int irq)
+{
+ unsigned int virt_irq, real_irq;
+ int sbusl = pil_to_sbus[irq];
+
+ if (sbusl)
+ real_irq = sun4d_encode_real_irq(sdev->bus->board,
+ sbusl, sdev->slot);
+ else
+ real_irq = irq & 0xf;
+ virt_irq = virt_irq_alloc(real_irq, irq & 0xf);
+ if (virt_irq) {
+ unsigned long val = real_irq;
+
+ set_irq_chip(virt_irq, &sun4d_irq);
+ set_irq_chip_data(virt_irq, (void *) val);
+ }
+ return irq;
+}
+*/
+unsigned int sun4d_build_device_irq(struct platform_device *op,
+ unsigned int interrupt)
{
- 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;
@@ -592,7 +451,7 @@ int sun4d_build_device_irq(struct platform_device *op, int interrupt)
sbusl = pil_to_sbus[interrupt];
if (sbusl)
- return (((board + 1) << 5) + (sbusl << 2) + slot);
+ return sun4d_encode_real_irq(board, sbusl, slot);
err_out:
return interrupt;
@@ -602,8 +461,6 @@ 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);
@@ -100,6 +100,11 @@
struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS];
struct sun4m_irq_global __iomem *sun4m_irq_global;
+struct sun4m_handler_data {
+ bool percpu;
+ long mask;
+};
+
/* Dave Redman (djhr@tadpole.co.uk)
* The sun4m interrupt registers.
*/
@@ -144,7 +149,7 @@ struct sun4m_irq_global __iomem *sun4m_irq_global;
#define SUN4M_TIMER_IRQ (OBP_INT_LEVEL_ONBOARD | 10)
#define SUM4M_PROFILE_IRQ (OBP_INT_LEVEL_ONBOARD | 14)
-static unsigned long irq_mask[0x50] = {
+static unsigned long sun4m_imask[0x50] = {
/* 0x00 - SMP */
0, SUN4M_SOFT_INT(1),
SUN4M_SOFT_INT(2), SUN4M_SOFT_INT(3),
@@ -182,105 +187,103 @@ static unsigned long irq_mask[0x50] = {
0, SUN4M_INT_VME(6), 0, 0
};
-static unsigned long sun4m_get_irqmask(unsigned int irq)
+static void sun4m_disable_irq(struct irq_data *data)
{
- unsigned long mask;
-
- if (irq < 0x50)
- mask = irq_mask[irq];
- else
- mask = 0;
+ struct sun4m_handler_data *handler_data = data->handler_data;
+ int cpu = smp_processor_id();
- if (!mask)
- printk(KERN_ERR "sun4m_get_irqmask: IRQ%d has no valid mask!\n",
- irq);
+ if (handler_data->mask) {
+ unsigned long flags;
- return mask;
+ local_irq_save(flags);
+ if (handler_data->percpu) {
+ sbus_writel(handler_data->mask, &sun4m_irq_percpu[cpu]->set);
+ } else {
+ sbus_writel(handler_data->mask, &sun4m_irq_global->mask_set);
+ }
+ local_irq_restore(flags);
+ }
}
-static void sun4m_disable_irq(unsigned int irq_nr)
+static void sun4m_enable_irq(struct irq_data *data)
{
- unsigned long mask, flags;
+ struct sun4m_handler_data *handler_data = data->handler_data;
int cpu = smp_processor_id();
- mask = sun4m_get_irqmask(irq_nr);
- local_irq_save(flags);
- if (irq_nr > 15)
- sbus_writel(mask, &sun4m_irq_global->mask_set);
- else
- sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
- local_irq_restore(flags);
-}
-
-static void sun4m_enable_irq(unsigned int irq_nr)
-{
- unsigned long mask, flags;
- int cpu = smp_processor_id();
+ if (handler_data->mask) {
+ unsigned long flags;
- /* Dreadful floppy hack. When we use 0x2b instead of
- * 0x0b the system blows (it starts to whistle!).
- * So we continue to use 0x0b. Fixme ASAP. --P3
- */
- if (irq_nr != 0x0b) {
- mask = sun4m_get_irqmask(irq_nr);
- local_irq_save(flags);
- if (irq_nr > 15)
- sbus_writel(mask, &sun4m_irq_global->mask_clear);
- else
- sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
- local_irq_restore(flags);
- } else {
local_irq_save(flags);
- sbus_writel(SUN4M_INT_FLOPPY, &sun4m_irq_global->mask_clear);
+ if (handler_data->percpu) {
+ sbus_writel(handler_data->mask, &sun4m_irq_percpu[cpu]->clear);
+ } else {
+ sbus_writel(handler_data->mask, &sun4m_irq_global->mask_clear);
+ }
local_irq_restore(flags);
}
}
-static unsigned long cpu_pil_to_imask[16] = {
-/*0*/ 0x00000000,
-/*1*/ 0x00000000,
-/*2*/ SUN4M_INT_SBUS(0) | SUN4M_INT_VME(0),
-/*3*/ SUN4M_INT_SBUS(1) | SUN4M_INT_VME(1),
-/*4*/ SUN4M_INT_SCSI,
-/*5*/ SUN4M_INT_SBUS(2) | SUN4M_INT_VME(2),
-/*6*/ SUN4M_INT_ETHERNET,
-/*7*/ SUN4M_INT_SBUS(3) | SUN4M_INT_VME(3),
-/*8*/ SUN4M_INT_VIDEO,
-/*9*/ SUN4M_INT_SBUS(4) | SUN4M_INT_VME(4) | SUN4M_INT_MODULE_ERR,
-/*10*/ SUN4M_INT_REALTIME,
-/*11*/ SUN4M_INT_SBUS(5) | SUN4M_INT_VME(5) | SUN4M_INT_FLOPPY,
-/*12*/ SUN4M_INT_SERIAL | SUN4M_INT_KBDMS,
-/*13*/ SUN4M_INT_SBUS(6) | SUN4M_INT_VME(6) | SUN4M_INT_AUDIO,
-/*14*/ SUN4M_INT_E14,
-/*15*/ SUN4M_INT_ERROR,
-};
-
-/* We assume the caller has disabled local interrupts when these are called,
- * or else very bizarre behavior will result.
- */
-static void sun4m_disable_pil_irq(unsigned int pil)
+static unsigned int sun4m_startup_irq(struct irq_data *data)
{
- sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_set);
+ irq_link(data->irq);
+ sun4m_enable_irq(data);
+ return 0;
}
-static void sun4m_enable_pil_irq(unsigned int pil)
+static struct irq_chip sun4m_irq = {
+ .name = "sun4m",
+ .irq_startup = sun4m_startup_irq,
+ .irq_enable = sun4m_enable_irq,
+ .irq_disable = sun4m_disable_irq,
+ .irq_ack = sun4m_disable_irq,
+};
+
+
+static unsigned int sun4m_build_device_irq(struct platform_device *op,
+ unsigned int real_irq)
{
- sbus_writel(cpu_pil_to_imask[pil], &sun4m_irq_global->mask_clear);
+ struct sun4m_handler_data *handler_data;
+ unsigned int irq;
+ unsigned int pil;
+
+ if (real_irq >= OBP_INT_LEVEL_VME) {
+ prom_printf("Bogus sun4m IRQ %u\n", real_irq);
+ prom_halt();
+ }
+ pil = (real_irq & 0xf);
+ irq = irq_alloc(real_irq, pil);
+
+ if (irq == 0)
+ goto out;
+
+ handler_data = get_irq_data(irq);
+ if (unlikely(handler_data))
+ goto out;
+
+ handler_data = kzalloc(sizeof(struct sun4m_handler_data), GFP_ATOMIC);
+ if (unlikely(!handler_data)) {
+ prom_printf("IRQ: kzalloc(sun4m_handler_data) failed.\n");
+ prom_halt();
+ }
+
+ handler_data->mask = sun4m_imask[real_irq];
+ handler_data->percpu = irq < OBP_INT_LEVEL_ONBOARD;
+ set_irq_chip(irq, &sun4m_irq);
+ set_irq_data(irq, handler_data);
+
+out:
+ return irq;
}
#ifdef CONFIG_SMP
static void sun4m_send_ipi(int cpu, int level)
{
- unsigned long mask = sun4m_get_irqmask(level);
-
- sbus_writel(mask, &sun4m_irq_percpu[cpu]->set);
+ sbus_writel(SUN4M_SOFT_INT(level), &sun4m_irq_percpu[cpu]->set);
}
static void sun4m_clear_ipi(int cpu, int level)
{
- unsigned long mask = sun4m_get_irqmask(level);
-
- sbus_writel(mask, &sun4m_irq_percpu[cpu]->clear);
+ sbus_writel(SUN4M_SOFT_INT(level), &sun4m_irq_percpu[cpu]->clear);
}
static void sun4m_set_udt(int cpu)
@@ -358,6 +361,7 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn)
{
struct device_node *dp = of_find_node_by_name(NULL, "counter");
int i, err, len, num_cpu_timers;
+ unsigned int irq;
const u32 *addr;
if (!dp) {
@@ -384,8 +388,9 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn)
master_l10_counter = &timers_global->l10_count;
- err = request_irq(SUN4M_TIMER_IRQ, counter_fn,
- (IRQF_DISABLED | SA_STATIC_ALLOC), "timer", NULL);
+ irq = sun4m_build_device_irq(NULL, SUN4M_TIMER_IRQ);
+
+ err = request_irq(irq, counter_fn, IRQF_DISABLED, "timer", NULL);
if (err) {
printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n",
err);
@@ -452,14 +457,12 @@ void __init sun4m_init_IRQ(void)
if (num_cpu_iregs == 4)
sbus_writel(0, &sun4m_irq_global->interrupt_target);
- BTFIXUPSET_CALL(enable_irq, sun4m_enable_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(disable_irq, sun4m_disable_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(enable_pil_irq, sun4m_enable_pil_irq, BTFIXUPCALL_NORM);
- BTFIXUPSET_CALL(disable_pil_irq, sun4m_disable_pil_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(clear_clock_irq, sun4m_clear_clock_irq, BTFIXUPCALL_NORM);
BTFIXUPSET_CALL(load_profile_irq, sun4m_load_profile_irq, BTFIXUPCALL_NORM);
sparc_irq_config.init_timers = sun4m_init_timers;
+ sparc_irq_config.build_device_irq = sun4m_build_device_irq;
+ sparc_irq_config.profile_irq = SUM4M_PROFILE_IRQ;
#ifdef CONFIG_SMP
BTFIXUPSET_CALL(set_cpu_int, sun4m_send_ipi, BTFIXUPCALL_NORM);
@@ -277,7 +277,7 @@ static void __cpuinit smp_setup_percpu_timer(void)
load_profile_irq(cpu, lvl14_resolution);
if (cpu == boot_cpu_id)
- enable_pil_irq(14);
+ enable_irq(sparc_irq_config.profile_irq);
}
static void __init smp4m_blackbox_id(unsigned *addr)