/*
|
/*
|
* ints.c -- 680x0 Linux general interrupt handling code
|
* ints.c -- 680x0 Linux general interrupt handling code
|
*
|
*
|
* This file is subject to the terms and conditions of the GNU General Public
|
* This file is subject to the terms and conditions of the GNU General Public
|
* License. See the file COPYING in the main directory of this archive
|
* License. See the file COPYING in the main directory of this archive
|
* for more details.
|
* for more details.
|
*
|
*
|
* 07/03/96: Timer initialization, and thus mach_sched_init(),
|
* 07/03/96: Timer initialization, and thus mach_sched_init(),
|
* removed from request_irq() and moved to init_time().
|
* removed from request_irq() and moved to init_time().
|
* We should therefore consider renaming our add_isr() and
|
* We should therefore consider renaming our add_isr() and
|
* remove_isr() to request_irq() and free_irq()
|
* remove_isr() to request_irq() and free_irq()
|
* respectively, so they are compliant with the other
|
* respectively, so they are compliant with the other
|
* architectures. /Jes
|
* architectures. /Jes
|
*/
|
*/
|
|
|
#include <linux/types.h>
|
#include <linux/types.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/kernel_stat.h>
|
#include <linux/kernel_stat.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
|
|
#include <asm/system.h>
|
#include <asm/system.h>
|
#include <asm/irq.h>
|
#include <asm/irq.h>
|
#include <asm/traps.h>
|
#include <asm/traps.h>
|
#include <asm/page.h>
|
#include <asm/page.h>
|
#include <asm/machdep.h>
|
#include <asm/machdep.h>
|
|
|
/* list is accessed 0-6 for IRQs 1-7 */
|
/* list is accessed 0-6 for IRQs 1-7 */
|
static isr_node_t *isr_list[7];
|
static isr_node_t *isr_list[7];
|
|
|
/* The number of spurious interrupts */
|
/* The number of spurious interrupts */
|
volatile unsigned long num_spurious;
|
volatile unsigned long num_spurious;
|
/*
|
/*
|
unsigned long interrupt_stack[PAGE_SIZE/sizeof(long)];
|
unsigned long interrupt_stack[PAGE_SIZE/sizeof(long)];
|
*/
|
*/
|
|
|
/*
|
/*
|
* void init_IRQ(void)
|
* void init_IRQ(void)
|
*
|
*
|
* Parameters: None
|
* Parameters: None
|
*
|
*
|
* Returns: Nothing
|
* Returns: Nothing
|
*
|
*
|
* This function should be called during kernel startup to initialize
|
* This function should be called during kernel startup to initialize
|
* the IRQ handling routines.
|
* the IRQ handling routines.
|
*/
|
*/
|
|
|
void init_IRQ(void)
|
void init_IRQ(void)
|
{
|
{
|
/* Setup interrupt stack pointer */
|
/* Setup interrupt stack pointer */
|
/*
|
/*
|
asm ("movec %0,%/isp"
|
asm ("movec %0,%/isp"
|
: : "r" (interrupt_stack + sizeof (interrupt_stack) / sizeof (long)));
|
: : "r" (interrupt_stack + sizeof (interrupt_stack) / sizeof (long)));
|
*/
|
*/
|
mach_init_INTS ();
|
mach_init_INTS ();
|
}
|
}
|
|
|
void insert_isr (isr_node_t **listp, isr_node_t *node)
|
void insert_isr (isr_node_t **listp, isr_node_t *node)
|
{
|
{
|
unsigned long spl;
|
unsigned long spl;
|
isr_node_t *cur;
|
isr_node_t *cur;
|
|
|
save_flags(spl);
|
save_flags(spl);
|
cli();
|
cli();
|
|
|
cur = *listp;
|
cur = *listp;
|
|
|
while (cur && cur->pri <= node->pri)
|
while (cur && cur->pri <= node->pri)
|
{
|
{
|
listp = &cur->next;
|
listp = &cur->next;
|
cur = cur->next;
|
cur = cur->next;
|
}
|
}
|
|
|
node->next = cur;
|
node->next = cur;
|
*listp = node;
|
*listp = node;
|
|
|
restore_flags(spl);
|
restore_flags(spl);
|
}
|
}
|
|
|
void delete_isr (isr_node_t **listp, isrfunc isr, void *data)
|
void delete_isr (isr_node_t **listp, isrfunc isr, void *data)
|
{
|
{
|
unsigned long flags;
|
unsigned long flags;
|
isr_node_t *np;
|
isr_node_t *np;
|
|
|
save_flags(flags);
|
save_flags(flags);
|
cli();
|
cli();
|
for (np = *listp; np; listp = &np->next, np = *listp) {
|
for (np = *listp; np; listp = &np->next, np = *listp) {
|
if (np->isr == isr && np->data == data) {
|
if (np->isr == isr && np->data == data) {
|
*listp = np->next;
|
*listp = np->next;
|
/* Mark it as free. */
|
/* Mark it as free. */
|
np->isr = NULL;
|
np->isr = NULL;
|
restore_flags(flags);
|
restore_flags(flags);
|
return;
|
return;
|
}
|
}
|
}
|
}
|
restore_flags(flags);
|
restore_flags(flags);
|
printk ("delete_isr: isr %p not found on list!\n", isr);
|
printk ("delete_isr: isr %p not found on list!\n", isr);
|
}
|
}
|
|
|
#define NUM_ISR_NODES 100
|
#define NUM_ISR_NODES 100
|
static isr_node_t nodes[NUM_ISR_NODES];
|
static isr_node_t nodes[NUM_ISR_NODES];
|
|
|
isr_node_t *new_isr_node(void)
|
isr_node_t *new_isr_node(void)
|
{
|
{
|
isr_node_t *np;
|
isr_node_t *np;
|
|
|
for (np = nodes; np < &nodes[NUM_ISR_NODES]; np++)
|
for (np = nodes; np < &nodes[NUM_ISR_NODES]; np++)
|
if (np->isr == NULL)
|
if (np->isr == NULL)
|
return np;
|
return np;
|
|
|
printk ("new_isr_node: out of nodes");
|
printk ("new_isr_node: out of nodes");
|
return NULL;
|
return NULL;
|
}
|
}
|
|
|
int add_isr (unsigned long source, isrfunc isr, int pri, void *data,
|
int add_isr (unsigned long source, isrfunc isr, int pri, void *data,
|
char *name)
|
char *name)
|
{
|
{
|
isr_node_t *p;
|
isr_node_t *p;
|
|
|
if (source & IRQ_MACHSPEC)
|
if (source & IRQ_MACHSPEC)
|
{
|
{
|
return mach_add_isr (source, isr, pri, data, name);
|
return mach_add_isr (source, isr, pri, data, name);
|
}
|
}
|
|
|
if (source < IRQ1 || source > IRQ7)
|
if (source < IRQ1 || source > IRQ7)
|
panic ("add_isr: Incorrect IRQ source %ld from %s\n", source, name);
|
panic ("add_isr: Incorrect IRQ source %ld from %s\n", source, name);
|
|
|
p = new_isr_node();
|
p = new_isr_node();
|
if (p == NULL)
|
if (p == NULL)
|
return 0;
|
return 0;
|
p->isr = isr;
|
p->isr = isr;
|
p->pri = pri;
|
p->pri = pri;
|
p->data = data;
|
p->data = data;
|
p->name = name;
|
p->name = name;
|
p->next = NULL;
|
p->next = NULL;
|
|
|
insert_isr (&isr_list[source-1], p);
|
insert_isr (&isr_list[source-1], p);
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
int remove_isr (unsigned long source, isrfunc isr, void *data)
|
int remove_isr (unsigned long source, isrfunc isr, void *data)
|
{
|
{
|
if (source & IRQ_MACHSPEC)
|
if (source & IRQ_MACHSPEC)
|
return mach_remove_isr (source, isr, data);
|
return mach_remove_isr (source, isr, data);
|
|
|
if (source < IRQ1 || source > IRQ7) {
|
if (source < IRQ1 || source > IRQ7) {
|
printk ("remove_isr: Incorrect IRQ source %ld\n", source);
|
printk ("remove_isr: Incorrect IRQ source %ld\n", source);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
delete_isr (&isr_list[source - 1], isr, data);
|
delete_isr (&isr_list[source - 1], isr, data);
|
return 1;
|
return 1;
|
}
|
}
|
|
|
void call_isr_list(int irq, isr_node_t *p, struct pt_regs *fp)
|
void call_isr_list(int irq, isr_node_t *p, struct pt_regs *fp)
|
{
|
{
|
while (p) {
|
while (p) {
|
p->isr (irq, fp, p->data);
|
p->isr (irq, fp, p->data);
|
p = p->next;
|
p = p->next;
|
}
|
}
|
}
|
}
|
|
|
asmlinkage void process_int(int vec, struct pt_regs *regs)
|
asmlinkage void process_int(int vec, struct pt_regs *regs)
|
{
|
{
|
int level;
|
int level;
|
|
|
if (vec >= VECOFF(VEC_INT1) && vec <= VECOFF(VEC_INT7))
|
if (vec >= VECOFF(VEC_INT1) && vec <= VECOFF(VEC_INT7))
|
level = (vec - VECOFF(VEC_SPUR)) >> 2;
|
level = (vec - VECOFF(VEC_SPUR)) >> 2;
|
else {
|
else {
|
if (mach_process_int)
|
if (mach_process_int)
|
mach_process_int(vec, regs);
|
mach_process_int(vec, regs);
|
else
|
else
|
panic("Can't process interrupt vector 0x%03x\n", vec);
|
panic("Can't process interrupt vector 0x%03x\n", vec);
|
return;
|
return;
|
}
|
}
|
|
|
kstat.interrupts[level]++;
|
kstat.interrupts[level]++;
|
call_isr_list (level, isr_list[level-1], regs);
|
call_isr_list (level, isr_list[level-1], regs);
|
}
|
}
|
|
|
int request_irq(unsigned int irq,
|
int request_irq(unsigned int irq,
|
void (*handler)(int, void *, struct pt_regs *),
|
void (*handler)(int, void *, struct pt_regs *),
|
unsigned long flags, const char * devname, void *dev_id)
|
unsigned long flags, const char * devname, void *dev_id)
|
{
|
{
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
|
|
void free_irq(unsigned int irq, void *dev_id)
|
void free_irq(unsigned int irq, void *dev_id)
|
{
|
{
|
}
|
}
|
|
|
/*
|
/*
|
* Do we need these probe functions on the m68k?
|
* Do we need these probe functions on the m68k?
|
*/
|
*/
|
unsigned long probe_irq_on (void)
|
unsigned long probe_irq_on (void)
|
{
|
{
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int probe_irq_off (unsigned long irqs)
|
int probe_irq_off (unsigned long irqs)
|
{
|
{
|
return 0;
|
return 0;
|
}
|
}
|
|
|
void enable_irq(unsigned int irq_nr)
|
void enable_irq(unsigned int irq_nr)
|
{
|
{
|
if ((irq_nr & IRQ_MACHSPEC) && mach_enable_irq)
|
if ((irq_nr & IRQ_MACHSPEC) && mach_enable_irq)
|
mach_enable_irq(irq_nr);
|
mach_enable_irq(irq_nr);
|
}
|
}
|
|
|
void disable_irq(unsigned int irq_nr)
|
void disable_irq(unsigned int irq_nr)
|
{
|
{
|
if ((irq_nr & IRQ_MACHSPEC) && mach_disable_irq)
|
if ((irq_nr & IRQ_MACHSPEC) && mach_disable_irq)
|
mach_disable_irq(irq_nr);
|
mach_disable_irq(irq_nr);
|
}
|
}
|
|
|
int get_irq_list(char *buf)
|
int get_irq_list(char *buf)
|
{
|
{
|
int i, len = 0;
|
int i, len = 0;
|
isr_node_t *p;
|
isr_node_t *p;
|
|
|
/* autovector interrupts */
|
/* autovector interrupts */
|
for (i = IRQ1; i <= IRQ7; ++i) {
|
for (i = IRQ1; i <= IRQ7; ++i) {
|
if (!isr_list[i-1])
|
if (!isr_list[i-1])
|
continue;
|
continue;
|
len += sprintf(buf+len, "auto %2d: %8d ", i, kstat.interrupts[i]);
|
len += sprintf(buf+len, "auto %2d: %8d ", i, kstat.interrupts[i]);
|
for (p = isr_list[i-1]; p; p = p->next) {
|
for (p = isr_list[i-1]; p; p = p->next) {
|
len += sprintf(buf+len, "%s\n", p->name);
|
len += sprintf(buf+len, "%s\n", p->name);
|
if (p->next)
|
if (p->next)
|
len += sprintf(buf+len, " ");
|
len += sprintf(buf+len, " ");
|
}
|
}
|
}
|
}
|
|
|
len = mach_get_irq_list(buf, len);
|
len = mach_get_irq_list(buf, len);
|
return len;
|
return len;
|
}
|
}
|
|
|