/************************************************************************** Copyright (c) 2001-2019, Intel Corporation All rights reserved. Source code in this module is released to Microsoft per agreement INTC093053_DA solely for the purpose of supporting Intel Ethernet hardware in the Ethernet transport layer of Microsoft's Kernel Debugger. ***************************************************************************/ /** @file FVL specific functions implementation. Contains hardware initialization functions, implementation of C Shared code defined memory management functions, internal register read/write functions. Also contains implementation hw dependant functionality used internally by UNDI commands. Copyright (C) 2016 Intel Corporation.
All rights reserved. This software and associated documentation (if any) is furnished undr a license and may only be used or copied in accordance with the terms of the license. Except as permitted by such license, no part of this software or documentation may be reproduced, stored in a retrieval system, or transmitted in any form or by any means without the express written consent of Intel Corporation. **/ #include "I40e.h" #include #ifndef INTEL_KDNET #include "EepromConfig.h" #endif #include "DeviceSupport.h" #ifndef INTEL_KDNET extern EFI_DRIVER_BINDING_PROTOCOL gUndiDriverBinding; #endif extern EFI_GUID gEfiStartStopProtocolGuid; // // Global variables for blocking IO // STATIC BOOLEAN gInitializeLock = TRUE; #ifndef INTEL_KDNET STATIC EFI_LOCK gLock; #endif VOID i40eBlockIt( IN I40E_DRIVER_DATA *AdapterInfo, IN UINT32 flag ); #ifdef INTEL_KDNET VOID KdNetUNDIMapMem ( UINT64 Context, UINT64 VirtualAddress, UINT32 Size, UINT32 Direction, UINT64 MappedAddressPtr); VOID i40eMapMem( IN I40E_DRIVER_DATA *AdapterInfo, IN UINT64 VirtualAddress, IN UINT32 Size, OUT UINT64 *MappedAddress ) /*++ Routine Description: Maps a virtual address to a physical address. This is necessary for runtime functionality and also on some platforms that have a chipset that cannot allow DMAs above 4GB. Arguments: AdapterInfo - Pointer to the NIC data structure information which the UNDI driver is layering on. VirtualAddress - Virtual Address of the data buffer to map. Size - Minimum size of the buffer to map. MappedAddress - Pointer to store the address of the mapped buffer. Returns: --*/ { if (AdapterInfo->Map_Mem != NULL) { *MappedAddress = 0; (*AdapterInfo->Map_Mem) ( AdapterInfo->Unique_ID, VirtualAddress, Size, TO_DEVICE, (UINT64)MappedAddress ); // // Workaround: on some systems mapmem may not be implemented correctly, so just // pass back the original address // if (*MappedAddress == 0) { *MappedAddress = VirtualAddress; } } else { //*MappedAddress = (KdGetPhysicalAddress((VOID *)(UINTN)VirtualAddress)).QuadPart; KdNetUNDIMapMem((UINT64)(UINTN)NULL, VirtualAddress, Size, TO_DEVICE, (UINT64)(UINTN)(VOID *)MappedAddress); if (*MappedAddress == 0) { *MappedAddress = VirtualAddress; } } } #endif #ifdef INTEL_KDNET VOID i40eUnMapMem( IN I40E_DRIVER_DATA *AdapterInfo, IN UINT64 VirtualAddress, IN UINT32 Size, IN UINT64 MappedAddress ) /*++ Routine Description: Maps a virtual address to a physical address. This is necessary for runtime functionality and also on some platforms that have a chipset that cannot allow DMAs above 4GB. Arguments: XgbeAdapter - Pointer to the NIC data structure information which the UNDI driver is layering on. VirtualAddress - Virtual Address of the data buffer to map. Size - Minimum size of the buffer to map. MappedAddress - Pointer to store the address of the mapped buffer. Returns: --*/ { if (AdapterInfo->UnMap_Mem != NULL) { (*AdapterInfo->UnMap_Mem) ( AdapterInfo->Unique_ID, VirtualAddress, Size, TO_DEVICE, (UINT64)MappedAddress ); } } #endif /** This is the drivers copy function so it does not need to rely on the BootServices copy which goes away at runtime. This copy function allows 64-bit or 32-bit copies depending on platform architecture. On Itanium we must check that both addresses are naturally aligned before attempting a 64-bit copy. @param[in] Dest Destination memory pointer. @param[in] Source Source memory pointer. @param[in] Count Number of bytes to copy. @retval VOID **/ VOID i40eMemCopy( IN UINT8 *Dest, IN UINT8 *Source, IN UINT32 Count ) { UINT32 BytesToCopy; UINT32 IntsToCopy; UINTN *SourcePtr; UINTN *DestPtr; UINT8 *SourceBytePtr; UINT8 *DestBytePtr; IntsToCopy = Count / sizeof(UINTN); BytesToCopy = Count % sizeof(UINTN); #ifdef EFI64 // // Itanium cannot handle memory accesses that are not naturally aligned. Determine // if 64-bit copy is even possible with these start addresses. // if (((((UINTN)Source) & 0x0007) != 0) || ((((UINTN)Dest) & 0x0007) != 0)) { IntsToCopy = 0; BytesToCopy = Count; } #endif SourcePtr = (UINTN *)Source; DestPtr = (UINTN *)Dest; while (IntsToCopy > 0) { *DestPtr = *SourcePtr; SourcePtr++; DestPtr++; IntsToCopy--; } // // Copy the leftover bytes. // SourceBytePtr = (UINT8 *)SourcePtr; DestBytePtr = (UINT8 *)DestPtr; while (BytesToCopy > 0) { *DestBytePtr = *SourceBytePtr; SourceBytePtr++; DestBytePtr++; BytesToCopy--; } } // // Each context sub-line consists of 128 bits (16 bytes) of data // #define SUB_LINE_LENGTH 0x10 #define LANCTXCTL_QUEUE_TYPE_TX 0x1 #define LANCTXCTL_QUEUE_TYPE_RX 0x0 #ifndef INTEL_KDNET EFI_STATUS HMCDump( I40E_DRIVER_DATA *AdapterInfo, UINT16 QueueNumber, enum i40e_hmc_lan_rsrc_type hmc_type ) { UINT32 ControlReg; UINT32 byte_length = 0; UINT32 queue_type = 0; UINT32 sub_line = 0; switch (hmc_type) { case I40E_HMC_LAN_RX: byte_length = I40E_HMC_OBJ_SIZE_RXQ; queue_type = LANCTXCTL_QUEUE_TYPE_RX; break; case I40E_HMC_LAN_TX: byte_length = I40E_HMC_OBJ_SIZE_TXQ; queue_type = LANCTXCTL_QUEUE_TYPE_TX; break; default: return EFI_DEVICE_ERROR; } #ifdef FORTVILLE_A0_SUPPORT Print(L"I40E_GLGEN_CSR_DEBUG_C_CSR_ADDR_PROT_MASK value before %x\n", i40eRead32(AdapterInfo, I40E_GLGEN_CSR_DEBUG_C)); i40eWrite32(AdapterInfo, I40E_GLGEN_CSR_DEBUG_C, i40eRead32(AdapterInfo, I40E_GLGEN_CSR_DEBUG_C) & (~(I40E_GLGEN_CSR_DEBUG_C_CSR_ADDR_PROT_MASK))); Print(L"I40E_GLGEN_CSR_DEBUG_C_CSR_ADDR_PROT_MASK value after %x\n", i40eRead32(AdapterInfo, I40E_GLGEN_CSR_DEBUG_C)); #endif for (sub_line = 0; sub_line < (byte_length / SUB_LINE_LENGTH); sub_line++) { ControlReg = ((UINT32)QueueNumber << I40E_PFCM_LANCTXCTL_QUEUE_NUM_SHIFT) | ((UINT32)queue_type << I40E_PFCM_LANCTXCTL_QUEUE_TYPE_SHIFT) | ((UINT32)sub_line << I40E_PFCM_LANCTXCTL_SUB_LINE_SHIFT) | ((UINT32)0 << I40E_PFCM_LANCTXCTL_OP_CODE_SHIFT); #ifdef FORTVILLE_A0_SUPPORT i40eWrite32(AdapterInfo, I40E_PFCM_LANCTXCTL(AdapterInfo->Function), ControlReg); #else i40eWrite32(AdapterInfo, I40E_PFCM_LANCTXCTL, ControlReg); #endif Print(L"I40E_PFCM_LANCTXCTL = %x\n", ControlReg); #ifdef FORTVILLE_A0_SUPPORT while ((i40eRead32(AdapterInfo, I40E_PFCM_LANCTXSTAT(AdapterInfo->Function)) & I40E_PFCM_LANCTXSTAT_CTX_DONE_MASK) == 0); #else while ((i40eRead32(AdapterInfo, I40E_PFCM_LANCTXSTAT) & I40E_PFCM_LANCTXSTAT_CTX_DONE_MASK) == 0); #endif Print(L"HMC function %d, Queue %d, Type %d, sub_line %x: %x %x %x %x\n", AdapterInfo->Function, QueueNumber, queue_type, sub_line, #ifdef FORTVILLE_A0_SUPPORT i40eRead32(AdapterInfo, I40E_PFCM_LANCTXDATA(0, AdapterInfo->Function)), i40eRead32(AdapterInfo, I40E_PFCM_LANCTXDATA(1, AdapterInfo->Function)), i40eRead32(AdapterInfo, I40E_PFCM_LANCTXDATA(2, AdapterInfo->Function)), i40eRead32(AdapterInfo, I40E_PFCM_LANCTXDATA(3, AdapterInfo->Function))); #else i40eRead32(AdapterInfo, I40E_PFCM_LANCTXDATA(0)), i40eRead32(AdapterInfo, I40E_PFCM_LANCTXDATA(1)), i40eRead32(AdapterInfo, I40E_PFCM_LANCTXDATA(2)), i40eRead32(AdapterInfo, I40E_PFCM_LANCTXDATA(3))); #endif } #ifdef FORTVILLE_A0_SUPPORT i40eWrite32(AdapterInfo, I40E_GLGEN_CSR_DEBUG_C, i40eRead32(AdapterInfo, I40E_GLGEN_CSR_DEBUG_C) | (I40E_GLGEN_CSR_DEBUG_C_CSR_ADDR_PROT_MASK)); #endif return EFI_SUCCESS; } #endif /** Copies the frame from one of the Rx buffers to the command block passed in as part of the cpb parameter. The flow: Ack the interrupt, setup the pointers, find where the last block copied is, check to make sure we have actually received something, and if we have then we do a lot of work. The packet is checked for errors, adjust the amount to copy if the buffer is smaller than the packet, copy the packet to the EFI buffer, and then figure out if the packet was targetted at us, broadcast, multicast or if we are all promiscuous. We then put some of the more interesting information (protocol, src and dest from the packet) into the db that is passed to us. Finally we clean up the frame, set the return value to _SUCCESS, and inc the index, watching for wrapping. Then with all the loose ends nicely wrapped up, fade to black and return. @param[in] AdapterInfo Pointer to the driver data @param[in] CpbReceive Pointer (Ia-64 friendly) to the command parameter block. The frame will be placed inside of it. @param[in] DbReceive The data buffer. The out of band method of passing pre-digested information to the protocol. @retval PXE_STATCODE, either _NO_DATA if there is no data, or _SUCCESS if we passed the goods to the protocol. **/ UINTN i40eReceive( IN I40E_DRIVER_DATA *AdapterInfo, PXE_CPB_RECEIVE *CpbReceive, PXE_DB_RECEIVE *DbReceive, UINT16 queue ) { PXE_FRAME_TYPE PacketType; union i40e_32byte_rx_desc *ReceiveDescriptor; ETHER_HEADER *EtherHeader; PXE_STATCODE StatCode; UINT16 i; UINT16 TempLen; UINT8 *PacketPtr; UINT32 RxStatus; UINT32 RxError; UINT16 RxPacketLength; UINT16 RxHeaderLength; UINT16 RxSph; UINT16 RxPType; UINT64 DescQWord; PacketType = PXE_FRAME_TYPE_NONE; StatCode = PXE_STATCODE_NO_DATA; // // Get a pointer to the buffer that should have a rx in it, IF one is really there. // ReceiveDescriptor = I40E_RX_DESC(&AdapterInfo->vsi.RxRing[queue], AdapterInfo->vsi.RxRing[queue].next_to_use); DescQWord = ReceiveDescriptor->wb.qword1.status_error_len; RxStatus = (UINT32)((DescQWord & I40E_RXD_QW1_STATUS_MASK) >> I40E_RXD_QW1_STATUS_SHIFT); if ((RxStatus & (1 << I40E_RX_DESC_STATUS_DD_SHIFT)) != 0) { RxPacketLength = (UINT16)((DescQWord & I40E_RXD_QW1_LENGTH_PBUF_MASK) >> I40E_RXD_QW1_LENGTH_PBUF_SHIFT); RxHeaderLength = (UINT16)((DescQWord & I40E_RXD_QW1_LENGTH_HBUF_MASK) >> I40E_RXD_QW1_LENGTH_HBUF_SHIFT); RxSph = (UINT16)((DescQWord & I40E_RXD_QW1_LENGTH_SPH_MASK) >> I40E_RXD_QW1_LENGTH_SPH_SHIFT); RxError = (UINT32)((DescQWord & I40E_RXD_QW1_ERROR_MASK) >> I40E_RXD_QW1_ERROR_SHIFT); RxPType = (UINT16)((DescQWord & I40E_RXD_QW1_PTYPE_MASK) >> I40E_RXD_QW1_PTYPE_SHIFT); // // Just to make sure we don't try to copy a zero length, only copy a positive sized packet. // if ((RxPacketLength != 0) && (RxError == 0)) { // // If the buffer passed us is smaller than the packet, only copy the size of the buffer. // TempLen = RxPacketLength; if (RxPacketLength > (INT16)CpbReceive->BufferLen) { TempLen = (UINT16)CpbReceive->BufferLen; AdapterInfo->hw.numPktOverflow++; } // // Copy the packet from our list to the EFI buffer. // i40eMemCopy( (INT8 *)(UINTN)CpbReceive->BufferAddr, AdapterInfo->vsi.RxRing[queue].BufferAddresses[AdapterInfo->vsi.RxRing[queue].next_to_use], TempLen ); PacketPtr = (UINT8 *)(UINTN)CpbReceive->BufferAddr; DEBUGDUMP(RX, ("%02x:%02x:%02x:%02x:%02x:%02x %02x:%02x:%02x:%02x:%02x:%02x %02x%02x %02x %02x\n", PacketPtr[0x0], PacketPtr[0x1], PacketPtr[0x2], PacketPtr[0x3], PacketPtr[0x4], PacketPtr[0x5], PacketPtr[0x6], PacketPtr[0x7], PacketPtr[0x8], PacketPtr[0x9], PacketPtr[0xA], PacketPtr[0xB], PacketPtr[0xC], PacketPtr[0xD], PacketPtr[0xE], PacketPtr[0xF])); // // Fill the DB with needed information // DbReceive->FrameLen = RxPacketLength; // includes header DbReceive->MediaHeaderLen = PXE_MAC_HEADER_LEN_ETHER; EtherHeader = (ETHER_HEADER *)(UINTN)PacketPtr; // // Figure out if the packet was meant for us, was a broadcast, multicast or we // recieved a frame in promiscuous mode. // for (i = 0; i < PXE_HWADDR_LEN_ETHER; i++) { if (EtherHeader->dest_addr[i] != AdapterInfo->hw.mac.perm_addr[i]) { break; } } // // if we went the whole length of the header without breaking out then the packet is // directed at us. // if (i >= PXE_HWADDR_LEN_ETHER) { PacketType = PXE_FRAME_TYPE_UNICAST; } else { // // Compare it against our broadcast node address // for (i = 0; i < PXE_HWADDR_LEN_ETHER; i++) { if (EtherHeader->dest_addr[i] != AdapterInfo->BroadcastNodeAddress[i]) { break; } } // // If we went the whole length of the header without breaking out then the packet is directed at us via broadcast // if (i >= PXE_HWADDR_LEN_ETHER) { PacketType = PXE_FRAME_TYPE_BROADCAST; } else { // // That leaves multicast or we must be in promiscuous mode. Check for the Mcast bit in the address. // otherwise its a promiscuous receive. // if ((EtherHeader->dest_addr[0] & 1) == 1) { PacketType = PXE_FRAME_TYPE_MULTICAST; } else { PacketType = PXE_FRAME_TYPE_PROMISCUOUS; } } } DEBUGPRINT(RX, ("Status %x, Length %d, PacketType = %d\n", RxStatus, RxPacketLength, PacketType)); DbReceive->Type = PacketType; // // Put the protocol (UDP, TCP/IP) in the data buffer. // DbReceive->Protocol = EtherHeader->type; for (i = 0; i < PXE_HWADDR_LEN_ETHER; i++) { DbReceive->SrcAddr[i] = EtherHeader->src_addr[i]; DbReceive->DestAddr[i] = EtherHeader->dest_addr[i]; } DEBUGDUMP(RX, ("RxRing.next_to_use: %x, BufAddr: %x\n", AdapterInfo->vsi.RxRing[queue].next_to_use, (UINT64)(AdapterInfo->vsi.RxRing[queue].BufferAddresses[AdapterInfo->vsi.RxRing[queue].next_to_use]))); AdapterInfo->hw.numRxSuccessful++; StatCode = PXE_STATCODE_SUCCESS; } else { DEBUGPRINT(CRITICAL, ("ERROR: RxPacketLength: %x, RxError: %x \n", RxPacketLength, RxError)); } // // Clean up the packet and restore the buffer address // AdapterInfo->hw.numRxTotal++; ReceiveDescriptor->wb.qword1.status_error_len = 0; i40eMapMem( AdapterInfo, (UINT64)(UINTN)AdapterInfo->vsi.RxRing[queue].BufferAddresses[AdapterInfo->vsi.RxRing[queue].next_to_use], 0, &ReceiveDescriptor->read.pkt_addr); // // Move the current cleaned buffer pointer, being careful to wrap it as needed. Then update the hardware, // so it knows that an additional buffer can be used. // i40eWrite32(AdapterInfo, I40E_QRX_TAIL(queue), AdapterInfo->vsi.RxRing[queue].next_to_use); AdapterInfo->vsi.RxRing[queue].next_to_use++; if (AdapterInfo->vsi.RxRing[queue].next_to_use == AdapterInfo->vsi.RxRing[queue].count) { AdapterInfo->vsi.RxRing[queue].next_to_use = 0; } } return StatCode; }; /** Takes a command block pointer (cpb) and sends the frame. Takes either one fragment or many and places them onto the wire. Cleanup of the send happens in the function UNDI_Status in Decode.c @param[in] AdapterInfo Pointer to the instance data @param[in] cpb The command parameter block address. 64 bits since this is Itanium(tm) processor friendly @param[in] opflags The operation flags, tells if there is any special sauce on this transmit @retval PXE_STATCODE_SUCCESS if the frame goes out, PXE_STATCODE_DEVICE_FAILURE if it didn't PXE_STATCODE_BUSY if they need to call again later. **/ UINTN i40eTransmit( I40E_DRIVER_DATA *AdapterInfo, UINT64 cpb, UINT16 opflags, UINT16 queue ) { PXE_CPB_TRANSMIT_FRAGMENTS *TxFrags; PXE_CPB_TRANSMIT *TxBuffer; struct i40e_tx_desc *TransmitDescriptor; UINT32 TDCommand = 0; UINT32 TDOffset = 0; UINT32 TDTag = 0; INT32 WaitMsec; UINT32 i; UINT16 Size; UINT8 *PacketPtr; // // Transmit buffers must be freed by the upper layer before we can transmit any more. // if (AdapterInfo->vsi.TxRing[queue].TxBufferUsed[AdapterInfo->vsi.TxRing[queue].next_to_use] != 0) { DEBUGPRINT(CRITICAL, ("TX buffers have all been used!\n")); return PXE_STATCODE_QUEUE_FULL; } // // Make some short cut pointers so we don't have to worry about typecasting later. // If the TX has fragments we will use the // tx_tpr_f pointer, otherwise the tx_ptr_l (l is for linear) // TxBuffer = (PXE_CPB_TRANSMIT *)(UINTN)cpb; TxFrags = (PXE_CPB_TRANSMIT_FRAGMENTS *)(UINTN)cpb; if (AdapterInfo->VlanEnable) { TDCommand |= I40E_TX_DESC_CMD_IL2TAG1; TDTag = AdapterInfo->VlanTag; } // // quicker pointer to the next available Tx descriptor to use. // TransmitDescriptor = I40E_TX_DESC(&AdapterInfo->vsi.TxRing[queue], AdapterInfo->vsi.TxRing[queue].next_to_use); // // Opflags will tell us if this Tx has fragments // So far the linear case (the no fragments case, the else on this if) is the majority // of all frames sent. // if (opflags & PXE_OPFLAGS_TRANSMIT_FRAGMENTED) { // // this count cannot be more than 8; // DEBUGPRINT(TX, ("Fragments %x\n", TxFrags->FragCnt)); // // for each fragment, give it a descriptor, being sure to keep track of the number used. // for (i = 0; i < TxFrags->FragCnt; i++) { // // Put the size of the fragment in the descriptor // i40eMapMem( AdapterInfo, (UINT64)(UINTN)TxFrags->FragDesc[i].FragAddr, 0, &TransmitDescriptor->buffer_addr); Size = (UINT16)TxFrags->FragDesc[i].FragLen; TransmitDescriptor->cmd_type_offset_bsz = I40E_TX_DESC_DTYPE_DATA | ((UINT64)TDCommand << I40E_TXD_QW1_CMD_SHIFT) | ((UINT64)TDOffset << I40E_TXD_QW1_OFFSET_SHIFT) | ((UINT64)Size << I40E_TXD_QW1_TX_BUF_SZ_SHIFT) | ((UINT64)TDTag << I40E_TXD_QW1_L2TAG1_SHIFT); AdapterInfo->vsi.TxRing[queue].TxBufferUsed[AdapterInfo->vsi.TxRing[queue].next_to_use] = TxFrags->FragDesc[i].FragAddr; // // If this is the last fragment we must also set the EOP bit // if ((i + 1) == TxFrags->FragCnt) { TransmitDescriptor->cmd_type_offset_bsz |= (UINT64)I40E_TXD_CMD << I40E_TXD_QW1_CMD_SHIFT; } // // move our software counter passed the frame we just used, watching for wrapping // DEBUGPRINT(TX, ("Advancing TX pointer %x\n", AdapterInfo->vsi.TxRing[queue].next_to_use)); AdapterInfo->vsi.TxRing[queue].next_to_use++; if (AdapterInfo->vsi.TxRing[queue].next_to_use == AdapterInfo->vsi.TxRing[queue].count) { AdapterInfo->vsi.TxRing[queue].next_to_use = 0; } TransmitDescriptor = I40E_TX_DESC(&AdapterInfo->vsi.TxRing[queue], AdapterInfo->vsi.TxRing[queue].next_to_use); } } else { i40eMapMem( AdapterInfo, (UINT64)(UINTN)TxBuffer->FrameAddr, 0, &TransmitDescriptor->buffer_addr); Size = (UINT16)((UINT16)TxBuffer->DataLen + TxBuffer->MediaheaderLen); TransmitDescriptor->cmd_type_offset_bsz = I40E_TX_DESC_DTYPE_DATA | ((UINT64)TDCommand << I40E_TXD_QW1_CMD_SHIFT) | ((UINT64)TDOffset << I40E_TXD_QW1_OFFSET_SHIFT) | ((UINT64)Size << I40E_TXD_QW1_TX_BUF_SZ_SHIFT) | ((UINT64)TDTag << I40E_TXD_QW1_L2TAG1_SHIFT); TransmitDescriptor->cmd_type_offset_bsz |= (UINT64)I40E_TXD_CMD << I40E_TXD_QW1_CMD_SHIFT; AdapterInfo->vsi.TxRing[queue].TxBufferUsed[AdapterInfo->vsi.TxRing[queue].next_to_use] = TransmitDescriptor->buffer_addr; // // Move our software counter passed the frame we just used, watching for wrapping // AdapterInfo->vsi.TxRing[queue].next_to_use++; if (AdapterInfo->vsi.TxRing[queue].next_to_use == AdapterInfo->vsi.TxRing[queue].count) { AdapterInfo->vsi.TxRing[queue].next_to_use = 0; } DEBUGDUMP(TX, ("Length = %d, Buffer addr %x, cmd_type_offset_bsz %x \n", Size, TransmitDescriptor->buffer_addr, TransmitDescriptor->cmd_type_offset_bsz)); PacketPtr = (UINT8 *)(UINTN)TransmitDescriptor->buffer_addr; DEBUGDUMP(TX, ("%02x:%02x:%02x:%02x:%02x:%02x %02x:%02x:%02x:%02x:%02x:%02x %02x%02x %02x %02x\n", PacketPtr[0x0], PacketPtr[0x1], PacketPtr[0x2], PacketPtr[0x3], PacketPtr[0x4], PacketPtr[0x5], PacketPtr[0x6], PacketPtr[0x7], PacketPtr[0x8], PacketPtr[0x9], PacketPtr[0xA], PacketPtr[0xB], PacketPtr[0xC], PacketPtr[0xD], PacketPtr[0xE], PacketPtr[0xF])); } i40eBlockIt(AdapterInfo, TRUE); i40eWrite32(AdapterInfo, I40E_QTX_TAIL(queue), AdapterInfo->vsi.TxRing[queue].next_to_use); i40eBlockIt(AdapterInfo, FALSE); // // If the opflags tells us to wait for the packet to hit the wire, we will wait. // if ((opflags & PXE_OPFLAGS_TRANSMIT_BLOCK) != 0) { WaitMsec = 10000; while ((TransmitDescriptor->cmd_type_offset_bsz & I40E_TX_DESC_DTYPE_DESC_DONE) == 0) { DelayInMicroseconds(AdapterInfo, 10); WaitMsec -= 10; if (WaitMsec <= 0) { break; } } // // If we waited for a while, and it didn't finish then the HW must be bad. // if ((TransmitDescriptor->cmd_type_offset_bsz & I40E_TX_DESC_DTYPE_DESC_DONE) == 0) { DEBUGPRINT(CRITICAL, ("Device failure\n")); return PXE_STATCODE_DEVICE_FAILURE; } else { DEBUGPRINT(TX, ("Transmit success\n")); } } return PXE_STATCODE_SUCCESS; }; /** Free TX buffers that have been transmitted by the hardware. @param[in] AdapterInfo Pointer to the NIC data structure information which the UNDI driver is layering on. @param[in] NumEntries Number of entries in the array which can be freed. @param[in] TxBuffer Array to pass back free TX buffer @retval Number of TX buffers written. **/ UINT16 i40eFreeTxBuffers( I40E_DRIVER_DATA *AdapterInfo, IN UINT16 NumEntries, OUT UINT64 *TxBuffer ) { struct i40e_tx_desc *TransmitDescriptor; UINT16 i; UINT16 queue; i = 0; for (queue = 0; queue <= I40E_LAN_QP_INDEX; queue++) { do { if (i >= NumEntries) { DEBUGPRINT(TX, ("Exceeded number of DB entries, i=%d, NumEntries=%d\n", i, NumEntries)); break; } TransmitDescriptor = I40E_TX_DESC(&AdapterInfo->vsi.TxRing[queue], AdapterInfo->vsi.TxRing[queue].next_to_clean); DEBUGPRINT(TX, ("TXDesc:%d Addr:%x, ctob: %x\n", AdapterInfo->vsi.TxRing[queue].next_to_clean, TransmitDescriptor->buffer_addr, TransmitDescriptor->cmd_type_offset_bsz)); if ((TransmitDescriptor->cmd_type_offset_bsz & I40E_TX_DESC_DTYPE_DESC_DONE) != 0) { if (AdapterInfo->vsi.TxRing[queue].TxBufferUsed[AdapterInfo->vsi.TxRing[queue].next_to_clean] == 0) { DEBUGPRINT(CRITICAL, ("ERROR: TX buffer complete without being marked used!\n")); break; } DEBUGPRINT(TX, ("Cleaning buffer address %d, %x\n", i, TxBuffer[i])); TxBuffer[i] = AdapterInfo->vsi.TxRing[queue].TxBufferUsed[AdapterInfo->vsi.TxRing[queue].next_to_clean]; i++; AdapterInfo->vsi.TxRing[queue].TxBufferUsed[AdapterInfo->vsi.TxRing[queue].next_to_clean] = 0; TransmitDescriptor->cmd_type_offset_bsz &= ~I40E_TXD_QW1_DTYPE_MASK; AdapterInfo->vsi.TxRing[queue].next_to_clean++; if (AdapterInfo->vsi.TxRing[queue].next_to_clean >= AdapterInfo->vsi.TxRing[queue].count) { AdapterInfo->vsi.TxRing[queue].next_to_clean = 0; } AdapterInfo->hw.numTx++; } else { DEBUGPRINT(TX, ("TX Descriptor %d not done\n", AdapterInfo->vsi.TxRing[queue].next_to_clean)); break; } } while (AdapterInfo->vsi.TxRing[queue].next_to_use != AdapterInfo->vsi.TxRing[queue].next_to_clean); } return i; } enum i40e_status_code I40eReceiveStart( IN I40E_DRIVER_DATA *AdapterInfo, UINT32 queue ); enum i40e_status_code I40eReceiveStop( IN I40E_DRIVER_DATA *AdapterInfo, UINT32 queue ); /** Sets receive filters. @param[in] AdapterInfo Pointer to the adapter structure @param[in] NewFilter A PXE_OPFLAGS bit field indicating what filters to use. @retval VOID **/ VOID i40eSetFilter( I40E_DRIVER_DATA IN *AdapterInfo, UINT16 IN NewFilter ) { BOOLEAN ChangedPromiscuousFlag; BOOLEAN ChangedMulticastPromiscuousFlag; BOOLEAN ChangedBroadcastFlag; enum i40e_status_code I40eStatus; DEBUGPRINT(RXFILTER, ("NewFilter %x= \n", NewFilter)); ChangedPromiscuousFlag = FALSE; ChangedMulticastPromiscuousFlag = FALSE; ChangedBroadcastFlag = FALSE; if (NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS) { if (!AdapterInfo->vsi.EnablePromiscuous) { ChangedPromiscuousFlag = TRUE; } AdapterInfo->vsi.EnablePromiscuous = TRUE; DEBUGPRINT(RXFILTER, (" Promiscuous\n")); } if (NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST) { if (!AdapterInfo->vsi.EnableBroadcast) { ChangedBroadcastFlag = TRUE; } AdapterInfo->vsi.EnableBroadcast = TRUE; DEBUGPRINT(RXFILTER, (" Broadcast\n")); } if (NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) { if (!AdapterInfo->vsi.EnableMulticastPromiscuous) { ChangedMulticastPromiscuousFlag = TRUE; } AdapterInfo->vsi.EnableMulticastPromiscuous = TRUE; DEBUGPRINT(RXFILTER, (" MulticastPromiscuous\n")); } if (!AdapterInfo->DriverBusy) { if (ChangedPromiscuousFlag || ChangedMulticastPromiscuousFlag || ChangedBroadcastFlag) { I40eStatus = i40e_aq_set_vsi_unicast_promiscuous( &AdapterInfo->hw, AdapterInfo->vsi.seid, AdapterInfo->vsi.EnablePromiscuous, NULL ); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_set_vsi_unicast_promiscuous returned %d\n", I40eStatus)); } I40eStatus = i40e_aq_set_vsi_multicast_promiscuous( &AdapterInfo->hw, AdapterInfo->vsi.seid, AdapterInfo->vsi.EnablePromiscuous || AdapterInfo->vsi.EnableMulticastPromiscuous, NULL ); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_set_vsi_multicast_promiscuous returned %d\n", I40eStatus)); } I40eStatus = i40e_aq_set_vsi_broadcast( &AdapterInfo->hw, AdapterInfo->vsi.seid, AdapterInfo->vsi.EnablePromiscuous || AdapterInfo->vsi.EnableBroadcast, NULL ); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_set_vsi_broadcast returned %d\n", I40eStatus)); } } } AdapterInfo->RxFilter |= NewFilter; return; }; /** Clears receive filters. @param[in] AdapterInfo Pointer to the adapter structure @param[in] NewFilter A PXE_OPFLAGS bit field indicating what filters to clear. @retval VOID **/ VOID i40eClearFilter( I40E_DRIVER_DATA *AdapterInfo, UINT16 NewFilter ) { BOOLEAN ChangedPromiscuousFlag; BOOLEAN ChangedMulticastPromiscuousFlag; BOOLEAN ChangedBroadcastFlag; enum i40e_status_code I40eStatus; ChangedPromiscuousFlag = FALSE; ChangedMulticastPromiscuousFlag = FALSE; ChangedBroadcastFlag = FALSE; DEBUGPRINT(RXFILTER, ("NewFilter %x= \n", NewFilter)); if (NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS) { if (AdapterInfo->vsi.EnablePromiscuous) { ChangedPromiscuousFlag = TRUE; } AdapterInfo->vsi.EnablePromiscuous = FALSE; DEBUGPRINT(RXFILTER, (" Promiscuous\n")); } if (NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST) { if (AdapterInfo->vsi.EnableBroadcast) { ChangedBroadcastFlag = TRUE; } AdapterInfo->vsi.EnableBroadcast = FALSE; DEBUGPRINT(RXFILTER, (" Broadcast\n")); } if (NewFilter & PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST) { if (AdapterInfo->vsi.EnableMulticastPromiscuous) { ChangedMulticastPromiscuousFlag = TRUE; } AdapterInfo->vsi.EnableMulticastPromiscuous = FALSE; DEBUGPRINT(RXFILTER, (" MulticastPromiscuous\n")); } if (!AdapterInfo->DriverBusy) { if (ChangedPromiscuousFlag || ChangedMulticastPromiscuousFlag || ChangedBroadcastFlag) { I40eStatus = i40e_aq_set_vsi_unicast_promiscuous( &AdapterInfo->hw, AdapterInfo->vsi.seid, AdapterInfo->vsi.EnablePromiscuous, NULL ); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_set_vsi_unicast_promiscuous returned %d\n", I40eStatus)); } I40eStatus = i40e_aq_set_vsi_multicast_promiscuous( &AdapterInfo->hw, AdapterInfo->vsi.seid, AdapterInfo->vsi.EnableMulticastPromiscuous || AdapterInfo->vsi.EnablePromiscuous, NULL ); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_set_vsi_multicast_promiscuous returned %d\n", I40eStatus)); } I40eStatus = i40e_aq_set_vsi_broadcast( &AdapterInfo->hw, AdapterInfo->vsi.seid, AdapterInfo->vsi.EnableBroadcast || AdapterInfo->vsi.EnablePromiscuous, NULL ); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_set_vsi_broadcast returned %d\n", I40eStatus)); } } } AdapterInfo->RxFilter &= ~NewFilter; return; }; VOID I40eSetMcastList( I40E_DRIVER_DATA *AdapterInfo ) { struct i40e_aqc_remove_macvlan_element_data MacVlanElementsToRemove[MAX_MCAST_ADDRESS_CNT]; struct i40e_aqc_add_macvlan_element_data MacVlanElementsToAdd[MAX_MCAST_ADDRESS_CNT]; enum i40e_status_code I40eStatus = I40E_SUCCESS; UINTN i; UINT16 Length; DEBUGDUMP(INIT, ("SM(%d,%d):", AdapterInfo->vsi.McastListToProgram.Length, AdapterInfo->vsi.CurrentMcastList.Length)); DEBUGPRINT(RXFILTER, ("McastListToProgram.Length = %d\n", AdapterInfo->vsi.McastListToProgram.Length)); DEBUGPRINT(RXFILTER, ("CurrentMcastList.Length = %d\n", AdapterInfo->vsi.CurrentMcastList.Length)); if (!AdapterInfo->DriverBusy) { // // Remove existing elements from the Forwarding Table // Ensure no overflow of MacVlanElementsToRemove. // Length = AdapterInfo->vsi.CurrentMcastList.Length; if (Length > 0) { if (Length > MAX_MCAST_ADDRESS_CNT) { Length = MAX_MCAST_ADDRESS_CNT; } for (i = 0; i < Length; i++) { DEBUGPRINT(RXFILTER, ("Remove MAC %d, %x:%x:%x:%x:%x:%x\n", i, AdapterInfo->vsi.CurrentMcastList.McAddr[i][0], AdapterInfo->vsi.CurrentMcastList.McAddr[i][1], AdapterInfo->vsi.CurrentMcastList.McAddr[i][2], AdapterInfo->vsi.CurrentMcastList.McAddr[i][3], AdapterInfo->vsi.CurrentMcastList.McAddr[i][4], AdapterInfo->vsi.CurrentMcastList.McAddr[i][5] )); CopyMem( MacVlanElementsToRemove[i].mac_addr, &AdapterInfo->vsi.CurrentMcastList.McAddr[i], 6 ); MacVlanElementsToRemove[i].vlan_tag = 0; MacVlanElementsToRemove[i].flags = I40E_AQC_MACVLAN_DEL_IGNORE_VLAN | I40E_AQC_MACVLAN_DEL_PERFECT_MATCH; } I40eStatus = i40e_aq_remove_macvlan( &AdapterInfo->hw, AdapterInfo->vsi.seid, MacVlanElementsToRemove, Length, NULL ); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_remove_macvlan returned %d, aq error = %d\n", I40eStatus, AdapterInfo->hw.aq.asq_last_status)); for (i = 0; i < Length; i++) { DEBUGPRINT(CRITICAL, ("i40e_aq_remove_macvlan %d, %d\n", i, MacVlanElementsToRemove[i].error_code)); } } } // // Add new elements to the Forwarding Table // Ensure no overflow of MacVlanElementsToAdd. // Length = AdapterInfo->vsi.McastListToProgram.Length; if (Length > 0) { if (Length > MAX_MCAST_ADDRESS_CNT) { Length = MAX_MCAST_ADDRESS_CNT; } for (i = 0; i < Length; i++) { DEBUGPRINT(RXFILTER, ("Add MAC %d, %x:%x:%x:%x:%x:%x\n", i, AdapterInfo->vsi.McastListToProgram.McAddr[i][0], AdapterInfo->vsi.McastListToProgram.McAddr[i][1], AdapterInfo->vsi.McastListToProgram.McAddr[i][2], AdapterInfo->vsi.McastListToProgram.McAddr[i][3], AdapterInfo->vsi.McastListToProgram.McAddr[i][4], AdapterInfo->vsi.McastListToProgram.McAddr[i][5] )); CopyMem( MacVlanElementsToAdd[i].mac_addr, &AdapterInfo->vsi.McastListToProgram.McAddr[i], 6 ); MacVlanElementsToAdd[i].vlan_tag = 0; MacVlanElementsToAdd[i].flags = I40E_AQC_MACVLAN_ADD_IGNORE_VLAN | I40E_AQC_MACVLAN_ADD_PERFECT_MATCH; } I40eStatus = i40e_aq_add_macvlan( &AdapterInfo->hw, AdapterInfo->vsi.seid, MacVlanElementsToAdd, Length, NULL ); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_add_macvlan returned %d, aq error = %d\n", I40eStatus, AdapterInfo->hw.aq.asq_last_status)); for (i = 0; i < Length; i++) { DEBUGPRINT(RXFILTER, ("i40e_aq_add_macvlan %d, %d\n", i, MacVlanElementsToAdd[i].match_method)); } } } } else { Length = AdapterInfo->vsi.McastListToProgram.Length; if (Length > MAX_MCAST_ADDRESS_CNT) { Length = MAX_MCAST_ADDRESS_CNT; } } // // Update CurrentMcastList // CopyMem( AdapterInfo->vsi.CurrentMcastList.McAddr, AdapterInfo->vsi.McastListToProgram.McAddr, Length * PXE_MAC_LENGTH); AdapterInfo->vsi.CurrentMcastList.Length = Length; } /** Start Rx and Tx rings Enable rings by using Queue enable registers @param[in] AdapterInfo Pointer to the adapter structure @retval i40e_status_code **/ enum i40e_status_code I40eReceiveStart( IN I40E_DRIVER_DATA *AdapterInfo, UINT32 queue ) { struct i40e_hw *Hw; UINTN j; Hw = &AdapterInfo->hw; Hw->numReceiveStart++; // // Tell HW that we intend to enable the Tx queue // i40e_pre_tx_queue_cfg(Hw, queue, TRUE); // // Enable both Tx and Rx queues by setting proper bits in I40E_QTX_ENA // and I40E_QRX_ENA registers. Wait and check if status bits are changed. // wr32(Hw, I40E_QTX_ENA(queue), (rd32(Hw, I40E_QTX_ENA(queue)) | I40E_QTX_ENA_QENA_REQ_MASK)); wr32(Hw, I40E_QRX_ENA(queue), (rd32(Hw, I40E_QRX_ENA(queue)) | I40E_QRX_ENA_QENA_REQ_MASK)); #define START_RINGS_TIMEOUT 100 for (j = 0; j < START_RINGS_TIMEOUT; j++) { if (rd32(Hw, I40E_QTX_ENA(queue)) & I40E_QTX_ENA_QENA_STAT_MASK) { break; } #ifndef INTEL_KDNET gBS->Stall(10); #else KeStallExecutionProcessor(10); #endif } if (j >= START_RINGS_TIMEOUT) { DEBUGPRINT(CRITICAL, ("Tx ring enable timed out, value %x\n", rd32(Hw, I40E_QTX_ENA(queue)))); return I40E_ERR_TIMEOUT; } else { DEBUGPRINT(INIT, ("Tx ring enabled\n")); } // // Wait for the Rx queue status // for (; j < START_RINGS_TIMEOUT; j++) { if (rd32(Hw, I40E_QRX_ENA(queue)) & I40E_QRX_ENA_QENA_STAT_MASK) { break; } #ifndef INTEL_KDNET gBS->Stall(10); #else KeStallExecutionProcessor(10); #endif } if (j >= START_RINGS_TIMEOUT) { DEBUGPRINT(CRITICAL, ("Rx ring enable timed out, value %x\n", rd32(Hw, I40E_QRX_ENA(queue)))); return I40E_ERR_TIMEOUT; } else { DEBUGPRINT(INIT, ("Rx ring enabled\n")); } AdapterInfo->ReceiveStarted = TRUE; return I40E_SUCCESS; } enum i40e_status_code I40eReceiveStop( IN I40E_DRIVER_DATA *AdapterInfo, UINT32 queue ) { struct i40e_hw *Hw; UINTN j; UINT32 Reg = 0; enum i40e_status_code Status; Hw = &AdapterInfo->hw; Hw->numReceiveStop++; if (AdapterInfo->hw.mac.type == I40E_MAC_X722) { Reg = rd32(Hw, I40E_PRTDCB_TC2PFC_RCB); wr32(Hw, I40E_PRTDCB_TC2PFC_RCB, 0); } // // Tell HW that we intend to disable the Tx queue // i40e_pre_tx_queue_cfg(Hw, queue, FALSE); // // Disable both Tx and Rx queues by setting proper bits in I40E_QTX_ENA // and I40E_QRX_ENA registers. Wait and check if status bits are changed. // wr32(Hw, I40E_QTX_ENA(queue), (rd32(Hw, I40E_QTX_ENA(queue)) & ~I40E_QTX_ENA_QENA_REQ_MASK)); wr32(Hw, I40E_QRX_ENA(queue), (rd32(Hw, I40E_QRX_ENA(queue)) & ~I40E_QRX_ENA_QENA_REQ_MASK)); for (j = 0; j < STOP_RINGS_TIMEOUT; j++) { if (!(rd32(Hw, I40E_QTX_ENA(queue)) & I40E_QTX_ENA_QENA_STAT_MASK)) { break; } #ifndef INTEL_KDNET gBS->Stall(10); #else KeStallExecutionProcessor(10); #endif } if (j >= STOP_RINGS_TIMEOUT) { DEBUGPRINT( CRITICAL, ("Tx ring disable timed out, value %x\n", rd32(Hw, I40E_QTX_ENA(queue))) ); Status = I40E_ERR_TIMEOUT; goto ON_EXIT; } DEBUGPRINT(INIT, ("Tx ring disabled\n")); // // Use the remaining delay time to check the Rx queue status // for (j = 0; j < STOP_RINGS_TIMEOUT; j++) { if (!(rd32(Hw, I40E_QRX_ENA(queue)) & I40E_QRX_ENA_QENA_STAT_MASK)) { break; } #ifndef INTEL_KDNET gBS->Stall(10); #else KeStallExecutionProcessor(10); #endif } if (j >= STOP_RINGS_TIMEOUT) { DEBUGPRINT( CRITICAL, ("Rx ring disable timed out, value %x\n", rd32(Hw, I40E_QRX_ENA(queue))) ); Status = I40E_ERR_TIMEOUT; goto ON_EXIT; } DEBUGPRINT(INIT, ("Rx ring disabled\n")); #ifndef INTEL_KDNET gBS->Stall(50000); #else KeStallExecutionProcessor(50000); #endif AdapterInfo->ReceiveStarted = FALSE; Status = I40E_SUCCESS; ON_EXIT: if (AdapterInfo->hw.mac.type == I40E_MAC_X722) { wr32(Hw, I40E_PRTDCB_TC2PFC_RCB, Reg); } return Status; } EFI_STATUS I40eSetupPfQueues( IN I40E_DRIVER_DATA *AdapterInfo ) { UINT32 Reg; UINT16 FirstQueue; UINT16 LastQueue; Reg = i40e_read_rx_ctl(&AdapterInfo->hw, I40E_PFLAN_QALLOC); FirstQueue = (Reg & I40E_PFLAN_QALLOC_FIRSTQ_MASK) >> I40E_PFLAN_QALLOC_FIRSTQ_SHIFT; LastQueue = (Reg & I40E_PFLAN_QALLOC_LASTQ_MASK) >> I40E_PFLAN_QALLOC_LASTQ_SHIFT; DEBUGPRINT(INIT, ("PF Queues - first %x, last: %x\n", FirstQueue, LastQueue)); AdapterInfo->vsi.base_queue = FirstQueue; return EFI_SUCCESS; } /** Configure transmit and receive descriptor rings in HMC context @param[in] AdapterInfo A pointer to structure containing driver data @retval EFI_STATUS EFI_SUCCESS if procedure returned succesfully, otherwise error code **/ EFI_STATUS I40eConfigureTxRxQueues( IN I40E_DRIVER_DATA *AdapterInfo, UINT16 queue ) { enum i40e_status_code I40eStatus = I40E_SUCCESS; struct i40e_hw *Hw; struct i40e_hmc_obj_txq TxHmcContext; struct i40e_hmc_obj_rxq RxHmcContext; union i40e_32byte_rx_desc *ReceiveDescriptor; UINTN i; #ifndef INTEL_KDNET EFI_STATUS Status = EFI_SUCCESS; #else enum i40e_status_code ret_code; #endif UINT32 QTxCtrl; UINT32 QRxTail; DEBUGPRINT(INIT, ("\n")); Hw = &AdapterInfo->hw; // // Now associate the queue with the PCI function // QTxCtrl = I40E_QTX_CTL_PF_QUEUE; QTxCtrl |= ((Hw->bus.func << I40E_QTX_CTL_PF_INDX_SHIFT) & I40E_QTX_CTL_PF_INDX_MASK); wr32(Hw, I40E_QTX_CTL(queue), QTxCtrl); // // Clear the context structure before use // ZeroMem(&TxHmcContext, sizeof(struct i40e_hmc_obj_txq)); TxHmcContext.new_context = 1; TxHmcContext.base = (UINT64)((AdapterInfo->vsi.TxRing[queue].desc.pa + 127) >> 7); TxHmcContext.qlen = AdapterInfo->vsi.TxRing[queue].count; // // Disable FCoE // TxHmcContext.fc_ena = 0; TxHmcContext.timesync_ena = 0; TxHmcContext.fd_ena = 0; TxHmcContext.alt_vlan_ena = 0; // // By default all traffic is assigned to TC0 // TxHmcContext.rdylist = AdapterInfo->vsi.info.qs_handle[0]; TxHmcContext.rdylist_act = 0; // // Clear the context in the HMC // #ifdef DIRECT_QUEUE_CTX_PROGRAMMING // // Do nothing // #else I40eStatus = i40e_clear_lan_tx_queue_context(Hw, queue); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("Failed to clear LAN Tx queue context on Tx ring, error: %d\n", I40eStatus)); return EFI_DEVICE_ERROR; } #endif // // Set the context in the HMC // #ifdef DIRECT_QUEUE_CTX_PROGRAMMING I40eStatus = i40e_set_lan_tx_queue_context_directly(Hw, AdapterInfo->vsi.base_queue, &TxHmcContext); //HMCDump(AdapterInfo, 0, I40E_HMC_LAN_TX); #else I40eStatus = i40e_set_lan_tx_queue_context(Hw, queue, &TxHmcContext); #endif if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("Failed to set LAN Tx queue context on Tx ring, error: %d\n", I40eStatus)); return EFI_DEVICE_ERROR; } // // Clear the context structure first // ZeroMem(&RxHmcContext, sizeof(struct i40e_hmc_obj_rxq)); AdapterInfo->vsi.RxRing[queue].rx_buf_len = I40E_RXBUFFER_2048; // // No packet split // AdapterInfo->vsi.RxRing[queue].rx_hdr_len = 0; RxHmcContext.head = 0; RxHmcContext.cpuid = 0; RxHmcContext.dbuff = (UINT8)(AdapterInfo->vsi.RxRing[queue].rx_buf_len >> I40E_RXQ_CTX_DBUFF_SHIFT); RxHmcContext.hbuff = (UINT8)(AdapterInfo->vsi.RxRing[queue].rx_hdr_len >> I40E_RXQ_CTX_HBUFF_SHIFT); RxHmcContext.base = (UINT64)((AdapterInfo->vsi.RxRing[queue].desc.pa + 127) >> 7); RxHmcContext.qlen = AdapterInfo->vsi.RxRing[queue].count; // // 32 byte descriptors in use // RxHmcContext.dsize = 1; RxHmcContext.dtype = I40E_RX_DTYPE_NO_SPLIT; RxHmcContext.hsplit_0 = I40E_HMC_OBJ_RX_HSPLIT_0_NO_SPLIT; RxHmcContext.rxmax = 0x2800; RxHmcContext.tphrdesc_ena = 0; RxHmcContext.tphwdesc_ena = 0; RxHmcContext.tphdata_ena = 0; RxHmcContext.tphhead_ena = 0; RxHmcContext.lrxqthresh = (I40E_NUM_TX_RX_DESCRIPTORS / 64); RxHmcContext.crcstrip = 1; RxHmcContext.l2tsel = 1; RxHmcContext.showiv = 1; // // No FCoE // RxHmcContext.fc_ena = 0; RxHmcContext.prefena = 1; // // Clear the context in the HMC // #ifdef DIRECT_QUEUE_CTX_PROGRAMMING #else I40eStatus = i40e_clear_lan_rx_queue_context(Hw, queue); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("Failed to clear LAN Rx queue context on Rx ring, error: %d\n", I40eStatus)); return EFI_DEVICE_ERROR; } #endif // // Set the context in the HMC // #ifdef DIRECT_QUEUE_CTX_PROGRAMMING I40eStatus = i40e_set_lan_rx_queue_context_directly(Hw, AdapterInfo->vsi.base_queue, &RxHmcContext); //HMCDump(AdapterInfo, 0, I40E_HMC_LAN_RX); #else I40eStatus = i40e_set_lan_rx_queue_context(Hw, queue, &RxHmcContext); #endif if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("Failed to set LAN Rx queue context on Rx ring, error: %d\n", I40eStatus)); return EFI_DEVICE_ERROR; } // // Initialize tail register // Workaround for Rx queue initialization: // The tail should be cleared and only then it should be set to the end of the queue // i40eWrite32(AdapterInfo, I40E_QRX_TAIL(queue), 0); i40eWrite32(AdapterInfo, I40E_QRX_TAIL(queue), AdapterInfo->vsi.RxRing[queue].count - 1); AdapterInfo->vsi.RxRing[queue].next_to_use = 0; QRxTail = i40eRead32(AdapterInfo, I40E_QRX_TAIL(queue)); DEBUGPRINT(INIT, ("QRXTail %d\n", QRxTail)); // // Determine the overall size of memory needed for receive buffers and allocate memory // AdapterInfo->vsi.RxRing[queue].RxBuffer.size = AdapterInfo->vsi.RxRing[queue].count * (AdapterInfo->vsi.RxRing[queue].rx_buf_len + RECEIVE_BUFFER_ALIGN_SIZE); AdapterInfo->vsi.RxRing[queue].RxBuffer.size = ALIGN( AdapterInfo->vsi.RxRing[queue].RxBuffer.size, 4096 ); #ifndef INTEL_KDNET Status = AdapterInfo->PciIo->AllocateBuffer( AdapterInfo->PciIo, AllocateAnyPages, EfiBootServicesData, UNDI_MEM_PAGES(AdapterInfo->vsi.RxRing[queue].RxBuffer.size), (VOID **)&AdapterInfo->vsi.RxRing[queue].RxBuffer.va, 0 ); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("Unable to allocate memory for the Rx buffers, size=%d\n", AdapterInfo->vsi.RxRing[queue].RxBuffer.size)); return Status; } #else ret_code = i40e_allocate_dma_mem(Hw, &AdapterInfo->vsi.RxRing[queue].RxBuffer, i40e_mem_rx_data, AdapterInfo->vsi.RxRing[queue].RxBuffer.size, I40E_RX_DATA_ALIGNMENT); if (ret_code) return EFI_OUT_OF_RESOURCES; #endif AdapterInfo->vsi.RxRing[queue].RxBuffer.va = (VOID *)((UINT8 *)AdapterInfo->vsi.RxRing[queue].RxBuffer.va + RECEIVE_BUFFER_PADDING); // // Link the RX Descriptors to the receive buffers and cleanup descriptors // for (i = 0; i < AdapterInfo->vsi.RxRing[queue].count; i++) { ReceiveDescriptor = I40E_RX_DESC(&AdapterInfo->vsi.RxRing[queue], i); AdapterInfo->vsi.RxRing[queue].BufferAddresses[i] = (UINT8 *)AdapterInfo->vsi.RxRing[queue].RxBuffer.va + i * (AdapterInfo->vsi.RxRing[queue].rx_buf_len + RECEIVE_BUFFER_ALIGN_SIZE); i40eMapMem( AdapterInfo, (UINT64)(UINTN)AdapterInfo->vsi.RxRing[queue].BufferAddresses[i], 0, &ReceiveDescriptor->read.pkt_addr); ReceiveDescriptor->read.hdr_addr = 0; ReceiveDescriptor->wb.qword1.status_error_len = 0; } return EFI_SUCCESS; } /** Free resources allocated for transmit and receive descriptor rings and remove HMC contexts @param[in] AdapterInfo A pointer to structure containing driver data @retval EFI_STATUS EFI_SUCCESS if procedure returned succesfully, otherwise error code **/ EFI_STATUS I40eFreeTxRxQueues( IN I40E_DRIVER_DATA *AdapterInfo, UINT16 queue ) { enum i40e_status_code I40eStatus; struct i40e_hw *Hw; EFI_STATUS Status; DEBUGPRINT(INIT, ("\n")); Hw = &AdapterInfo->hw; I40eStatus = I40E_SUCCESS; Status = EFI_DEVICE_ERROR; #ifndef DIRECT_QUEUE_CTX_PROGRAMMING // // Clear the context in the HMC // I40eStatus = i40e_clear_lan_tx_queue_context(Hw, queue); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("Failed to clear LAN Tx queue context on Tx ring, error: %d\n", I40eStatus)); return EFI_DEVICE_ERROR; } // // Clear the context in the HMC // I40eStatus = i40e_clear_lan_rx_queue_context(Hw, queue); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("Failed to clear LAN Rx queue context on Rx ring, error: %d\n", I40eStatus)); return EFI_DEVICE_ERROR; } #endif #ifdef INTEL_KDNET Status = EFI_SUCCESS; #else Status = AdapterInfo->PciIo->FreeBuffer( AdapterInfo->PciIo, UNDI_MEM_PAGES(AdapterInfo->vsi.RxRing[queue].RxBuffer.Size), (VOID *)AdapterInfo->vsi.RxRing[queue].RxBuffer.Data); #endif return Status; } /** Allocate memory resources for the Tx and Rx descriptors @param[in] AdapterInfo A pointer to structure containing driver data @retval EFI_STATUS EFI_SUCCESS if procedure returned succesfully, otherwise error code **/ EFI_STATUS I40eSetupTxRxResources( IN I40E_DRIVER_DATA *AdapterInfo, UINT16 queue ) { UINTN i; #ifndef INTEL_KDNET EFI_STATUS Status = EFI_SUCCESS; #else enum i40e_status_code ret_code; struct i40e_hw *hw; #endif #ifdef INTEL_KDNET hw = &AdapterInfo->hw; #endif AdapterInfo->vsi.TxRing[queue].count = AdapterInfo->vsi.num_desc; AdapterInfo->vsi.TxRing[queue].size = 0; AdapterInfo->vsi.RxRing[queue].count = AdapterInfo->vsi.num_desc; AdapterInfo->vsi.RxRing[queue].size = 0; // // This block is for Tx descriptiors // Round up to nearest 4K AdapterInfo->vsi.TxRing[queue].size = AdapterInfo->vsi.TxRing[queue].count * sizeof(struct i40e_tx_desc); AdapterInfo->vsi.TxRing[queue].size = ALIGN(AdapterInfo->vsi.TxRing[queue].size, 4096); #ifndef INTEL_KDNET // // Allocate memory // Status = AdapterInfo->PciIo->AllocateBuffer( AdapterInfo->PciIo, AllocateAnyPages, EfiBootServicesData, UNDI_MEM_PAGES(AdapterInfo->vsi.TxRing[queue].size), (VOID **)&AdapterInfo->vsi.TxRing[queue].desc.va, 0 ); AdapterInfo->vsi.TxRing[queue].desc.size = AdapterInfo->vsi.TxRing[queue].size; DEBUGPRINT(INIT, ("AdapterInfo->vsi.tx_ring.desc: %X\n", AdapterInfo->vsi.TxRing[queue].desc.va)); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("Unable to allocate memory for the Tx descriptor ring, size=%d\n", AdapterInfo->vsi.TxRing[queue].size)); return Status; } #else ret_code = i40e_allocate_dma_mem(hw, &AdapterInfo->vsi.TxRing[queue].desc, i40e_mem_tx_ring, AdapterInfo->vsi.TxRing[queue].size, I40E_TXRX_DESC_ALIGNMENT); if (ret_code) return EFI_OUT_OF_RESOURCES; #endif AdapterInfo->vsi.TxRing[queue].next_to_use = 0; AdapterInfo->vsi.TxRing[queue].next_to_clean = 0; // // All available transmit descriptors are free by default // for (i = 0; i < AdapterInfo->vsi.TxRing[queue].count; i++) { AdapterInfo->vsi.TxRing[queue].TxBufferUsed[i] = 0; } // // This block is for Rx descriptors // Use 16 byte descriptors as we are in PXE MODE. // /* Round up to nearest 4K */ AdapterInfo->vsi.RxRing[queue].size = AdapterInfo->vsi.RxRing[queue].count * sizeof(union i40e_32byte_rx_desc); AdapterInfo->vsi.RxRing[queue].size = ALIGN(AdapterInfo->vsi.RxRing[queue].size, 4096); // // Allocate memory // #ifndef INTEL_KDNET Status = AdapterInfo->PciIo->AllocateBuffer( AdapterInfo->PciIo, AllocateAnyPages, EfiBootServicesData, UNDI_MEM_PAGES(AdapterInfo->vsi.RxRing[queue].size), (VOID **)&AdapterInfo->vsi.RxRing[queue].desc.va, 0 ); AdapterInfo->vsi.RxRing[queue].desc.size = AdapterInfo->vsi.RxRing[queue].size; DEBUGPRINT(INIT, ("AdapterInfo->vsi.rx_ring.desc: %X\n", AdapterInfo->vsi.RxRing[queue].desc.va)); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("Unable to allocate memory for the Rx descriptor ring, size=%d\n", AdapterInfo->vsi.RxRing[queue].size)); return Status; } #else ret_code = i40e_allocate_dma_mem(hw, &AdapterInfo->vsi.RxRing[queue].desc, i40e_mem_rx_ring, AdapterInfo->vsi.RxRing[queue].size, I40E_TXRX_DESC_ALIGNMENT); if (ret_code) return EFI_OUT_OF_RESOURCES; #endif AdapterInfo->vsi.RxRing[queue].next_to_clean = 0; AdapterInfo->vsi.RxRing[queue].next_to_use = 0; return EFI_SUCCESS; } /** Free memory resources for the Tx and Rx descriptors @param[in] AdapterInfo A pointer to structure containing driver data @retval EFI_STATUS EFI_SUCCESS if procedure returned succesfully, otherwise error code **/ EFI_STATUS I40eFreeTxRxResources( IN I40E_DRIVER_DATA *AdapterInfo, UINT16 queue ) { EFI_STATUS Status; #ifdef INTEL_KDNET Status = EFI_SUCCESS; #else Status = AdapterInfo->PciIo->FreeBuffer( AdapterInfo->PciIo, UNDI_MEM_PAGES(AdapterInfo->vsi.TxRing[queue].size), AdapterInfo->vsi.TxRing[queue].desc); #endif AdapterInfo->vsi.TxRing[queue].desc.va = NULL; if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("Unable to free memory for the Tx descriptor ring %r, size=%d\n", Status, AdapterInfo->vsi.TxRing[queue].size)); return Status; } #ifdef INTEL_KDNET Status = EFI_SUCCESS; #else Status = AdapterInfo->PciIo->FreeBuffer( AdapterInfo->PciIo, UNDI_MEM_PAGES(AdapterInfo->vsi.RxRing[queue].size), AdapterInfo->vsi.RxRing[queue].desc); #endif AdapterInfo->vsi.RxRing[queue].desc.va = NULL; if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("Unable to free memory for the Rx descriptor ring, size=%d\n", AdapterInfo->vsi.RxRing[queue].size)); return Status; } return Status; } /** Get the current switch configuration from the device and extract a few useful SEID values. @param[in] AdapterInfo A pointer to structure containing driver data @retval EFI_STATUS EFI_SUCCESS if procedure returned succesfully, otherwise error code **/ EFI_STATUS i40eReadSwitchConfiguration( IN I40E_DRIVER_DATA *AdapterInfo ) { struct i40e_aqc_get_switch_config_resp *SwitchConfig; UINT8 AqBuffer[I40E_AQ_LARGE_BUF]; enum i40e_status_code I40eStatus; int i; UINT16 StartSeid; if (AdapterInfo == NULL) { return EFI_INVALID_PARAMETER; } SwitchConfig = (struct i40e_aqc_get_switch_config_resp *)AqBuffer; StartSeid = 0; I40eStatus = i40e_aq_get_switch_config(&AdapterInfo->hw, SwitchConfig, sizeof(AqBuffer), &StartSeid, NULL); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("get switch config failed %d aq_err=%x\n", I40eStatus, AdapterInfo->hw.aq.asq_last_status)); return EFI_DEVICE_ERROR; } if (SwitchConfig->header.num_reported == 0) { DEBUGPRINT(CRITICAL, ("No data returned in the SwitchConfig \n")); return EFI_DEVICE_ERROR; } for (i = 0; i < SwitchConfig->header.num_reported; i++) { DEBUGPRINT(INIT, ("type=%d seid=%d uplink=%d downlink=%d\n", SwitchConfig->element[i].element_type, SwitchConfig->element[i].seid, SwitchConfig->element[i].uplink_seid, SwitchConfig->element[i].downlink_seid) ); switch (SwitchConfig->element[i].element_type) { case I40E_SWITCH_ELEMENT_TYPE_MAC: AdapterInfo->mac_seid = SwitchConfig->element[i].seid; break; case I40E_SWITCH_ELEMENT_TYPE_VEB: AdapterInfo->veb_seid = SwitchConfig->element[i].seid; break; case I40E_SWITCH_ELEMENT_TYPE_PF: AdapterInfo->pf_seid = SwitchConfig->element[i].seid; AdapterInfo->main_vsi_seid = SwitchConfig->element[i].uplink_seid; break; case I40E_SWITCH_ELEMENT_TYPE_VSI: AdapterInfo->main_vsi_seid = SwitchConfig->element[i].seid; AdapterInfo->pf_seid = SwitchConfig->element[i].downlink_seid; AdapterInfo->mac_seid = SwitchConfig->element[i].uplink_seid; break; case I40E_SWITCH_ELEMENT_TYPE_VF: case I40E_SWITCH_ELEMENT_TYPE_EMP: case I40E_SWITCH_ELEMENT_TYPE_BMC: case I40E_SWITCH_ELEMENT_TYPE_PE: case I40E_SWITCH_ELEMENT_TYPE_PA: /* ignore these for now */ break; default: DEBUGPRINT(CRITICAL, ("Unknown element type=%d seid=%d\n", SwitchConfig->element[i].element_type, SwitchConfig->element[i].seid)); break; } } return EFI_SUCCESS; } //TODO: Add the return value to function /** Turn on vlan stripping for the vsi @param[in] AdapterInfo A pointer to structure containing driver data @retval EFI_STATUS **/ EFI_STATUS i40eDisableVlanStripping( IN I40E_DRIVER_DATA *AdapterInfo ) { struct i40e_vsi_context VsiContext; enum i40e_status_code I40eStatus; if ((AdapterInfo->vsi.info.valid_sections & I40E_AQ_VSI_PROP_VLAN_VALID) == I40E_AQ_VSI_PROP_VLAN_VALID) { if ((AdapterInfo->vsi.info.port_vlan_flags & I40E_AQ_VSI_PVLAN_EMOD_MASK) == I40E_AQ_VSI_PVLAN_EMOD_MASK) { DEBUGPRINT(INIT, ("VLAN stripping already disabled\n")); return EFI_SUCCESS; } } AdapterInfo->vsi.info.valid_sections |= I40E_AQ_VSI_PROP_VLAN_VALID; AdapterInfo->vsi.info.port_vlan_flags = I40E_AQ_VSI_PVLAN_MODE_ALL | I40E_AQ_VSI_PVLAN_EMOD_NOTHING; VsiContext.seid = AdapterInfo->main_vsi_seid; CopyMem(&VsiContext.info, &AdapterInfo->vsi.info, sizeof(AdapterInfo->vsi.info)); I40eStatus = i40e_aq_update_vsi_params(&AdapterInfo->hw, &VsiContext, NULL); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("Update vsi failed, aq_err=%d\n", AdapterInfo->hw.aq.asq_last_status)); return EFI_DEVICE_ERROR; } return EFI_SUCCESS; } #ifdef SV_SUPPORT STATIC UINT8 DebugBuf[4096]; VOID DumpInternalFwHwData( I40E_DRIVER_DATA *AdapterInfo ) { enum i40e_status_code I40eStatus; UINT32 i, ii; UINT32 StartIndex, NextStartIndex; UINT16 RetBufSize; UINT8 TableId, NextTableId; TableId = 0; StartIndex = 0; ii = 0; DEBUGDUMP(INIT, ("\n%d: ", TableId)); while (1) { I40eStatus = i40e_aq_debug_dump( &AdapterInfo->hw, I40E_AQ_CLUSTER_ID_TXSCHED, TableId, StartIndex, sizeof(DebugBuf), DebugBuf, &RetBufSize, &NextTableId, &NextStartIndex, NULL ); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_debug_dump failed, aq_err=%d\n", AdapterInfo->hw.aq.asq_last_status)); return; } // // Print data to console // for (i = 0; i < RetBufSize; i++) { // if ((ii++ % 32) == 0) { // DEBUGDUMP(INIT, ("\n")); // } DEBUGDUMP(INIT, ("%02x", DebugBuf[i])); } if (TableId == NextTableId) { // // Advance index, table remains the same // StartIndex = NextStartIndex; } else if (NextTableId != 0xFF) { // // Next table // TableId = NextTableId; StartIndex = 0; DEBUGDUMP(INIT, ("\n%d: ", TableId)); ii = 0; } else { // // This is the end // return; } } } #endif #define DumpVsiCtxRegister(_RegName) \ DEBUGDUMP (INIT, (#_RegName"(%x) = %x\n", \ ##_RegName## (AdapterInfo->vsi.id), rd32 (&AdapterInfo->hw, ##_RegName## (VsiId))) \ ); #define DumpRegister(_RegName) \ DEBUGDUMP (INIT, (#_RegName"(%x) = %x\n", \ ##_RegName##, rd32 (&AdapterInfo->hw, ##_RegName##)) \ ); #if (0) VOID DumpVsiContext( I40E_DRIVER_DATA *AdapterInfo, UINT32 VsiId ) { UINTN i; #define I40E_VSI_L2TAGSTXVALID(_VSI) (0x00042800 + ((_VSI) * 4)) DumpVsiCtxRegister(I40E_VSI_TAR); DumpVsiCtxRegister(I40E_VSI_TAIR); DumpVsiCtxRegister(I40E_VSI_TIR_0); DumpVsiCtxRegister(I40E_VSI_TIR_1); DumpVsiCtxRegister(I40E_VSI_TIR_2); DumpVsiCtxRegister(I40E_VSI_L2TAGSTXVALID); DumpVsiCtxRegister(I40E_VSI_TSR); DumpVsiCtxRegister(I40E_VSI_TUPR); DumpVsiCtxRegister(I40E_VSI_TUPIOM); DumpVsiCtxRegister(I40E_VSI_RUPR); DumpVsiCtxRegister(I40E_VSI_SRCSWCTRL); DumpVsiCtxRegister(I40E_VSI_RXSWCTRL); DumpVsiCtxRegister(I40E_VSI_VSI2F); DumpVsiCtxRegister(I40E_VSI_PORT); DumpVsiCtxRegister(I40E_VSILAN_QBASE); DumpVsiCtxRegister(I40E_VSIQF_CTL); DEBUGDUMP(INIT, ("I40E_VSILAN_QTABLE (0..7): ")); for (i = 0; i < 8; i++) { DEBUGDUMP(INIT, ("%x ", rd32(&AdapterInfo->hw, I40E_VSILAN_QTABLE(i, VsiId)))); } DEBUGDUMP(INIT, ("\n")); DumpVsiCtxRegister(I40E_VSIQF_CTL); DEBUGDUMP(INIT, ("I40E_VSIQF_TCREGION (0..3): ")); for (i = 0; i < 4; i++) { DEBUGDUMP(INIT, ("%x ", rd32(&AdapterInfo->hw, I40E_VSIQF_TCREGION(i, VsiId)))); } DEBUGDUMP(INIT, ("\n")); DumpRegister(I40E_PRT_L2TAGSEN); DumpRegister(I40E_PRT_TDPUL2TAGSEN); DumpRegister(I40E_PRT_PPRSL2TAGSEN); DEBUGDUMP(INIT, ("I40E_GLTDPU_L2TAGCTRL (0..7): ")); for (i = 0; i < 8; i++) { DEBUGDUMP(INIT, ("%x ", rd32(&AdapterInfo->hw, I40E_GLTDPU_L2TAGCTRL(i)))); } DEBUGDUMP(INIT, ("\n")); DEBUGDUMP(INIT, ("I40E_GL_SWT_L2TAGTXIB (0..7): ")); for (i = 0; i < 8; i++) { DEBUGDUMP(INIT, ("%x ", rd32(&AdapterInfo->hw, I40E_GL_SWT_L2TAGTXIB(i)))); } DEBUGDUMP(INIT, ("\n")); DEBUGDUMP(INIT, ("I40E_GL_SWT_L2TAGRXEB (0..7): ")); for (i = 0; i < 8; i++) { DEBUGDUMP(INIT, ("%x ", rd32(&AdapterInfo->hw, I40E_GL_SWT_L2TAGRXEB(i)))); } DEBUGDUMP(INIT, ("\n")); } #endif /** * I40eSetupPFSwitch - setup the initial LAN and VMDq switch * @pf: board private * * This adds the VEB into the internal switch, makes sure the main * LAN VSI is connected correctly, allocates and connects all the * VMDq VSIs, and sets the base queue index for each VSI. * * Returns 0 on success, negative value on failure **/ EFI_STATUS I40eSetupPFSwitch( I40E_DRIVER_DATA *AdapterInfo ) { enum i40e_status_code I40eStatus; EFI_STATUS Status; struct i40e_vsi_context vsi_ctx; struct i40e_aqc_add_macvlan_element_data MacVlan; struct i40e_filter_control_settings FilterControlSettings; UINT8 cnt = 0; I40eStatus = I40E_SUCCESS; Status = EFI_SUCCESS; // // Read the default switch configuration // Status = i40eReadSwitchConfiguration(AdapterInfo); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("i40eReadSwitchConfiguration returned %r\n", Status)); return Status; } // // Get main VSI parameters // I40eStatus = I40eGetVsiParams(AdapterInfo, &vsi_ctx); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_get_vsi_params returned %d, aq_err %d\n", I40eStatus, AdapterInfo->hw.aq.asq_last_status)); return EFI_DEVICE_ERROR; } DEBUGPRINT(INIT, ("VSI params: vsi_number=%d, vsi_ctx.info.qs_handle[0]=%d\n", vsi_ctx.vsi_number, vsi_ctx.info.qs_handle[0]) ); AdapterInfo->vsi.id = vsi_ctx.vsi_number; // // Determine which queues are used by this PF // I40eSetupPfQueues(AdapterInfo); // // Set number of queue pairs we use to 1 // AdapterInfo->num_lan_qps = I40E_LAN_QP_INDEX+1; // // Check if VSI has enough queue pairs // if ((AdapterInfo->hw.func_caps.num_tx_qp < AdapterInfo->num_lan_qps) || (AdapterInfo->hw.func_caps.num_rx_qp < AdapterInfo->num_lan_qps)) { DEBUGPRINT(CRITICAL, ("Not enough qps available\n")); return EFI_DEVICE_ERROR; } // // Store VSI parameters in VSI structure // AdapterInfo->vsi.type = I40E_VSI_MAIN; AdapterInfo->vsi.flags = 0; AdapterInfo->vsi.num_queue_pairs = AdapterInfo->num_lan_qps; AdapterInfo->vsi.num_desc = I40E_NUM_TX_RX_DESCRIPTORS; AdapterInfo->vsi.seid = AdapterInfo->main_vsi_seid; CopyMem(&AdapterInfo->vsi.info, &vsi_ctx.info, sizeof(vsi_ctx.info)); ZeroMem(&FilterControlSettings, sizeof(FilterControlSettings)); FilterControlSettings.hash_lut_size = I40E_HASH_LUT_SIZE_128; FilterControlSettings.enable_ethtype = TRUE; FilterControlSettings.enable_macvlan = TRUE; I40eStatus = i40e_set_filter_control(&AdapterInfo->hw, &FilterControlSettings); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_set_filter_control returned %d, aq_err %d\n", I40eStatus, AdapterInfo->hw.aq.asq_last_status)); return EFI_DEVICE_ERROR; } // // HSD 5281411 VLANs are not working with latest NVMs // Starting with NVM 4.2.2 firmware default filter setting is no // longer accept tagged packets // by default // SetMem(&MacVlan, sizeof(struct i40e_aqc_add_macvlan_element_data), 0); MacVlan.flags = I40E_AQC_MACVLAN_ADD_IGNORE_VLAN | I40E_AQC_MACVLAN_ADD_PERFECT_MATCH; CopyMem(MacVlan.mac_addr, &AdapterInfo->hw.mac.addr, sizeof(MacVlan.mac_addr)); I40eStatus = i40e_aq_add_macvlan(&AdapterInfo->hw, AdapterInfo->main_vsi_seid, &MacVlan, 1, NULL); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_add_macvlan returned %d, aq_err %d\n", I40eStatus, AdapterInfo->hw.aq.asq_last_status)); return EFI_DEVICE_ERROR; } SetMem(&MacVlan, sizeof(struct i40e_aqc_add_macvlan_element_data), 0); MacVlan.flags = I40E_AQC_MACVLAN_ADD_IGNORE_VLAN | I40E_AQC_MACVLAN_ADD_PERFECT_MATCH; for (cnt = 0; cnt < 6; cnt++) { MacVlan.mac_addr[cnt] = 0xFF; } I40eStatus = i40e_aq_add_macvlan(&AdapterInfo->hw, AdapterInfo->main_vsi_seid, &MacVlan, 1, NULL); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_add_macvlan returned %d, aq_err %d\n", I40eStatus, AdapterInfo->hw.aq.asq_last_status)); return EFI_DEVICE_ERROR; } I40eStatus = i40e_aq_set_mac_config(&AdapterInfo->hw, 0x5F2, TRUE, I40E_AQ_SET_MAC_CONFIG_PACING_NONE, NULL); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_add_macvlan returned %d, aq_err %d\n", I40eStatus, AdapterInfo->hw.aq.asq_last_status)); return EFI_DEVICE_ERROR; } // // Configure VLAN stripping on Rx packets // Status = i40eDisableVlanStripping(AdapterInfo); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("i40eDisableVlanStripping returned %r\n", Status)); return Status; } return Status; } EFI_STATUS i40ePciInit( I40E_DRIVER_DATA *AdapterInfo ) /*++ Routine Description: This function performs PCI-E initialization for the device. Arguments: AdapterInfo - Pointer to adapter structure Returns: EFI_STATUS --*/ { #ifdef INTEL_KDNET EFI_STATUS Status = EFI_SUCCESS; #else EFI_STATUS Status; UINT64 NewCommand; UINT64 Result; BOOLEAN PciAttributesSaved; NewCommand = 0; Result = 0; PciAttributesSaved = FALSE; #endif #ifndef INTEL_KDNET // // Save original PCI attributes // Status = AdapterInfo->PciIo->Attributes( AdapterInfo->PciIo, EfiPciIoAttributeOperationGet, 0, &AdapterInfo->OriginalPciAttributes ); if (EFI_ERROR(Status)) { goto error; } PciAttributesSaved = TRUE; // // Get the PCI Command options that are supported by this controller. // Status = AdapterInfo->PciIo->Attributes( AdapterInfo->PciIo, EfiPciIoAttributeOperationSupported, 0, &Result ); DEBUGPRINT(INIT, ("Attributes supported %x\n", Result)); if (!EFI_ERROR(Status)) { // // Set the PCI Command options to enable device memory mapped IO, // port IO, and bus mastering. // Status = AdapterInfo->PciIo->Attributes( AdapterInfo->PciIo, EfiPciIoAttributeOperationEnable, Result & (EFI_PCI_DEVICE_ENABLE | EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE), &NewCommand ); } if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("PciIo->Attributes returned %r\n", Status)); goto error; } #endif #ifndef INTEL_KDNET AdapterInfo->PciIo->GetLocation( AdapterInfo->PciIo, &AdapterInfo->Segment, &AdapterInfo->Bus, &AdapterInfo->Device, &AdapterInfo->Function ); #else GetDeviceLocation(&AdapterInfo->Segment, &AdapterInfo->Bus, &AdapterInfo->Device, &AdapterInfo->Function); #endif // // Read all the registers from the device's PCI Configuration space // #ifndef INTEL_KDNET AdapterInfo->PciIo->Pci.Read( AdapterInfo->PciIo, EfiPciIoWidthUint32, 0, MAX_PCI_CONFIG_LEN, AdapterInfo->PciConfig ); return Status; #else GetDevicePciDataByOffset(&AdapterInfo->PciConfig, 0, MAX_PCI_CONFIG_LEN * sizeof(UINT32)); #endif #ifndef INTEL_KDNET error : if (PciAttributesSaved) { // // Restore original PCI attributes // AdapterInfo->PciIo->Attributes( AdapterInfo->PciIo, EfiPciIoAttributeOperationSet, AdapterInfo->OriginalPciAttributes, NULL ); } #endif return Status; } EFI_STATUS I40eReadMacAddress( I40E_DRIVER_DATA *AdapterInfo ) { enum i40e_status_code I40eStatus; struct i40e_hw *hw; hw = &AdapterInfo->hw; // // Get current MAC Address using the shared code function // I40eStatus = i40e_get_mac_addr(hw, hw->mac.addr); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_get_mac_addr returned %d\n", I40eStatus)); return EFI_DEVICE_ERROR; } // // Assume this is also a permanent address and save it for the future // CopyMem(hw->mac.perm_addr, hw->mac.addr, ETHER_MAC_ADDR_LEN); DEBUGPRINT(INIT, ("MAC Address = %02x:%02x:%02x:%02x:%02x:%02x\n", AdapterInfo->hw.mac.addr[0], AdapterInfo->hw.mac.addr[1], AdapterInfo->hw.mac.addr[2], AdapterInfo->hw.mac.addr[3], AdapterInfo->hw.mac.addr[4], AdapterInfo->hw.mac.addr[5])); return EFI_SUCCESS; } /** Configures internal interrupt causes on current PF. @param[in] AdapterInfo Pointer to the NIC data structure information which the UNDI driver is layering on @return Interrupt causes are configured for current PF **/ VOID I40eConfigureInterrupts( I40E_DRIVER_DATA *AdapterInfo ) { UINT32 RegVal = 0; // // Associate MSI-x vector 1 to RxQ 0. // Function operated as PMD will not have MSI-x vector enabled in extended // CS or in vector control. This is fine. For PMD the internal intpr logic // needs to be enabled to flush the read at descriptor granularity. // RegVal = rd32(&AdapterInfo->hw, I40E_QINT_RQCTL(0)); RegVal |= I40E_QINT_RQCTL_ITR_INDX_MASK | I40E_QINT_RQCTL_CAUSE_ENA_MASK | (0x1 << I40E_QINT_RQCTL_MSIX_INDX_SHIFT) | I40E_QINT_RQCTL_NEXTQ_INDX_MASK | I40E_QUEUE_TYPE_RX << I40E_QINT_RQCTL_NEXTQ_TYPE_SHIFT; wr32(&AdapterInfo->hw, I40E_QINT_RQCTL(0), RegVal); // // Enable intrpt cause and set to no intrpt moderation // RegVal = I40E_PFINT_DYN_CTLN_INTENA_MASK | I40E_PFINT_DYN_CTLN_CLEARPBA_MASK | I40E_PFINT_DYN_CTLN_ITR_INDX_MASK; wr32(&AdapterInfo->hw, I40E_PFINT_DYN_CTLN(0), RegVal); // // Configure MSI-x vector 1 linked list to point only to RxQ 0 // RegVal = rd32(&AdapterInfo->hw, I40E_PFINT_LNKLSTN(0)); RegVal = (0 << I40E_PFINT_LNKLSTN_FIRSTQ_INDX_SHIFT) | (I40E_QUEUE_TYPE_RX << I40E_PFINT_LNKLSTN_FIRSTQ_TYPE_SHIFT); wr32(&AdapterInfo->hw, I40E_PFINT_LNKLSTN(0), RegVal); // // Disable intrpt automasking for PMD. Since this feature is handled at // device granularity, MP device drivers handling other functions of the // device should not assume automasking to be enabled by default. // RegVal = rd32(&AdapterInfo->hw, I40E_GLINT_CTL); RegVal |= I40E_GLINT_CTL_DIS_AUTOMASK_N_MASK; wr32(&AdapterInfo->hw, I40E_GLINT_CTL, RegVal); } /** Disables internal interrupt causes on current PF. @param[in] AdapterInfo Pointer to the NIC data structure information which the UNDI driver is layering on @return Interrupt causes are disabled for current PF **/ VOID I40eDisableInterrupts( I40E_DRIVER_DATA *AdapterInfo ) { UINT16 queue; // Disable all non-queue interrupt causes wr32(&AdapterInfo->hw, I40E_PFINT_ICR0_ENA, 0); for (queue = 0; queue <= I40E_LAN_QP_INDEX; queue++) { // Disable receive queue interrupt causes wr32(&AdapterInfo->hw, I40E_QINT_RQCTL(queue), 0); // Disable transmit queue interrupt wr32(&AdapterInfo->hw, I40E_QINT_TQCTL(queue), 0); } } EFI_STATUS I40eInitHw( I40E_DRIVER_DATA *AdapterInfo ) { enum i40e_status_code i40eStatus; EFI_STATUS Status; struct i40e_hw *hw; UINT8 AqFailures = 0; UINT16 queue = 0; UINT32 TmpReg0; UINT32 TmpReg1; hw = &AdapterInfo->hw; hw->numPartialInit++; // // Initialize HMC structure for this Lan function. We need 1 Tx and 1 Rx queue. // FCoE parameters are zeroed // #ifdef DIRECT_QUEUE_CTX_PROGRAMMING UNREFERENCED_1PARAMETER(i40eStatus) #else i40eStatus = i40e_init_lan_hmc(hw, I40E_LAN_QP_INDEX+1, I40E_LAN_QP_INDEX+1, 0, 0); if (i40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_init_lan_hmc returned %d\n", i40eStatus)); return EFI_DEVICE_ERROR; } i40eStatus = i40e_configure_lan_hmc(hw, I40E_HMC_MODEL_DIRECT_ONLY); if (i40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_configure_lan_hmc returned %d\n", i40eStatus)); return EFI_DEVICE_ERROR; } #endif // // Enable LFC mode. We only have 16 descriptors in the Rx ring and such configuration // without LFC or PFC enabled cannot handle all boot scenarios due to packet dropping. // Do not enable LFC if Receive Link Flow Control or PFC is already enabled // TmpReg0 = i40eRead32(AdapterInfo, I40E_PRTDCB_MFLCN); TmpReg1 = i40eRead32(AdapterInfo, I40E_PRTMAC_HSEC_CTL_RX_ENABLE_PPP); // // I40E_PRTMAC_HSEC_CTL_RX_ENABLE_PPP contents may be invalid in 10G mode // if (TmpReg1 == 0xDEADBEEF) { TmpReg1 = 0x0; } if (((TmpReg0 & I40E_PRTDCB_MFLCN_RPFCM_MASK) == 0) && ((TmpReg0 & I40E_PRTDCB_MFLCN_RFCE_MASK) == 0) && ((TmpReg1 & I40E_PRTMAC_HSEC_CTL_RX_ENABLE_PPP_HSEC_CTL_RX_ENABLE_PPP_MASK) == 0)) { UINT32 ManagementCtrlReg = 0; // // We will not check the status here. We need to proceed with initialization // even if FC cannot be turned on. // ManagementCtrlReg = rd32(&AdapterInfo->hw, I40E_PRT_MNG_MANC); if ((ManagementCtrlReg & I40E_PRT_MNG_MANC_EN_BMC2NET_MASK) && (ManagementCtrlReg & I40E_PRT_MNG_MANC_RCV_TCO_EN_MASK)) { // // If the port is used for management traffic do not set flow control // HSD7661281 and HSD7660426 // } else { AdapterInfo->hw.fc.requested_mode = I40E_FC_NONE; AdapterInfo->WaitingForLinkUp = IsLinkUp(AdapterInfo); i40e_set_fc(&AdapterInfo->hw, &AqFailures, TRUE); } } Status = I40eSetupPFSwitch(AdapterInfo); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("I40eSetupPFSwitch returned %r\n", Status)); return Status; } i40e_add_filter_to_drop_tx_flow_control_frames(hw, AdapterInfo->vsi.seid); i40eStatus = i40e_aq_set_port_parameters(hw, 0, FALSE, TRUE, FALSE, NULL); if (i40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_set_port_parameters returned %d\n", i40eStatus)); return EFI_DEVICE_ERROR; } for (queue = 0; queue <= I40E_LAN_QP_INDEX; queue++) { Status = I40eSetupTxRxResources(AdapterInfo, queue); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("I40eSetupTxRxResources returned %r\n", Status)); return Status; } Status = I40eConfigureTxRxQueues(AdapterInfo, queue); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("I40eConfigureTxRxQueues returned %r\n", Status)); return Status; } // // Bring up the connection // i40e_up_complete in linux driver // Configure legacy mode interrupts in the HW: i40e_configure_msi_and_legacy // I40eReceiveStart(AdapterInfo, queue); // // Enable interrupts generation // i40e_irq_dynamic_enable_icr0 // } I40eConfigureInterrupts(AdapterInfo); AdapterInfo->vsi.txRxQP = I40E_LAN_QP_INDEX; AdapterInfo->HwInitialized = TRUE; return Status; } PXE_STATCODE I40eInitialize( I40E_DRIVER_DATA *AdapterInfo ) { #ifndef AVOID_HW_REINITIALIZATION EFI_STATUS Status; #endif PXE_STATCODE PxeStatcode; DEBUGPRINT(INIT, ("-->\n")); PxeStatcode = PXE_STATCODE_SUCCESS; // // Do not try to initialize hw again when it is already initialized // if (AdapterInfo->HwInitialized == FALSE) { DEBUGPRINT(INIT, ("Hw is not initialized, calling I40eInitHw\n")); #ifndef AVOID_HW_REINITIALIZATION Status = I40eInitHw(AdapterInfo); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("I40eInitHw returns %r\n", Status)); PxeStatcode = PXE_STATCODE_NOT_STARTED; } #endif } AdapterInfo->DriverBusy = FALSE; return PxeStatcode; } PXE_STATCODE I40eShutdown( I40E_DRIVER_DATA *AdapterInfo ) { PXE_STATCODE PxeStatcode; UINT16 queue; #ifndef AVOID_HW_REINITIALIZATION enum i40e_status_code I40eStatus = I40E_SUCCESS; #endif DEBUGPRINT(INIT, ("-->\n")); #if(0) if (AdapterInfo->hw.bus.func == 1) { DumpInternalFwHwData(AdapterInfo); } #endif AdapterInfo->hw.numShutdown++; if (AdapterInfo->HwInitialized == FALSE) { PxeStatcode = PXE_STATCODE_SUCCESS; return PxeStatcode; } #ifndef AVOID_HW_REINITIALIZATION for (queue = 0; queue <= I40E_LAN_QP_INDEX; queue++) { I40eStatus = I40eReceiveStop(AdapterInfo, queue); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("I40eReceiveStop returned %d\n", I40eStatus)); } I40eStatus = I40eFreeTxRxQueues(AdapterInfo, queue); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("I40eFreeTxRxQueues returned %d\n", I40eStatus)); } I40eStatus = I40eFreeTxRxResources(AdapterInfo, queue); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("I40eFreeTxRxResources returned %d\n", I40eStatus)); } } #ifndef DIRECT_QUEUE_CTX_PROGRAMMING I40eStatus = i40e_shutdown_lan_hmc(&AdapterInfo->hw); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_shutdown_lan_hmc returned %d\n", I40eStatus)); } #endif AdapterInfo->HwInitialized = FALSE; #endif PxeStatcode = PXE_STATCODE_SUCCESS; return PxeStatcode; } PXE_STATCODE I40eReset( I40E_DRIVER_DATA *AdapterInfo, UINT16 OpFlags ) { PXE_STATCODE PxeStatcode; #ifndef AVOID_HW_REINITIALIZATION EFI_STATUS Status; #endif AdapterInfo->hw.numReset++; DEBUGPRINT(INIT, ("-->\n")); // // Do not reinitialize the adapter when it has already been initialized // This saves the time required for initialization // if (AdapterInfo->HwInitialized == FALSE) { #ifndef AVOID_HW_REINITIALIZATION Status = I40eInitHw(AdapterInfo); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("XgbeInitHw returns %r\n", Status)); return PXE_STATCODE_NOT_STARTED; } #endif } else { DEBUGPRINT(I40E, ("Skipping adapter reset\n")); } PxeStatcode = PXE_STATCODE_SUCCESS; return PxeStatcode; } EFI_STATUS I40eDiscoverCapabilities( I40E_DRIVER_DATA *AdapterInfo ) /*++ Routine Description: Read function capabilities using AQ command. Arguments: AdapterInfo - Pointer to adapter structure Returns: EFI_STATUS --*/ { struct i40e_aqc_list_capabilities_element_resp *CapabilitiesBuffer; EFI_STATUS Status; enum i40e_status_code I40eStatus; UINT16 BufferSize; UINT16 BufferSizeNeeded; BufferSize = I40E_NUM_AQ_BUF_SIZE; do { #ifndef INTEL_KDNET Status = gBS->AllocatePool( EfiBootServicesData, BufferSize, (VOID **)&CapabilitiesBuffer ); if (EFI_ERROR(Status)) { return Status; } #else struct i40e_aqc_list_capabilities_element_resp CapabilitiesBufferArray[I40E_CAPABILITIES_ARRAY_ELEMENTS]; CapabilitiesBuffer = CapabilitiesBufferArray; Status = EFI_SUCCESS; #endif I40eStatus = i40e_aq_discover_capabilities( &AdapterInfo->hw, CapabilitiesBuffer, BufferSize, &BufferSizeNeeded, i40e_aqc_opc_list_func_capabilities, NULL ); // // Free memory that was required only internally // in i40e_aq_discover_capabilities // #ifndef INTEL_KDNET gBS->FreePool(CapabilitiesBuffer); #endif if (AdapterInfo->hw.aq.asq_last_status == I40E_AQ_RC_ENOMEM) { // // Buffer passed was to small, use buffer size returned by the function // BufferSize = BufferSizeNeeded; } else if (AdapterInfo->hw.aq.asq_last_status != I40E_AQ_RC_OK) { Status = EFI_DEVICE_ERROR; return Status; } } while (I40eStatus != I40E_SUCCESS); return Status; } EFI_STATUS I40eCheckResetDone( I40E_DRIVER_DATA *AdapterInfo, UINT32 ResetMask ) { EFI_STATUS Status; UINT32 Reg; UINTN i = 0; struct i40e_hw *hw; hw = &AdapterInfo->hw; Status = EFI_SUCCESS; #define I40E_GLNVM_ULD_TIMEOUT 1000000 // // First wait until device becomes active. // while (1) { Reg = rd32(hw, I40E_GLGEN_RSTAT); if ((Reg & I40E_GLGEN_RSTAT_DEVSTATE_MASK) == 0) { break; } DelayInMicroseconds(AdapterInfo, 100); if (i++ > I40E_GLNVM_ULD_TIMEOUT) { DEBUGPRINT(CRITICAL, ("Device activation error\n")); Status = EFI_DEVICE_ERROR; break; } } i = 0; // // Now wait for reset done indication. // Reg = rd32(hw, I40E_GLNVM_ULD); while (1) { Reg = rd32(hw, I40E_GLNVM_ULD); if ((Reg & ResetMask) == ResetMask) { break; } DelayInMicroseconds(AdapterInfo, 100); if (i++ > I40E_GLNVM_ULD_TIMEOUT) { DEBUGPRINT(CRITICAL, ("Timeout waiting for reset done\n")); Status = EFI_DEVICE_ERROR; break; } } return Status; } EFI_STATUS I40eTriggerGlobalReset( I40E_DRIVER_DATA *AdapterInfo ) { UINT32 Reg; struct i40e_hw *Hw; Hw = &AdapterInfo->hw; Reg = 0x1 << I40E_GLGEN_RTRIG_GLOBR_SHIFT; wr32(Hw, I40E_GLGEN_RTRIG, Reg); return EFI_SUCCESS; } EFI_STATUS i40eTmpMfpInitialization( I40E_DRIVER_DATA *AdapterInfo ) { enum i40e_status_code I40eStatus; UINT32 DWords[30]; BOOLEAN ResetNeeded; DWords[0] = 0x80000001; // Port enabled DWords[1] = 0x80000001; // Advertise 10G speed DWords[2] = 0x00000000; // Enable Rx flow control DWords[3] = 0x00000000; // Enable Tx flow control DWords[4] = 0x80000050; // VLAN Tag for DCC DWords[5] = 0x80000001; // Ethernet only DWords[6] = 0x80000000; // UP DWords[7] = 0x12233222; // Lower MAC DW DWords[8] = 0x80005566; // Upper MAC DW DWords[9] = 0x80000060; // Outer vlan tag DWords[10] = 0x8000000A; // Min BW DWords[11] = 0x8000000A; // Max BW DWords[12] = 0x80000001; // Boot enabled DWords[13] = 0x80000001; // PF enabled DWords[14] = 0xC0000000; // SRIOV auto I40eStatus = i40e_aq_alternate_write_indirect(&AdapterInfo->hw, 32, 15, DWords); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_alternate_write_indirect returned %x\n", I40eStatus)); return EFI_DEVICE_ERROR; } DWords[7] = 0x12233333; // Lower MAC DW DWords[8] = 0x80005566; // Upper MAC DW DWords[9] = 0x80000061; // Outer vlan tag I40eStatus = i40e_aq_alternate_write_indirect(&AdapterInfo->hw, 32 + 30, 15, DWords); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_alternate_write_indirect returned %x\n", I40eStatus)); return EFI_DEVICE_ERROR; } DWords[7] = 0x12233444; // Lower MAC DW DWords[8] = 0x80005566; // Upper MAC DW DWords[9] = 0x80000062; // Outer vlan tag DWords[13] = 0x80000000; // PF enabled I40eStatus = i40e_aq_alternate_write_indirect(&AdapterInfo->hw, 32 + 60, 15, DWords); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_alternate_write_indirect returned %x\n", I40eStatus)); return EFI_DEVICE_ERROR; } DWords[7] = 0x12233555; // Lower MAC DW DWords[8] = 0x80005566; // Upper MAC DW DWords[9] = 0x80000063; // Outer vlan tag DWords[13] = 0x80000000; // PF enabled I40eStatus = i40e_aq_alternate_write_indirect(&AdapterInfo->hw, 32 + 90, 15, DWords); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_alternate_write_indirect returned %x\n", I40eStatus)); return EFI_DEVICE_ERROR; } I40eStatus = i40e_aq_alternate_write_done(&AdapterInfo->hw, 0, &ResetNeeded); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_alternate_write_done returned %x\n", I40eStatus)); return EFI_DEVICE_ERROR; } DEBUGPRINT(INIT, ("Done with MFP Init\n")); #ifndef INTEL_KDNET gBS->Stall(1000000); #else KeStallExecutionProcessor(1000000); #endif return EFI_SUCCESS; } BOOLEAN I40eAquireControllerHw( I40E_DRIVER_DATA *AdapterInfo ) /*++ Routine Description: This function checks if any other instance of driver is loaded on this PF by reading PFGEN_DRUN register. If not it writes the bit in the register to let know other components that the PF is in use. Arguments: AdapterInfo - Pointer to adapter structure Returns: TRUE - the PF is free to use for Tx/Rx --*/ { UINT32 RegValue; RegValue = rd32(&AdapterInfo->hw, I40E_PFGEN_DRUN); if (RegValue & I40E_PFGEN_DRUN_DRVUNLD_MASK) { // // bit set means other driver is loaded on this pf // return FALSE; } RegValue |= I40E_PFGEN_DRUN_DRVUNLD_MASK; wr32(&AdapterInfo->hw, I40E_PFGEN_DRUN, RegValue); return TRUE; } VOID I40eReleaseControllerHw( I40E_DRIVER_DATA *AdapterInfo ) /*++ Routine Description: Release this PF by clearing the bit in PFGEN_DRUN register. Arguments: AdapterInfo - Pointer to adapter structure Returns: --*/ { UINT32 RegValue; RegValue = rd32(&AdapterInfo->hw, I40E_PFGEN_DRUN); RegValue &= ~I40E_PFGEN_DRUN_DRVUNLD_MASK; wr32(&AdapterInfo->hw, I40E_PFGEN_DRUN, RegValue); } static inline void i40e_flex_payload_reg_init(struct i40e_hw *hw) { wr32(hw, I40E_GLQF_ORT(18), 0x00000030); wr32(hw, I40E_GLQF_ORT(19), 0x00000030); wr32(hw, I40E_GLQF_ORT(26), 0x0000002B); wr32(hw, I40E_GLQF_ORT(30), 0x0000002B); wr32(hw, I40E_GLQF_ORT(33), 0x000000E0); wr32(hw, I40E_GLQF_ORT(34), 0x000000E3); wr32(hw, I40E_GLQF_ORT(35), 0x000000E6); wr32(hw, I40E_GLQF_ORT(20), 0x00000031); wr32(hw, I40E_GLQF_ORT(23), 0x00000031); wr32(hw, I40E_GLQF_ORT(63), 0x0000002D); /* GLQF_PIT Registers */ wr32(hw, I40E_GLQF_PIT(16), 0x00007480); wr32(hw, I40E_GLQF_PIT(17), 0x00007440); } EFI_STATUS i40eFirstTimeInit( I40E_DRIVER_DATA *AdapterInfo ) /*++ Routine Description: This function is called as early as possible during driver start to ensure the hardware has enough time to autonegotiate when the real SNP device initialize call is made. Arguments: AdapterInfo - Pointer to adapter structure Returns: EFI_STATUS --*/ { PCI_CONFIG_HEADER *PciConfigHeader; enum i40e_status_code I40eStatus; EFI_STATUS Status; struct i40e_hw *hw; hw = &AdapterInfo->hw; hw->numFullInit++; hw->back = AdapterInfo; AdapterInfo->DriverBusy = FALSE; if (rd32(hw, I40E_GLPCI_CAPSUP) & I40E_GLPCI_CAPSUP_ARI_EN_MASK) { DEBUGPRINT(INIT, ("ARI Enabled\n")); AdapterInfo->AriCapabilityEnabled = TRUE; } if (AdapterInfo->AriCapabilityEnabled) { // // Reinterpret PCI Function and Device when ARI is enabled // AdapterInfo->Function = (8 * AdapterInfo->Device) + AdapterInfo->Function; AdapterInfo->Device = 0; } AdapterInfo->MediaStatusChecked = FALSE; AdapterInfo->LastMediaStatus = FALSE; hw->bus.device = (UINT16)AdapterInfo->Device; hw->bus.func = (UINT16)AdapterInfo->Function; PciConfigHeader = (PCI_CONFIG_HEADER *)&AdapterInfo->PciConfig[0]; DEBUGPRINT(INIT, ("PCI Command Register = %X\n", PciConfigHeader->Command)); DEBUGPRINT(INIT, ("PCI Status Register = %X\n", PciConfigHeader->Status)); DEBUGPRINT(INIT, ("PCI VendorID = %X\n", PciConfigHeader->VendorID)); DEBUGPRINT(INIT, ("PCI DeviceID = %X\n", PciConfigHeader->DeviceID)); DEBUGPRINT(INIT, ("PCI SubVendorID = %X\n", PciConfigHeader->SubVendorID)); DEBUGPRINT(INIT, ("PCI SubSystemID = %X\n", PciConfigHeader->SubSystemID)); // DEBUGPRINT (INIT, ("PCI Segment = %X\n", AdapterInfo->Segment)); DEBUGPRINT(INIT, ("PCI Bus = %X\n", AdapterInfo->Bus)); DEBUGPRINT(INIT, ("PCI Device = %X\n", AdapterInfo->Device)); DEBUGPRINT(INIT, ("PCI Function = %X\n", AdapterInfo->Function)); ZeroMem(AdapterInfo->BroadcastNodeAddress, PXE_MAC_LENGTH); SetMem(AdapterInfo->BroadcastNodeAddress, PXE_HWADDR_LEN_ETHER, 0xFF); ZeroMem(&AdapterInfo->vsi.CurrentMcastList, sizeof(AdapterInfo->vsi.CurrentMcastList)); // // Initialize all parameters needed for the shared code // hw->hw_addr = (UINT8*)(UINTN)PciConfigHeader->BaseAddressReg_0; hw->vendor_id = PciConfigHeader->VendorID; hw->device_id = PciConfigHeader->DeviceID; hw->revision_id = (UINT8)PciConfigHeader->RevID; hw->subsystem_vendor_id = PciConfigHeader->SubVendorID; hw->subsystem_device_id = PciConfigHeader->SubSystemID; hw->revision_id = (UINT8)PciConfigHeader->RevID; hw->adapter_stopped = TRUE; #define PCI_CLASS_MASK 0xFF00 #define PCI_SUBCLASS_MASK 0x00FF AdapterInfo->PciClass = (UINT8)((PciConfigHeader->ClassID & PCI_CLASS_MASK) >> 8); AdapterInfo->PciSubClass = (UINT8)(PciConfigHeader->ClassID) & PCI_SUBCLASS_MASK; #ifndef FORTVILLE_A0_SUPPORT if (hw->subsystem_device_id == 0) { // // Read Subsystem ID from PFPCI_SUBSYSID // hw->subsystem_device_id = (UINT16)(rd32(hw, 0x000BE100) & 0xFFFF); } #endif /* FORTVILLE_A0_SUPPORT */ // // Find out if this function is already used by legacy component // AdapterInfo->UNDIEnabled = I40eAquireControllerHw(AdapterInfo); DEBUGPRINT(INIT, ("I40eAquireControllerHw returned %d\n", AdapterInfo->UNDIEnabled)); // // Setup AQ initialization parameters: 32 descriptor rings, 4kB buffers // hw->aq.num_arq_entries = I40E_NUM_AQ_DESCRIPTORS; hw->aq.num_asq_entries = I40E_NUM_AQ_DESCRIPTORS; hw->aq.arq_buf_size = I40E_NUM_AQ_BUF_SIZE; hw->aq.asq_buf_size = I40E_NUM_AQ_BUF_SIZE; I40eStatus = i40e_init_shared_code(hw); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_init_shared_code returned %d\n", I40eStatus)); Status = EFI_DEVICE_ERROR; goto ErrorReleaseController; } i40e_flex_payload_reg_init(hw); #ifdef INTEL_KDNET // // Page aligned device allocated host memory. Allocated as part of // KdGetHardwareContextSize() // AdapterInfo->MemoryPtr = (UINT64)(UINTN)GetDeviceMemory(); #endif if (AdapterInfo->UNDIEnabled) { // // Do not execute PF Reset if we do not initialize UNDI // This would break Tx/Rx of legacy driver working in background // i40e_clear_hw(hw); I40eStatus = i40e_pf_reset(hw); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_pf_reset failed %d\n", I40eStatus)); Status = EFI_DEVICE_ERROR; goto ErrorReleaseController; } } DEBUGPRINT(INIT, ("Initializing PF %d\n", hw->bus.func)); AdapterInfo->FwVersionSupported = TRUE; I40eStatus = i40e_init_adminq(hw); if (I40eStatus == I40E_ERR_FIRMWARE_API_VERSION) { // // Firmware version is newer then expected. Refrain from further initialization // and report error status thru the Driver Health Protocol // AdapterInfo->FwVersionSupported = FALSE; return EFI_UNSUPPORTED; } if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_init_adminq returned %d\n", I40eStatus)); Status = EFI_DEVICE_ERROR; goto ErrorReleaseController; } i40e_clear_pxe_mode(hw); DEBUGPRINT(INIT, ("FW API Info: api_maj_ver: %x, api_min_ver: %x\n", hw->aq.api_maj_ver, hw->aq.api_min_ver)); Status = I40eDiscoverCapabilities(AdapterInfo); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("I40eDiscoverCapabilities(func) returned %r\n", Status)); goto ErrorReleaseController; } // Wait at least 40ms due to I2C access issues #ifndef INTEL_KDNET gBS->Stall(100 * 1000); #else KeStallExecutionProcessor(100 * 1000); #endif AdapterInfo->QualifiedSfpModule = IsQualifiedSfpModule(AdapterInfo); // // Determine existing PF/Port configuration // This is to correctly match partitions, pfs and ports // Status = ReadMfpConfiguration(AdapterInfo); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("ReadMfpConfiguration returned %r\n", Status)); goto ErrorReleaseController; } if (AdapterInfo->UNDIEnabled) { return EFI_SUCCESS; } else { return EFI_ACCESS_DENIED; } ErrorReleaseController: I40eReleaseControllerHw(AdapterInfo); return Status; }; #define VPDADDR 0xE2 #define VPDDATA 0xE4 #define VPD_READ_MASK 0x0000 #define VPD_WRITE_MASK 0x8000 #define VPD_FLAG_MASK 0x8000 #ifdef VPD_CONFIG_ACCESS EFI_STATUS ReadVPDRegion( IN I40E_DRIVER_DATA *AdapterInfo, UINT32 *Buffer, UINTN BufferSize ) { UINTN i; UINT32 Data; UINT16 Address; for (i = 0; i < BufferSize; i++) { Address = VPD_READ_MASK; Address |= sizeof(UINT32) * i; AdapterInfo->PciIo->Pci.Write( AdapterInfo->PciIo, EfiPciIoWidthUint16, VPDADDR, 1, &Address ); do { AdapterInfo->PciIo->Pci.Read( AdapterInfo->PciIo, EfiPciIoWidthUint16, VPDADDR, 1, &Address ); } while ((Address & VPD_FLAG_MASK) != VPD_WRITE_MASK); AdapterInfo->PciIo->Pci.Read( AdapterInfo->PciIo, EfiPciIoWidthUint32, VPDDATA, 1, &Data ); Buffer[i] = Data; } return EFI_SUCCESS; } EFI_STATUS WriteVPDRegion( IN I40E_DRIVER_DATA *AdapterInfo, UINT32 *Buffer, UINTN BufferSize ) { UINTN i; UINT32 Data; UINT16 Address; for (i = 0; i < BufferSize; i++) { Address = VPD_WRITE_MASK; Address |= sizeof(UINT32) * i; Data = Buffer[i]; AdapterInfo->PciIo->Pci.Write( AdapterInfo->PciIo, EfiPciIoWidthUint32, VPDDATA, 1, &Data ); AdapterInfo->PciIo->Pci.Write( AdapterInfo->PciIo, EfiPciIoWidthUint16, VPDADDR, 1, &Address ); do { AdapterInfo->PciIo->Pci.Read( AdapterInfo->PciIo, EfiPciIoWidthUint16, VPDADDR, 1, &Address ); } while ((Address & VPD_FLAG_MASK) != VPD_READ_MASK); } return EFI_SUCCESS; } #endif /* VPD_CONFIG_ACCESS */ #define IOADDR 0x98 #define IODATA 0x9C UINT32 i40eRead32( IN I40E_DRIVER_DATA *AdapterInfo, IN UINT32 Port ) /*++ Routine Description: This function calls the MemIo callback to read a dword from the device's address space Arguments: AdapterInfo - Adapter structure Port - Address to read from Returns: Results - The data read from the port. --*/ { UINT32 Results; #ifdef CONFIG_ACCESS_TO_CSRS { UINT32 ConfigSpaceAddress; ConfigSpaceAddress = Port | 0x80000000; AdapterInfo->PciIo->Pci.Write( AdapterInfo->PciIo, EfiPciIoWidthUint32, IOADDR, 1, &ConfigSpaceAddress ); AdapterInfo->PciIo->Pci.Read( AdapterInfo->PciIo, EfiPciIoWidthUint32, IODATA, 1, &Results ); ConfigSpaceAddress = 0; AdapterInfo->PciIo->Pci.Write( AdapterInfo->PciIo, EfiPciIoWidthUint32, IOADDR, 1, &ConfigSpaceAddress ); } #else MemoryFence(); #ifndef INTEL_KDNET AdapterInfo->PciIo->Mem.Read( AdapterInfo->PciIo, EfiPciIoWidthUint32, 0, Port, 1, (VOID *)(&Results) ); #else ReadDeviceMemory((VOID *)(&Results), 0, Port, sizeof(UINT32), 1); #endif // INTEL_KDNET MemoryFence(); #endif return Results; } VOID i40eWrite32( IN I40E_DRIVER_DATA *AdapterInfo, IN UINT32 Port, IN UINT32 Data ) /*++ Routine Description: This function calls the MemIo callback to write a word from the device's address space Arguments: AdapterInfo - Adapter structure Data - Data to write to Port Port - Address to write to Returns: none --*/ { UINT32 Value; Value = Data; #ifdef CONFIG_ACCESS_TO_CSRS { UINT32 ConfigSpaceAddress; ConfigSpaceAddress = Port | 0x80000000; AdapterInfo->PciIo->Pci.Write( AdapterInfo->PciIo, EfiPciIoWidthUint32, IOADDR, 1, &ConfigSpaceAddress ); AdapterInfo->PciIo->Pci.Write( AdapterInfo->PciIo, EfiPciIoWidthUint32, IODATA, 1, &Value ); ConfigSpaceAddress = 0; AdapterInfo->PciIo->Pci.Write( AdapterInfo->PciIo, EfiPciIoWidthUint32, IOADDR, 1, &ConfigSpaceAddress ); } #else MemoryFence(); #ifndef INTEL_KDNET AdapterInfo->PciIo->Mem.Write( AdapterInfo->PciIo, EfiPciIoWidthUint32, 0, Port, 1, (VOID *)(&Value) ); #else WriteDeviceMemory((VOID *)(&Value), 0, Port, sizeof(UINT32), 1); #endif // INTEL_KDNET MemoryFence(); #endif return; } VOID DelayInMicroseconds( IN I40E_DRIVER_DATA *AdapterInfo, UINT32 MicroSeconds ) /*++ Routine Description: Arguments: AdapterInfo - Pointer to the NIC data structure information which the UNDI driver is layering on.. MicroSeconds - Time to delay in Microseconds. Returns: --*/ { if (AdapterInfo->Delay != NULL) { (*AdapterInfo->Delay) (AdapterInfo->Unique_ID, MicroSeconds); } else { #ifndef INTEL_KDNET gBS->Stall(MicroSeconds); #else KeStallExecutionProcessor(MicroSeconds); #endif } } #ifndef INTEL_KDNET enum i40e_status_code i40eAllocateMem( struct i40e_hw *hw, struct i40e_virt_mem *mem, u32 size ) /*++ Routine Description: OS specific memory alloc for shared code Arguments: hw - pointer to the HW structure mem - ptr to mem struct to fill out size - size of memory requested Returns i40e_status_code --*/ { EFI_STATUS Status; if (NULL == mem) { return I40E_ERR_PARAM; } mem->size = size; mem->va = NULL; Status = gBS->AllocatePool( EfiBootServicesData, size, (VOID **)&mem->va ); if ((NULL != mem->va) && (Status == EFI_SUCCESS)) { //DEBUGPRINT(INIT, ("%a: Requested: %d, Allocated size: %d\n", // __FUNCTION__, size, mem->size)); ZeroMem(mem->va, size); return I40E_SUCCESS; } else { DEBUGPRINT(CRITICAL, ("Error: Requested: %d, Allocated size: %d\n", size, mem->size)); return I40E_ERR_NO_MEMORY; } } #endif enum i40e_status_code i40eFreeMem( struct i40e_hw *hw, struct i40e_virt_mem *mem ) /*++ Routine Description: OS specific memory free for shared code Arguments: hw - pointer to the HW structure mem - ptr to mem struct to fill out Returns i40e_status_code --*/ { if (NULL == mem) { return I40E_ERR_PARAM; } if (NULL == mem->va) { // // Nothing to free // return I40E_SUCCESS; } #ifndef INTEL_KDNET gBS->FreePool((VOID *)mem->va); #else ZeroMem(mem->va, mem->size); mem->va = NULL; mem->size = 0; #endif return I40E_SUCCESS; } #ifndef INTEL_KDNET /** * i40e_init_spinlock_d - OS specific spinlock init for shared code * @sp: pointer to a spinlock declared in driver space **/ void i40eInitSpinLock(struct i40e_spinlock *sp) { #ifndef INTEL_KDNET InitializeSpinLock(&sp->SpinLock); #else InitializeSpinLock(&sp->Lock); #endif } /** * i40e_acquire_spinlock_d - OS specific spinlock acquire for shared code * @sp: pointer to a spinlock declared in driver space **/ void i40eAcquireSpinLock(struct i40e_spinlock *sp) { #ifndef INTEL_KDNET AcquireSpinLock(&sp->SpinLock); #else AcquireSpinLock(&sp->Lock); #endif } /** * i40e_release_spinlock_d - OS specific spinlock release for shared code * @sp: pointer to a spinlock declared in driver space **/ void i40eReleaseSpinLock(struct i40e_spinlock *sp) { #ifndef INTEL_KDNET ReleaseSpinLock(&sp->SpinLock); #else ReleaseSpinLock(&sp->Lock); #endif } /** * i40e_destroy_spinlock_d - OS specific spinlock destroy for shared code * @sp: pointer to a spinlock declared in driver space **/ void i40eDestroySpinLock(struct i40e_spinlock *sp) { return; } #endif enum i40e_status_code i40eAllocateDmaMem( struct i40e_hw *hw, struct i40e_dma_mem *mem, u64 size, u32 alignment) { EFI_STATUS Status; I40E_DRIVER_DATA *AdapterInfo = (I40E_DRIVER_DATA *)hw->back; if (NULL == mem) { return I40E_ERR_PARAM; } mem->size = (UINT32)ALIGN(size, alignment); #ifndef INTEL_KDNET // // Allocate memory for transmit and receive resources. // Status = AdapterInfo->PciIo->AllocateBuffer( AdapterInfo->PciIo, AllocateAnyPages, EfiBootServicesData, UNDI_MEM_PAGES(mem->size), (VOID **)&mem->va, 0 ); mem->pa = (UINT64)mem->va; if ((NULL != mem->va) && (EFI_SUCCESS == Status)) { Status = I40E_SUCCESS; } else { DEBUGPRINT(CRITICAL, ("Error: Requested: %d, Allocated size: %d\n", size, mem->size)); Status = I40E_ERR_NO_MEMORY; } #else AdapterInfo->MemoryPtr = ALIGN(AdapterInfo->MemoryPtr, alignment); mem->va = (VOID *)(UINTN)AdapterInfo->MemoryPtr; i40eMapMem( AdapterInfo, (UINT64)(UINTN)mem->va, mem->size, &mem->pa); AdapterInfo->MemoryPtr += (UINT64)mem->size; Status = EFI_SUCCESS; #endif return Status; } enum i40e_status_code i40eFreeDmaMem( struct i40e_hw *hw, struct i40e_dma_mem *mem ) { #ifndef INTEL_KDNET EFI_STATUS Status; #endif I40E_DRIVER_DATA *AdapterInfo = (I40E_DRIVER_DATA *)hw->back; if (NULL == mem) { return I40E_ERR_PARAM; } #ifndef INTEL_KDNET // // Free memory allocated for transmit and receive resources. // Status = AdapterInfo->PciIo->FreeBuffer( AdapterInfo->PciIo, UNDI_MEM_PAGES(mem->size), (VOID *)mem->va ); if (EFI_ERROR(Status)) { return I40E_ERR_BAD_PTR; } #else i40eUnMapMem( AdapterInfo, (UINT64)mem->va, mem->size, mem->pa ); mem->va = NULL; mem->pa = 0; mem->size = 0; #endif return I40E_SUCCESS; } VOID i40eBlockIt( IN I40E_DRIVER_DATA *AdapterInfo, IN UINT32 flag ) /*++ Routine Description: Arguments: AdapterInfo - Pointer to the NIC data structure information which the UNDI driver is layering on.. flag - Block flag Returns: --*/ { if (AdapterInfo->Block != NULL) { (*AdapterInfo->Block) (AdapterInfo->Unique_ID, flag); } else { #ifndef INTEL_KDNET if (gInitializeLock) { EfiInitializeLock(&gLock, TPL_NOTIFY); gInitializeLock = FALSE; } if (flag != 0) { EfiAcquireLock(&gLock); } else { EfiReleaseLock(&gLock); } #endif } } BOOLEAN IsLinkUp( IN I40E_DRIVER_DATA *AdapterInfo ) { enum i40e_status_code I40eStatus; I40eStatus = i40e_aq_get_link_info(&AdapterInfo->hw, TRUE, NULL, NULL); if (I40eStatus != I40E_SUCCESS) { return FALSE; } return AdapterInfo->hw.phy.link_info.link_info & I40E_AQ_LINK_UP; } EFI_STATUS GetLinkSpeedCapability( IN I40E_DRIVER_DATA *AdapterInfo, OUT UINT8 *AllowedLinkSpeeds ) { enum i40e_status_code I40eStatus; struct i40e_aq_get_phy_abilities_resp PhyAbilites; // // Use AQ command to retrieve phy capabilities // I40eStatus = i40e_aq_get_phy_capabilities( &AdapterInfo->hw, FALSE, FALSE, &PhyAbilites, NULL ); if (I40eStatus != I40E_SUCCESS) { return EFI_DEVICE_ERROR; } *AllowedLinkSpeeds = PhyAbilites.link_speed; return EFI_SUCCESS; } #ifndef INTEL_KDNET UINT8 GetLinkSpeed( IN I40E_DRIVER_DATA *AdapterInfo ) { enum i40e_aq_link_speed Speed; UINT8 LinkSpeed; LinkSpeed = LINK_SPEED_UNKNOWN; Speed = i40e_get_link_speed(&AdapterInfo->hw); switch (Speed) { case I40E_LINK_SPEED_100MB: LinkSpeed = LINK_SPEED_100FULL; break; case I40E_LINK_SPEED_1GB: LinkSpeed = LINK_SPEED_1000FULL; break; case I40E_LINK_SPEED_10GB: LinkSpeed = LINK_SPEED_10000FULL; break; case I40E_LINK_SPEED_20GB: LinkSpeed = LINK_SPEED_20000; break; case I40E_LINK_SPEED_40GB: LinkSpeed = LINK_SPEED_40000; break; default: LinkSpeed = LINK_SPEED_UNKNOWN; } return LinkSpeed; } #endif // !kdnet enum i40e_chip_type I40eGetFortvilleChipType( IN I40E_DRIVER_DATA *AdapterInfo ) { switch (AdapterInfo->hw.device_id) { // // X710 10 Gig devices // case I40E_DEV_ID_SFP_XL710: case I40E_DEV_ID_KX_C: case I40E_DEV_ID_10G_BASE_T: case I40E_DEV_ID_10G_BASE_T4: return I40E_CHIP_X710; break; // // XL710 40 Gig devices // case I40E_DEV_ID_KX_B: case I40E_DEV_ID_QSFP_A: case I40E_DEV_ID_QSFP_B: case I40E_DEV_ID_QSFP_C: return I40E_CHIP_XL710; break; default: return I40E_CHIP_UNKNOWN; } } BOOLEAN GetEEECapability( IN I40E_DRIVER_DATA *AdapterInfo ) { enum i40e_status_code I40eStatus; struct i40e_aq_get_phy_abilities_resp PhyAbilites; // // Use AQ command to retrieve phy capabilities // I40eStatus = i40e_aq_get_phy_capabilities( &AdapterInfo->hw, FALSE, FALSE, &PhyAbilites, NULL ); if (I40eStatus != I40E_SUCCESS) { return FALSE; } if ((PhyAbilites.eee_capability & (I40E_AQ_EEE_100BASE_TX | I40E_AQ_EEE_1000BASE_T | I40E_AQ_EEE_10GBASE_T | I40E_AQ_EEE_1000BASE_KX | I40E_AQ_EEE_10GBASE_KX4 | I40E_AQ_EEE_10GBASE_KR)) != 0) { return TRUE; } return FALSE; } BOOLEAN IsQualifiedSfpModule( IN I40E_DRIVER_DATA *AdapterInfo ) /*++ Routine Description: Check if current SFP module used for this port is qualified module Arguments: AdapterInfo - Pointer to the NIC data structure information which the UNDI driver is layering on.. Returns: FALSE if module is not qualified and module qualification is enabled on the port otherwise TRUE --*/ { enum i40e_status_code I40eStatus; UINTN i = 0; // Workaround: Admin queue command Get Link Status sometimes fails // We need to wait for at least 40 ms before calling Get Link Status again. do { I40eStatus = i40e_aq_get_link_info(&AdapterInfo->hw, TRUE, NULL, NULL); if (I40eStatus == I40E_AQ_RC_EIO) { #ifndef INTEL_KDNET gBS->Stall(50 * 1000); #else KeStallExecutionProcessor(50 * 1000); #endif i++; } else { break; } } while (i < 10); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_get_link_info returned %d\n", I40eStatus)); return FALSE; } if ((AdapterInfo->hw.phy.link_info.link_info & I40E_AQ_LINK_UP)) { DEBUGPRINT(INIT, ("Link is up \n")); // // Link is up. Module is qualified // return TRUE; } else { // // Need to check for qualified module here // if ((AdapterInfo->hw.phy.link_info.link_info & I40E_AQ_MEDIA_AVAILABLE) && (!(AdapterInfo->hw.phy.link_info.an_info & I40E_AQ_QUALIFIED_MODULE))) // // Unqualified module was detected // return FALSE; } // // Link is down // Module is qualified or qualification process is not available // return TRUE; } VOID BlinkLeds( IN I40E_DRIVER_DATA *AdapterInfo, IN UINT32 Time ) { u32 led_status; if (AdapterInfo->hw.device_id == I40E_DEV_ID_10G_BASE_T || AdapterInfo->hw.device_id == I40E_DEV_ID_10G_BASE_T4 ) { i40e_blink_phy_link_led(&AdapterInfo->hw, Time, 500); } else { // // Get current LED state // led_status = i40e_led_get(&AdapterInfo->hw); // // Turn on blinking LEDs and wait required ammount of time // i40e_led_set(&AdapterInfo->hw, 0xF, TRUE); DelayInMicroseconds(AdapterInfo, Time * 1000 * 1000); // // Restore LED state // i40e_led_set(&AdapterInfo->hw, led_status, FALSE); } } EFI_STATUS ReadMfpConfiguration( IN I40E_DRIVER_DATA *AdapterInfo ) /*++ Routine Description: This functions initialize table containing PF numbers for partitions related to this port. Arguments: AdapterInfo - Pointer to the NIC data structure information which the UNDI driver is layering on.. Partition - Partition number (1 based) Returns: PCI function number --*/ { #ifndef INTEL_KDNET UINT32 i; #else UINT8 i; #endif UINT32 PortNumber; UINT8 PartitionCount = 0; UINT32 PortNum; UINT32 FunctionStatus; UINT16 PortNumValues[16]; EFI_STATUS Status; Status = EepromGetMaxPfPerPortNumber( AdapterInfo, &AdapterInfo->PfPerPortMaxNumber ); if (EFI_ERROR(Status)) { DEBUGPRINT(CRITICAL, ("EepromGetMaxPfPerPortNumber returned %r\n", Status)); return Status; } // // Read port number for current PF and save its value // PortNumber = i40eRead32( AdapterInfo, I40E_PFGEN_PORTNUM ); PortNumber &= I40E_PFGEN_PORTNUM_PORT_NUM_MASK; AdapterInfo->PhysicalPortNumber = PortNumber; DEBUGPRINT(INIT, ("PhysicalPortNumber: %d\n", PortNumber)); EepromReadPortnumValues(AdapterInfo, &PortNumValues[0], 16); // // Run through all pfs and collect information on pfs (partitions) associated // to the same port as our base partition // for (i = 0; i < I40E_MAX_PF_NUMBER; i++) { PortNum = PortNumValues[i]; PortNum &= I40E_PFGEN_PORTNUM_PORT_NUM_MASK; if (AdapterInfo->hw.func_caps.valid_functions & (1 << i)) { FunctionStatus = 1; } else { FunctionStatus = 0; } if (PortNum == PortNumber) { // // Partition is connected to the same port as the base partition // Save information on the status of this partition // if (FunctionStatus != 0) { AdapterInfo->PartitionEnabled[PartitionCount] = TRUE; } else { AdapterInfo->PartitionEnabled[PartitionCount] = FALSE; } // // Save PCI function number // AdapterInfo->PartitionPfNumber[PartitionCount] = i; DEBUGPRINT(INIT, ("Partition %d, PF:%d, enabled:%d\n", PartitionCount, AdapterInfo->PartitionPfNumber[PartitionCount], AdapterInfo->PartitionEnabled[PartitionCount])); PartitionCount++; } } return EFI_SUCCESS; } /** Read VSI parameters @param[in] AdapterInfo Pointer to the NIC data structure information which the UNDI driver is layerin @param[out] vsi_ctx resulting VSI context @retval EFI_SUCCESS VSI context successfully read @retval EFI_DEVICE_ERROR VSI context read error **/ EFI_STATUS I40eGetVsiParams( IN I40E_DRIVER_DATA *AdapterInfo, OUT struct i40e_vsi_context *vsi_ctx ) { EFI_STATUS I40eStatus; ZeroMem(vsi_ctx, sizeof(struct i40e_vsi_context)); vsi_ctx->seid = AdapterInfo->main_vsi_seid; vsi_ctx->pf_num = AdapterInfo->hw.pf_id; vsi_ctx->uplink_seid = AdapterInfo->mac_seid; vsi_ctx->vf_num = 0; I40eStatus = i40e_aq_get_vsi_params(&AdapterInfo->hw, vsi_ctx, NULL); if (I40eStatus != I40E_SUCCESS) { DEBUGPRINT(CRITICAL, ("i40e_aq_get_vsi_params returned %d, aq_err %d\n", I40eStatus, AdapterInfo->hw.aq.asq_last_status)); return EFI_DEVICE_ERROR; } return EFI_SUCCESS; }