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