version 1.1.1.1, 2008/06/03 10:38:46 |
version 1.1.1.1.2.1, 2008/08/13 17:12:31 |
|
|
#include <irq.h> |
#include <irq.h> |
|
|
/* forward declarations */ |
/* forward declarations */ |
static void irq_thread(u_long); |
static void irq_thread(void *); |
|
|
static struct irq *irq_table[NIRQS]; /* IRQ descriptor table */ |
static struct irq *irq_table[NIRQS]; /* IRQ descriptor table */ |
static volatile int nr_irq_locks; /* lock count for interrupt */ |
static volatile int nr_irq_locks; /* lock counter */ |
static volatile int saved_irq_state; /* IRQ state saved by irq_lock() */ |
static volatile int saved_irq_state; /* state saved by irq_lock() */ |
|
|
/* |
/* |
* irq_attach - attach ISR and IST to the specified interrupt. |
* irq_attach - attach ISR and IST to the specified interrupt. |
* @vector: interrupt vector number |
|
* @prio: interrupt priority level. |
|
* @shared: true if it allows the IRQ sharing. |
|
* @isr: pointer to the interrupt service routine. |
|
* @ist: pointer to the interrupt service thread. NULL for no ist. |
|
* |
* |
* irq_attach() returns irq handle which is needed for irq_detach(). |
* Returns irq handle, or NULL on failure. The interrupt of |
* Or, it returns -1 if it failed. |
* attached irq will be unmasked (enabled) in this routine. |
* The interrupt of attached irq will be unmasked (enabled) in this |
|
* routine. |
|
* |
|
* TODO: Interrupt sharing is not supported, for now. |
* TODO: Interrupt sharing is not supported, for now. |
*/ |
*/ |
int |
irq_t |
irq_attach(int vector, int prio, int shared, int (*isr)(int), |
irq_attach(int vector, int prio, int shared, int (*isr)(int), void (*ist)(int)) |
void (*ist)(int)) |
|
{ |
{ |
struct irq *irq; |
struct irq *irq; |
thread_t th; |
|
int mode; |
int mode; |
|
|
ASSERT(irq_level == 0); |
ASSERT(irq_level == 0); |
|
|
sched_lock(); |
sched_lock(); |
if ((irq = kmem_alloc(sizeof(struct irq))) == NULL) { |
if ((irq = kmem_alloc(sizeof(struct irq))) == NULL) { |
sched_unlock(); |
sched_unlock(); |
return -1; |
return NULL; |
} |
} |
memset(irq, 0, sizeof(struct irq)); |
memset(irq, 0, sizeof(struct irq)); |
irq->vector = vector; |
irq->vector = vector; |
|
|
/* |
/* |
* Create a new thread for IST. |
* Create a new thread for IST. |
*/ |
*/ |
th = kernel_thread(ISTPRIO(prio), irq_thread, (u_long)irq); |
irq->thread = kthread_create(&irq_thread, irq, ISTPRIO(prio)); |
if (th == NULL) |
if (irq->thread == NULL) { |
panic("irq_attach"); |
kmem_free(irq); |
irq->thread = th; |
sched_unlock(); |
event_init(&irq->ist_event, "interrupt"); |
return NULL; |
|
} |
|
event_init(&irq->istevt, "interrupt"); |
} |
} |
irq_table[vector] = irq; |
irq_table[vector] = irq; |
mode = shared ? IMODE_LEVEL : IMODE_EDGE; |
|
|
|
irq_lock(); |
irq_lock(); |
|
mode = shared ? IMODE_LEVEL : IMODE_EDGE; |
interrupt_setup(vector, mode); |
interrupt_setup(vector, mode); |
interrupt_unmask(vector, prio); |
interrupt_unmask(vector, prio); |
irq_unlock(); |
irq_unlock(); |
|
|
sched_unlock(); |
sched_unlock(); |
printk("IRQ%d attached priority=%d\n", vector, prio); |
DPRINTF(("IRQ%d attached priority=%d\n", vector, prio)); |
return (int)irq; |
return irq; |
} |
} |
|
|
/* |
/* |
* Detach an interrupt handler from the interrupt chain. |
* Detach an interrupt handler from the interrupt chain. |
* The detached interrupt will be masked off if nobody attaches |
* The detached interrupt will be masked off if nobody |
* to it, anymore. |
* attaches to it, anymore. |
*/ |
*/ |
void |
void |
irq_detach(int handle) |
irq_detach(irq_t irq) |
{ |
{ |
struct irq *irq = (struct irq *)handle; |
|
|
|
ASSERT(irq_level == 0); |
ASSERT(irq_level == 0); |
ASSERT(irq); |
ASSERT(irq); |
ASSERT(irq->vector < NIRQS); |
ASSERT(irq->vector < NIRQS); |
|
|
|
|
irq_table[irq->vector] = NULL; |
irq_table[irq->vector] = NULL; |
if (irq->thread != NULL) |
if (irq->thread != NULL) |
thread_kill(irq->thread); |
kthread_terminate(irq->thread); |
|
|
kmem_free(irq); |
kmem_free(irq); |
} |
} |
|
|
|
|
* Lock IRQ. |
* Lock IRQ. |
* |
* |
* All H/W interrupts are masked off. |
* All H/W interrupts are masked off. |
* Caller is no need to save the interrupt state before irq_lock() |
* Caller is no need to save the interrupt state before |
* because it is automatically restored in irq_unlock() when no one |
* irq_lock() because it is automatically restored in |
* is locking the IRQ anymore. |
* irq_unlock() when no one is locking the IRQ anymore. |
*/ |
*/ |
void |
void |
irq_lock(void) |
irq_lock(void) |
|
|
interrupt_disable(); |
interrupt_disable(); |
if (++nr_irq_locks == 1) |
if (++nr_irq_locks == 1) |
saved_irq_state = s; |
saved_irq_state = s; |
|
|
|
ASSERT(nr_irq_locks != 0); |
} |
} |
|
|
/* |
/* |
* Unlock IRQ. |
* Unlock IRQ. |
* |
* |
* If lock count becomes 0, the interrupt is restored to original |
* If lock count becomes 0, the interrupt is restored to |
* state at first irq_lock() call. |
* original state at first irq_lock() call. |
*/ |
*/ |
void |
void |
irq_unlock(void) |
irq_unlock(void) |
|
|
* This is a common dispatcher to all interrupt threads. |
* This is a common dispatcher to all interrupt threads. |
*/ |
*/ |
static void |
static void |
irq_thread(u_long arg) |
irq_thread(void *arg) |
{ |
{ |
int vec; |
int vec; |
void (*func)(int); |
void (*func)(int); |
|
|
|
|
for (;;) { |
for (;;) { |
interrupt_disable(); |
interrupt_disable(); |
if (irq->ist_request <= 0) { |
if (irq->istreq <= 0) { |
/* |
/* |
* Since the interrupt is disabled above, an |
* Since the interrupt is disabled above, |
* interrupt for this vector keeps pending until |
* an interrupt for this vector keeps |
* this thread enters sleep state. Thus, we don't |
* pending until this thread enters sleep |
* lose any IST requests even if the interrupt |
* state. Thus, we don't lose any IST |
* is fired here. |
* requests even if the interrupt is fired |
|
* here. |
*/ |
*/ |
sched_sleep(&irq->ist_event); |
sched_sleep(&irq->istevt); |
} |
} |
irq->ist_request--; |
irq->istreq--; |
ASSERT(irq->ist_request >= 0); |
ASSERT(irq->istreq >= 0); |
interrupt_enable(); |
interrupt_enable(); |
|
|
/* Call IST */ |
/* |
(func)(vec); |
* Call IST |
|
*/ |
|
(*func)(vec); |
} |
} |
/* NOTREACHED */ |
/* NOTREACHED */ |
} |
} |
|
|
/* |
/* |
* Interrupt handler. |
* Interrupt handler. |
* |
* |
* This routine will call the corresponding ISR for the requested |
* This routine will call the corresponding ISR for the |
* interrupt vector. This routine is called from the code in the |
* requested interrupt vector. This routine is called from |
* architecture dependent layer. We assumes the scheduler is already |
* the code in the architecture dependent layer. We |
* locked by caller. |
* assumes the scheduler is already locked by caller. |
*/ |
*/ |
void |
void |
irq_handler(int vector) |
irq_handler(int vector) |
|
|
return; /* Ignore stray interrupt */ |
return; /* Ignore stray interrupt */ |
ASSERT(irq->isr); |
ASSERT(irq->isr); |
|
|
/* Call ISR */ |
/* |
rc = (irq->isr)(vector); |
* Call ISR |
|
*/ |
|
rc = (*irq->isr)(vector); |
|
|
if (rc == INT_CONTINUE) { |
if (rc == INT_CONTINUE) { |
/* Kick IST */ |
/* |
|
* Kick IST |
|
*/ |
ASSERT(irq->ist); |
ASSERT(irq->ist); |
irq->ist_request++; |
irq->istreq++; |
sched_wakeup(&irq->ist_event); |
sched_wakeup(&irq->istevt); |
|
ASSERT(irq->istreq != 0); |
} |
} |
irq->count++; |
|
} |
} |
|
|
#if defined(DEBUG) && defined(CONFIG_KDUMP) |
|
void |
void |
irq_dump(void) |
|
{ |
|
int vector; |
|
struct irq *irq; |
|
|
|
printk("IRQ dump:\n"); |
|
printk(" vector isr ist prio count\n"); |
|
printk(" ------ -------- -------- -------- --------\n"); |
|
|
|
for (vector = 0; vector < NIRQS; vector++) { |
|
irq = irq_table[vector]; |
|
if (irq) { |
|
printk(" %4d %08x %08x %3d %8d\n", |
|
vector, irq->isr, irq->ist, |
|
(irq->thread ? irq->thread->prio : 0), |
|
irq->count); |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
void |
|
irq_init(void) |
irq_init(void) |
{ |
{ |
/* |
|
* Start interrupt processing. |
/* Start interrupt processing. */ |
*/ |
|
interrupt_enable(); |
interrupt_enable(); |
} |
} |