/*
 * $QNXLicenseC: 
 * Copyright 2007, QNX Software Systems.  
 *  
 * Licensed under the Apache License, Version 2.0 (the "License"). You  
 * may not reproduce, modify or distribute this software except in  
 * compliance with the License. You may obtain a copy of the License  
 * at: http://www.apache.org/licenses/LICENSE-2.0  
 *  
 * Unless required by applicable law or agreed to in writing, software  
 * distributed under the License is distributed on an "AS IS" basis,  
 * WITHOUT WARRANTIES OF ANY KIND, either express or implied. 
 * 
 * This file may contain contributions from others, either as  
 * contributors under the License or as licensors under other terms.   
 * Please review this entire file for other proprietary rights or license  
 * notices, as well as the QNX Development Suite License Guide at  
 * http://licensing.qnx.com/license-guide/ for other information. 
 * $ 
 */

#include "startup.h"
#include <x86/intr.h>

//
// Initialize interrupt controller hardware & intrinfo structure in the
// system page.
// This code is hardware dependant and may have to be changed
// changed by end users.
//
// This version is for the standard IBM PC interrupt controller setup.

#define MASTER		FIRST_HARDWARE_INTERRUPT
#define SLAVE		(MASTER+8)

const static struct startup_intrinfo intrs[] = {
	{ _NTO_INTR_CLASS_EXTERNAL, 7, _NTO_INTR_SPARE, MASTER, 1, 0,
		{INTR_GENFLAG_NOGLITCH, 0, &interrupt_id_8259m},
		{0, 0, &interrupt_eoi_8259m},
		&interrupt_mask_8259m, &interrupt_unmask_8259m, &interrupt_config_8259m,
	},
	{ _NTO_INTR_CLASS_EXTERNAL+7, 1, _NTO_INTR_SPARE, MASTER+7, 1, 0,
		{0, 0, &interrupt_id_8259m7},
		{0, 0, &interrupt_eoi_8259m},
		&interrupt_mask_8259m7, &interrupt_unmask_8259m7, 0,
	},
	{ _NTO_INTR_CLASS_EXTERNAL+8, 7, _NTO_INTR_SPARE, SLAVE, 1, 0,
		{INTR_GENFLAG_NOGLITCH, 0, &interrupt_id_8259s},
		{0, 0, &interrupt_eoi_8259s},
		&interrupt_mask_8259s, &interrupt_unmask_8259s, 0,
	},
	{ _NTO_INTR_CLASS_EXTERNAL+15, 1, _NTO_INTR_SPARE, SLAVE+7, 1, 0,
		{0, 0, &interrupt_id_8259s7},
		{0, 0, &interrupt_eoi_8259s},
		&interrupt_mask_8259s7, &interrupt_unmask_8259s7, 0,
	},
	{ X86_INTR_CLASS_NMI, 2, _NTO_INTR_SPARE, 2, 0, INTR_FLAG_NMI | INTR_FLAG_CPU_FAULT,
		{0, 0, &interrupt_id_nmi_bios},
		{INTR_GENFLAG_LOAD_INTRMASK, 0, &interrupt_eoi_nmi_bios},
		&interrupt_mask_nmi_bios, &interrupt_unmask_nmi_bios, &interrupt_config_nmi_bios,
	},
};

void
init_intrinfo_8259(unsigned top_prio_irq) {

	// Reprogram 8259 slave, edge mode, normal nested, isr visible
	out8(0xa0, 0x11);  io_safe();
	out8(0xa1, SLAVE); io_safe();
	out8(0xa1, 0x02);  io_safe();
	out8(0xa1, 0x01);  io_safe();
	out8(0xa1, 0xff);
	out8(0xa0, 0x0b);

	// Reprogram 8259 master, edge mode, normal nested, isr visible
	out8(0x20, 0x11);   io_safe();
	out8(0x21, MASTER); io_safe();
	out8(0x21, 0x04);   io_safe();
	out8(0x21, 0x01);   io_safe();
	out8(0x21, 0xfb);   io_safe();
	out8(0x20, 0x0b);   io_safe();// OCW3
	
	/*
		Here we're sending the OCW2, to pin down the	
		*lowest* priority IRQ, and, in effect, choosing
		the highest one as well, since the highest priority
		IRQ must follow in rotation from the lowest.

		eg.		pin 2 as the lowest, then the order is:
				3,4,5,6,7,0,1,2 -> (slave) 8,9,10,11,12,13,14,15
	*/

	out8(0x20, 0x20);   io_safe();// OCW2  -- EOI

	/* OCW2 to pin lowest prio IRQ */
	out8(0x20, (((top_prio_irq-1) & 0x07) | 0xc0));  io_safe();

	// Make sure both NMI sources are masked
	out8(0x61,(in8(0x61) & 0x3) | 0x0c);
	// Global NMI enable
	out8(0x70, 0x7f);

	add_interrupt_array(intrs, sizeof(intrs));
}

