www.pudn.com > 9054Src.rar > PlxInterrupt.c


/******************************************************************************* 
 * Copyright (c) 2006 PLX Technology, Inc. 
 * 
 * PLX Technology Inc. licenses this software under specific terms and 
 * conditions.  Use of any of the software or derviatives thereof in any 
 * product without a PLX Technology chip is strictly prohibited. 
 * 
 * PLX Technology, Inc. provides this software AS IS, WITHOUT ANY WARRANTY, 
 * EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF 
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  PLX makes no guarantee 
 * or representations regarding the use of, or the results of the use of, 
 * the software and documentation in terms of correctness, accuracy, 
 * reliability, currentness, or otherwise; and you rely on the software, 
 * documentation and results solely at your own risk. 
 * 
 * IN NO EVENT SHALL PLX BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS, 
 * LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES 
 * OF ANY KIND.  IN NO EVENT SHALL PLX'S TOTAL LIABILITY EXCEED THE SUM 
 * PAID TO PLX FOR THE PRODUCT LICENSED HEREUNDER. 
 * 
 ******************************************************************************/ 
 
/****************************************************************************** 
 * 
 * File Name: 
 * 
 *      PlxInterrupt.c 
 * 
 * Description: 
 * 
 *      This file handles interrupts for the PLX device 
 * 
 * Notes: 
 * 
 *      This driver handles interrupt sharing with other PCI device drivers. 
 *      The PLX chip is checked to see if it is the true source of an interrupt. 
 *      If an interrupt is truly active, the interrupt source is cleared and 
 *      any waiting user-mode applications are notified. 
 * 
 * Revision History: 
 * 
 *      02-01-06 : PCI SDK v4.40 
 * 
 ******************************************************************************/ 
 
 
#include "DriverDefs.h" 
#include "PlxInterrupt.h" 
#include "PciSupport.h" 
#include "SupportFunc.h" 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  OnInterrupt 
 * 
 * Description:  The Interrupt Service Routine for the PLX device 
 * 
 ******************************************************************************/ 
BOOLEAN 
OnInterrupt( 
    PKINTERRUPT pInterrupt, 
    PVOID       ServiceContext 
    ) 
{ 
    U32               RegValue; 
    U32               RegPciInt; 
    U32               InterruptSource; 
    DEVICE_EXTENSION *pdx; 
 
 
    // Get the device extension 
    pdx = (DEVICE_EXTENSION *)ServiceContext; 
 
    // Read interrupt status register 
    RegPciInt = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_INT_CTRL_STAT 
            ); 
 
    /**************************************************** 
     * If the chip is in a low power state, then local 
     * register reads are disabled and will always return 
     * 0xFFFFFFFF.  If the PLX chip's interrupt is shared 
     * with another PCI device, the PXL ISR may continue 
     * to be called.  This case is handled to avoid 
     * erroneous reporting of an active interrupt. 
     ***************************************************/ 
    if (RegPciInt == 0xFFFFFFFF) 
        return FALSE; 
 
    // Check for master PCI interrupt enable 
    if ((RegPciInt & (1 << 8)) == 0) 
        return FALSE; 
 
    // Verify that an interrupt is truly active 
 
    // Clear the interrupt type flag 
    InterruptSource = INTR_TYPE_NONE; 
 
    // Check if PCI Doorbell Interrupt is active and not masked 
    if ((RegPciInt & (1 << 13)) && (RegPciInt & (1 << 9))) 
    { 
        InterruptSource |= INTR_TYPE_DOORBELL; 
    } 
 
    // Check if PCI Abort Interrupt is active and not masked 
    if ((RegPciInt & (1 << 14)) && (RegPciInt & (1 << 10))) 
    { 
        InterruptSource |= INTR_TYPE_PCI_ABORT; 
    } 
 
    // Check if Local->PCI Interrupt is active and not masked 
    if ((RegPciInt & (1 << 15)) && (RegPciInt & (1 << 11))) 
    { 
        InterruptSource |= INTR_TYPE_LOCAL_1; 
    } 
 
    // Check if DMA Channel 0 Interrupt is active and not masked 
    if ((RegPciInt & (1 << 21)) && (RegPciInt & (1 << 18))) 
    { 
        // Verify the DMA interrupt is routed to PCI 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA0_MODE 
                ); 
 
        if (RegValue & (1 << 17)) 
        { 
            InterruptSource |= INTR_TYPE_DMA_0; 
        } 
    } 
 
    // Check if DMA Channel 1 Interrupt is active and not masked 
    if ((RegPciInt & (1 << 22)) && (RegPciInt & (1 << 19))) 
    { 
        // Verify the DMA interrupt is routed to PCI 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA1_MODE 
                ); 
 
        if (RegValue & (1 << 17)) 
        { 
            InterruptSource |= INTR_TYPE_DMA_1; 
        } 
    } 
 
    // Check if MU Outbound Post interrupt is active 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_OUTPOST_INT_STAT 
            ); 
 
    if (RegValue & (1 << 3)) 
    { 
        // Check if MU Outbound Post interrupt is not masked 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_OUTPOST_INT_MASK 
                ); 
 
        if ((RegValue & (1 << 3)) == 0) 
        { 
            InterruptSource |= INTR_TYPE_OUTBOUND_POST; 
        } 
    } 
 
    // Return if no interrupts are active 
    if (InterruptSource == INTR_TYPE_NONE) 
        return FALSE; 
 
    // At this point, the device interrupt is verified 
 
    // Mask the PCI Interrupt 
    PLX_REG_WRITE( 
        pdx, 
        PCI9054_INT_CTRL_STAT, 
        RegPciInt & ~(1 << 8) 
        ); 
 
    // 
    // Schedule deferred procedure (DPC) to complete interrupt processing 
    // 
 
    KeInsertQueueDpc( 
        &(pdx->DpcForIsr), 
        (VOID *)(PLX_UINT_PTR)InterruptSource, 
        NULL 
        ); 
 
    return TRUE; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  DpcForIsr 
 * 
 * Description:  This routine will be triggered by the ISR to service an interrupt. 
 * 
 ******************************************************************************/ 
