df9ee29270
Fix the IRQ flag handling naming. In linux/irqflags.h under one configuration, it maps: local_irq_enable() -> raw_local_irq_enable() local_irq_disable() -> raw_local_irq_disable() local_irq_save() -> raw_local_irq_save() ... and under the other configuration, it maps: raw_local_irq_enable() -> local_irq_enable() raw_local_irq_disable() -> local_irq_disable() raw_local_irq_save() -> local_irq_save() ... This is quite confusing. There should be one set of names expected of the arch, and this should be wrapped to give another set of names that are expected by users of this facility. Change this to have the arch provide: flags = arch_local_save_flags() flags = arch_local_irq_save() arch_local_irq_restore(flags) arch_local_irq_disable() arch_local_irq_enable() arch_irqs_disabled_flags(flags) arch_irqs_disabled() arch_safe_halt() Then linux/irqflags.h wraps these to provide: raw_local_save_flags(flags) raw_local_irq_save(flags) raw_local_irq_restore(flags) raw_local_irq_disable() raw_local_irq_enable() raw_irqs_disabled_flags(flags) raw_irqs_disabled() raw_safe_halt() with type checking on the flags 'arguments', and then wraps those to provide: local_save_flags(flags) local_irq_save(flags) local_irq_restore(flags) local_irq_disable() local_irq_enable() irqs_disabled_flags(flags) irqs_disabled() safe_halt() with tracing included if enabled. The arch functions can now all be inline functions rather than some of them having to be macros. Signed-off-by: David Howells <dhowells@redhat.com> [X86, FRV, MN10300] Signed-off-by: Chris Metcalf <cmetcalf@tilera.com> [Tile] Signed-off-by: Michal Simek <monstr@monstr.eu> [Microblaze] Tested-by: Catalin Marinas <catalin.marinas@arm.com> [ARM] Acked-by: Thomas Gleixner <tglx@linutronix.de> Acked-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com> [AVR] Acked-by: Tony Luck <tony.luck@intel.com> [IA-64] Acked-by: Hirokazu Takata <takata@linux-m32r.org> [M32R] Acked-by: Greg Ungerer <gerg@uclinux.org> [M68K/M68KNOMMU] Acked-by: Ralf Baechle <ralf@linux-mips.org> [MIPS] Acked-by: Kyle McMartin <kyle@mcmartin.ca> [PA-RISC] Acked-by: Paul Mackerras <paulus@samba.org> [PowerPC] Acked-by: Martin Schwidefsky <schwidefsky@de.ibm.com> [S390] Acked-by: Chen Liqin <liqin.chen@sunplusct.com> [Score] Acked-by: Matt Fleming <matt@console-pimps.org> [SH] Acked-by: David S. Miller <davem@davemloft.net> [Sparc] Acked-by: Chris Zankel <chris@zankel.net> [Xtensa] Reviewed-by: Richard Henderson <rth@twiddle.net> [Alpha] Reviewed-by: Yoshinori Sato <ysato@users.sourceforge.jp> [H8300] Cc: starvik@axis.com [CRIS] Cc: jesper.nilsson@axis.com [CRIS] Cc: linux-cris-kernel@axis.com
680 lines
16 KiB
C
680 lines
16 KiB
C
/*
|
|
* arch/sparc/kernel/irq.c: Interrupt request handling routines. On the
|
|
* Sparc the IRQs are basically 'cast in stone'
|
|
* and you are supposed to probe the prom's device
|
|
* node trees to find out who's got which IRQ.
|
|
*
|
|
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
|
|
* Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
|
|
* Copyright (C) 1995,2002 Pete A. Zaitcev (zaitcev@yahoo.com)
|
|
* Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
|
|
* Copyright (C) 1998-2000 Anton Blanchard (anton@samba.org)
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/linkage.h>
|
|
#include <linux/kernel_stat.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/random.h>
|
|
#include <linux/init.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/threads.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/seq_file.h>
|
|
|
|
#include <asm/ptrace.h>
|
|
#include <asm/processor.h>
|
|
#include <asm/system.h>
|
|
#include <asm/psr.h>
|
|
#include <asm/smp.h>
|
|
#include <asm/vaddrs.h>
|
|
#include <asm/timer.h>
|
|
#include <asm/openprom.h>
|
|
#include <asm/oplib.h>
|
|
#include <asm/traps.h>
|
|
#include <asm/irq.h>
|
|
#include <asm/io.h>
|
|
#include <asm/pgalloc.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/pcic.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/irq_regs.h>
|
|
#include <asm/leon.h>
|
|
|
|
#include "kernel.h"
|
|
#include "irq.h"
|
|
|
|
#ifdef CONFIG_SMP
|
|
#define SMP_NOP2 "nop; nop;\n\t"
|
|
#define SMP_NOP3 "nop; nop; nop;\n\t"
|
|
#else
|
|
#define SMP_NOP2
|
|
#define SMP_NOP3
|
|
#endif /* SMP */
|
|
unsigned long arch_local_irq_save(void)
|
|
{
|
|
unsigned long retval;
|
|
unsigned long tmp;
|
|
|
|
__asm__ __volatile__(
|
|
"rd %%psr, %0\n\t"
|
|
SMP_NOP3 /* Sun4m + Cypress + SMP bug */
|
|
"or %0, %2, %1\n\t"
|
|
"wr %1, 0, %%psr\n\t"
|
|
"nop; nop; nop\n"
|
|
: "=&r" (retval), "=r" (tmp)
|
|
: "i" (PSR_PIL)
|
|
: "memory");
|
|
|
|
return retval;
|
|
}
|
|
EXPORT_SYMBOL(arch_local_irq_save);
|
|
|
|
void arch_local_irq_enable(void)
|
|
{
|
|
unsigned long tmp;
|
|
|
|
__asm__ __volatile__(
|
|
"rd %%psr, %0\n\t"
|
|
SMP_NOP3 /* Sun4m + Cypress + SMP bug */
|
|
"andn %0, %1, %0\n\t"
|
|
"wr %0, 0, %%psr\n\t"
|
|
"nop; nop; nop\n"
|
|
: "=&r" (tmp)
|
|
: "i" (PSR_PIL)
|
|
: "memory");
|
|
}
|
|
EXPORT_SYMBOL(arch_local_irq_enable);
|
|
|
|
void arch_local_irq_restore(unsigned long old_psr)
|
|
{
|
|
unsigned long tmp;
|
|
|
|
__asm__ __volatile__(
|
|
"rd %%psr, %0\n\t"
|
|
"and %2, %1, %2\n\t"
|
|
SMP_NOP2 /* Sun4m + Cypress + SMP bug */
|
|
"andn %0, %1, %0\n\t"
|
|
"wr %0, %2, %%psr\n\t"
|
|
"nop; nop; nop\n"
|
|
: "=&r" (tmp)
|
|
: "i" (PSR_PIL), "r" (old_psr)
|
|
: "memory");
|
|
}
|
|
EXPORT_SYMBOL(arch_local_irq_restore);
|
|
|
|
/*
|
|
* Dave Redman (djhr@tadpole.co.uk)
|
|
*
|
|
* IRQ numbers.. These are no longer restricted to 15..
|
|
*
|
|
* this is done to enable SBUS cards and onboard IO to be masked
|
|
* correctly. using the interrupt level isn't good enough.
|
|
*
|
|
* For example:
|
|
* A device interrupting at sbus level6 and the Floppy both come in
|
|
* at IRQ11, but enabling and disabling them requires writing to
|
|
* different bits in the SLAVIO/SEC.
|
|
*
|
|
* As a result of these changes sun4m machines could now support
|
|
* directed CPU interrupts using the existing enable/disable irq code
|
|
* with tweaks.
|
|
*
|
|
*/
|
|
|
|
static void irq_panic(void)
|
|
{
|
|
extern char *cputypval;
|
|
prom_printf("machine: %s doesn't have irq handlers defined!\n",cputypval);
|
|
prom_halt();
|
|
}
|
|
|
|
void (*sparc_init_timers)(irq_handler_t ) =
|
|
(void (*)(irq_handler_t )) irq_panic;
|
|
|
|
/*
|
|
* 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....
|
|
*
|
|
*/
|
|
#define MAX_STATIC_ALLOC 4
|
|
struct irqaction static_irqaction[MAX_STATIC_ALLOC];
|
|
int static_irq_count;
|
|
|
|
static struct {
|
|
struct irqaction *action;
|
|
int flags;
|
|
} sparc_irq[NR_IRQS];
|
|
#define SPARC_IRQ_INPROGRESS 1
|
|
|
|
/* Used to protect the IRQ action lists */
|
|
DEFINE_SPINLOCK(irq_action_lock);
|
|
|
|
int show_interrupts(struct seq_file *p, void *v)
|
|
{
|
|
int i = *(loff_t *) v;
|
|
struct irqaction * action;
|
|
unsigned long flags;
|
|
#ifdef CONFIG_SMP
|
|
int j;
|
|
#endif
|
|
|
|
if (sparc_cpu_model == sun4d) {
|
|
extern int show_sun4d_interrupts(struct seq_file *, void *);
|
|
|
|
return show_sun4d_interrupts(p, v);
|
|
}
|
|
spin_lock_irqsave(&irq_action_lock, flags);
|
|
if (i < NR_IRQS) {
|
|
action = sparc_irq[i].action;
|
|
if (!action)
|
|
goto out_unlock;
|
|
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]);
|
|
}
|
|
#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_putc(p, '\n');
|
|
}
|
|
out_unlock:
|
|
spin_unlock_irqrestore(&irq_action_lock, flags);
|
|
return 0;
|
|
}
|
|
|
|
void free_irq(unsigned int irq, void *dev_id)
|
|
{
|
|
struct irqaction * action;
|
|
struct irqaction **actionp;
|
|
unsigned long flags;
|
|
unsigned int cpu_irq;
|
|
|
|
if (sparc_cpu_model == sun4d) {
|
|
extern void sun4d_free_irq(unsigned int, void *);
|
|
|
|
sun4d_free_irq(irq, dev_id);
|
|
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;
|
|
}
|
|
|
|
spin_lock_irqsave(&irq_action_lock, flags);
|
|
|
|
actionp = &sparc_irq[cpu_irq].action;
|
|
action = *actionp;
|
|
|
|
if (!action->handler) {
|
|
printk("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("Trying to free free shared IRQ%d\n",irq);
|
|
goto out_unlock;
|
|
}
|
|
} else if (action->flags & IRQF_SHARED) {
|
|
printk("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("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);
|
|
|
|
if (!sparc_irq[cpu_irq].action)
|
|
__disable_irq(irq);
|
|
|
|
out_unlock:
|
|
spin_unlock_irqrestore(&irq_action_lock, flags);
|
|
}
|
|
|
|
EXPORT_SYMBOL(free_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;
|
|
|
|
cpu_irq = irq & (NR_IRQS - 1);
|
|
while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS)
|
|
cpu_relax();
|
|
}
|
|
EXPORT_SYMBOL(synchronize_irq);
|
|
#endif /* SMP */
|
|
|
|
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("IO device interrupt, irq = %d\n", irq);
|
|
printk("PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc,
|
|
regs->npc, regs->u_regs[14]);
|
|
if (action) {
|
|
printk("Expecting: ");
|
|
for (i = 0; i < 16; i++)
|
|
if (action->handler)
|
|
printk("[%s:%d:0x%x] ", action->name,
|
|
(int) i, (unsigned int) action->handler);
|
|
}
|
|
printk("AIEEE\n");
|
|
panic("bogus interrupt received");
|
|
}
|
|
|
|
void handler_irq(int irq, struct pt_regs * regs)
|
|
{
|
|
struct pt_regs *old_regs;
|
|
struct irqaction * action;
|
|
int cpu = smp_processor_id();
|
|
#ifdef CONFIG_SMP
|
|
extern void smp4m_irq_rotate(int cpu);
|
|
#endif
|
|
|
|
old_regs = set_irq_regs(regs);
|
|
irq_enter();
|
|
disable_pil_irq(irq);
|
|
#ifdef CONFIG_SMP
|
|
/* Only rotate on lower priority IRQs (scsi, ethernet, etc.). */
|
|
if((sparc_cpu_model==sun4m) && (irq < 10))
|
|
smp4m_irq_rotate(cpu);
|
|
#endif
|
|
action = sparc_irq[irq].action;
|
|
sparc_irq[irq].flags |= SPARC_IRQ_INPROGRESS;
|
|
kstat_cpu(cpu).irqs[irq]++;
|
|
do {
|
|
if (!action || !action->handler)
|
|
unexpected_irq(irq, NULL, regs);
|
|
action->handler(irq, action->dev_id);
|
|
action = action->next;
|
|
} while (action);
|
|
sparc_irq[irq].flags &= ~SPARC_IRQ_INPROGRESS;
|
|
enable_pil_irq(irq);
|
|
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)
|
|
{
|
|
struct irqaction *action;
|
|
unsigned long flags;
|
|
unsigned int cpu_irq;
|
|
int ret;
|
|
#ifdef CONFIG_SMP
|
|
struct tt_entry *trap_table;
|
|
extern struct tt_entry trapbase_cpu1, trapbase_cpu2, trapbase_cpu3;
|
|
#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);
|
|
|
|
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("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("Fast 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;
|
|
}
|
|
|
|
/* Dork with trap table if we get this far. */
|
|
#define INSTANTIATE(table) \
|
|
table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_one = SPARC_RD_PSR_L0; \
|
|
table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two = \
|
|
SPARC_BRANCH((unsigned long) handler, \
|
|
(unsigned long) &table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two);\
|
|
table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_three = SPARC_RD_WIM_L3; \
|
|
table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;
|
|
|
|
INSTANTIATE(sparc_ttable)
|
|
#ifdef CONFIG_SMP
|
|
trap_table = &trapbase_cpu1; INSTANTIATE(trap_table)
|
|
trap_table = &trapbase_cpu2; INSTANTIATE(trap_table)
|
|
trap_table = &trapbase_cpu3; INSTANTIATE(trap_table)
|
|
#endif
|
|
#undef INSTANTIATE
|
|
/*
|
|
* XXX Correct thing whould be to flush only I- and D-cache lines
|
|
* which contain the handler in question. But as of time of the
|
|
* writing we have no CPU-neutral interface to fine-grained flushes.
|
|
*/
|
|
flush_cache_all();
|
|
|
|
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;
|
|
}
|
|
|
|
/* These variables are used to access state from the assembler
|
|
* interrupt handler, floppy_hardint, so we cannot put these in
|
|
* the floppy driver image because that would not work in the
|
|
* modular case.
|
|
*/
|
|
volatile unsigned char *fdc_status;
|
|
EXPORT_SYMBOL(fdc_status);
|
|
|
|
char *pdma_vaddr;
|
|
EXPORT_SYMBOL(pdma_vaddr);
|
|
|
|
unsigned long pdma_size;
|
|
EXPORT_SYMBOL(pdma_size);
|
|
|
|
volatile int doing_pdma;
|
|
EXPORT_SYMBOL(doing_pdma);
|
|
|
|
char *pdma_base;
|
|
EXPORT_SYMBOL(pdma_base);
|
|
|
|
unsigned long pdma_areasize;
|
|
EXPORT_SYMBOL(pdma_areasize);
|
|
|
|
extern void floppy_hardint(void);
|
|
|
|
static irq_handler_t floppy_irq_handler;
|
|
|
|
void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs)
|
|
{
|
|
struct pt_regs *old_regs;
|
|
int cpu = smp_processor_id();
|
|
|
|
old_regs = set_irq_regs(regs);
|
|
disable_pil_irq(irq);
|
|
irq_enter();
|
|
kstat_cpu(cpu).irqs[irq]++;
|
|
floppy_irq_handler(irq, dev_id);
|
|
irq_exit();
|
|
enable_pil_irq(irq);
|
|
set_irq_regs(old_regs);
|
|
// XXX Eek, it's totally changed with preempt_count() and such
|
|
// if (softirq_pending(cpu))
|
|
// do_softirq();
|
|
}
|
|
|
|
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) {
|
|
extern int sun4d_request_irq(unsigned int,
|
|
irq_handler_t ,
|
|
unsigned long, const char *, void *);
|
|
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("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("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);
|
|
|
|
/* djhr
|
|
* This could probably be made indirect too and assigned in the CPU
|
|
* bits of the code. That would be much nicer I think and would also
|
|
* fit in with the idea of being able to tune your kernel for your machine
|
|
* by removing unrequired machine and device support.
|
|
*
|
|
*/
|
|
|
|
void __init init_IRQ(void)
|
|
{
|
|
extern void sun4c_init_IRQ( void );
|
|
extern void sun4m_init_IRQ( void );
|
|
extern void sun4d_init_IRQ( void );
|
|
|
|
switch(sparc_cpu_model) {
|
|
case sun4c:
|
|
case sun4:
|
|
sun4c_init_IRQ();
|
|
break;
|
|
|
|
case sun4m:
|
|
#ifdef CONFIG_PCI
|
|
pcic_probe();
|
|
if (pcic_present()) {
|
|
sun4m_pci_init_IRQ();
|
|
break;
|
|
}
|
|
#endif
|
|
sun4m_init_IRQ();
|
|
break;
|
|
|
|
case sun4d:
|
|
sun4d_init_IRQ();
|
|
break;
|
|
|
|
case sparc_leon:
|
|
leon_init_IRQ();
|
|
break;
|
|
|
|
default:
|
|
prom_printf("Cannot initialize IRQs on this Sun machine...");
|
|
break;
|
|
}
|
|
btfixup();
|
|
}
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
void init_irq_proc(void)
|
|
{
|
|
/* For now, nothing... */
|
|
}
|
|
#endif /* CONFIG_PROC_FS */
|