//-------------------------------------------------------------------------
// WINNT driver for PCICC32 interface from ARW Elektronik, Germany --------
// all around irq handling
//
// (c) 1999-2002 ARW Elektronik
//
// this source code is published under GPL (Open Source). You can use, redistrubute and
// modify it unless this header is not modified or deleted. No warranty is given that
// this software will work like expected.
// This product is not authorized for use as critical component in life support systems
// wihout the express written approval of ARW Elektronik Germany.
//
// Please announce changes and hints to ARW Elektronik
//
// what who when
// started AR 01.08.1999
// first release 1.0 AR 17.10.1999
// IoConnectInterrupt, share vector now true AR 04.03.2000
// globalInterruptEnabledStatus() added AR 24.02.2001
// added IRQ handling AR 24.02.2001
// added WITH_IRQS switch to switsch off irqs AR 04.10.2001
// changed making procedure (only VCC > 6.0) AR 30.05.2002
//
//-------------------------------------------------------------------------
// INCLUDES
//
#define WITH_IRQS // comment out for interrupt handling excludes
// ACHTUNG TEST
//-------------------------------------------------------------------------
// INCLUDES
//
#include <ntddk.h>
#include <pcicc32_drv.h>
#include <pcicc32_i.h>
#include <pcicc32.h>
#include <pcicc32_local.h>
//------------------------------------------------------------------------
// DEFINES
//
#ifndef DWORD
#define DWORD ULONG
#endif
#ifndef WORD
#define WORD USHORT
#endif
//------------------------------------------------------------------------
// GLOBALS
//
//------------------------------------------------------------------------
// FUNCTIONS
//
//------------------------------------------------------------------------
// enable and disable of interrupts
//
void globalInterruptEnable(PCIADA *pciada)
{
#ifdef WITH_IRQS
WRITE_REGISTER_USHORT(pciada->pwIntCSR, ENABLE_PCIADA_IRQS);
#endif
}
void globalInterruptDisable(PCIADA *pciada)
{
WRITE_REGISTER_USHORT(pciada->pwIntCSR, DISABLE_PCIADA_IRQS);
}
unsigned short globalInterruptEnabledStatus(PCIADA *pciada)
{
unsigned short wStatus;
wStatus = READ_REGISTER_USHORT(pciada->pwIntCSR);
if ((wStatus & ENABLE_PCIADA_IRQS) == ENABLE_PCIADA_IRQS)
return 1;
return 0;
}
//------------------------------------------------------------------------
// determine which interrupt and evaluates status if appropriate
//
static int evaluateIrqStatus(PCIADA *pciada, ULONG *dwIrqStatus)
{
volatile USHORT wCntrl;
int result = 0;
wCntrl = READ_REGISTER_USHORT(pciada->pwCntrl);
if (wCntrl & 0x100) // pciada switched on ?
{
volatile USHORT wIntCSR = READ_REGISTER_USHORT(pciada->pwIntCSR);
if (wIntCSR & 0x0040) // are the interrupts enabled?
{
if (wIntCSR & 0x20)
{
// it's the pci interrupt # 2
globalInterruptDisable(pciada); // disable following interrupts
// get current Cntrl - and clear interrupt
WRITE_REGISTER_USHORT(pciada->pwCntrl, (USHORT)(wCntrl & ~0x0100)); // disable
WRITE_REGISTER_USHORT(pciada->pwCntrl, wCntrl); // enable again
*dwIrqStatus = CONNECTION_TIMEOUT;
result = 1;
}
if (wIntCSR & 0x04)
{
globalInterruptDisable(pciada); // disable following interrupts
*dwIrqStatus = READ_REGISTER_ULONG(_DWORD_NAF(pciada->pvVirtIfr, 28, 2, 0));
// clear pending interrupt - LAM-FF
WRITE_REGISTER_USHORT(_WORD_NAF(pciada->pvVirtIfr, 28, 0, 16), 0);
result = 1;
}
}
}
return result;
}
//------------------------------------------------------------------------
// main interrupt handler function
//
static BOOLEAN irq_service(PKINTERRUPT Interrupt, PVOID ServiceContext)
{
PCIADA *pciada = (PCIADA *)ServiceContext;
#ifndef WITH_IRQS
return FALSE;
#endif
if (!evaluateIrqStatus(pciada, &pciada->dwIrqStatus))
{
// KdPrint(("Not my irq.\n"));
return FALSE;
}
// fire custom deffered procedure call
KeInsertQueueDpc(&pciada->kDPCobj, (PVOID)pciada, (PVOID)&pciada->dwIrqStatus);
KdPrint(("irq_service(0x%08x)\n", pciada->dwIrqStatus));
return TRUE;
}
//------------------------------------------------------------------------
// translate interrupt resources for PCIADAs to hardware independent ones
//
NTSTATUS PCICC32TranslateInterrupt(PDEVICE_OBJECT device_Obj)
{
int i;
NTSTATUS result = STATUS_SUCCESS;
DEVICE_EXT *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
int nPCIADAs = pDevExt->nPCIADAs;
PCIADA *pciada;
KdPrint(("PCICC32TranslateInterrupt()\n"));
for (i = 0; i < nPCIADAs; i++)
{
pciada = &pDevExt->pciada[i];
KdPrint(("In - Bus:%d, IrqLine:%d\n", pciada->Bus, pciada->Irql));
if (pciada->Irql)
{
pciada->Vector = HalGetInterruptVector(PCIBus, pciada->Bus,
pciada->Irql, pciada->Vector,
&pciada->Irql, &pciada->Affinity);
KdPrint(("Out - Irql:%d, Vector: %d, Affinity:%d\n", pciada->Irql, pciada->Vector, pciada->Affinity));
}
else
result = STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT;
}
return result;
}
//------------------------------------------------------------------------
// connect service routines to all PCIADA interrupts
//
NTSTATUS PCICC32ConnectInterrupt(PDEVICE_OBJECT device_Obj)
{
#ifdef WITH_IRQS
int i;
NTSTATUS result = STATUS_SUCCESS;
DEVICE_EXT *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
int nPCIADAs = pDevExt->nPCIADAs;
PCIADA *pciada;
KdPrint(("PCICC32ConnectInterrupt()\n"));
// connect the interrupts to service routines
for (i = 0; i < nPCIADAs; i++)
{
pciada = &pDevExt->pciada[i];
pciada->InterruptObject = NULL;
if (pciada->Vector)
result = IoConnectInterrupt(&pciada->InterruptObject,
irq_service,
(PVOID)pciada,
NULL,
pciada->Vector,
pciada->Irql,
pciada->Irql,
LevelSensitive,
TRUE,
pciada->Affinity,
FALSE);
KdPrint(("irq_service:%p, VirtVect:%d, Irql:%d, hIrql:%d, Aff:0x%08x, Status:0x%08x\n",
irq_service, pciada->Vector, pciada->Irql,
pciada->Irql, pciada->Affinity, result));
if (result != STATUS_SUCCESS)
break;
}
if (result == STATUS_SUCCESS)
{
KdPrint(("PCICC32ConnectInterrupt() OK.\n"));
}
return result;
#else
return STATUS_SUCCESS;
#endif
}
//------------------------------------------------------------------------
// dis-connect service routines to all PCIADA interrupts
//
NTSTATUS PCICC32DisConnectInterrupt(PDEVICE_OBJECT device_Obj)
{
#ifdef WITH_IRQS
int i;
DEVICE_EXT *pDevExt = (DEVICE_EXT*)device_Obj->DeviceExtension;
int nPCIADAs = pDevExt->nPCIADAs;
PCIADA *pciada;
KdPrint(("DisConnectInterrupt()\n"));
// dis connect the interrupts to service routines
for (i = 0; i < nPCIADAs; i++)
{
pciada = &pDevExt->pciada[i];
KdPrint(("IrqObj:0x%08x\n", pciada->InterruptObject));
if (pciada->InterruptObject)
{
IoDisconnectInterrupt(pciada->InterruptObject);
pciada->InterruptObject = 0;
}
}
#endif
return STATUS_SUCCESS;
}