/*
 *	linux/arch/i86/kernel/irq.c
 *
 *	Copyright (C) 1992 Linus Torvalds
 *	Modified for Linux/8086 by Alan Cox Nov 1995.
 *
 * This file contains the code used by various IRQ handling routines:
 * asking for different IRQ's should be done through these routines
 * instead of just grabbing them. Thus setups with different IRQ numbers
 * shouldn't result in any weird surprises, and installing new handlers
 * should be easier.
 */

#include <linuxmt/config.h>
#include <linuxmt/types.h>
#include <linuxmt/errno.h>
#include <linuxmt/sched.h>
#include <linuxmt/errno.h>
#include <linuxmt/timex.h>

#include <arch/system.h>
#include <arch/irq.h>
#include <arch/io.h>
#include <arch/keyboard.h>

struct irqaction 
{
	void (*handler)();
	unsigned long flags;
	unsigned long mask;
	char *name;
};

#define IRQF_BAD	1		/* This IRQ is bad if it occurs */
	
static struct irqaction irq_action[32]= { NULL,};

unsigned char cache_21 = 0xff;
unsigned char cache_A1 = 0xff;

void disable_irq(irq_nr)
unsigned int irq_nr;
{
	unsigned long flags;
	unsigned char mask = 1 << (irq_nr & 7);
	save_flags(flags);
	if(irq_nr < 8) {
		cli();
		cache_21 |= mask;
		outb(cache_21,0x21);
		restore_flags(flags);
		return;
	}
	cli();
	outb(cache_A1,0xA1);
	restore_flags(flags);
}

void enable_irq(irq_nr)
unsigned int irq_nr;
{
	unsigned long flags;
	unsigned char mask;

	mask = ~(1 << (irq_nr & 7));
	save_flags(flags);
	if (irq_nr < 8) {
		cli();
		cache_21 &= mask;
		outb(cache_21,0x21);
		restore_flags(flags);
		return;
	}
	cli();
	cache_A1 &= mask;
	outb(cache_A1,0xA1);
	restore_flags(flags);
}

/*
 *	Called by the assembler hooks
 */

int lastirq;
 
void do_IRQ(i, regs)
void *regs;
{
	struct irqaction *irq=irq_action + i;

	lastirq = i;	
	if(irq->handler) {
		irq->handler(i,regs);
		lastirq = -1;
	}
	else if(irq->flags&IRQF_BAD)
	{
		if(irq>16)
			printk("Unexpected trap: %d\n", irq-16);
		else
			printk("Unexpected interrupt: %d\n", irq);
	}
}

/*
 *	Low level IRQ control.
 */
 
unsigned long __save_flags()
{
#asm
	pushf
	pop ax
#endasm
}

void restore_flags(flags)
unsigned long flags;
{
	unsigned long v=flags;
#asm
	push ax
	popf
#endasm
}

void cli()
{
#asm
	cli
#endasm
}

void sti()
{
#asm
	sti
#endasm
}
		
int request_irq(irq, handler, irqflags, devname)
int irq;
void (*handler);
unsigned long irqflags;
char *devname;	
{
	struct irqaction *action;
	unsigned long flags;
	
	if (irq > 15)
		return -EINVAL;
	if (irq > 7 && arch_cpu<2)
		return -EINVAL;		/* AT interrupt line on an XT */
	if (irq == 2 && arch_cpu>1)
		irq = 9;		/* Map IRQ 9/2 over */
	action = irq + irq_action;
	if(action->handler)
		return -EBUSY;
	if(!handler)
		return -EINVAL;
	save_flags(flags);
	cli();
	action->handler = handler;
	action->flags = irqflags;
	action->mask = 0;
	action->name = devname;
	enable_irq(irq);
	restore_flags(flags);
	return 0;
}

void free_irq(irq)
unsigned int irq;
{
	struct irqaction * action = irq + irq_action;
	unsigned long flags;

	if (irq > 15) {
		printk("Trying to free IRQ%d\n",irq);
		return;
	}
	if (!action->handler) {
		printk("Trying to free free IRQ%d\n",irq);
		return;
	}
	save_flags(flags);
	cli();
	if (irq < 8) {
		cache_21 |= 1 << irq;
		outb(0x21,cache_21);
	} else {
		cache_A1 |= 1 << (irq-8);
		outb(0xA1,cache_A1);
	}
	action->handler = NULL;
	action->flags = 0;
	action->mask = 0;
	action->name = NULL;
	restore_flags(flags);
}

/*
 *	Test timer tick routine
 */

unsigned long jiffies=0;

void timer_tick()
{
	jiffies++;
#if 1
	if (((int)jiffies & 7) == 0)
		need_resched=1;	/* how primitive can you get? */
#endif
#if 0
	#asm
	! rotate the 20th character on the 3rd screen line
	push es
	mov ax,#0xb80a
	mov es,ax
	seg es
	inc 40
	pop es
	#endasm
#endif
}

/*
 *	IRQ setup.
 *
 */
 
void init_IRQ()
{
	struct irqaction *irq=irq_action + 16;
	int ct;
	
	irqtab_init();			/* Store DS */

#if 0
	/* set the clock to 100 Hz */
	outb_p(0x43,0x34);		/* binary, mode 2, LSB/MSB, ch 0 */
	outb_p(156,0x40);		/* LSB */
	outb(46,0x40);			/* MSB */
#endif

	/*
	 *	Set off the initial timer interrupt handler
	 */
	 
	if(request_irq(0, timer_tick, 0, "timer"))
		panic("Unable to get timer");
	 	
	/*
	 *	Set off the initial keyboard interrupt handler
	 */

	if(request_irq(1, keyboard_irq, 0, "keyboard"))
		panic("Unable to get keyboard");
	/*
	 *	Enable the drop through interrupts.
	 */
	 	
	enable_irq(14);		/* AT ST506 */
	enable_irq(15);		/* AHA1542 */
	enable_irq(5);		/* XT ST506 */
	enable_irq(2);		/* Cascade */
	enable_irq(6);		/* Floppy */
	
	/*
	 *	All traps are bad initially. One day IRQ's will be.
	 */
	 
	for(ct=16;ct<31;ct++)
		irq++->flags|=IRQF_BAD;
}

