Subversion Repositories f9daq

Rev

Blame | Last modification | View Log | RSS feed

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