737 lines
23 KiB
C
737 lines
23 KiB
C
/* i8259Intr.c - Intel 8259A PIC (Programmable Interrupt Controller) driver */
|
|
|
|
/*
|
|
* Copyright (c) 1993-2008,2014 Wind River Systems, Inc.
|
|
*
|
|
* The right to copy, distribute, modify or otherwise make use
|
|
* of this software may be licensed only pursuant to the terms
|
|
* of an applicable Wind River license agreement.
|
|
*/
|
|
|
|
/*
|
|
modification history
|
|
--------------------
|
|
01x,24sep14,j_l VXW6-83369 - Use INCLUDE_SYMMETRIC_IO_MODE
|
|
01w,27aug08,jpb Renamed VSB header file
|
|
01v,18jun08,jpb Renamed _WRS_VX_SMP to _WRS_CONFIG_SMP. Added include path
|
|
for kernel configurations options set in vsb.
|
|
01u,18jun07,scm remove reference to sysCpuNumGet, replace with
|
|
vxCpuIndexGet...
|
|
01t,09oct06,scm remove sysBp dependencey for SMP...
|
|
01s,09nov05,mdo Fixes for apigen
|
|
01r,07may04,zmm Fix diab compiler warnings.
|
|
01q,11nov02,hdn fixed the IRQ15 spurious interrupt handling (spr 74209)
|
|
01p,16apr02,hdn added support for the Special Fully Nested Mode, IRQ0 Early
|
|
EOI Mode, IRQ0 Special Mask Mode, AEOI Mode (spr 76411)
|
|
01o,29mar02,hdn added forgotten back slash to the optimized code
|
|
01n,21mar02,hdn fixed doc build by removing comments in the optimized code
|
|
01m,25feb02,hdn updated i8259Init() for HTT (spr 73738)
|
|
added optimized version of EOI routines
|
|
01l,11sep01,hdn updated comment regarding the spr-28547 fix.
|
|
01k,27aug99,hdn fixed a bug for stray interrupt in i8259IntBoi(SPR 28547).
|
|
01j,11oct98,ms fixed compiler warning via typecast
|
|
01i,04jun98,hdn changed a method to exit i8259IntBoi().
|
|
01h,25may98,hdn changed function name starting from "i8259".
|
|
added i8259IntEoiMaster(), i8259IntEoiSlave(), i8259IntBoi().
|
|
removed sysIntEOI().
|
|
01g,17mar97,hdn added sysIntLock(), sysIntUnlock() for the system mode.
|
|
01f,25jun96,hdn added sysIntLevel() for windview.
|
|
01e,23may96,wlf doc: cleanup.
|
|
01d,14jun95,hdn renamed sysEndOfInt to sysIntEOI.
|
|
moved global function prototypes to sysLib.h.
|
|
01c,08aug94,hdn stopped toggling IRQ9 in enabling and disabling.
|
|
01b,22apr94,hdn made IRQ9 off in the initialization.
|
|
moved sysVectorIRQ0 to sysLib.c.
|
|
01a,05sep93,hdn written.
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
This module is a driver for the Intel 8259A PIC (Programmable Interrupt
|
|
Controller). The Intel 8259A handles up to 8 vectored priority interrupts
|
|
for the CPU. It is cascadable for up to 64 vectored priority interrupts,
|
|
though this driver assumes two cascaded 8259A. It is initialized for
|
|
"Fully Nested Mode", "Non-Specific EOI" mode.
|
|
|
|
Fully Nested Mode.
|
|
In this mode, interrupt requests are ordered in priority
|
|
from 0 through 7 (0 is the highest priority). When an interrupt is
|
|
acknowledged, the highest priority request is determined and its vector is
|
|
placed on the bus. Additionally, a bit of the Interrupt Service (IS)
|
|
register is set. This bit remains set until the microprocessor issues an
|
|
EOI command immediately before returning from the service routine. While
|
|
the IS bit is set, all further interrupts of the same or lower priority
|
|
are inhibited, while higher level interrupts are allowed. The
|
|
i8259IntEoiSlaveNfnm() routine is used to issue the EOI command. The PICs
|
|
in a PC typically operate in this mode (normal nested mode). In this mode,
|
|
while the slave PIC is being serviced by the master PIC, the slave PIC blocks
|
|
all higher priority interrupt requests. Alternatively, to allow interrupts of
|
|
a higher priority, enable the Special Fully Nested Mode.
|
|
|
|
Special Fully Nested Mode: I8259_SPECIAL_FULLY_NESTED_MODE.
|
|
This mode is similar to the Fully Nested Mode with the following exceptions:
|
|
1) When an interrupt request from a slave PIC is in service, the slave is
|
|
not locked out from the master's priority logic and further interrupt
|
|
requests from higher priority IRs within the slave will be recognized by
|
|
the master and will initiate interrupts to the processor. 2) When exiting
|
|
the interrupt service routine, the software must check whether or not the
|
|
interrupt serviced was the only interrupt request from the slave. If it
|
|
was the only interrupt request, a non-specific EOI is sent to the master.
|
|
If not, no EOI is sent. This is implemented by the i8259EoiSlaveSfnm()
|
|
routine.
|
|
|
|
Non-Specific EOI: When the 8259A is operated in the Fully Nested Mode, it
|
|
can determine which IS bit to reset on EOI. When a non-specific EOI
|
|
command is issued, the 8259A will automatically reset the highest IS bit of
|
|
those that are set, since in the fully nested mode the highest IS level is
|
|
the last level acknowledged and serviced.
|
|
|
|
Spurious/Phantom Interrupt: The IR inputs must remain high until after the
|
|
falling edge of the first INTA. If the IR input goes low before this time,
|
|
a DEFAULT(Spurious/Phantom) IR7 will occur when the CPU acknowledges the
|
|
interrupt. The interrupt handler should simply return without sending
|
|
an EOI command.
|
|
|
|
The PIC(8259A) IRQ0 is hard wired to the PIT(8253) channel 0 in a PC
|
|
motherboard. IRQ0 is the highest priority in the 8259A interrupt
|
|
controller. Thus, the system clock interrupt handler blocks all lower
|
|
level interrupts. This may cause a delay of the lower level interrupts in
|
|
some situations even though the system clock interrupt handler finishes its
|
|
job without any delay. This is quite natural from the hardware point of
|
|
view, but may not be ideal from the application software standpoint. The
|
|
following modes are supplied to mitigate this situation by providing
|
|
corresponding configuration macros in the BSP. The three modes are mutually
|
|
exclusive.
|
|
|
|
Early EOI Issue in IRQ0 ISR.
|
|
In this mode, the EOI command is issued before the IRQ0 system clock interrupt
|
|
service routine starts the kernel work. This lowers the IRQ0 ISR blocking
|
|
level to the next lower level. If no IRQs are in service, the next lower
|
|
level is the lowest level. If IRQn is in service, the next lower level
|
|
corresponds to the next lower priority. As a result, the kernel work in the
|
|
system clock interrupt service routine can be interrupted by an interrupt
|
|
with a higher priority than the blocking level. The i8259IntBoiEem() routine
|
|
is called before the interrupt service routine, and no EOI is sent after
|
|
the interrupt service routine.
|
|
|
|
Special Mask Mode in IRQ0 ISR.
|
|
In this mode, the Special Mask Mode is used in the IRQ0 system clock
|
|
service routine. This lowers the blocking level to the specified level
|
|
(currently hard coded to the lowest level). The i8259IntBoiSmm() routine
|
|
is called before the interrupt service routine, and the i8259IntEoiSmm()
|
|
routine is called after the interrupt service routine.
|
|
|
|
Automatic EOI Mode: I8259_AUTO_EOI.
|
|
This mode provides no nested multi-level interrupt structure in PIC1. The
|
|
EOI command is automatically sent to the master PIC at the end of the
|
|
interrupt acknowledge cycle. Thus, no software intervention is needed. The
|
|
i8259IntBoi() routine is called before the IRQ7 and IRQ15 interrupt service
|
|
routines. Either the i8259IntEoiSlaveNfnm() routine or the
|
|
i8259IntEoiSlaveSfnm() routine is called after the slave PIC's interrupt
|
|
service routine.
|
|
|
|
SEE ALSO: pc386/target.ref
|
|
|
|
INCLUDE FILES: i8259.h
|
|
*/
|
|
|
|
#include <vsbConfig.h>
|
|
|
|
#include <drv/intrCtl/i8259.h>
|
|
|
|
|
|
/* externs */
|
|
|
|
IMPORT void intBoiExit ();
|
|
IMPORT UINT32 sysStrayIntCount;
|
|
|
|
#ifndef _WRS_CONFIG_SMP
|
|
IMPORT BOOL sysBp; /* TRUE(default) for BP, FALSE for AP */
|
|
#endif /* _WRS_CONFIG_SMP */
|
|
|
|
|
|
/* defines */
|
|
|
|
#define I8259_EOI_OPTIMIZED /* use the optimized version */
|
|
|
|
|
|
/* globals */
|
|
|
|
|
|
/* local */
|
|
|
|
#if !defined(SYMMETRIC_IO_MODE) && !defined(INCLUDE_SYMMETRIC_IO_MODE)
|
|
LOCAL INT8 i8259IntMask1; /* interrupt mask for PIC1 */
|
|
LOCAL INT8 i8259IntMask2; /* interrupt mask for PIC2 */
|
|
LOCAL INT8 i8259Mask = 0; /* interrupt mask for PIC1 */
|
|
#endif /* !SYMMETRIC_IO_MODE && !INCLUDE_SYMMETRIC_IO_MODE */
|
|
|
|
|
|
/* forward static functions */
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259Init - initialize the PIC
|
|
*
|
|
* This routine initializes the PIC.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
VOID i8259Init (void)
|
|
{
|
|
UINT8 icw4 = 0x01;
|
|
|
|
/* return if it is not the Boot Processor (BP) */
|
|
|
|
#ifndef _WRS_CONFIG_SMP
|
|
if (sysBp == FALSE)
|
|
#else
|
|
if (vxCpuIndexGet() != 0)
|
|
#endif /* _WRS_CONFIG_SMP */
|
|
return;
|
|
|
|
/* initialize the PIC (Programmable Interrupt Controller) */
|
|
|
|
sysOutByte (PIC_port1 (PIC1_BASE_ADR),0x11); /* ICW1 */
|
|
sysOutByte (PIC_port2 (PIC1_BASE_ADR),INT_NUM_IRQ0); /* ICW2 */
|
|
sysOutByte (PIC_port2 (PIC1_BASE_ADR),0x04); /* ICW3 */
|
|
|
|
#ifdef I8259_SPECIAL_FULLY_NESTED_MODE
|
|
icw4 |= 0x10; /* SFNM */
|
|
#endif /* I8259_SPECIAL_FULLY_NESTED_MODE */
|
|
|
|
#ifdef I8259_AUTO_EOI
|
|
icw4 |= 0x02; /* AEOI */
|
|
#endif /* I8259_AUTO_EOI */
|
|
|
|
sysOutByte (PIC_port2 (PIC1_BASE_ADR),icw4); /* ICW4 */
|
|
|
|
sysOutByte (PIC_port1 (PIC2_BASE_ADR),0x11); /* ICW1 */
|
|
sysOutByte (PIC_port2 (PIC2_BASE_ADR),(char) (INT_NUM_IRQ0+8)); /* ICW2 */
|
|
sysOutByte (PIC_port2 (PIC2_BASE_ADR),0x02); /* ICW3 */
|
|
sysOutByte (PIC_port2 (PIC2_BASE_ADR),0x01); /* ICW4 */
|
|
|
|
/* disable interrupts */
|
|
|
|
sysOutByte (PIC_IMASK (PIC1_BASE_ADR), (char)0xfb);
|
|
sysOutByte (PIC_IMASK (PIC2_BASE_ADR), (char)0xff);
|
|
}
|
|
|
|
#if !defined(SYMMETRIC_IO_MODE) && !defined(INCLUDE_SYMMETRIC_IO_MODE)
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntBoiEem - issue EOI before the IRQ0 interrupt handler
|
|
*
|
|
* This routine is called before the IRQ0 interrupt handler that is PIT(8253)
|
|
* channel 0 system clock interrupt handler in the Early EOI Mode.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
VOID i8259IntBoiEem
|
|
(
|
|
int irqNo /* IRQ number of the interrupt */
|
|
)
|
|
{
|
|
INT32 oldLevel = intLock (); /* LOCK INTERRUPT */
|
|
|
|
sysOutByte (PIC_IACK (PIC1_BASE_ADR), 0x20); /* NS EOI to PIC1 */
|
|
|
|
intUnlock (oldLevel); /* UNLOCK INTERRUPT */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntBoiSmm - enable Special Mask Mode before the IRQ0 interrupt handler
|
|
*
|
|
* This routine is called before the IRQ0 interrupt handler that is PIT(8253)
|
|
* channel 0 system clock interrupt handler, in the Special Mask Mode.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
VOID i8259IntBoiSmm
|
|
(
|
|
int irqNo /* IRQ number of the interrupt */
|
|
)
|
|
{
|
|
INT32 oldLevel = intLock (); /* LOCK INTERRUPT */
|
|
|
|
sysOutByte (PIC_port1 (PIC1_BASE_ADR), 0x68); /* enable SMM PIC1 */
|
|
i8259Mask = sysInByte (PIC_IMASK (PIC1_BASE_ADR)); /* save int mask */
|
|
sysOutByte (PIC_IMASK (PIC1_BASE_ADR), 0x01); /* unlock except IRQ0 */
|
|
|
|
intUnlock (oldLevel); /* UNLOCK INTERRUPT */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntBoi - detect whether it is spurious interrupt or not
|
|
*
|
|
* This routine is called before the user's interrupt handler to detect the
|
|
* spurious interrupt.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
VOID i8259IntBoi
|
|
(
|
|
INT32 irqNo /* IRQ number of the interrupt */
|
|
)
|
|
{
|
|
INT32 oldLevel;
|
|
INT8 inserviceReg;
|
|
|
|
/* we are interested in IRQ7 and IRQ15 */
|
|
|
|
if ((irqNo != 7) && (irqNo != 15))
|
|
return;
|
|
|
|
/* if ISR bit is not set, we change the return address */
|
|
|
|
oldLevel = intLock (); /* LOCK INTERRUPT */
|
|
|
|
if (irqNo == 7)
|
|
{
|
|
sysOutByte (PIC_port1 (PIC1_BASE_ADR), 0x0b);
|
|
inserviceReg = sysInByte (PIC_port1 (PIC1_BASE_ADR));
|
|
}
|
|
else
|
|
{
|
|
sysOutByte (PIC_port1 (PIC2_BASE_ADR), 0x0b);
|
|
inserviceReg = sysInByte (PIC_port1 (PIC2_BASE_ADR));
|
|
|
|
/* check bit7 for spurious interrupt from the slave PIC IRQ15 */
|
|
|
|
if ((inserviceReg & 0x80) == 0)
|
|
{
|
|
|
|
/* make sure no IRQs in the slave PIC are in service */
|
|
|
|
if (inserviceReg == 0)
|
|
{
|
|
|
|
/* check the master PIC's in-service register */
|
|
|
|
sysOutByte (PIC_port1 (PIC1_BASE_ADR), 0x0b);
|
|
inserviceReg = sysInByte (PIC_port1 (PIC1_BASE_ADR));
|
|
|
|
/* send non-specific EOI to the master PIC IRQ2 */
|
|
|
|
if (inserviceReg & 0x04)
|
|
sysOutByte (PIC_IACK (PIC1_BASE_ADR), I8259_EOI);
|
|
}
|
|
}
|
|
}
|
|
|
|
intUnlock (oldLevel); /* UNLOCK INTERRUPT */
|
|
|
|
/*
|
|
* another implementation idea...
|
|
*
|
|
* *((UINT32 *)&irqNo - 1) = (UINT32)intBoiExit;
|
|
* This changes the return addr on the stack. This is architecture
|
|
* specific and tricky. Thus making following change may be good idea.
|
|
*
|
|
* - let this routine return OK or ERROR
|
|
* - let interrupt stub code check the ret value and jump to intExit.
|
|
*
|
|
* New code would be like this:
|
|
* :
|
|
* if ((irqNo != 7) && (irqNo != 15))
|
|
* return (OK);
|
|
* :
|
|
* if ((inserviceReg & 0x80) == 0) * check bit7 for IRQ7 and IRQ15 *
|
|
* {
|
|
* sysStrayIntCount++; * increment the counter *
|
|
* return (ERROR);
|
|
* }
|
|
* }
|
|
*
|
|
* 00 e8 kk kk kk kk call _intEnt * tell kernel
|
|
* 05 50 pushl %eax * save regs
|
|
* 06 52 pushl %edx
|
|
* 07 51 pushl %ecx
|
|
* 08 68 pp pp pp pp pushl $_parameterBoi * push BOI param
|
|
* 13 e8 rr rr rr rr call _routineBoi * call BOI routine
|
|
* addl $4, %esp
|
|
* cmpl $0, %eax
|
|
* jne intConnectCode0
|
|
* 18 68 pp pp pp pp pushl $_parameter * push param
|
|
* 23 e8 rr rr rr rr call _routine * call C routine
|
|
* addl $4, %esp
|
|
* 28 68 pp pp pp pp pushl $_parameterEoi * push EOI param
|
|
* 33 e8 rr rr rr rr call _routineEoi * call EOI routine
|
|
* 38 83 c4 04 addl $4, %esp * pop param
|
|
* intConnectCode0:
|
|
* 41 59 popl %ecx * restore regs
|
|
* 42 5a popl %edx
|
|
* 43 58 popl %eax
|
|
* 44 e9 kk kk kk kk jmp _intExit * exit via kernel
|
|
*/
|
|
|
|
if ((inserviceReg & 0x80) == 0) /* check bit7 for IRQ7 and IRQ15 */
|
|
{
|
|
sysStrayIntCount++; /* increment the counter */
|
|
*((UINT32 *)&irqNo - 1) = (UINT32)intBoiExit; /* change return addr */
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntEoiSmm - disable Special Mask Mode with EOI signal to the master PIC
|
|
*
|
|
* This routine is called at the end of the IRQ0 interrupt handler that is
|
|
* PIT(8253) channel 0 system clock interrupt handler in the Special Mask Mode.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
VOID i8259IntEoiSmm
|
|
(
|
|
int irqNo /* IRQ number to send EOI */
|
|
)
|
|
{
|
|
INT32 oldLevel = intLock (); /* LOCK INTERRUPT */
|
|
|
|
sysOutByte (PIC_IMASK (PIC1_BASE_ADR), i8259Mask); /* restore old mask */
|
|
sysOutByte (PIC_port1 (PIC1_BASE_ADR), 0x08); /* disable SMM PIC1 */
|
|
sysOutByte (PIC_IACK (PIC1_BASE_ADR), 0x20); /* NS EOI to PIC1 */
|
|
|
|
intUnlock (oldLevel); /* UNLOCK INTERRUPT */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntEoiMaster - send EOI(end of interrupt) signal to the master PIC.
|
|
*
|
|
* This routine is called at the end of the interrupt handler.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
VOID i8259IntEoiMaster
|
|
(
|
|
INT32 irqNo /* IRQ number to send EOI */
|
|
)
|
|
{
|
|
#ifdef I8259_EOI_OPTIMIZED /* optimized version */
|
|
|
|
WRS_ASM (" \
|
|
pushfl; \
|
|
cli; \
|
|
movl $0x20, %edx; \
|
|
movl $0x20, %eax; \
|
|
outb %al, %dx; \
|
|
popfl; \
|
|
");
|
|
|
|
#else /* portable version */
|
|
|
|
INT32 oldLevel = intLock (); /* LOCK INTERRUPTS */
|
|
|
|
sysOutByte (PIC_IACK (PIC1_BASE_ADR), I8259_EOI); /* non-specific EOI */
|
|
|
|
intUnlock (oldLevel); /* UNLOCK INTERRUPTS */
|
|
|
|
#endif /* I8259_EOI_OPTIMIZED */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntEoiSlave - send EOI(end of interrupt) signal to the slave PIC.
|
|
*
|
|
* This routine is called at the end of the interrupt handler in the Normal
|
|
* Fully Nested Mode. This is kept for the backward compatibility.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
VOID i8259IntEoiSlave
|
|
(
|
|
INT32 irqNo /* IRQ number to send EOI */
|
|
)
|
|
{
|
|
#ifdef I8259_EOI_OPTIMIZED /* optimized version */
|
|
|
|
WRS_ASM (" \
|
|
pushfl; \
|
|
cli; \
|
|
movl $0xa0, %edx; \
|
|
movl $0x20, %eax; \
|
|
outb %al, %dx; \
|
|
movl $0x20, %edx; \
|
|
outb %al, %dx; \
|
|
popfl; \
|
|
");
|
|
|
|
#else /* portable version */
|
|
|
|
INT32 oldLevel = intLock (); /* LOCK INTERRUPTS */
|
|
|
|
sysOutByte (PIC_IACK (PIC2_BASE_ADR), I8259_EOI); /* non-specific EOI */
|
|
sysOutByte (PIC_IACK (PIC1_BASE_ADR), I8259_EOI); /* non-specific EOI */
|
|
|
|
intUnlock (oldLevel); /* UNLOCK INTERRUPTS */
|
|
|
|
#endif /* I8259_EOI_OPTIMIZED */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntEoiSlaveNfnm - send EOI(end of interrupt) signal to the slave PIC.
|
|
*
|
|
* This routine is called at the end of the interrupt handler in the Normal
|
|
* Fully Nested Mode.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
VOID i8259IntEoiSlaveNfnm
|
|
(
|
|
INT32 irqNo /* IRQ number to send EOI */
|
|
)
|
|
{
|
|
#ifdef I8259_EOI_OPTIMIZED /* optimized version */
|
|
|
|
WRS_ASM (" \
|
|
pushfl; \
|
|
cli; \
|
|
movl $0xa0, %edx; \
|
|
movl $0x20, %eax; \
|
|
outb %al, %dx; \
|
|
movl $0x20, %edx; \
|
|
outb %al, %dx; \
|
|
popfl; \
|
|
");
|
|
|
|
#else /* portable version */
|
|
|
|
INT32 oldLevel = intLock (); /* LOCK INTERRUPTS */
|
|
|
|
sysOutByte (PIC_IACK (PIC2_BASE_ADR), I8259_EOI); /* non-specific EOI */
|
|
sysOutByte (PIC_IACK (PIC1_BASE_ADR), I8259_EOI); /* non-specific EOI */
|
|
|
|
intUnlock (oldLevel); /* UNLOCK INTERRUPTS */
|
|
|
|
#endif /* I8259_EOI_OPTIMIZED */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntEoiSlaveSfnm - send EOI(end of interrupt) signal to the slave PIC.
|
|
*
|
|
* This routine is called at the end of the interrupt handler in the Special
|
|
* Fully Nested Mode.
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
VOID i8259IntEoiSlaveSfnm
|
|
(
|
|
int irqNo /* IRQ number to send EOI */
|
|
)
|
|
{
|
|
INT8 inserviceReg;
|
|
INT32 oldLevel = intLock (); /* LOCK INTERRUPT */
|
|
|
|
sysOutByte (PIC_IACK (PIC2_BASE_ADR), 0x20); /* NS EOI to PIC2 */
|
|
|
|
sysOutByte (PIC_port1 (PIC2_BASE_ADR), 0x0b); /* issue OCW3 */
|
|
inserviceReg = sysInByte (PIC_port1 (PIC2_BASE_ADR)); /* read ISR */
|
|
if (inserviceReg == 0)
|
|
sysOutByte (PIC_IACK (PIC1_BASE_ADR), 0x20); /* NS EOI to PIC1 */
|
|
|
|
intUnlock (oldLevel); /* UNLOCK INTERRUPT */
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntDisable - disable a specified PIC interrupt input line
|
|
*
|
|
* This routine disables a specified PIC interrupt input line.
|
|
*
|
|
* RETURNS: OK, always.
|
|
*
|
|
* SEE ALSO: i8259IntEnable()
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
LOCAL STATUS i8259IntDisable
|
|
(
|
|
INT32 irqNo /* IRQ number to disable */
|
|
)
|
|
{
|
|
|
|
if (irqNo < 8)
|
|
{
|
|
sysOutByte (PIC_IMASK (PIC1_BASE_ADR),
|
|
sysInByte (PIC_IMASK (PIC1_BASE_ADR)) | (1 << irqNo));
|
|
}
|
|
else
|
|
{
|
|
sysOutByte (PIC_IMASK (PIC2_BASE_ADR),
|
|
sysInByte (PIC_IMASK (PIC2_BASE_ADR)) | (1 << (irqNo - 8)));
|
|
}
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntEnable - enable a specified PIC interrupt input line
|
|
*
|
|
* This routine enables a specified PIC interrupt input line.
|
|
*
|
|
* RETURNS: OK, always.
|
|
*
|
|
* SEE ALSO: i8259IntDisable()
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
LOCAL STATUS i8259IntEnable
|
|
(
|
|
INT32 irqNo /* IRQ number to enable */
|
|
)
|
|
{
|
|
|
|
if (irqNo < 8)
|
|
{
|
|
sysOutByte (PIC_IMASK (PIC1_BASE_ADR),
|
|
sysInByte (PIC_IMASK (PIC1_BASE_ADR)) & ~(1 << irqNo));
|
|
}
|
|
else
|
|
{
|
|
sysOutByte (PIC_IMASK (PIC2_BASE_ADR),
|
|
sysInByte (PIC_IMASK (PIC2_BASE_ADR)) & ~(1 << (irqNo - 8)));
|
|
}
|
|
|
|
return (OK);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntLock - lock out all PIC interrupts
|
|
*
|
|
* This routine saves the mask and locks out all PIC interrupts.
|
|
* It should be called in the interrupt disable state(IF bit is 0).
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* SEE ALSO: i8259IntUnlock()
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
LOCAL VOID i8259IntLock (void)
|
|
|
|
{
|
|
|
|
i8259IntMask1 = sysInByte (PIC_IMASK (PIC1_BASE_ADR));
|
|
i8259IntMask2 = sysInByte (PIC_IMASK (PIC2_BASE_ADR));
|
|
sysOutByte (PIC_IMASK (PIC1_BASE_ADR), (char)0xff);
|
|
sysOutByte (PIC_IMASK (PIC2_BASE_ADR), (char)0xff);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntUnlock - unlock the PIC interrupts
|
|
*
|
|
* This routine restores the mask and unlocks the PIC interrupts
|
|
* It should be called in the interrupt disable state(IF bit is 0).
|
|
*
|
|
* RETURNS: N/A
|
|
*
|
|
* SEE ALSO: i8259IntLock()
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
LOCAL VOID i8259IntUnlock (void)
|
|
|
|
{
|
|
|
|
sysOutByte (PIC_IMASK (PIC1_BASE_ADR), i8259IntMask1);
|
|
sysOutByte (PIC_IMASK (PIC2_BASE_ADR), i8259IntMask2);
|
|
}
|
|
|
|
#if FALSE
|
|
|
|
/*
|
|
* There is a function sysIntLevel() which gets the interrupt level from the
|
|
* interrupt stub, so we don't need it anymore.
|
|
*/
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* i8259IntLevel - Get IRQ number by reading Interrupt Service Register.
|
|
*
|
|
* This routine is called to get an IRQ number in service.
|
|
*
|
|
* RETURNS: 0 - 15
|
|
*
|
|
* ERRNO
|
|
*/
|
|
|
|
LOCAL INT32 i8259IntLevel (void)
|
|
|
|
{
|
|
INT32 inserviceReg;
|
|
INT32 irq;
|
|
INT32 oldLevel;
|
|
INT32 retry;
|
|
|
|
oldLevel = intLock ();
|
|
|
|
for (retry=0; retry < 1; retry ++)
|
|
{
|
|
sysOutByte (PIC_port1 (PIC1_BASE_ADR), 0x0b);
|
|
inserviceReg = sysInByte (PIC_port1 (PIC1_BASE_ADR));
|
|
for (irq=0; irq < 8; irq++)
|
|
if ((inserviceReg & 1) && (irq != 2))
|
|
goto i8259IntLevelExit;
|
|
else
|
|
inserviceReg >>= 1;
|
|
|
|
sysOutByte (PIC_port1 (PIC2_BASE_ADR), 0x0b);
|
|
inserviceReg = sysInByte (PIC_port1 (PIC2_BASE_ADR));
|
|
for (irq=8; irq < 16; irq++)
|
|
if (inserviceReg & 1)
|
|
goto i8259IntLevelExit;
|
|
else
|
|
inserviceReg >>= 1;
|
|
}
|
|
|
|
i8259IntLevelExit:
|
|
intUnlock (oldLevel);
|
|
|
|
return (irq);
|
|
}
|
|
|
|
#endif /* FALSE */
|
|
|
|
#endif /* !SYMMETRIC_IO_MODE && !INCLUDE_SYMMETRIC_IO_MODE */
|