/*
 * ints.c -- 680x0 Linux general interrupt handling code
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file README.legal in the main directory of this archive
 * for more details.
 */

#include <asm/system.h>

#include <linux/types.h>
#include <linux/config.h>
#include <linux/interrupt.h>
#include <linux/sched.h>

/* list is accessed 0-6 for IRQs 1-7 and accessed 7-22 for MFP 0-15 */
static isr_node_t *isr_list[IRQMF15];

/* The number of spurious interrupts */
unsigned long num_spurious;

unsigned long intr_count = 0;
unsigned long bh_active = 0;
unsigned long bh_mask = 0xFFFFFFFF;
struct bh_struct bh_base[32]; 

struct intframe {
	unsigned long  regs[4];      /* d0-d1,a0-a1 */
	unsigned short sr;	     /* status register */
	unsigned long  pc;	     /* program counter */
	unsigned       format :  4;  /* frame format specifier */
	unsigned       vector : 12;  /* vector offset */
};

/*
 * void init_INTS(void)
 *
 * Parameters:	None
 *
 * Returns:	Nothing
 *
 * This function should be called during kernel startup to initialize
 * the IRQ handling routines.
 */

void init_INTS(void)
{
    int i;

    /* initialize the ISR list pointers */
    for (i = 0; i < IRQMF15; i++)
			isr_list[i] = NULL;

    num_spurious = 0;

    /* intialize the bottom half routines. */
    for (i = 0; i < 32; i++) {
	    bh_base[i].routine = NULL;
	    bh_base[i].data = NULL;
    }
    bh_active = 0;
    intr_count = 0;

    mach_init_INTS ();
    /* enable CPU interrupts */
    sti(); 
}

/*
 * do_bottom_half() runs at normal kernel priority: all interrupts
 * enabled.  do_bottom_half() is atomic with respect to itself: a
 * bottom_half handler need not be re-entrant.
 */
asmlinkage void do_bottom_half(void)
{
	unsigned long active;
	unsigned long mask, left;
	struct bh_struct *bh;

	bh = bh_base;
	active = bh_active & bh_mask;

	for (mask = 1, left = ~0 ; left & active ; bh++,mask += mask,left += left) {
		if (mask & active) {
			void (*fn)(void *);
			bh_active &= ~mask;
			fn = bh->routine;
			if (!fn)
				goto bad_bh;
			fn(bh->data);
		}
	}
	return;
bad_bh:
	printk ("irq.c:bad bottom half entry\n");
}

void insert_isr (isr_node_t **listp, isr_node_t *node)
{
    unsigned long spl;
    isr_node_t *cur, *last = NULL;

    save_flags(spl);
    cli();

    cur = *listp;

    if (!cur || cur->pri > node->pri) {
			if (cur)
	  	  node->next = cur;
			*listp = node;
			restore_flags (spl);
			return;
    }

    while (cur->next && cur->pri <= node->pri)
    {
			last = cur;
			cur = cur->next;
    }

    if (!cur->next) {
			cur->next = node;
    } else {
			node->next = cur;
			last->next = node;
    }

    restore_flags(spl);
}

isr_node_t *new_isr_node(void)
{
    isr_node_t *np;

    static isr_node_t nodes[32];
    static int curnode = 0;

    np = &nodes[curnode++];
    if (curnode == 100)
			panic ("new_isr_node: out of nodes");

    return np;
}

int add_isr (unsigned long source, isrfunc isr, int pri, void *data)
{
    isr_node_t *p;

    if (source < IRQ1 || source > IRQMF15)
    	panic ("add_isr: Incorrect IRQ source");

    p = new_isr_node();
    p->isr = isr;
    p->pri = pri;
    p->data = data;
    p->next = NULL;

    insert_isr (&isr_list[source-1], p);
        
    return 1;
}

void call_isr_list(isr_node_t *p, struct intframe *fp)
{
    while (p) {
 			p->isr (fp, p->data);
			p = p->next;
    } 
}


asmlinkage void process_int(int level, struct intframe *fp)
{
   if(level<8) 
    call_isr_list (isr_list[level-1], fp);
   else
    call_isr_list (isr_list[level-33], fp); 
}
