/*
 *	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/irq.h>
#include <arch/io.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[16];

static unsigned char cache_21 = 0xff;
static 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
 */
 
void do_IRQ(i, regs)
void *regs;
{
	struct irqaction *irq=irq_action + i;
	
	if(irq->handler)
		irq->handler(i,regs);
	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;

	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(cache_21,0x21);
	} else {
		cache_A1 |= 1 << (irq-8);
		outb(cache_A1,0xA1);
	}
	action->handler = NULL;
	action->flags = 0;
	action->mask = 0;
	action->name = NULL;
	restore_flags(flags);
}

/*
 *	IRQ setup.
 *
 */
 
void init_IRQ()
{
	struct irqaction *irq=irq_action + 16;
	int ct;
	/* set the clock to 100 Hz */
	outb_p(0x34,0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
	outb_p(LATCH & 0xff , 0x40);	/* LSB */
	outb(LATCH >> 8 , 0x40);	/* MSB */
	
	/*
	 *	All traps are bad initially. One day IRQ's will be.
	 */
	 
	for(ct=16;ct<31;ct++)
		irq++->flags|=IRQF_BAD;
}

