/*
|
/*
|
* linux/arch/arm/kernel/irq.c
|
* linux/arch/arm/kernel/irq.c
|
*
|
*
|
* Copyright (C) 1992 Linus Torvalds
|
* Copyright (C) 1992 Linus Torvalds
|
* Modifications for ARM processor Copyright (C) 1995, 1996 Russell King.
|
* Modifications for ARM processor Copyright (C) 1995, 1996 Russell King.
|
*
|
*
|
* This file contains the code used by various IRQ handling routines:
|
* This file contains the code used by various IRQ handling routines:
|
* asking for different IRQ's should be done through these routines
|
* asking for different IRQ's should be done through these routines
|
* instead of just grabbing them. Thus setups with different IRQ numbers
|
* instead of just grabbing them. Thus setups with different IRQ numbers
|
* shouldn't result in any weird surprises, and installing new handlers
|
* shouldn't result in any weird surprises, and installing new handlers
|
* should be easier.
|
* should be easier.
|
*/
|
*/
|
|
|
/*
|
/*
|
* IRQ's are in fact implemented a bit like signal handlers for the kernel.
|
* IRQ's are in fact implemented a bit like signal handlers for the kernel.
|
* Naturally it's not a 1:1 relation, but there are similarities.
|
* Naturally it's not a 1:1 relation, but there are similarities.
|
*/
|
*/
|
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/kernel_stat.h>
|
#include <linux/kernel_stat.h>
|
#include <linux/signal.h>
|
#include <linux/signal.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
#include <linux/timex.h>
|
#include <linux/timex.h>
|
#include <linux/malloc.h>
|
#include <linux/malloc.h>
|
#include <linux/random.h>
|
#include <linux/random.h>
|
|
|
#include <asm/io.h>
|
#include <asm/io.h>
|
#include <asm/system.h>
|
#include <asm/system.h>
|
#include <asm/hardware.h>
|
#include <asm/hardware.h>
|
#include <asm/irq.h>
|
#include <asm/irq.h>
|
|
|
#include <asm/arch/irq.h>
|
#include <asm/arch/irq.h>
|
|
|
void disable_irq(unsigned int irq_nr)
|
void disable_irq(unsigned int irq_nr)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
#ifdef cliIF
|
#ifdef cliIF
|
save_flags(flags);
|
save_flags(flags);
|
cliIF();
|
cliIF();
|
#else
|
#else
|
save_flags_cli (flags);
|
save_flags_cli (flags);
|
#endif
|
#endif
|
mask_irq(irq_nr);
|
mask_irq(irq_nr);
|
restore_flags(flags);
|
restore_flags(flags);
|
}
|
}
|
|
|
void enable_irq(unsigned int irq_nr)
|
void enable_irq(unsigned int irq_nr)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
|
|
#ifdef cliIF
|
#ifdef cliIF
|
save_flags (flags);
|
save_flags (flags);
|
cliIF();
|
cliIF();
|
#else
|
#else
|
save_flags_cli (flags);
|
save_flags_cli (flags);
|
#endif
|
#endif
|
unmask_irq(irq_nr);
|
unmask_irq(irq_nr);
|
restore_flags(flags);
|
restore_flags(flags);
|
}
|
}
|
|
|
BUILD_IRQ(FIRST,0,0x01)
|
BUILD_IRQ(FIRST,0,0x01)
|
BUILD_IRQ(FIRST,1,0x02)
|
BUILD_IRQ(FIRST,1,0x02)
|
BUILD_IRQ(FIRST,2,0x04)
|
BUILD_IRQ(FIRST,2,0x04)
|
BUILD_IRQ(FIRST,3,0x08)
|
BUILD_IRQ(FIRST,3,0x08)
|
BUILD_IRQ(FIRST,4,0x10)
|
BUILD_IRQ(FIRST,4,0x10)
|
BUILD_IRQ(FIRST,5,0x20)
|
BUILD_IRQ(FIRST,5,0x20)
|
BUILD_IRQ(FIRST,6,0x40)
|
BUILD_IRQ(FIRST,6,0x40)
|
BUILD_IRQ(FIRST,7,0x80)
|
BUILD_IRQ(FIRST,7,0x80)
|
BUILD_IRQ(SECOND,8,0x01)
|
BUILD_IRQ(SECOND,8,0x01)
|
BUILD_IRQ(SECOND,9,0x02)
|
BUILD_IRQ(SECOND,9,0x02)
|
BUILD_IRQ(SECOND,10,0x04)
|
BUILD_IRQ(SECOND,10,0x04)
|
BUILD_IRQ(SECOND,11,0x08)
|
BUILD_IRQ(SECOND,11,0x08)
|
BUILD_IRQ(SECOND,12,0x10)
|
BUILD_IRQ(SECOND,12,0x10)
|
BUILD_IRQ(SECOND,13,0x20)
|
BUILD_IRQ(SECOND,13,0x20)
|
BUILD_IRQ(SECOND,14,0x40)
|
BUILD_IRQ(SECOND,14,0x40)
|
BUILD_IRQ(SECOND,15,0x80)
|
BUILD_IRQ(SECOND,15,0x80)
|
BUILD_IRQ(SECOND,16,0x01)
|
BUILD_IRQ(SECOND,16,0x01)
|
BUILD_IRQ(SECOND,17,0x02)
|
BUILD_IRQ(SECOND,17,0x02)
|
BUILD_IRQ(SECOND,18,0x04)
|
BUILD_IRQ(SECOND,18,0x04)
|
BUILD_IRQ(SECOND,19,0x08)
|
BUILD_IRQ(SECOND,19,0x08)
|
BUILD_IRQ(SECOND,20,0x10)
|
BUILD_IRQ(SECOND,20,0x10)
|
BUILD_IRQ(SECOND,21,0x20)
|
BUILD_IRQ(SECOND,21,0x20)
|
BUILD_IRQ(SECOND,22,0x40)
|
BUILD_IRQ(SECOND,22,0x40)
|
BUILD_IRQ(SECOND,23,0x80)
|
BUILD_IRQ(SECOND,23,0x80)
|
|
|
/*
|
/*
|
* Pointers to the low-level handlers: first the general ones, then the
|
* Pointers to the low-level handlers: first the general ones, then the
|
* fast ones, then the bad ones.
|
* fast ones, then the bad ones.
|
*/
|
*/
|
static void (* const interrupt[24])(void) = {
|
static void (* const interrupt[24])(void) = {
|
IRQ_INTERRUPT( 0), IRQ_INTERRUPT( 1), IRQ_INTERRUPT( 2), IRQ_INTERRUPT( 3),
|
IRQ_INTERRUPT( 0), IRQ_INTERRUPT( 1), IRQ_INTERRUPT( 2), IRQ_INTERRUPT( 3),
|
IRQ_INTERRUPT( 4), IRQ_INTERRUPT( 5), IRQ_INTERRUPT( 6), IRQ_INTERRUPT( 7),
|
IRQ_INTERRUPT( 4), IRQ_INTERRUPT( 5), IRQ_INTERRUPT( 6), IRQ_INTERRUPT( 7),
|
IRQ_INTERRUPT( 8), IRQ_INTERRUPT( 9), IRQ_INTERRUPT(10), IRQ_INTERRUPT(11),
|
IRQ_INTERRUPT( 8), IRQ_INTERRUPT( 9), IRQ_INTERRUPT(10), IRQ_INTERRUPT(11),
|
IRQ_INTERRUPT(12), IRQ_INTERRUPT(13), IRQ_INTERRUPT(14), IRQ_INTERRUPT(15),
|
IRQ_INTERRUPT(12), IRQ_INTERRUPT(13), IRQ_INTERRUPT(14), IRQ_INTERRUPT(15),
|
IRQ_INTERRUPT(16), IRQ_INTERRUPT(17), IRQ_INTERRUPT(18), IRQ_INTERRUPT(19),
|
IRQ_INTERRUPT(16), IRQ_INTERRUPT(17), IRQ_INTERRUPT(18), IRQ_INTERRUPT(19),
|
IRQ_INTERRUPT(20), IRQ_INTERRUPT(21), IRQ_INTERRUPT(22), IRQ_INTERRUPT(23)
|
IRQ_INTERRUPT(20), IRQ_INTERRUPT(21), IRQ_INTERRUPT(22), IRQ_INTERRUPT(23)
|
};
|
};
|
|
|
static void (* const fast_interrupt[24])(void) = {
|
static void (* const fast_interrupt[24])(void) = {
|
FAST_INTERRUPT( 0), FAST_INTERRUPT( 1), FAST_INTERRUPT( 2), FAST_INTERRUPT( 3),
|
FAST_INTERRUPT( 0), FAST_INTERRUPT( 1), FAST_INTERRUPT( 2), FAST_INTERRUPT( 3),
|
FAST_INTERRUPT( 4), FAST_INTERRUPT( 5), FAST_INTERRUPT( 6), FAST_INTERRUPT( 7),
|
FAST_INTERRUPT( 4), FAST_INTERRUPT( 5), FAST_INTERRUPT( 6), FAST_INTERRUPT( 7),
|
FAST_INTERRUPT( 8), FAST_INTERRUPT( 9), FAST_INTERRUPT(10), FAST_INTERRUPT(11),
|
FAST_INTERRUPT( 8), FAST_INTERRUPT( 9), FAST_INTERRUPT(10), FAST_INTERRUPT(11),
|
FAST_INTERRUPT(12), FAST_INTERRUPT(13), FAST_INTERRUPT(14), FAST_INTERRUPT(15),
|
FAST_INTERRUPT(12), FAST_INTERRUPT(13), FAST_INTERRUPT(14), FAST_INTERRUPT(15),
|
FAST_INTERRUPT(16), FAST_INTERRUPT(17), FAST_INTERRUPT(18), FAST_INTERRUPT(19),
|
FAST_INTERRUPT(16), FAST_INTERRUPT(17), FAST_INTERRUPT(18), FAST_INTERRUPT(19),
|
FAST_INTERRUPT(20), FAST_INTERRUPT(21), FAST_INTERRUPT(22), FAST_INTERRUPT(23)
|
FAST_INTERRUPT(20), FAST_INTERRUPT(21), FAST_INTERRUPT(22), FAST_INTERRUPT(23)
|
};
|
};
|
|
|
static void (* const bad_interrupt[24])(void) = {
|
static void (* const bad_interrupt[24])(void) = {
|
BAD_INTERRUPT( 0), BAD_INTERRUPT( 1), BAD_INTERRUPT( 2), BAD_INTERRUPT( 3),
|
BAD_INTERRUPT( 0), BAD_INTERRUPT( 1), BAD_INTERRUPT( 2), BAD_INTERRUPT( 3),
|
BAD_INTERRUPT( 4), BAD_INTERRUPT( 5), BAD_INTERRUPT( 6), BAD_INTERRUPT( 7),
|
BAD_INTERRUPT( 4), BAD_INTERRUPT( 5), BAD_INTERRUPT( 6), BAD_INTERRUPT( 7),
|
BAD_INTERRUPT( 8), BAD_INTERRUPT( 9), BAD_INTERRUPT(10), BAD_INTERRUPT(11),
|
BAD_INTERRUPT( 8), BAD_INTERRUPT( 9), BAD_INTERRUPT(10), BAD_INTERRUPT(11),
|
BAD_INTERRUPT(12), BAD_INTERRUPT(13), BAD_INTERRUPT(14), BAD_INTERRUPT(15),
|
BAD_INTERRUPT(12), BAD_INTERRUPT(13), BAD_INTERRUPT(14), BAD_INTERRUPT(15),
|
BAD_INTERRUPT(16), BAD_INTERRUPT(17), BAD_INTERRUPT(18), BAD_INTERRUPT(19),
|
BAD_INTERRUPT(16), BAD_INTERRUPT(17), BAD_INTERRUPT(18), BAD_INTERRUPT(19),
|
BAD_INTERRUPT(20), BAD_INTERRUPT(21), BAD_INTERRUPT(22), BAD_INTERRUPT(23)
|
BAD_INTERRUPT(20), BAD_INTERRUPT(21), BAD_INTERRUPT(22), BAD_INTERRUPT(23)
|
};
|
};
|
|
|
static void (* const probe_interrupt[16])(void) = {
|
static void (* const probe_interrupt[16])(void) = {
|
PROBE_INTERRUPT( 0), PROBE_INTERRUPT( 1), PROBE_INTERRUPT( 2), PROBE_INTERRUPT( 3),
|
PROBE_INTERRUPT( 0), PROBE_INTERRUPT( 1), PROBE_INTERRUPT( 2), PROBE_INTERRUPT( 3),
|
PROBE_INTERRUPT( 4), PROBE_INTERRUPT( 5), PROBE_INTERRUPT( 6), PROBE_INTERRUPT( 7),
|
PROBE_INTERRUPT( 4), PROBE_INTERRUPT( 5), PROBE_INTERRUPT( 6), PROBE_INTERRUPT( 7),
|
PROBE_INTERRUPT( 8), PROBE_INTERRUPT( 9), PROBE_INTERRUPT(10), PROBE_INTERRUPT(11),
|
PROBE_INTERRUPT( 8), PROBE_INTERRUPT( 9), PROBE_INTERRUPT(10), PROBE_INTERRUPT(11),
|
PROBE_INTERRUPT(12), PROBE_INTERRUPT(13), PROBE_INTERRUPT(14), PROBE_INTERRUPT(15)
|
PROBE_INTERRUPT(12), PROBE_INTERRUPT(13), PROBE_INTERRUPT(14), PROBE_INTERRUPT(15)
|
};
|
};
|
|
|
/*
|
/*
|
* Initial irq handlers.
|
* Initial irq handlers.
|
*/
|
*/
|
|
|
static void no_action (int irq, void *dev_id, struct pt_regs *regs)
|
static void no_action (int irq, void *dev_id, struct pt_regs *regs)
|
{
|
{
|
}
|
}
|
|
|
struct irqaction *irq_action[NR_IRQS];
|
struct irqaction *irq_action[NR_IRQS];
|
|
|
unsigned long validirqs[4] = {
|
unsigned long validirqs[4] = {
|
0x003fffff,
|
0x003fffff,
|
0x000001ff,
|
0x000001ff,
|
0x000000ff,
|
0x000000ff,
|
0x00000000
|
0x00000000
|
};
|
};
|
|
|
void (*irqjump[NR_IRQS])(void) = {
|
void (*irqjump[NR_IRQS])(void) = {
|
PROBE_INTERRUPT( 0), PROBE_INTERRUPT( 1), PROBE_INTERRUPT( 2), PROBE_INTERRUPT( 3),
|
PROBE_INTERRUPT( 0), PROBE_INTERRUPT( 1), PROBE_INTERRUPT( 2), PROBE_INTERRUPT( 3),
|
PROBE_INTERRUPT( 4), PROBE_INTERRUPT( 5), PROBE_INTERRUPT( 6), PROBE_INTERRUPT( 7),
|
PROBE_INTERRUPT( 4), PROBE_INTERRUPT( 5), PROBE_INTERRUPT( 6), PROBE_INTERRUPT( 7),
|
PROBE_INTERRUPT( 8), PROBE_INTERRUPT( 9), PROBE_INTERRUPT(10), PROBE_INTERRUPT(11),
|
PROBE_INTERRUPT( 8), PROBE_INTERRUPT( 9), PROBE_INTERRUPT(10), PROBE_INTERRUPT(11),
|
PROBE_INTERRUPT(12), PROBE_INTERRUPT(13), PROBE_INTERRUPT(14), PROBE_INTERRUPT(15),
|
PROBE_INTERRUPT(12), PROBE_INTERRUPT(13), PROBE_INTERRUPT(14), PROBE_INTERRUPT(15),
|
PROBE_INTERRUPT(16), PROBE_INTERRUPT(17), PROBE_INTERRUPT(18), PROBE_INTERRUPT(19),
|
PROBE_INTERRUPT(16), PROBE_INTERRUPT(17), PROBE_INTERRUPT(18), PROBE_INTERRUPT(19),
|
PROBE_INTERRUPT(20), PROBE_INTERRUPT(21), PROBE_INTERRUPT(22), PROBE_INTERRUPT(23)
|
PROBE_INTERRUPT(20), PROBE_INTERRUPT(21), PROBE_INTERRUPT(22), PROBE_INTERRUPT(23)
|
};
|
};
|
|
|
int get_irq_list(char *buf)
|
int get_irq_list(char *buf)
|
{
|
{
|
int i, len = 0;
|
int i, len = 0;
|
struct irqaction * action;
|
struct irqaction * action;
|
|
|
for (i = 0 ; i < NR_IRQS ; i++) {
|
for (i = 0 ; i < NR_IRQS ; i++) {
|
action = irq_action[i];
|
action = irq_action[i];
|
if (!action)
|
if (!action)
|
continue;
|
continue;
|
len += sprintf(buf+len, "%2d: %10u %c %s",
|
len += sprintf(buf+len, "%2d: %10u %c %s",
|
i, kstat.interrupts[i],
|
i, kstat.interrupts[i],
|
(action->flags & SA_INTERRUPT) ? '+' : ' ',
|
(action->flags & SA_INTERRUPT) ? '+' : ' ',
|
action->name);
|
action->name);
|
for (action = action->next; action; action = action->next) {
|
for (action = action->next; action; action = action->next) {
|
len += sprintf(buf+len, ",%s %s",
|
len += sprintf(buf+len, ",%s %s",
|
(action->flags & SA_INTERRUPT) ? " +" : "",
|
(action->flags & SA_INTERRUPT) ? " +" : "",
|
action->name);
|
action->name);
|
}
|
}
|
len += sprintf(buf+len, "\n");
|
len += sprintf(buf+len, "\n");
|
}
|
}
|
return len;
|
return len;
|
}
|
}
|
|
|
/*
|
/*
|
* do_IRQ handles IRQ's that have been installed without the
|
* do_IRQ handles IRQ's that have been installed without the
|
* SA_INTERRUPT flag: it uses the full signal-handling return
|
* SA_INTERRUPT flag: it uses the full signal-handling return
|
* and runs with other interrupts enabled. All relatively slow
|
* and runs with other interrupts enabled. All relatively slow
|
* IRQ's should use this format: notably the keyboard/timer
|
* IRQ's should use this format: notably the keyboard/timer
|
* routines.
|
* routines.
|
*/
|
*/
|
asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
|
asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
|
{
|
{
|
struct irqaction * action = *(irq + irq_action);
|
struct irqaction * action = *(irq + irq_action);
|
int do_random = 0;
|
int do_random = 0;
|
|
|
kstat.interrupts[irq]++;
|
kstat.interrupts[irq]++;
|
|
|
while (action) {
|
while (action) {
|
do_random |= action->flags;
|
do_random |= action->flags;
|
action->handler(irq, action->dev_id, regs);
|
action->handler(irq, action->dev_id, regs);
|
action = action->next;
|
action = action->next;
|
}
|
}
|
if (do_random & SA_SAMPLE_RANDOM)
|
if (do_random & SA_SAMPLE_RANDOM)
|
add_interrupt_randomness(irq);
|
add_interrupt_randomness(irq);
|
}
|
}
|
|
|
/*
|
/*
|
* do_fast_IRQ handles IRQ's that don't need the fancy interrupt return
|
* do_fast_IRQ handles IRQ's that don't need the fancy interrupt return
|
* stuff - the handler is also running with interrupts disabled unless
|
* stuff - the handler is also running with interrupts disabled unless
|
* it explicitly enables them later.
|
* it explicitly enables them later.
|
*/
|
*/
|
asmlinkage void do_fast_IRQ(int irq)
|
asmlinkage void do_fast_IRQ(int irq)
|
{
|
{
|
struct irqaction * action = *(irq + irq_action);
|
struct irqaction * action = *(irq + irq_action);
|
int do_random = 0;
|
int do_random = 0;
|
|
|
kstat.interrupts[irq]++;
|
kstat.interrupts[irq]++;
|
|
|
while (action) {
|
while (action) {
|
do_random |= action->flags;
|
do_random |= action->flags;
|
action->handler(irq, action->dev_id, NULL);
|
action->handler(irq, action->dev_id, NULL);
|
action = action->next;
|
action = action->next;
|
}
|
}
|
if (do_random & SA_SAMPLE_RANDOM)
|
if (do_random & SA_SAMPLE_RANDOM)
|
add_interrupt_randomness(irq);
|
add_interrupt_randomness(irq);
|
}
|
}
|
|
|
#define SA_PROBE SA_ONESHOT
|
#define SA_PROBE SA_ONESHOT
|
|
|
int setup_arm_irq(int irq, struct irqaction * new)
|
int setup_arm_irq(int irq, struct irqaction * new)
|
{
|
{
|
int shared = 0;
|
int shared = 0;
|
struct irqaction *old, **p;
|
struct irqaction *old, **p;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
p = irq_action + irq;
|
p = irq_action + irq;
|
|
|
if ((old = *p) != NULL) {
|
if ((old = *p) != NULL) {
|
/* Can't share interrupts unless both agree to */
|
/* Can't share interrupts unless both agree to */
|
if (!(old->flags & new->flags & SA_SHIRQ))
|
if (!(old->flags & new->flags & SA_SHIRQ))
|
return -EBUSY;
|
return -EBUSY;
|
|
|
/* Can't share interrupts unless both are same type */
|
/* Can't share interrupts unless both are same type */
|
if ((old->flags ^ new->flags) & SA_INTERRUPT)
|
if ((old->flags ^ new->flags) & SA_INTERRUPT)
|
return -EBUSY;
|
return -EBUSY;
|
|
|
/* add new interrupt at end of irq queue */
|
/* add new interrupt at end of irq queue */
|
do {
|
do {
|
p = &old->next;
|
p = &old->next;
|
old = *p;
|
old = *p;
|
} while (old);
|
} while (old);
|
shared = 1;
|
shared = 1;
|
}
|
}
|
|
|
if (new->flags & SA_SAMPLE_RANDOM)
|
if (new->flags & SA_SAMPLE_RANDOM)
|
rand_initialize_irq(irq);
|
rand_initialize_irq(irq);
|
|
|
save_flags_cli(flags);
|
save_flags_cli(flags);
|
|
|
*p = new;
|
*p = new;
|
|
|
if (!shared) {
|
if (!shared) {
|
if (irq < 24) {
|
if (irq < 24) {
|
if (!(new->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
|
if (!(new->flags & SA_PROBE)) { /* SA_ONESHOT is used by probing */
|
if (new->flags & SA_INTERRUPT)
|
if (new->flags & SA_INTERRUPT)
|
irqjump[irq] = fast_interrupt[irq];
|
irqjump[irq] = fast_interrupt[irq];
|
else
|
else
|
irqjump[irq] = interrupt[irq];
|
irqjump[irq] = interrupt[irq];
|
} else
|
} else
|
irqjump[irq] = probe_interrupt[irq];
|
irqjump[irq] = probe_interrupt[irq];
|
}
|
}
|
unmask_irq(irq);
|
unmask_irq(irq);
|
}
|
}
|
restore_flags(flags);
|
restore_flags(flags);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* Using "struct sigaction" is slightly silly, but there
|
* Using "struct sigaction" is slightly silly, but there
|
* are historical reasons and it works well, so..
|
* are historical reasons and it works well, so..
|
*/
|
*/
|
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
|
int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
|
unsigned long irq_flags, const char * devname, void *dev_id)
|
unsigned long irq_flags, const char * devname, void *dev_id)
|
{
|
{
|
unsigned long retval;
|
unsigned long retval;
|
struct irqaction *action;
|
struct irqaction *action;
|
|
|
if (irq >= NR_IRQS || !(validirqs[irq >> 5] & (1 << (irq & 31))))
|
if (irq >= NR_IRQS || !(validirqs[irq >> 5] & (1 << (irq & 31))))
|
return -EINVAL;
|
return -EINVAL;
|
if (!handler)
|
if (!handler)
|
return -EINVAL;
|
return -EINVAL;
|
|
|
action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
|
action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
|
if (!action)
|
if (!action)
|
return -ENOMEM;
|
return -ENOMEM;
|
|
|
action->handler = handler;
|
action->handler = handler;
|
action->flags = irq_flags;
|
action->flags = irq_flags;
|
action->mask = 0;
|
action->mask = 0;
|
action->name = devname;
|
action->name = devname;
|
action->next = NULL;
|
action->next = NULL;
|
action->dev_id = dev_id;
|
action->dev_id = dev_id;
|
|
|
retval = setup_arm_irq(irq, action);
|
retval = setup_arm_irq(irq, action);
|
|
|
if (retval)
|
if (retval)
|
kfree(action);
|
kfree(action);
|
return retval;
|
return retval;
|
}
|
}
|
|
|
void free_irq(unsigned int irq, void *dev_id)
|
void free_irq(unsigned int irq, void *dev_id)
|
{
|
{
|
struct irqaction * action, **p;
|
struct irqaction * action, **p;
|
unsigned long flags;
|
unsigned long flags;
|
|
|
if (irq >= NR_IRQS || !(validirqs[irq >> 5] & (1 << (irq & 31)))) {
|
if (irq >= NR_IRQS || !(validirqs[irq >> 5] & (1 << (irq & 31)))) {
|
printk (KERN_ERR "Trying to free IRQ%d\n",irq);
|
printk (KERN_ERR "Trying to free IRQ%d\n",irq);
|
#ifdef CONFIG_DEBUG_ERRORS
|
#ifdef CONFIG_DEBUG_ERRORS
|
__backtrace();
|
__backtrace();
|
#endif
|
#endif
|
return;
|
return;
|
}
|
}
|
|
|
for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
|
for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
|
if (action->dev_id != dev_id)
|
if (action->dev_id != dev_id)
|
continue;
|
continue;
|
|
|
/* Found it - now free it */
|
/* Found it - now free it */
|
save_flags_cli (flags);
|
save_flags_cli (flags);
|
*p = action->next;
|
*p = action->next;
|
if (!irq_action[irq]) {
|
if (!irq_action[irq]) {
|
mask_irq(irq);
|
mask_irq(irq);
|
if (irq < 24)
|
if (irq < 24)
|
irqjump[irq] = bad_interrupt[irq];
|
irqjump[irq] = bad_interrupt[irq];
|
}
|
}
|
restore_flags (flags);
|
restore_flags (flags);
|
kfree(action);
|
kfree(action);
|
return;
|
return;
|
}
|
}
|
printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
|
printk(KERN_ERR "Trying to free free IRQ%d\n",irq);
|
#ifdef CONFIG_DEBUG_ERRORS
|
#ifdef CONFIG_DEBUG_ERRORS
|
__backtrace();
|
__backtrace();
|
#endif
|
#endif
|
}
|
}
|
|
|
unsigned long probe_irq_on (void)
|
unsigned long probe_irq_on (void)
|
{
|
{
|
unsigned int i, irqs = 0, irqmask;
|
unsigned int i, irqs = 0, irqmask;
|
unsigned long delay;
|
unsigned long delay;
|
|
|
/* first snaffle up any unassigned irqs */
|
/* first snaffle up any unassigned irqs */
|
for (i = 15; i > 0; i--) {
|
for (i = 15; i > 0; i--) {
|
if (!request_irq (i, no_action, SA_PROBE, "probe", NULL))
|
if (!request_irq (i, no_action, SA_PROBE, "probe", NULL))
|
irqs |= 1 << i;
|
irqs |= 1 << i;
|
}
|
}
|
|
|
/* wait for spurious interrupts to mask themselves out again */
|
/* wait for spurious interrupts to mask themselves out again */
|
for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */
|
for (delay = jiffies + 2; delay > jiffies; ); /* min 10ms delay */
|
|
|
/* now filter out any obviously spurious interrupts */
|
/* now filter out any obviously spurious interrupts */
|
irqmask = ~get_enabled_irqs();
|
irqmask = ~get_enabled_irqs();
|
for (i = 15; i > 0; i--) {
|
for (i = 15; i > 0; i--) {
|
if (irqs & (1 << i) & irqmask) {
|
if (irqs & (1 << i) & irqmask) {
|
irqs ^= 1 << i;
|
irqs ^= 1 << i;
|
free_irq (i, NULL);
|
free_irq (i, NULL);
|
}
|
}
|
}
|
}
|
return irqs;
|
return irqs;
|
}
|
}
|
|
|
int probe_irq_off (unsigned long irqs)
|
int probe_irq_off (unsigned long irqs)
|
{
|
{
|
unsigned int i, irqmask;
|
unsigned int i, irqmask;
|
|
|
irqmask = ~get_enabled_irqs();
|
irqmask = ~get_enabled_irqs();
|
|
|
for (i = 15; i > 0; i--) {
|
for (i = 15; i > 0; i--) {
|
if (irqs & (1 << i))
|
if (irqs & (1 << i))
|
free_irq (i, NULL);
|
free_irq (i, NULL);
|
}
|
}
|
|
|
irqs &= irqmask;
|
irqs &= irqmask;
|
if (!irqs)
|
if (!irqs)
|
return 0;
|
return 0;
|
i = ffz (~irqs);
|
i = ffz (~irqs);
|
if (irqs != (irqs & (1 << i)))
|
if (irqs != (irqs & (1 << i)))
|
i = -i;
|
i = -i;
|
return i;
|
return i;
|
}
|
}
|
|
|
void init_IRQ(void)
|
void init_IRQ(void)
|
{
|
{
|
extern void init_dma(void);
|
extern void init_dma(void);
|
irq_init_irq();
|
irq_init_irq();
|
init_dma();
|
init_dma();
|
}
|
}
|
|
|