//-------------------------------------------------------------------------
 
// 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;
 
}