VOID 
DpcForIsr( 
    PKDPC pDpc, 
    PVOID pContext, 
    PVOID pArg1, 
    PVOID pArg2 
    ) 
{ 
    U32               RegValue; 
    U32               IntSource; 
    PLX_REG_DATA      RegData; 
    DEVICE_EXTENSION *pdx; 
 
 
    // Get the device extension 
    pdx = (DEVICE_EXTENSION *)pContext; 
 
    // Get interrupt source 
    IntSource = PtrToUlong(pArg1); 
 
    KeAcquireSpinLockAtDpcLevel( 
        &(pdx->Lock_HwAccess) 
        ); 
 
    // Local Interrupt 1 
    if (IntSource & INTR_TYPE_LOCAL_1) 
    { 
        // Synchronize access to Interrupt Control/Status Register 
        RegData.pdx         = pdx; 
        RegData.offset      = PCI9054_INT_CTRL_STAT; 
        RegData.BitsToSet   = 0; 
        RegData.BitsToClear = (1 << 11); 
 
        // Mask local interrupt 1 since true source is unknown 
        KeSynchronizeExecution( 
            pdx->pInterruptObject, 
            PlxSynchronizedRegisterModify, 
            (VOID *)&RegData 
            ); 
    } 
 
    // Doorbell Interrupt 
    if (IntSource & INTR_TYPE_DOORBELL) 
    { 
        // Get Doorbell register 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_PCI_DOORBELL 
                ); 
 
        // Clear Doorbell interrupt 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_PCI_DOORBELL, 
            RegValue 
            ); 
 
        // Save this value in case it is requested later 
        pdx->IntrDoorbellValue = RegValue; 
    } 
 
    // PCI Abort interrupt 
    if (IntSource & INTR_TYPE_PCI_ABORT) 
    { 
        // Get the PCI Command register 
        PLX_PCI_REG_READ( 
            pdx, 
            CFG_COMMAND, 
            &RegValue 
            ); 
 
        // Write to back to clear PCI Abort 
        PLX_PCI_REG_WRITE( 
            pdx, 
            CFG_COMMAND, 
            RegValue 
            ); 
    } 
 
    // DMA Channel 0 interrupt 
    if (IntSource & INTR_TYPE_DMA_0) 
    { 
        // Get DMA Control/Status 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA_COMMAND_STAT 
                ); 
 
        // Clear DMA interrupt 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA_COMMAND_STAT, 
            RegValue | (1 << 3) 
            ); 
 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA0_MODE 
                ); 
 
        // Check if SGL is enabled & cleanup 
        if (RegValue & (1 << 9)) 
        { 
            PlxSglDmaTransferComplete( 
                pdx, 
                0 
                ); 
        } 
    } 
 
    // DMA Channel 1 interrupt 
    if (IntSource & INTR_TYPE_DMA_1) 
    { 
        // Get DMA Control/Status 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA_COMMAND_STAT 
                ); 
 
        // Clear DMA interrupt 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA_COMMAND_STAT, 
            RegValue | (1 << 11) 
            ); 
 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA1_MODE 
                ); 
 
        // Check if SGL is enabled & cleanup 
        if (RegValue & (1 << 9)) 
        { 
            PlxSglDmaTransferComplete( 
                pdx, 
                1 
                ); 
        } 
    } 
 
    // Outbound post FIFO interrupt 
    if (IntSource & INTR_TYPE_OUTBOUND_POST) 
    { 
        // Mask Outbound Post interrupt 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_OUTPOST_INT_MASK, 
            (1 << 3) 
            ); 
    } 
 
    // Save the interrupt sources 
    pdx->InterruptSource = IntSource; 
 
    KeReleaseSpinLockFromDpcLevel( 
        &(pdx->Lock_HwAccess) 
        ); 
 
    // Signal any objects waiting for notification 
    PlxSignalNotifications( 
        pdx, 
        IntSource 
        ); 
 
    // Re-enable the PCI interrupt 
    KeSynchronizeExecution( 
        pdx->pInterruptObject, 
        PlxChipPciInterruptEnable, 
        pdx 
        ); 
}