/*
 * 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>

/*
 * ++roman: The number of possible interrupts is 7 only if there are no
 * non-autovec ints. But e.g. the Atari uses a lot of them. So I defined
 * a constant NUM_INT_SOURCES in interrupt.h that gives the total number of
 * possible interrputs. A constant FIRST_NONAUTO_VEC is defined also.
 */

/* list is accessed 0-6 for IRQs 1-7 */
static isr_node_t *isr_list[NUM_INT_SOURCES];

/* The number of spurious interrupts */
volatile unsigned long num_spurious;

volatile unsigned long intr_count = 0;
unsigned long bh_active = 0;
unsigned long bh_mask = 0xFFFFFFFF;
struct bh_struct bh_base[32]; 

/*
 * 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 < NUM_INT_SOURCES; 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 >= sizeof(nodes)/sizeof(nodes[0]))
	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 & IRQ_MACHSPEC)
    {
	return mach_add_isr (source, isr, pri, data);
    }

    if (source < IRQ1 || source > NUM_INT_SOURCES)
	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;
    }
}

#define	DEBUG_LOST_INT_DECR

asmlinkage void process_int(int level, struct intframe *fp)
{
#ifdef DEBUG_LOST_INT_DECR
	int cnt = intr_count;
	static int last_spur = 0;
#endif
	
#if NUM_INT_SOURCES > 7
	/* Possibility for non-autovec ints! */
	if (level < 8)
		/* autovector int */
		call_isr_list (isr_list[level-1], fp);
	else
		/* non-autovec */
		call_isr_list (isr_list[level-FIRST_NONAUTO_VEC+24+7], fp);
#else
	call_isr_list (isr_list[level-1], fp);
#endif

#ifdef DEBUG_LOST_INT_DECR
	if (cnt != intr_count) {
		printk( "intr_count=%d != old_cnt=%d; resetted\n", (int)intr_count, cnt );
		intr_count = cnt;
	}
	if (num_spurious != last_spur) {
		printk( "num_spurious is now %lu\n", num_spurious );
		last_spur = num_spurious;
	}
#endif
}

