www.pudn.com > PlxSdk.rar > PlxInterrupt.c


/*******************************************************************************
 * Copyright (c) 2007 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
 *
 * Revision History:
 *
 *      04-01-07 : PLX SDK v5.00
 *
 ******************************************************************************/


#include 
#include 
#include 
#include 
#include 
#include 
#include   // Note: interrupt.h must be last to avoid compiler
                              //       errors with some 2.4 kernel headers
#include "PciSupport.h"
#include "PlxChipFn.h"
#include "PlxInterrupt.h"
#include "SupportFunc.h"




/******************************************************************************
 *
 * Function   :  OnInterrupt
 *
 * Description:  The Interrupt Service Routine for the PLX device
 *
 ******************************************************************************/
irqreturn_t
OnInterrupt(
    int   irq,
    void *dev_id
  #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19))
  , struct pt_regs *regs
  #endif
    )
{
    U32               RegValue;
    U32               RegPciInt;
    U32               InterruptSource;
    DEVICE_EXTENSION *pdx;


    // Get the device extension
    pdx = (DEVICE_EXTENSION *)dev_id;

    // Read interrupt status register
    RegPciInt =
        PLX_9000_REG_READ(
            pdx,
            PCI8311_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 PLX_IRQ_RETVAL(IRQ_NONE);

    // Check for master PCI interrupt enable
    if ((RegPciInt & (1 << 8)) == 0)
        return PLX_IRQ_RETVAL(IRQ_NONE);

    // 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_9000_REG_READ(
                pdx,
                PCI8311_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_9000_REG_READ(
                pdx,
                PCI8311_DMA1_MODE
                );

        if (RegValue & (1 << 17))
        {
            InterruptSource |= INTR_TYPE_DMA_1;
        }
    }

    // Check if MU Outbound Post interrupt is active
    RegValue =
        PLX_9000_REG_READ(
            pdx,
            PCI8311_OUTPOST_INT_STAT
            );

    if (RegValue & (1 << 3))
    {
        // Check if MU Outbound Post interrupt is not masked
        RegValue =
            PLX_9000_REG_READ(
                pdx,
                PCI8311_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 PLX_IRQ_RETVAL(IRQ_NONE);

    // At this point, the device interrupt is verified

    // Mask the PCI Interrupt
    PLX_9000_REG_WRITE(
        pdx,
        PCI8311_INT_CTRL_STAT,
        RegPciInt & ~(1 << 8)
        );

    //
    // Schedule deferred procedure (DPC) to complete interrupt processing
    //

    // Provide interrupt source to DPC
    pdx->Source_Ints = InterruptSource;

#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
    // Reset task structure
    pdx->Task_DpcForIsr.sync = 0;

    // Add task to system immediate queue
    queue_task(
        &(pdx->Task_DpcForIsr),
        &tq_immediate
        );

    // Mark queue for Bottom-half processing
    mark_bh(
        IMMEDIATE_BH
        );
#else
    // Add task to system work queue
    schedule_work(
        &(pdx->Task_DpcForIsr)
        );
#endif

    return PLX_IRQ_RETVAL(IRQ_HANDLED);
}




/******************************************************************************
 *
 * Function   :  DpcForIsr
 *
 * Description:  This routine will be triggered by the ISR to service an interrupt
 *
 ******************************************************************************/
void
DpcForIsr(
    PLX_DPC_PARAM *pArg1
    )
{
    U32                 RegValue;
    DEVICE_EXTENSION   *pdx;
    PLX_INTERRUPT_DATA  IntData;


    // Get the device extension
    pdx =
        container_of(
            pArg1,
            DEVICE_EXTENSION,
            Task_DpcForIsr
            );

    // Get interrupt source
    IntData.Source_Ints     = pdx->Source_Ints;
    IntData.Source_Doorbell = 0;

    // Local Interrupt 1
    if (IntData.Source_Ints & INTR_TYPE_LOCAL_1)
    {
        // Synchronize access to Interrupt Control/Status Register
        spin_lock(
            &(pdx->Lock_Isr)
            );

        // Mask local interrupt 1 since true source is unknown
        RegValue =
            PLX_9000_REG_READ(
                pdx,
                PCI8311_INT_CTRL_STAT
                );

        RegValue &= ~(1 << 11);

        PLX_9000_REG_WRITE(
            pdx,
            PCI8311_INT_CTRL_STAT,
            RegValue
            );

        spin_unlock(
            &(pdx->Lock_Isr)
            );
    }

    // Doorbell Interrupt
    if (IntData.Source_Ints & INTR_TYPE_DOORBELL)
    {
        // Get Doorbell register
        RegValue =
            PLX_9000_REG_READ(
                pdx,
                PCI8311_PCI_DOORBELL
                );

        // Clear Doorbell interrupt
        PLX_9000_REG_WRITE(
            pdx,
            PCI8311_PCI_DOORBELL,
            RegValue
            );

        // Save the doorbell value
        IntData.Source_Doorbell = RegValue;
    }

    // PCI Abort interrupt
    if (IntData.Source_Ints & 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 (IntData.Source_Ints & INTR_TYPE_DMA_0)
    {
        // Get DMA Control/Status
        RegValue =
            PLX_9000_REG_READ(
                pdx,
                PCI8311_DMA_COMMAND_STAT
                );

        // Clear DMA interrupt
        PLX_9000_REG_WRITE(
            pdx,
            PCI8311_DMA_COMMAND_STAT,
            RegValue | (1 << 3)
            );

        RegValue =
            PLX_9000_REG_READ(
                pdx,
                PCI8311_DMA0_MODE
                );

        // Check if SGL is enabled & cleanup
        if (RegValue & (1 << 9))
        {
            PlxSglDmaTransferComplete(
                pdx,
                0
                );
        }
    }

    // DMA Channel 1 interrupt
    if (IntData.Source_Ints & INTR_TYPE_DMA_1)
    {
        // Get DMA Control/Status
        RegValue =
            PLX_9000_REG_READ(
                pdx,
                PCI8311_DMA_COMMAND_STAT
                );

        // Clear DMA interrupt
        PLX_9000_REG_WRITE(
            pdx,
            PCI8311_DMA_COMMAND_STAT,
            RegValue | (1 << 11)
            );

        RegValue =
            PLX_9000_REG_READ(
                pdx,
                PCI8311_DMA1_MODE
                );

        // Check if SGL is enabled & cleanup
        if (RegValue & (1 << 9))
        {
            PlxSglDmaTransferComplete(
                pdx,
                1
                );
        }
    }

    // Outbound post FIFO interrupt
    if (IntData.Source_Ints & INTR_TYPE_OUTBOUND_POST)
    {
        // Mask Outbound Post interrupt
        PLX_9000_REG_WRITE(
            pdx,
            PCI8311_OUTPOST_INT_MASK,
            (1 << 3)
            );
    }

    // Signal any objects waiting for notification
    PlxSignalNotifications(
        pdx,
        &IntData
        );

    // Re-enable interrupts
    PlxChipInterruptsEnable(
        pdx
        );
}