/******************************************************************************* * * Function : PlxChip_MailboxWrite * * Description: Writes to a PLX mailbox register * ******************************************************************************/ PLX_STATUS PlxChip_MailboxWrite( DEVICE_EXTENSION *pdx, U16 mailbox, U32 value ) { U16 offset; // Verify valid mailbox if (((S16)mailbox < 0) || ((S16)mailbox > 7)) { return ApiInvalidIndex; } // Set mailbox register base if ((mailbox == 0) || (mailbox == 1)) offset = 0x78; else offset = 0x40; // Calculate mailbox offset offset = offset + (mailbox * sizeof(U32)); // Write mailbox PLX_9000_REG_WRITE( pdx, offset, value ); return ApiSuccess; }
/****************************************************************************** * * Function : PlxChip_EepromWriteByOffset * * Description: Write a 32-bit value to the EEPROM at a specified offset * ******************************************************************************/ PLX_STATUS PlxChip_EepromWriteByOffset( DEVICE_EXTENSION *pdx, U16 offset, U32 value ) { U32 RegisterSave; // Verify the offset if ((offset & 0x3) || (offset > 0x200)) { DebugPrintf(("ERROR - Invalid EEPROM offset\n")); return ApiInvalidOffset; } // Unprotect the EEPROM for write access RegisterSave = PLX_9000_REG_READ( pdx, PCI9056_ENDIAN_DESC ); PLX_9000_REG_WRITE( pdx, PCI9056_ENDIAN_DESC, RegisterSave & ~(0xFF << 16) ); // Write to EEPROM Plx9000_EepromWriteByOffset( pdx, offset, value ); // Restore EEPROM Write-Protected Address Boundary PLX_9000_REG_WRITE( pdx, PCI9056_ENDIAN_DESC, RegisterSave ); return ApiSuccess; }
/******************************************************************************* * * Function : PlxSynchronizedRegisterModify * * Description: Synchronized function with ISR to modify a PLX register * ******************************************************************************/ BOOLEAN PlxSynchronizedRegisterModify( PLX_REG_DATA *pRegData ) { unsigned long flags; U32 RegValue; /************************************************* * This routine sychronizes modification of a * register with the ISR. To do this, it uses * a special spinlock routine provided by the * kernel, which will temporarily disable interrupts. * This code should also work on SMP systems. ************************************************/ // Disable interrupts and acquire lock spin_lock_irqsave( &(pRegData->pdx->Lock_Isr), flags ); RegValue = PLX_9000_REG_READ( pRegData->pdx, pRegData->offset ); RegValue |= pRegData->BitsToSet; RegValue &= ~(pRegData->BitsToClear); PLX_9000_REG_WRITE( pRegData->pdx, pRegData->offset, RegValue ); // Re-enable interrupts and release lock spin_unlock_irqrestore( &(pRegData->pdx->Lock_Isr), flags ); return TRUE; }
/******************************************************************************* * * 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; // Disable interrupts and acquire lock spin_lock( &(pdx->Lock_Isr) ); // Read interrupt status register RegPciInt = PLX_9000_REG_READ( pdx, PCI9056_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) { spin_unlock( &(pdx->Lock_Isr) ); return IRQ_RETVAL(IRQ_NONE); } // Check for master PCI interrupt enable if ((RegPciInt & (1 << 8)) == 0) { spin_unlock( &(pdx->Lock_Isr) ); return 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, PCI9056_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, PCI9056_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, PCI9056_OUTPOST_INT_STAT ); if (RegValue & (1 << 3)) { // Check if MU Outbound Post interrupt is not masked RegValue = PLX_9000_REG_READ( pdx, PCI9056_OUTPOST_INT_MASK ); if ((RegValue & (1 << 3)) == 0) { InterruptSource |= INTR_TYPE_OUTBOUND_POST; } } // Return if no interrupts are active if (InterruptSource == INTR_TYPE_NONE) { spin_unlock( &(pdx->Lock_Isr) ); return IRQ_RETVAL(IRQ_NONE); } // At this point, the device interrupt is verified // Mask the PCI Interrupt PLX_9000_REG_WRITE( pdx, PCI9056_INT_CTRL_STAT, RegPciInt & ~(1 << 8) ); // Re-enable interrupts and release lock spin_unlock( &(pdx->Lock_Isr) ); // // Schedule deferred procedure (DPC) to complete interrupt processing // // Provide interrupt source to DPC pdx->Source_Ints = InterruptSource; // If device is no longer started, do not schedule a DPC if (pdx->State != PLX_STATE_STARTED) return IRQ_RETVAL(IRQ_HANDLED); // Add task to system work queue schedule_work( &(pdx->Task_DpcForIsr) ); // Flag a DPC is pending pdx->bDpcPending = TRUE; return 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; unsigned long flags; DEVICE_EXTENSION *pdx; PLX_INTERRUPT_DATA IntData; // Get the device extension pdx = container_of( pArg1, DEVICE_EXTENSION, Task_DpcForIsr ); // Abort DPC if device is being stopped and resources released if ((pdx->State != PLX_STATE_STARTED) || (pdx->PciBar[0].pVa == NULL)) { DebugPrintf(("DPC aborted, device is stopping\n")); // Flag DPC is no longer pending pdx->bDpcPending = FALSE; return; } // 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_irqsave( &(pdx->Lock_Isr), flags ); // Mask local interrupt 1 since true source is unknown RegValue = PLX_9000_REG_READ( pdx, PCI9056_INT_CTRL_STAT ); RegValue &= ~(1 << 11); PLX_9000_REG_WRITE( pdx, PCI9056_INT_CTRL_STAT, RegValue ); spin_unlock_irqrestore( &(pdx->Lock_Isr), flags ); } // Doorbell Interrupt if (IntData.Source_Ints & INTR_TYPE_DOORBELL) { // Get Doorbell register RegValue = PLX_9000_REG_READ( pdx, PCI9056_PCI_DOORBELL ); // Clear Doorbell interrupt PLX_9000_REG_WRITE( pdx, PCI9056_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, 0x04, &RegValue ); // Write to back to clear PCI Abort PLX_PCI_REG_WRITE( pdx, 0x04, RegValue ); } // DMA Channel 0 interrupt if (IntData.Source_Ints & INTR_TYPE_DMA_0) { // Get DMA Control/Status RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); // Clear DMA interrupt PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue | (1 << 3) ); RegValue = PLX_9000_REG_READ( pdx, PCI9056_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, PCI9056_DMA_COMMAND_STAT ); // Clear DMA interrupt PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue | (1 << 11) ); RegValue = PLX_9000_REG_READ( pdx, PCI9056_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, PCI9056_OUTPOST_INT_MASK, (1 << 3) ); } // Signal any objects waiting for notification PlxSignalNotifications( pdx, &IntData ); // Re-enable interrupts PlxChipInterruptsEnable( pdx ); // Flag a DPC is no longer pending pdx->bDpcPending = FALSE; }
/****************************************************************************** * * Function : PlxChip_DmaControl * * Description: Control the DMA engine * ******************************************************************************/ PLX_STATUS PlxChip_DmaControl( DEVICE_EXTENSION *pdx, U8 channel, PLX_DMA_COMMAND command, VOID *pOwner ) { U8 shift; U32 RegValue; // Verify valid DMA channel switch (channel) { case 0: case 1: break; default: DebugPrintf(("ERROR - Invalid DMA channel\n")); return ApiDmaChannelInvalid; } // Verify owner if ((pdx->DmaInfo[channel].bOpen) && (pdx->DmaInfo[channel].pOwner != pOwner)) { DebugPrintf(("ERROR - DMA owned by different process\n")); return ApiDeviceInUse; } // Set shift for status register shift = (channel * 8); switch (command) { case DmaPause: // Pause the DMA Channel RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue & ~((1 << 0) << shift) ); // Check if the transfer has completed RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); if (RegValue & ((1 << 4) << shift)) return ApiDmaDone; break; case DmaResume: // Verify that the DMA Channel is paused RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); if ((RegValue & (((1 << 4) | (1 << 0)) << shift)) == 0) { PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue | ((1 << 0) << shift) ); } else { return ApiDmaInProgress; } break; case DmaAbort: // Pause the DMA Channel RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue & ~((1 << 0) << shift) ); // Check if the transfer has completed RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); if (RegValue & ((1 << 4) << shift)) return ApiDmaDone; // Abort the transfer (should cause an interrupt) PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue | ((1 << 2) << shift) ); break; default: return ApiDmaCommandInvalid; } return ApiSuccess; }
/****************************************************************************** * * Function : PlxChip_DmaSetProperties * * Description: Sets the current DMA properties * ******************************************************************************/ PLX_STATUS PlxChip_DmaSetProperties( DEVICE_EXTENSION *pdx, U8 channel, PLX_DMA_PROP *pProp, VOID *pOwner ) { U16 OffsetMode; U32 RegValue; // Verify valid DMA channel switch (channel) { case 0: OffsetMode = PCI9056_DMA0_MODE; break; case 1: OffsetMode = PCI9056_DMA1_MODE; break; default: DebugPrintf(("ERROR - Invalid DMA channel\n")); return ApiDmaChannelInvalid; } // Verify owner if ((pdx->DmaInfo[channel].bOpen) && (pdx->DmaInfo[channel].pOwner != pOwner)) { DebugPrintf(("ERROR - DMA owned by different process\n")); return ApiDeviceInUse; } // Verify DMA not in progress if (PlxChip_DmaStatus( pdx, channel, pOwner ) != ApiDmaDone) { DebugPrintf(("ERROR - DMA transfer in progress\n")); return ApiDmaInProgress; } // Set DMA properties RegValue = (pProp->LocalBusWidth << 0) | (pProp->WaitStates << 2) | (pProp->ReadyInput << 6) | (pProp->BurstInfinite << 7) | (pProp->Burst << 8) | (pProp->SglMode << 9) | (pProp->DoneInterrupt << 10) | (pProp->ConstAddrLocal << 11) | (pProp->DemandMode << 12) | (pProp->WriteInvalidMode << 13) | (pProp->EnableEOT << 14) | (pProp->FastTerminateMode << 15) | (pProp->ClearCountMode << 16) | (pProp->RouteIntToPci << 17) | (pProp->DualAddressMode << 18) | (pProp->EOTEndLink << 19) | (pProp->ValidMode << 20) | (pProp->ValidStopControl << 21); // Update properties PLX_9000_REG_WRITE( pdx, OffsetMode, RegValue ); return ApiSuccess; }
/****************************************************************************** * * Function : PlxChip_BoardReset * * Description: Resets a device using software reset feature of PLX chip * ******************************************************************************/ PLX_STATUS PlxChip_BoardReset( DEVICE_EXTENSION *pdx ) { U8 MU_Enabled; U8 EepromPresent; U32 RegValue; U32 RegInterrupt; U32 RegHotSwap; U32 RegPowerMgmnt; // Clear any PCI errors (04[31:27]) PLX_PCI_REG_READ( pdx, 0x04, &RegValue ); if (RegValue & (0xf8 << 24)) { // Write value back to clear aborts PLX_PCI_REG_WRITE( pdx, 0x04, RegValue ); } // Save state of I2O Decode Enable RegValue = PLX_9000_REG_READ( pdx, PCI9056_FIFO_CTRL_STAT ); MU_Enabled = (U8)(RegValue & (1 << 0)); // Determine if an EEPROM is present RegValue = PLX_9000_REG_READ( pdx, PCI9056_EEPROM_CTRL_STAT ); // Make sure S/W Reset & EEPROM reload bits are clear RegValue &= ~((1 << 30) | (1 << 29)); // Remember if EEPROM is present EepromPresent = (U8)((RegValue >> 28) & (1 << 0)); // Save interrupt line PLX_PCI_REG_READ( pdx, 0x3C, &RegInterrupt ); // Save some registers if EEPROM present if (EepromPresent) { PLX_PCI_REG_READ( pdx, PCI9056_HS_CAP_ID, &RegHotSwap ); PLX_PCI_REG_READ( pdx, PCI9056_PM_CSR, &RegPowerMgmnt ); } // Issue Software Reset to hold PLX chip in reset PLX_9000_REG_WRITE( pdx, PCI9056_EEPROM_CTRL_STAT, RegValue | (1 << 30) ); // Delay for a bit Plx_sleep(100); // Bring chip out of reset PLX_9000_REG_WRITE( pdx, PCI9056_EEPROM_CTRL_STAT, RegValue ); // Issue EEPROM reload in case now programmed PLX_9000_REG_WRITE( pdx, PCI9056_EEPROM_CTRL_STAT, RegValue | (1 << 29) ); // Delay for a bit Plx_sleep(10); // Clear EEPROM reload PLX_9000_REG_WRITE( pdx, PCI9056_EEPROM_CTRL_STAT, RegValue ); // Restore I2O Decode Enable state if (MU_Enabled) { // Save state of I2O Decode Enable RegValue = PLX_9000_REG_READ( pdx, PCI9056_FIFO_CTRL_STAT ); PLX_9000_REG_WRITE( pdx, PCI9056_FIFO_CTRL_STAT, RegValue | (1 << 0) ); } // Restore interrupt line PLX_PCI_REG_WRITE( pdx, 0x3C, RegInterrupt ); // If EEPROM was present, restore registers if (EepromPresent) { // Mask out HS bits that can be cleared RegHotSwap &= ~((1 << 23) | (1 << 22) | (1 << 17)); PLX_PCI_REG_WRITE( pdx, PCI9056_HS_CAP_ID, RegHotSwap ); // Mask out PM bits that can be cleared RegPowerMgmnt &= ~(1 << 15); PLX_PCI_REG_READ( pdx, PCI9056_PM_CSR, &RegPowerMgmnt ); } return ApiSuccess; }
/****************************************************************************** * * Function : PlxChip_InterruptDisable * * Description: Disables specific interrupts of the PLX Chip * ******************************************************************************/ PLX_STATUS PlxChip_InterruptDisable( DEVICE_EXTENSION *pdx, PLX_INTERRUPT *pPlxIntr ) { U32 QueueCsr; U32 QueueCsr_Original; U32 RegValue; PLX_REG_DATA RegData; // Setup to synchronize access to Interrupt Control/Status Register RegData.pdx = pdx; RegData.offset = PCI9056_INT_CTRL_STAT; RegData.BitsToSet = 0; RegData.BitsToClear = 0; if (pPlxIntr->PciMain) RegData.BitsToClear |= (1 << 8); if (pPlxIntr->PciAbort) RegData.BitsToClear |= (1 << 10); if (pPlxIntr->TargetRetryAbort) RegData.BitsToClear |= (1 << 12); if (pPlxIntr->LocalToPci & (1 << 0)) RegData.BitsToClear |= (1 << 11); if (pPlxIntr->Doorbell) RegData.BitsToClear |= (1 << 9); if (pPlxIntr->DmaDone & (1 << 0)) { // Check if DMA interrupt is routed to PCI RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA0_MODE ); if (RegValue & (1 << 17)) { RegData.BitsToClear |= (1 << 18); // Disable DMA interrupt enable PLX_9000_REG_WRITE( pdx, PCI9056_DMA0_MODE, RegValue & ~(1 << 10) ); } } if (pPlxIntr->DmaDone & (1 << 1)) { // Check if DMA interrupt is routed to PCI RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA1_MODE ); if (RegValue & (1 << 17)) { RegData.BitsToClear |= (1 << 19); // Disable DMA interrupt enable PLX_9000_REG_WRITE( pdx, PCI9056_DMA1_MODE, RegValue & ~(1 << 10) ); } } // Inbound Post Queue Interrupt Control/Status Register QueueCsr_Original = PLX_9000_REG_READ( pdx, PCI9056_FIFO_CTRL_STAT ); QueueCsr = QueueCsr_Original; if (pPlxIntr->MuOutboundPost) { PLX_9000_REG_WRITE( pdx, PCI9056_OUTPOST_INT_MASK, (1 << 3) ); } if (pPlxIntr->MuInboundPost) QueueCsr |= (1 << 4); if (pPlxIntr->MuOutboundOverflow) QueueCsr |= (1 << 6); // Write register values if they have changed if (RegData.BitsToClear != 0) { // Synchronize write of Interrupt Control/Status Register PlxSynchronizedRegisterModify( &RegData ); } if (QueueCsr != QueueCsr_Original) { PLX_9000_REG_WRITE( pdx, PCI9056_FIFO_CTRL_STAT, QueueCsr ); } return ApiSuccess; }
/****************************************************************************** * * Function : PlxChip_DmaTransferUserBuffer * * Description: Transfers a user-mode buffer using SGL DMA * ******************************************************************************/ PLX_STATUS PlxChip_DmaTransferUserBuffer( DEVICE_EXTENSION *pdx, U8 channel, PLX_DMA_PARAMS *pParams, VOID *pOwner ) { U8 shift; U16 OffsetMode; U32 RegValue; U32 SglPciAddress; BOOLEAN bBits64; PLX_STATUS rc; // Verify DMA channel & setup register offsets switch (channel) { case 0: OffsetMode = PCI9056_DMA0_MODE; break; case 1: OffsetMode = PCI9056_DMA1_MODE; break; default: DebugPrintf(("ERROR - Invalid DMA channel\n")); return ApiDmaChannelInvalid; } // Verify owner if (pdx->DmaInfo[channel].pOwner != pOwner) { DebugPrintf(("ERROR - DMA owned by different process\n")); return ApiDeviceInUse; } // Set shift for status register shift = (channel * 8); // Verify that DMA is not in progress RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); if ((RegValue & ((1 << 4) << shift)) == 0) { DebugPrintf(("ERROR - DMA channel is currently active\n")); return ApiDmaInProgress; } spin_lock( &(pdx->Lock_Dma[channel]) ); // Verify DMA channel was opened if (pdx->DmaInfo[channel].bOpen == FALSE) { DebugPrintf(("ERROR - DMA channel has not been opened\n")); spin_unlock( &(pdx->Lock_Dma[channel]) ); return ApiDmaChannelUnavailable; } // Verify an SGL DMA transfer is not pending if (pdx->DmaInfo[channel].bSglPending) { DebugPrintf(("ERROR - An SGL DMA transfer is currently pending\n")); spin_unlock( &(pdx->Lock_Dma[channel]) ); return ApiDmaInProgress; } // Set the SGL DMA pending flag pdx->DmaInfo[channel].bSglPending = TRUE; spin_unlock( &(pdx->Lock_Dma[channel]) ); // Get DMA mode RegValue = PLX_9000_REG_READ( pdx, OffsetMode ); // Keep track if local address should remain constant if (RegValue & (1 << 11)) pdx->DmaInfo[channel].bConstAddrLocal = TRUE; else pdx->DmaInfo[channel].bConstAddrLocal = FALSE; // Page-lock user buffer & build SGL rc = PlxLockBufferAndBuildSgl( pdx, channel, pParams, &SglPciAddress, &bBits64 ); if (rc != ApiSuccess) { DebugPrintf(("ERROR - Unable to lock buffer and build SGL list\n")); pdx->DmaInfo[channel].bSglPending = FALSE; return rc; } spin_lock( &(pdx->Lock_Dma[channel]) ); // Disable valid mode RegValue &= ~(1 << 20); // Enable DMA chaining, interrupt, & route interrupt to PCI RegValue |= (1 << 9) | (1 << 10) | (1 << 17); // Enable dual-addressing DMA if 64-bit DMA is required if (bBits64) RegValue |= (1 << 18); else RegValue &= ~(1 << 18); PLX_9000_REG_WRITE( pdx, OffsetMode, RegValue ); // Clear DAC upper 32-bit PCI address in case it contains non-zero value PLX_9000_REG_WRITE( pdx, PCI9056_DMA0_PCI_DAC + (channel * sizeof(U32)), 0 ); // Write SGL physical address & set descriptors in PCI space PLX_9000_REG_WRITE( pdx, OffsetMode + 0x10, SglPciAddress | (1 << 0) ); // Enable DMA channel RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue | ((1 << 0) << shift) ); spin_unlock( &(pdx->Lock_Dma[channel]) ); DebugPrintf(("Starting DMA transfer...\n")); // Start DMA PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue | (((1 << 0) | (1 << 1)) << shift) ); return ApiSuccess; }
/****************************************************************************** * * Function : PlxChip_DmaTransferBlock * * Description: Performs DMA block transfer * ******************************************************************************/ PLX_STATUS PlxChip_DmaTransferBlock( DEVICE_EXTENSION *pdx, U8 channel, PLX_DMA_PARAMS *pParams, VOID *pOwner ) { U8 shift; U16 OffsetMode; U32 RegValue; // Verify DMA channel & setup register offsets switch (channel) { case 0: OffsetMode = PCI9056_DMA0_MODE; break; case 1: OffsetMode = PCI9056_DMA1_MODE; break; default: DebugPrintf(("ERROR - Invalid DMA channel\n")); return ApiDmaChannelInvalid; } // Verify owner if (pdx->DmaInfo[channel].pOwner != pOwner) { DebugPrintf(("ERROR - DMA owned by different process\n")); return ApiDeviceInUse; } // Set shift for status register shift = (channel * 8); // Verify that DMA is not in progress RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); if ((RegValue & ((1 << 4) << shift)) == 0) { DebugPrintf(("ERROR - DMA channel is currently active\n")); return ApiDmaInProgress; } spin_lock( &(pdx->Lock_Dma[channel]) ); // Verify DMA channel was opened if (pdx->DmaInfo[channel].bOpen == FALSE) { DebugPrintf(("ERROR - DMA channel has not been opened\n")); spin_unlock( &(pdx->Lock_Dma[channel]) ); return ApiDmaChannelUnavailable; } // Get DMA mode RegValue = PLX_9000_REG_READ( pdx, OffsetMode ); // Disable DMA chaining & SGL dual-addressing RegValue &= ~((1 << 9) | (1 << 18)); // Route interrupt to PCI RegValue |= (1 << 17); // Ignore interrupt if requested if (pParams->bIgnoreBlockInt) RegValue &= ~(1 << 10); else RegValue |= (1 << 10); PLX_9000_REG_WRITE( pdx, OffsetMode, RegValue ); // Write PCI Address PLX_9000_REG_WRITE( pdx, OffsetMode + 0x4, PLX_64_LOW_32(pParams->PciAddr) ); // Write Local Address PLX_9000_REG_WRITE( pdx, OffsetMode + 0x8, pParams->LocalAddr ); // Write Transfer Count PLX_9000_REG_WRITE( pdx, OffsetMode + 0xc, pParams->ByteCount ); // Write Descriptor Pointer if (pParams->Direction == PLX_DMA_LOC_TO_PCI) RegValue = (1 << 3); else RegValue = 0; PLX_9000_REG_WRITE( pdx, OffsetMode + 0x10, RegValue ); // Write Dual Address cycle with upper 32-bit PCI address PLX_9000_REG_WRITE( pdx, PCI9056_DMA0_PCI_DAC + (channel * sizeof(U32)), PLX_64_HIGH_32(pParams->PciAddr) ); // Enable DMA channel RegValue = PLX_9000_REG_READ( pdx, PCI9056_DMA_COMMAND_STAT ); PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue | ((1 << 0) << shift) ); spin_unlock( &(pdx->Lock_Dma[channel]) ); DebugPrintf(("Starting DMA transfer...\n")); // Start DMA PLX_9000_REG_WRITE( pdx, PCI9056_DMA_COMMAND_STAT, RegValue | (((1 << 0) | (1 << 1)) << shift) ); return ApiSuccess; }
/* **=========================================================================== ** 8.0 pcidriver_ioctl() **=========================================================================== ** Description: ** ** Parameters: cmd and a argument ** ** ** Returns: some stuff ... ** ** Globals: */ int plx_drv_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { driver_t *dev = file->private_data; static unsigned char localbuf[IOC_BUFSIZE]; /* Define "alias" names for the localbuf */ void *karg = localbuf; Register *reg = karg; unsigned long *klong = karg; Container *container = karg; int size = _IOC_SIZE(cmd); /* the size bitfield in cmd */ int retval = 0; /* success by default */ #if ZERO PDEBUG(("function: %s, file: %s line: %d invoked\n", __FUNCTION__, __FILE__, __LINE__)); #endif /* * Extract the type and number bitfields, and don't decode * wrong cmds: return EINVAL before verify_area() */ if (_IOC_TYPE(cmd) != MAG_NUM) return -ENOTTY; if (_IOC_NR(cmd) > IOC_MAXNR) return -ENOTTY; /* * The direction is a bitmask, and VERIFY_WRITE catches R/W * transfers. `Dir' is user-oriented, while * verify_area is kernel-oriented, so the concept of "read" and * "write" is reversed */ if (_IOC_DIR(cmd) & _IOC_READ) { if (!access_ok(VERIFY_WRITE, (void *)arg, size)) return -EFAULT; } else if (_IOC_DIR(cmd) & _IOC_WRITE) { if (!access_ok(VERIFY_READ, (void *)arg, size)) return -EFAULT; } #if ZERO PDEBUG(("ioctl.c: %08x %08lx: nr %i, size %i, dir %i\n", cmd, arg, _IOC_NR(cmd), _IOC_SIZE(cmd), _IOC_DIR(cmd))); #endif /* First, retrieve data from userspace */ if ((_IOC_DIR(cmd) & _IOC_WRITE) && (size <= IOC_BUFSIZE)) if (copy_from_user(karg, (void *)arg, size)) return -EFAULT; /* We are ready to switch ... */ switch (cmd) { case READ_PLX_REG: { if ((plx_read_plx_reg(dev, reg->addr, &(reg->data)) == 0)) { // PDEBUG(("plx_read_fpga_reg: addr: %x and data %x\n", reg->addr, reg->data)); } else return -EIO; } break; case WRITE_PLX_REG: { if((plx_write_plx_reg(dev, reg->addr, reg->data)) == 0) { // PDEBUG(("plx_write_fpga_reg: addr: %x and data %x\n", reg->addr, reg->data)); } else return -EIO; } break; case FILL_PCI_MEM: PDEBUG(("plx_ioctl: Fill PCI memory with test pattern!\n")); memset_io(dev->PciBar[2].pVa, 0x11, DMA_SIZE/4); memset_io((dev->PciBar[2].pVa)+0x2000, 0x22, DMA_SIZE/4); memset_io((dev->PciBar[2].pVa)+(0x2000*2), 0x33, DMA_SIZE/4); memset_io((dev->PciBar[2].pVa)+(0x2000*3), 0x44, DMA_SIZE/4); break; case READ_FPGA_REG: if ((plx_read_fpga_reg(dev, reg->addr, &(reg->data)) == 0)) { PDEBUG(("plx_read_fpga_reg: addr: %x and data %x\n", reg->addr, reg->data)); } else return -EIO; // Physical input/output error occured break; case WRITE_FPGA_REG: if((plx_write_fpga_reg(dev, reg->addr, reg->data)) == 0) { PDEBUG(("plx_write_fpga_reg: addr: %x and data %x\n", reg->addr, reg->data)); } else return -EIO; break; case WRITE_SRAM: if((plx_write_sram(dev, reg->addr, reg->data)) == 0) { PDEBUG(("plx_write_sram: addr: %x and data %x\n", reg->addr, reg->data)); } else return -EIO; break; case READ_COMMAND_LINK_REG: if ((plx_read_reg(dev, reg->addr, &(reg->data)) == 0)) { PDEBUG(("plx_read_reg: addr: %x and data %x\n", reg->addr, reg->data)); } else return -EIO; break; case WRITE_COMMAND_LINK_REG: PDEBUG(("ioctl.c: addr: %x and data %x\n", reg->addr, reg->data)); if((plx_write_reg(dev, reg->addr, reg->data)) == 0) { PDEBUG(("plx_write_reg: addr: %x and data %x\n", reg->addr, reg->data)); } else return -EIO; break; case IMMEDIATE_COMMAND: switch( *klong) { case XL_EXEC: /* Read the micro sec counter */ plx_diff_since_read(dev); case XL_NOP: case XL_INIT: case XL_STOP: if((plx_immediate_cmd(dev, *klong) == 0)) { } else { retval = -EIO; } break; default : retval = -EFAULT; } break; case ORDER_HW: switch (container->order) { case 0x1: // Dump PCI memory { int i; unsigned int tmp; PDEBUG(("ioctl: order_hw, start_addr%x\n", container->start_addr)); for (i = 0; i <16; i++) { tmp = SRAM_READ(dev, (container->start_addr) +(i*4)); container->array[i] = tmp; } } break; case 0x2: // Reset HW plx_board_reset(dev); break; case 0x3: // Read and clear device driver counters { container->array[0] = dev->cntr_circ_empty_rp; container->array[1] = dev->cntr_irq_count; container->array[2] = dev->cntr_irq_processed; container->array[3] = dev->cntr_irq_none; container->array[4] = dev->cntr_low_power_state; container->array[5] = dev->cntr_pci_master_disabled; container->array[6] = dev->cntr_lost_ints; container->array[7] = dev->cntr_read; container->array[8] = dev->cntr_poll; container->array[9] = dev->cntr_failed_dma; container->array[10] = dev->cntr_circ_full_wp; container->array[11] = dev->cntr_circ_full_ip; container->array[12] = dev->cntr_2_user_space; container->array[13] = dev->cntr_tmo_rdr; container->array[14] = dev->cntr_tmo_tbe; container->array[15] = dev->cntr_lost_hw_ints; dev->cntr_circ_empty_rp = dev->cntr_irq_count = dev->cntr_irq_processed = dev->cntr_irq_none = 0; dev->cntr_low_power_state = dev->cntr_pci_master_disabled = 0; dev->cntr_read = dev->cntr_poll = dev->cntr_failed_dma = dev->cntr_2_user_space = 0; dev->cntr_tmo_rdr = dev->cntr_tmo_tbe = dev->cntr_circ_full_wp = dev->cntr_lost_ints = 0; dev->cntr_lost_hw_ints = dev->cntr_circ_full_ip = 0; } break; case 0x4: break; case 0x5: #if ZERO PDEBUG(("order_hw: read time:%d\n", plx_diff_since_read(dev))); #endif break; #if DMA_DEBUG case 0x6: { int r; /* Write a test pattern at start address in kernel mem */ for(r = 0; r < NO_OF_BUFFERS ; r++) { *(unsigned long *)(dev->frames[r]) = 0xcdcdfbfb; } } break; case 0x7: { int r; /* Read test pattern at start address in kernel mem */ for(r = 0; r < NO_OF_BUFFERS ; r++) { PDEBUG(("r:%d:%lx\n", r, *(unsigned long *)(dev->frames[r]))); } } break; case 0x8: { U32 RegValue; /* Abort a DMA, should generate an interrupt */ RegValue = PLX_9000_REG_READ( dev, PCI8311_DMA_COMMAND_STAT ); // Abort the transfer (should cause an interrupt) PLX_9000_REG_WRITE( dev, PCI8311_DMA_COMMAND_STAT, RegValue | ((1 << 2)) ); } break; #endif case 0x9: PDEBUG(("order_hw: ip%d, wp%d, rp%d\n", dev->ip , dev->wp, dev->rp)); dev->wp = dev->rp = dev->ip = 0; break; /* Disable driver debug */ case 0xa: debug = 0; break; /* Enable driver debug */ case 0xb: debug = 1; break; /* Reset sequence counters in hw and driver */ case 0xc: dev->seq_cntr = 0; plx_immediate_cmd(dev, XL_INIT); break; /* Generate a local interrupt */ case 0xd: { u32 RegDetectorInt; RegDetectorInt = COMMAND_LINK_REG_READ(dev, PCI_MC_CARD_STATUS); RegDetectorInt |= (1<<29); COMMAND_LINK_REG_WRITE(dev,PCI_MC_CARD_STATUS,RegDetectorInt); } break; default: ERROR(("ioctl: no such order: %x\n", container->order)); retval = -EFAULT; break; } case DRIVER_VERSION: reg->data = DEVICE_DRIVER_VERSION; break; case READ_CONFIG_REG: { PLX_PCI_REG_READ(dev, reg->addr,®->data); } break; default: ERROR(("ioctl: no such command: %x\n", cmd)); return -ENOIOCTLCMD; } /* Finally, copy data to user space and return */ if (retval < 0) return retval; if ((_IOC_DIR(cmd) & _IOC_READ) && (size <= IOC_BUFSIZE)) if (copy_to_user((void *)arg, karg, size)) return -EFAULT; return retval; /* sometimes, positive is what I want */ }