/****************************************************************************** * * Function : PlxChipInterruptsDisable * * Description: Globally disables PLX chip interrupts * *****************************************************************************/ BOOLEAN PlxChipInterruptsDisable( DEVICE_EXTENSION *pdx ) { U32 RegValue; // Disable doorbell interrupts PLX_PCI_REG_READ( pdx, 0xc4, &RegValue ); RegValue &= ~0xFFFF; PLX_PCI_REG_WRITE( pdx, 0xc4, RegValue ); // Disable Message, S_RSTIN, S_PME, & GPIO interrupts PLX_PCI_REG_WRITE( pdx, 0xc8, 0x00000000 ); return TRUE; }
/******************************************************************************* * * Function : PlxGetExtendedCapabilityOffset * * Description: Scans the capability list to search for a specific capability * ******************************************************************************/ U16 PlxGetExtendedCapabilityOffset( DEVICE_EXTENSION *pdx, U16 CapabilityId ) { U16 Offset_Cap; U32 RegValue; // Get offset of first capability PLX_PCI_REG_READ( pdx, 0x34, // PCI capabilities pointer &RegValue ); // If link is down, PCI reg accesses will fail if (RegValue == (U32)-1) return 0; // Set first capability Offset_Cap = (U16)RegValue; // Traverse capability list searching for desired ID while ((Offset_Cap != 0) && (RegValue != (U32)-1)) { // Get next capability PLX_PCI_REG_READ( pdx, Offset_Cap, &RegValue ); if ((U8)RegValue == (U8)CapabilityId) { // Capability found, return base offset return Offset_Cap; } // Jump to next capability Offset_Cap = (U16)((RegValue >> 8) & 0xFF); } // Capability not found return 0; }
/******************************************************************************* * * Function : PlxChipTypeDetect * * Description: Attempts to determine PLX chip type and revision * ******************************************************************************/ PLX_STATUS PlxChipTypeDetect( DEVICE_EXTENSION *pdx ) { U32 RegValue; // Set default values pdx->Key.PlxChip = PLX_CHIP_TYPE; pdx->Key.PlxRevision = pdx->Key.Revision; pdx->Key.PlxFamily = PLX_FAMILY_BRIDGE_P2L; // Check hard-coded ID RegValue = PLX_9000_REG_READ( pdx, 0x70 ); if ((RegValue & 0xFFFF) == PLX_VENDOR_ID) { pdx->Key.PlxChip = (U16)(RegValue >> 16); // Get revision RegValue = PLX_9000_REG_READ( pdx, 0x74 // Revision ID ); // AA & AB versions have same revision ID if (RegValue == 0xA) { PLX_PCI_REG_READ( pdx, 0x08, &RegValue ); if ((RegValue & 0xFF) == 0x0B) pdx->Key.PlxRevision = 0xAB; else pdx->Key.PlxRevision = 0xAA; } else { // Check for AC revision if (RegValue == 0xC) pdx->Key.PlxRevision = 0xAC; else pdx->Key.PlxRevision = (U8)RegValue; } }
/******************************************************************************* * * 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 : StartDevice * * Description: Start a device * ******************************************************************************/ int StartDevice( DEVICE_OBJECT *fdo ) { int rc; U8 i; U8 ResourceCount; U32 RegValue; DEVICE_EXTENSION *pdx; if (fdo->DeviceExtension->State == PLX_STATE_STARTED) return 0; DebugPrintf(("Start device...\n")); pdx = fdo->DeviceExtension; ResourceCount = 0; for (i = 0; i < PCI_NUM_BARS_TYPE_00; ++i) { // Verify the address is valid if (pci_resource_start( pdx->pPciDevice, i ) == 0) { continue; } DebugPrintf((" Resource %02d\n", ResourceCount)); // Increment resource count ResourceCount++; // Get PCI physical address pdx->PciBar[i].Properties.Physical = pci_resource_start( pdx->pPciDevice, i ); // Determine resource type if (pci_resource_flags( pdx->pPciDevice, i ) & IORESOURCE_IO) { DebugPrintf((" Type : I/O\n")); // Make sure flags are cleared properly pdx->PciBar[i].Properties.Physical &= ~(0x3); pdx->PciBar[i].Properties.Flags = PLX_BAR_FLAG_IO; } else { DebugPrintf((" Type : Memory\n")); // Make sure flags are cleared properly pdx->PciBar[i].Properties.Physical &= ~(0xf); pdx->PciBar[i].Properties.Flags = PLX_BAR_FLAG_MEM; } // Set BAR as already probed pdx->PciBar[i].Properties.Flags |= PLX_BAR_FLAG_PROBED; // Get the actual BAR value PLX_PCI_REG_READ( pdx, 0x10 + (i * sizeof(U32)), &RegValue ); pdx->PciBar[i].Properties.BarValue = RegValue; // If 64-bit BAR, get upper address if (pci_resource_flags(pdx->pPciDevice, i) & IORESOURCE_MEM_64) { PLX_PCI_REG_READ( pdx, 0x10 + ((i+1) * sizeof(U32)), &RegValue ); pdx->PciBar[i].Properties.BarValue |= (U64)RegValue << 32; } DebugPrintf(( " PCI BAR %d: %08llX\n", i, pdx->PciBar[i].Properties.BarValue )); DebugPrintf(( " Phys Addr: %08llX\n", pdx->PciBar[i].Properties.Physical )); // Get the size pdx->PciBar[i].Properties.Size = pci_resource_len( pdx->pPciDevice, i ); DebugPrintf(( " Size : %8llx (%lld %s)\n", pdx->PciBar[i].Properties.Size, (pdx->PciBar[i].Properties.Size < ((U64)1 << 10)) ? pdx->PciBar[i].Properties.Size : pdx->PciBar[i].Properties.Size >> 10, (pdx->PciBar[i].Properties.Size < ((U64)1 << 10)) ? "bytes" : "KB" )); // Set flags if (pdx->PciBar[i].Properties.Flags & PLX_BAR_FLAG_MEM) { if (pci_resource_flags(pdx->pPciDevice, i) & IORESOURCE_MEM_64) pdx->PciBar[i].Properties.Flags |= PLX_BAR_FLAG_64_BIT; else pdx->PciBar[i].Properties.Flags |= PLX_BAR_FLAG_32_BIT; if (pci_resource_flags(pdx->pPciDevice, i) & IORESOURCE_PREFETCH) pdx->PciBar[i].Properties.Flags |= PLX_BAR_FLAG_PREFETCHABLE; DebugPrintf(( " Property : %sPrefetchable %d-bit\n", (pdx->PciBar[i].Properties.Flags & PLX_BAR_FLAG_PREFETCHABLE) ? "" : "Non-", (pdx->PciBar[i].Properties.Flags & PLX_BAR_FLAG_64_BIT) ? 64 : 32 )); } // Claim and map the resource rc = PlxPciBarResourceMap( pdx, i ); if (rc == 0) { if (pdx->PciBar[i].Properties.Flags & PLX_BAR_FLAG_MEM) { DebugPrintf(( " Kernel VA: %p\n", pdx->PciBar[i].pVa )); } } else { if (pdx->PciBar[i].Properties.Flags & PLX_BAR_FLAG_MEM) { ErrorPrintf((" Kernel VA: ERROR - Unable to map space to Kernel VA\n")); } } } // Make sure BAR 0 exists or the device can't be started if (pdx->PciBar[0].pVa == NULL) { ErrorPrintf(("ERROR - BAR 0 mapping is required for register access\n")); return -ENOSYS; } // Store BAR 0 kernel virtual address for register access pdx->pRegVa = pdx->PciBar[0].pVa; // Determine & store the PLX chip type PlxChipTypeDetect( pdx ); // Disable all interrupts PlxChipInterruptsDisable( pdx ); // Default interrupt to none pdx->IrqType = PLX_IRQ_TYPE_NONE; // Store PCI IRQ pdx->IrqPci = pdx->pPciDevice->irq; // Install the ISR if available if (pdx->pPciDevice->irq == 0) { DebugPrintf(("Device not using a PCI interrupt resource\n")); } else { // Default to INTx interrupt pdx->IrqType = PLX_IRQ_TYPE_INTX; // Install the ISR rc = request_irq( pdx->pPciDevice->irq, // The device IRQ OnInterrupt, // Interrupt handler PLX_IRQF_SHARED, // Flags, support interrupt sharing PLX_DRIVER_NAME, // The driver name pdx // Parameter to the ISR ); if (rc != 0) { ErrorPrintf(("ERROR - Unable to install ISR\n")); pdx->IrqType = PLX_IRQ_TYPE_NONE; } else { DebugPrintf(("Installed ISR for interrupt\n")); // Re-enable interrupts PlxChipInterruptsEnable( pdx ); } } // Update device state pdx->State = PLX_STATE_STARTED; return 0; }
/******************************************************************************* * * Function : AddDevice * * Description: Add a new device object to the driver * ******************************************************************************/ int AddDevice( DRIVER_OBJECT *pDriverObject, struct pci_dev *pPciDev ) { #if defined(PLX_DMA_SUPPORT) U8 i; #endif int status; U32 RegValue; DEVICE_OBJECT *fdo; DEVICE_OBJECT *pDevice; DEVICE_EXTENSION *pdx; // Allocate memory for the device object fdo = kmalloc( sizeof(DEVICE_OBJECT), GFP_KERNEL ); if (fdo == NULL) { ErrorPrintf(("ERROR - memory allocation for device object failed\n")); return (-ENOMEM); } // Initialize device object RtlZeroMemory( fdo, sizeof(DEVICE_OBJECT) ); fdo->DriverObject = pDriverObject; // Save parent driver object fdo->DeviceExtension = &(fdo->DeviceInfo); // Enable the device if (pci_enable_device( pPciDev ) == 0) { DebugPrintf(("Enabled PCI device\n")); } else { ErrorPrintf(("WARNING - PCI device enable failed\n")); } // Enable bus mastering pci_set_master( pPciDev ); // // Initialize the device extension // pdx = fdo->DeviceExtension; // Clear device extension RtlZeroMemory( pdx, sizeof(DEVICE_EXTENSION) ); // Store parent device object pdx->pDeviceObject = fdo; // Save the OS-supplied PCI object pdx->pPciDevice = pPciDev; // Set initial device device state pdx->State = PLX_STATE_STOPPED; // Set initial power state pdx->PowerState = PowerDeviceD0; // Store device location information pdx->Key.bus = pPciDev->bus->number; pdx->Key.slot = PCI_SLOT(pPciDev->devfn); pdx->Key.function = PCI_FUNC(pPciDev->devfn); pdx->Key.DeviceId = pPciDev->device; pdx->Key.VendorId = pPciDev->vendor; pdx->Key.SubVendorId = pPciDev->subsystem_vendor; pdx->Key.SubDeviceId = pPciDev->subsystem_device; pdx->Key.DeviceNumber = pDriverObject->DeviceCount; // Set API access mode pdx->Key.ApiMode = PLX_API_MODE_PCI; // Update Revision ID PLX_PCI_REG_READ( pdx, 0x08, // PCI Revision ID &RegValue ); pdx->Key.Revision = (U8)(RegValue & 0xFF); // Build device name sprintf( pdx->LinkName, PLX_DRIVER_NAME "-%d", pDriverObject->DeviceCount ); // Initialize work queue for ISR DPC queueing PLX_INIT_WORK( &(pdx->Task_DpcForIsr), DpcForIsr, // DPC routine &(pdx->Task_DpcForIsr) // DPC parameter (pre-2.6.20 only) ); // Initialize ISR spinlock spin_lock_init( &(pdx->Lock_Isr) ); // Initialize interrupt wait list INIT_LIST_HEAD( &(pdx->List_WaitObjects) ); spin_lock_init( &(pdx->Lock_WaitObjectsList) ); // Initialize physical memories list INIT_LIST_HEAD( &(pdx->List_PhysicalMem) ); spin_lock_init( &(pdx->Lock_PhysicalMemList) ); #if defined(PLX_DMA_SUPPORT) /**************************************************************** * Set the DMA mask * * Although PLX devices can handle 64-bit DMA addressing through * dual cycles, this driver does not support that feature. As * a result, the OS is notified to keep this device's DMA mask * to 32-bit, which is the default anyway. ***************************************************************/ Plx_dma_set_mask( pdx, PLX_DMA_BIT_MASK(32) ); // Set DMA buffer allocation mask. PLX DMA requires 32-bit for SGL buffers if (Plx_dma_set_coherent_mask( pdx, PLX_DMA_BIT_MASK(32) ) != 0) { ErrorPrintf(("WARNING - Set DMA coherent mask failed\n")); } // Initialize DMA spinlocks for (i = 0; i < NUM_DMA_CHANNELS; i++) { spin_lock_init( &(pdx->Lock_Dma[i]) ); } #endif // PLX_DMA_SUPPORT // // Add to driver device list // // Acquire Device List lock spin_lock( &(pDriverObject->Lock_DeviceList) ); // Get device list head pDevice = pDriverObject->DeviceObject; if (pDevice == NULL) { // Add device as first in list pDriverObject->DeviceObject = fdo; } else { // Go to end of list while (pDevice->NextDevice != NULL) pDevice = pDevice->NextDevice; // Add device to end of list pDevice->NextDevice = fdo; } // Increment device count pDriverObject->DeviceCount++; // Release Device List lock spin_unlock( &(pDriverObject->Lock_DeviceList) ); DebugPrintf(( "Created Device (%s)\n", pdx->LinkName )); // Start the device status = StartDevice( fdo ); if (status != 0) { RemoveDevice( fdo ); return status; } return 0; }
/******************************************************************************* * * Function : PlxInterruptDisable * * Description: Disables specific interrupts of the PLX Chip * ******************************************************************************/ PLX_STATUS PlxInterruptDisable( DEVICE_EXTENSION *pdx, PLX_INTERRUPT *pPlxIntr ) { U32 RegValue; PLX_REG_DATA RegData; // Only 16 doorbell interrupts are supported pPlxIntr->Doorbell &= 0xFFFF; // Disable doorbell interrupts if (pPlxIntr->Doorbell) { PLX_PCI_REG_READ( pdx, 0xc4, &RegValue ); // Clear doorbell interrupts that are set RegValue &= 0xFFFF0000 | (~pPlxIntr->Doorbell & 0xFFFF); PLX_PCI_REG_WRITE( pdx, 0xc4, RegValue ); } // Setup to synchronize access to interrupt register RegData.pdx = pdx; RegData.offset = 0xc8; RegData.BitsToSet = 0; RegData.BitsToClear = 0; if (pPlxIntr->Message & (1 << 0)) RegData.BitsToClear |= (1 << 24); if (pPlxIntr->Message & (1 << 1)) RegData.BitsToClear |= (1 << 25); if (pPlxIntr->Message & (1 << 2)) RegData.BitsToClear |= (1 << 26); if (pPlxIntr->Message & (1 << 3)) RegData.BitsToClear |= (1 << 27); if (pPlxIntr->ResetDeassert) RegData.BitsToClear |= (1 << 28); if (pPlxIntr->PmeDeassert) RegData.BitsToClear |= (1 << 29); if (pPlxIntr->GPIO_14_15) RegData.BitsToClear |= (1 << 30); if (pPlxIntr->GPIO_4_5) RegData.BitsToClear |= (1 << 31); // Write register values if they have changed if (RegData.BitsToClear != 0) { // Synchronize write to interrupt register PlxSynchronizedRegisterModify( &RegData ); } 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 : PlxPciBoardReset * * Description: Resets a device using software reset feature of PLX chip * ******************************************************************************/ VOID PlxPciBoardReset( DEVICE_EXTENSION *pdx ) { U8 MU_Enabled; U8 EepromPresent; U32 RegValue; U32 RegInterrupt; U32 RegMailbox0; U32 RegMailbox1; // Added to avoid compiler warnings RegMailbox0 = 0; RegMailbox1 = 0; // Clear any PCI errors PLX_PCI_REG_READ( pdx, CFG_COMMAND, &RegValue ); if (RegValue & (0xf8 << 24)) { // Write value back to clear aborts PLX_PCI_REG_WRITE( pdx, CFG_COMMAND, RegValue ); } // Save state of I2O Decode Enable RegValue = PLX_REG_READ( pdx, PCI9080_FIFO_CTRL_STAT ); MU_Enabled = (U8)(RegValue & (1 << 0)); // Determine if an EEPROM is present RegValue = PLX_REG_READ( pdx, PCI9080_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 some registers if EEPROM present if (RegValue & (1 << 28)) { RegMailbox0 = PLX_REG_READ( pdx, PCI9080_MAILBOX0 ); RegMailbox1 = PLX_REG_READ( pdx, PCI9080_MAILBOX1 ); PLX_PCI_REG_READ( pdx, CFG_INT_LINE, &RegInterrupt ); } // Issue Software Reset to hold PLX chip in reset PLX_REG_WRITE( pdx, PCI9080_EEPROM_CTRL_STAT, RegValue | (1 << 30) ); // Delay for a bit Plx_sleep(100); // Bring chip out of reset PLX_REG_WRITE( pdx, PCI9080_EEPROM_CTRL_STAT, RegValue ); // Issue EEPROM reload in case now programmed PLX_REG_WRITE( pdx, PCI9080_EEPROM_CTRL_STAT, RegValue | (1 << 29) ); // Delay for a bit Plx_sleep(10); // Clear EEPROM reload PLX_REG_WRITE( pdx, PCI9080_EEPROM_CTRL_STAT, RegValue ); // Restore I2O Decode Enable state if (MU_Enabled) { // Save state of I2O Decode Enable RegValue = PLX_REG_READ( pdx, PCI9080_FIFO_CTRL_STAT ); PLX_REG_WRITE( pdx, PCI9080_FIFO_CTRL_STAT, RegValue | (1 << 0) ); } // If EEPROM was present, restore registers if (EepromPresent) { // Restore saved registers PLX_REG_WRITE( pdx, PCI9080_MAILBOX0, RegMailbox0 ); PLX_REG_WRITE( pdx, PCI9080_MAILBOX1, RegMailbox1 ); PLX_PCI_REG_WRITE( pdx, CFG_INT_LINE, RegInterrupt ); } }
/* **=========================================================================== ** 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 */ }